Skip to main content

Secrets and environment variables

Most agents need two kinds of settings: plain configuration, like a log level or a base URL, and credentials, like an API key. The platform stores them separately on purpose. Env vars you can read back at any time. Secrets you can never read back — only their names. This page covers the per-agent version of both, which is the quick way to configure one agent.

Secrets vs env vars

SecretsEnv vars
Read back?No — key names only, values never returnedYes — names and values
ForAPI keys, tokens, passwordsNon-sensitive config
Reaches the agent asEnvironment variablesEnvironment variables
Change behaviorRolls a new revisionRolls a new revision

Both arrive in your agent's process as ordinary environment variables, which you read in Python with os.environ["MY_KEY"]. The only difference is what the platform will ever show back to you.

Every route on this page is a management route. Sign in first, and note that only the agent's owner or a project admin can call them. Add ?project=<slug> when the same agent name exists in more than one project.

Sharing a secret across agents?

Everything below stores a value on exactly one agent, with no version history. If you need a secret that several agents share, that keeps past versions, and whose reads are recorded, use the project secrets manager and attach the secret to your agents — see use secrets in workloads.

Key naming rule

Every secret and env key must be a valid environment variable name. The rule is ^[A-Za-z_][A-Za-z0-9_]*$: letters, digits, and underscores only, and it may not start with a digit. So DEMO_TOKEN is fine and my-key is not. Anything else is rejected with 400, and the two routes word the rejection differently.

The secrets routes (PUT and PATCH /v1/agents/{name}/secrets) answer:

invalid secret key (must be a valid environment variable name): my-key

The env route (PATCH /v1/agents/{name}/env) answers:

invalid env var name: my-key

Manage secrets

The three verbs behind every tab below:

MethodPathBehavior
GET/v1/agents/{name}/secretsList key names (never values)
PUT/v1/agents/{name}/secretsReplace the whole set with a flat map
PATCH/v1/agents/{name}/secretsMerge: set some keys, remove others

Set a secret

platformctl secrets set research-buddy DEMO_TOKEN=abc123

You should see:

set 1 secret(s) for research-buddy

This merges — it never touches keys you didn't name. The CLI has no secrets unset; use the curl tab's PATCH with remove for that.

List the key names

Values are never returned — not by any of these, and not by the API behind them. This answers "is MODEL_API_KEY set?" without anything being able to read what it is set to.

platformctl agents secrets get research-buddy

Note the different command group: reads live under platformctl agents secrets, while the write is platformctl secrets set on the service that holds the value.

Manage env vars

MethodPathBehavior
GET/v1/agents/{name}/envNames and values
PATCH/v1/agents/{name}/envMerge with {"set": {...}, "remove": [...]}
platformctl agents env set research-buddy LOG_LEVEL=debug
platformctl agents env get research-buddy

Remove one with --remove:

platformctl agents env set research-buddy --remove LOG_LEVEL

Every change rolls a new revision

A revision is an immutable snapshot, so a running revision never changes. A secret or env change therefore edits nothing in place. It creates a brand-new revision with the new values baked in, and traffic moves there once that revision is ready. Two things follow:

  • Changes are not instant. The new revision has to start before any request sees the new values.
  • A change behaves like a deploy. It shows up in the revisions list, and you can roll traffic back to the previous one.

Your model key

The platform does not supply a model credential. Agents call inference with your own key from Crusoe Intelligence Foundry — your billing, your quota, your rate limits. There is no platform key to inherit and none to fall back to.

Set it once for the project, and every agent in that project uses it:

curl -X PUT "$CAI_API/v1/projects/$PROJECT/inference" \
-H "Authorization: Bearer $CAI_TOKEN" \
-H 'Content-Type: application/json' \
-d '{"api_key":"<your Foundry key>"}'

In the console: Project Settings, which holds both of your project's own credentials — this Foundry key for inference, and your Crusoe Cloud key for the container registry and object storage. Until a key is mapped, deploying an agent that calls a model is refused with a message saying exactly this — it fails at deploy time rather than at the first invoke, and nothing you uploaded is touched. Functions and containers are unaffected; they never call the model.

To give one agent a different key — a separate bill, a separate quota — set MODEL_API_KEY as a secret on that agent:

platformctl secrets set research-buddy MODEL_API_KEY=sk-your-own-key

That overrides the project's key for that one agent. Clearing it (or setting it blank) goes back to inheriting the project key. To check which key is in force without revealing it, call the agent's GET /debug/config endpoint. It reports model_key_present: true/false and the resolved model name, never the key itself.

What the platform injects

Your agent starts with a set of variables the platform writes itself, as explicit container environment variables — which beat anything you store in the per-agent Secret. These are the ones your own code is most likely to read:

VariableValue in a deployed agent
CAI_API_URLThe platform API at its internal address, not the public one
CAI_PUBSUB_URLThe Pub/Sub REST API, at its internal address
CAI_VECTORDB_URLThe Vectors REST API, at its internal address
EMBED_BASE_URL, EMBED_MODELThe embedding endpoint (already ending in /v1), and the project's embedding model
CAI_PROJECT_ID, PROJECT_SHORTWhich project this agent belongs to
MCP_SERVERSThe project's ready MCP servers, each with its name, internal address and own bearer token
CRUSOE_MEMORYSTORE_ADDRThe agent's Memory Store address, where sessions are kept
CRUSOE_VECTORDB_URL, CRUSOE_VECTORDB_API_KEYThe agent's VectorDB, where the memory bank lives
SANDBOX_URLThe code sandbox that run_python calls
AGENT_NAME, AGENT_IMAGEThe agent's own identity and image
MODEL_BASE_URL, CHAT_MODEL, EMBED_MODEL, EMBED_BASE_URLModel routing

Those addresses are internal on purpose: they resolve only from inside the platform, and they are the addresses the project's network policy admits. Read each one from its variable rather than hard-coding it — the value is ours to change, the variable name is not. None of them reach a sandboxed tool — see tools.

Older names — VALKEY_ADDR, QDRANT_URL, QDRANT_API_KEY and their username/password companions — are still set alongside the CRUSOE_-prefixed ones so that agents deployed before the rename keep working. Runtime code reads the CRUSOE_ name first and falls back to the legacy one. Write new code against the CRUSOE_ names; treat the legacy aliases as deprecated.

Four of them are yours to override

MODEL_BASE_URL, CHAT_MODEL, EMBED_MODEL and EMBED_BASE_URL are deliberately overridable as plain env vars, so you can pin a model other than the project default or point an agent at your own inference endpoint:

platformctl agents env set research-buddy CHAT_MODEL=nvidia/NVIDIA-Nemotron-3-Super-120B-A12B

They are still refused as secret bindings — a hidden value that silently repoints inference is a different thing from a visible one you typed — which is why they also appear on the reserved list elsewhere.

Every other platform name is refused, with the reason

PATCH /v1/agents/{name}/env answers 400 and names each key and the rule it broke — for remove as well as set, because removing one cannot unset the platform's value either. MODEL_API_KEY is the one supported override, and it goes in a Secret, not in env.

The full list is kept in step with the API by a test, so read it there rather than here: overriding the platform's own variables. In shape it is two groups — credentials the platform is accountable for, and the wiring and identity that say where your agent's services are and which tenant it is.

Two things that reference page does not carry:

  • A second set of names cannot be bound from a Secret at all: TOOL_SANDBOX, PATH, HOME, LD_PRELOAD, PYTHONPATH, BAO_ADDR, BAO_TOKEN, NATS_URL, NATS_PASSWORD, KUBERNETES_SERVICE_HOST and KUBERNETES_SERVICE_PORT. A bind attempt is refused with <NAME> is reserved by the platform and cannot be bound.
  • The per-agent secrets routes (PUT and PATCH /v1/agents/{name}/secrets) still validate only the name format. A platform-owned name stored there is accepted, listed by GET /secrets, and then silently shadowed at deploy by the platform's own container env. Keep your own names in secrets — with the one deliberate exception of TOOL_SANDBOX=false, which the env route refuses and this route is the supported way to set.

Summary

QuestionAnswer
Can I read a secret value back?No. Names only, by design
Can I read env values back?Yes
Key format^[A-Za-z_][A-Za-z0-9_]*$
PUT vs PATCHPUT replaces everything; PATCH merges
When do changes take effect?When the new revision starts serving
Bring my own model keySecret MODEL_API_KEY on the agent
Turn off tool sandboxingplatformctl secrets set <agent> TOOL_SANDBOX=false — the env route refuses that name
Shared/versioned/audited secretsSecrets manager

Next steps