Skip to content

Using secret collections

Info

Learn the concepts and fundamentals of secret collections in the Explanation page.

A secret collection defines one or more secrets used by your optimization model during execution. You can attach secret collections to an instance configuration, or directly when starting a run.

Included in this how-to guide are instructions on how to interact with secrets collections using methods on the Application class. Go the reference section to see all the available parameters for each method.

Secret values are write-only

Once a secret is set, its value cannot be retrieved. Every secret value is returned masked as ***************. The mask is literal and is always 15 * characters, regardless of the length or content of the secret value, so keep your own copy of any value you may need later.

Create a secrets collection

Use the Application.new_secrets_collection method to create a new secret collection for an application. Pass the secrets keyword argument as a list of Secret objects. Each secret has the following attributes:

  • secret_type: Either SecretType.ENV or SecretType.FILE, which determines how the secret is injected into the runtime.
  • location: Where to place the secret.
    • SecretType.ENV: the environment variable name. E.g.: BURROW_ENTRANCE.
    • SecretType.FILE: the relative path from the execution directory. E.g.: licenses/burrow.entr.
  • value: The secret value as text (limited to 1 KB).
Secret(
    secret_type=SecretType.ENV,
    location="ACME_LICENSE_KEY",
    value="abc123",
)
Secret(
    secret_type=SecretType.FILE,
    location="config/app.conf",
    value="server=prod\nport=8080",
)

Here is an example that uses both secrets defined above to create a secrets collection.

import os

import nextmv
from nextmv import cloud
from nextmv.cloud import Secret, SecretType

client = cloud.Client(api_key=os.getenv("NEXTMV_API_KEY"))
app = cloud.Application.get(client=client, id="whispering-badger")
collection = app.new_secrets_collection(
    secrets=[
        Secret(secret_type=SecretType.ENV, location="ACME_LICENSE_KEY", value="abc123"),
        Secret(secret_type=SecretType.FILE, location="config/app.conf", value="server=prod\nport=8080"),
    ],
)

nextmv.write(collection.to_dict())
uv run main.py
{
  "id": "secrets-16g6w0fl",
  "application_id": "whispering-badger",
  "name": "secrets-16g6w0fl",
  "description": "",
  "created_at": "2026-07-28T19:06:24Z",
  "updated_at": "2026-07-28T19:06:24Z"
}

The call above will create a random ID, and use the same identifier for the collection's name. The name of the collection is used as a human-readable label. You can use the id and/or name keyword arguments to specify a custom ID and name for the collection. For example:

import os

import nextmv
from nextmv import cloud
from nextmv.cloud import Secret, SecretType

client = cloud.Client(api_key=os.getenv("NEXTMV_API_KEY"))
app = cloud.Application.get(client=client, id="whispering-badger")
collection = app.new_secrets_collection(
    secrets=[
        Secret(secret_type=SecretType.ENV, location="ACME_LICENSE_KEY", value="abc123"),
        Secret(secret_type=SecretType.FILE, location="config/app.conf", value="server=prod\nport=8080"),
    ],
    id="fluffy-secrets",
    name="Secrets kept by a fluffy bunny",
)

nextmv.write(collection.to_dict())
uv run main.py
{
  "id": "fluffy-secrets",
  "application_id": "whispering-badger",
  "name": "Secrets kept by a fluffy bunny",
  "description": "",
  "created_at": "2026-07-28T19:06:32Z",
  "updated_at": "2026-07-28T19:06:32Z"
}

Run with a secrets collection

Once a secrets collection has been created you can use it when starting a run in two ways:

  • Use directly when starting a run with the secrets_collection_id attribute of a RunConfiguration object, or
  • Attach it to an instance configuration with the secrets_collection_id attribute of an InstanceConfiguration object and use that instance when starting a run.

Here is an example where the secrets collection is used directly when starting a run.

import os

import nextmv
from nextmv import cloud
from nextmv import RunConfiguration

client = cloud.Client(api_key=os.getenv("NEXTMV_API_KEY"))
app = cloud.Application.get(client=client, id="whispering-badger")
run_result = app.new_run_with_result(
    input={"name": "world", "radius": 6378, "distance": 147.6},
    instance_id="latest",
    configuration=RunConfiguration(secrets_collection_id="fluffy-secrets"),
)

result = run_result.to_dict()
result["output"].pop("assets", None)  # Assets are omitted here for a cleaner display.

nextmv.write(result)
uv run main.py
{
  "description": "",
  "id": "latest-H584_yEvR",
  "metadata": {
    "application_id": "whispering-badger",
    "application_instance_id": "latest",
    "application_version_id": "",
    "created_at": "2026-07-28T19:06:41Z",
    "duration": 7176.0,
    "error": "",
    "execution_class": "6c9500mb870s",
    "execution_duration": 6112.0,
    "format": {
      "input": {
        "type": "json"
      },
      "output": {
        "type": "json"
      }
    },
    "initiated_at": "2026-07-28T19:06:42.275571Z",
    "input_size": 47.0,
    "metrics": {
      "message": "Hello, world",
      "value": 1.23
    },
    "options": {
      "active_options": {
        "details": "true"
      },
      "options_summary": [
        {
          "name": "details",
          "source": "version",
          "value": "true"
        }
      ]
    },
    "output_size": 25099.0,
    "queuing_disabled": false,
    "queuing_priority": 6,
    "run_type": {
      "type": "standard",
      "definition_id": "",
      "reference_id": ""
    },
    "runtime": "python-3_11",
    "secrets_collection_id": "fluffy-secrets",
    "status_v2": "succeeded"
  },
  "name": "",
  "user_email": "sebastian@nextmv.io",
  "console_url": "https://cloud.nextmv.io/app/whispering-badger/run/latest-H584_yEvR?view=details",
  "output": {
    "options": {
      "details": true
    },
    "solution": {
      "message": "Hello, world"
    },
    "metrics": {
      "value": 1.23,
      "message": "Hello, world"
    }
  }
}

Let's say we have an instance identified as production. First, let's update it to use the secrets collection.

import os

import nextmv
from nextmv import cloud

client = cloud.Client(api_key=os.getenv("NEXTMV_API_KEY"))
app = cloud.Application.get(client=client, id="whispering-badger")
instance = app.update_instance(
    id="production",
    configuration=cloud.InstanceConfiguration(secrets_collection_id="fluffy-secrets"),
)

nextmv.write(instance.to_dict())
uv run main.py
{
  "id": "production",
  "application_id": "whispering-badger",
  "version_id": "v0.0.1",
  "name": "The main production instance",
  "description": "",
  "configuration": {
    "execution_class": "6c9500mb870s",
    "secrets_collection_id": "fluffy-secrets",
    "queuing": {
      "priority": 6,
      "disabled": false
    }
  },
  "locked": false,
  "created_at": "2026-07-28T19:06:14.473179Z",
  "updated_at": "2026-07-28T19:07:06.343964Z"
}

We can now start a run using the production instance, and the secrets collection will be used automatically.

import os

import nextmv
from nextmv import cloud

client = cloud.Client(api_key=os.getenv("NEXTMV_API_KEY"))
app = cloud.Application.get(client=client, id="whispering-badger")
run_result = app.new_run_with_result(
    input={"name": "world", "radius": 6378, "distance": 147.6},
    instance_id="production",
)

result = run_result.to_dict()
result["output"].pop("assets", None)  # Assets are omitted here for a cleaner display.

nextmv.write(result)
uv run main.py
{
  "description": "",
  "id": "production-WLUS_sEDR",
  "metadata": {
    "application_id": "whispering-badger",
    "application_instance_id": "production",
    "application_version_id": "v0.0.1",
    "created_at": "2026-07-28T19:07:14Z",
    "duration": 5585.0,
    "error": "",
    "execution_class": "6c9500mb870s",
    "execution_duration": 5175.0,
    "format": {
      "input": {
        "type": "json"
      },
      "output": {
        "type": "json"
      }
    },
    "initiated_at": "2026-07-28T19:07:15.312348Z",
    "input_size": 47.0,
    "metrics": {
      "message": "Hello, world",
      "value": 1.23
    },
    "options": {
      "active_options": {
        "details": "true"
      },
      "options_summary": [
        {
          "name": "details",
          "source": "version",
          "value": "true"
        }
      ]
    },
    "output_size": 25099.0,
    "queuing_disabled": false,
    "queuing_priority": 6,
    "run_type": {
      "type": "standard",
      "definition_id": "",
      "reference_id": ""
    },
    "runtime": "python-3_11",
    "secrets_collection_id": "fluffy-secrets",
    "status_v2": "succeeded"
  },
  "name": "",
  "user_email": "sebastian@nextmv.io",
  "console_url": "https://cloud.nextmv.io/app/whispering-badger/run/production-WLUS_sEDR?view=details",
  "output": {
    "options": {
      "details": true
    },
    "solution": {
      "message": "Hello, world"
    },
    "metrics": {
      "value": 1.23,
      "message": "Hello, world"
    }
  }
}

Get a secrets collection

Use the Application.secrets_collection method to retrieve an existing secrets collection for an application by its ID. The method returns a SecretsCollection object.

Info

Secret values are write-only. Every secret's value is returned as the masked placeholder ***************, while its type and location stay visible.

import os

import nextmv
from nextmv import cloud

client = cloud.Client(api_key=os.getenv("NEXTMV_API_KEY"))
app = cloud.Application.get(client=client, id="whispering-badger")
collection = app.secrets_collection(secrets_collection_id="fluffy-secrets")

nextmv.write(collection.to_dict())
uv run main.py
{
  "id": "fluffy-secrets",
  "application_id": "whispering-badger",
  "name": "Secrets kept by a fluffy bunny",
  "description": "",
  "created_at": "2026-07-28T19:06:32Z",
  "updated_at": "2026-07-28T19:06:32Z",
  "secrets": [
    {
      "type": "env",
      "location": "ACME_LICENSE_KEY",
      "value": "***************"
    },
    {
      "type": "file",
      "location": "config/app.conf",
      "value": "***************"
    }
  ]
}

You can list all collections in the application using the Application.list_secrets_collections method. The value of the secrets is omitted in the output, as this method returns a list of SecretsCollectionSummary objects.

import json
import os

from nextmv import cloud

client = cloud.Client(api_key=os.getenv("NEXTMV_API_KEY"))
app = cloud.Application.get(client=client, id="whispering-badger")
collections = app.list_secrets_collections()

print(json.dumps([collection.to_dict() for collection in collections], indent=2))
uv run main.py
[
  {
    "id": "fluffy-secrets",
    "application_id": "whispering-badger",
    "name": "Secrets kept by a fluffy bunny",
    "description": "",
    "created_at": "2026-07-28T19:06:32Z",
    "updated_at": "2026-07-28T19:06:32Z"
  },
  {
    "id": "secrets-16g6w0fl",
    "application_id": "whispering-badger",
    "name": "secrets-16g6w0fl",
    "description": "",
    "created_at": "2026-07-28T19:06:24Z",
    "updated_at": "2026-07-28T19:06:24Z"
  }
]

Update a secrets collection

Use the Application.update_secrets_collection method to update the name, description, or secrets of a collection. If you don't pass secrets, the existing secrets are left as they are. If you pass secrets, they replace all the existing ones, unless you also set keep_existing=True. You cannot update the ID of a secrets collection.

Upgrade the Python SDK before updating secrets collections

Older versions of the Python SDK use the deprecated full-collection update (PUT) endpoint. If you don't pass secrets, the request is rejected with a 400 Bad Request error. Upgrade to the latest version of the nextmv package.

Here is an example that renames a collection and leaves its secrets as they are.

import os

import nextmv
from nextmv import cloud

client = cloud.Client(api_key=os.getenv("NEXTMV_API_KEY"))
app = cloud.Application.get(client=client, id="whispering-badger")
collection = app.update_secrets_collection(
    secrets_collection_id="fluffy-secrets",
    name="Secrets kept by a very fluffy bunny",
)

nextmv.write(collection.to_dict())
uv run main.py
{
  "id": "fluffy-secrets",
  "application_id": "whispering-badger",
  "name": "Secrets kept by a very fluffy bunny",
  "description": "",
  "created_at": "2026-07-28T19:06:32Z",
  "updated_at": "2026-07-28T19:07:42Z"
}

Here is an example that adds a secret and keeps the rest of the existing secrets.

import os

import nextmv
from nextmv import cloud
from nextmv.cloud import Secret, SecretType

client = cloud.Client(api_key=os.getenv("NEXTMV_API_KEY"))
app = cloud.Application.get(client=client, id="whispering-badger")
collection = app.update_secrets_collection(
    secrets_collection_id="fluffy-secrets",
    secrets=[
        Secret(secret_type=SecretType.ENV, location="CARROT_KEY", value="delicious-vegetables"),
    ],
    keep_existing=True,
)

nextmv.write(collection.to_dict())
uv run main.py
{
  "id": "fluffy-secrets",
  "application_id": "whispering-badger",
  "name": "Secrets kept by a very fluffy bunny",
  "description": "",
  "created_at": "2026-07-28T19:06:32Z",
  "updated_at": "2026-07-28T19:07:50Z"
}

Delete a secrets collection

Warning

Deleting a secrets collection is irreversible. All the secrets in the collection will be permanently deleted.

Delete a secrets collection using the Application.delete_secrets_collection method.

import os

from nextmv import cloud

client = cloud.Client(api_key=os.getenv("NEXTMV_API_KEY"))
app = cloud.Application.get(client=client, id="whispering-badger")
app.delete_secrets_collection(secrets_collection_id="fluffy-secrets")
uv run main.py