Skip to main content
Cargo resolves credentials and configuration from your environment — both inside define* resources (env() / secret() / workspaceEnv()) and for the CLI itself (CARGO_* variables). This page covers both, and how to deploy the same code to more than one workspace.

env(), secret() and workspaceEnv()

Three helpers, each with one behaviour. The first two read your local environment; the third reads nothing locally and points at the workspace’s own environment variables:
connectors/hubspot.ts
Always use secret() or workspaceEnv() — never env() — for credentials. Both are excluded from the content hash and from the deploy state; env() bakes the value into the hash, so rotating it reads as drift and the value is written to state.

How each behaves

  • env("NAME") reads process.env.NAME when the file is imported and inlines the value into the spec. If the variable is unset it returns a visible ${NAME} placeholder so the gap surfaces in cargo-ai project plan.
  • secret("NAME") returns a deferred reference (EncryptionRef). At apply time the project reads process.env.NAME, wraps it in Cargo’s encryption envelope, and sends it to the API. The deploy fails if the variable is not set locally — it does not fall back to the workspace catalog, so a credential’s behaviour never depends on what the deploying machine happened to have exported. Because the reference — not the value — enters the spec, rotating a secret doesn’t register as drift, and a plain deploy won’t push the new value (nothing in the hash changed). Re-apply the resource to roll a rotated secret: make any other change, or run cargo-ai project deploy --refresh.
  • workspaceEnv("KEY") returns a deferred pointer (WorkspaceEnvRef). At apply time it becomes a reference to the workspace variable named KEY; the value stays server-side and never reaches the deploying machine. It is read again on every use, so rotating it needs no re-apply at all — see rotating a value. cargo-ai project plan fails if the workspace holds no such variable.

Where each is accepted

The distinction is load-bearing, and the generated types enforce it: workspaceEnv() is deliberately a type error on a resource’s own env, because those runtimes already receive every workspace variable by inheritance — a pointer there could only restate a variable the resource can read anyway. Config is different: a connector’s credential is a single field, so pointing it at the catalog is the only way to make it rotate without a redeploy. See State & drift for how the content hash drives what a deploy considers changed.

Workspace environment variables

A workspace holds its own set of environment variables, managed under Settings → Environment (or with cargo-ai workspaceManagement envVar). Store a value once there instead of setting it on every worker, app and agent that needs it. Secrets are encrypted at rest and their values are never returned by the API. Every worker, app and agent in the workspace picks them up automatically — you do not declare them on the resource: An app bundle is served to the browser, which is why a secret is never put in one. Give an app its public configuration under a VITE_ key and keep the credential it must not expose on a worker instead. A variable set on the resource itself wins over the workspace variable with the same key, so a single worker can override one value without a copy of the rest. That is also what secret("NAME") does at deploy time: the value your shell exports is sent as that resource’s own value for this apply, and the catalog is left untouched.

Rotating a value

A resource never holds a copy of a workspace variable — it inherits it, or holds a workspaceEnv() pointer, resolved each time the value is needed. Rotating the value under Settings → Environment therefore reaches everything that reads it, with no redeploy. When it takes effect depends only on when the resource next reads it: Workers and apps bake their environment into a build artifact, so those two need a redeploy. Nothing else does.
Deleting a variable that a connector still points at breaks it at the next run, not at the next deploy. The failure names the missing variable, but check what references a value before removing it.
A workspaceEnv() pointer is checked at plan time, so a cargo-ai project deploy naming a variable the workspace does not hold fails before it creates anything. That check needs the API; an offline plan skips it.
The one exception is an integration that mints its own credentials during authentication — an OAuth flow that exchanges what you submit for its own tokens, as HubSpot and Slack do. What ends up stored is the provider’s token rather than your variable, so there is nothing left to keep live and rotating the variable has no effect. Reconnect the connector instead.

CLI environment variables

The cargo-ai CLI (and therefore cargo-ai project) reads three environment variables. They take precedence over the saved credentials file (~/.config/cargo-ai/credentials.json): In CI or an AI coding agent, set these instead of running cargo-ai login:
whoami reports the source as environment when a CARGO_API_TOKEN is set, or credentials-file when it’s reading the saved login.

Promoting code to a second workspace

The same project code can deploy to multiple workspaces (e.g. staging → production). Two things change per workspace; the code does not:
  1. Which workspace you target — set CARGO_WORKSPACE_UUID (or log in to that workspace), so cargo-ai project deploy resolves the right target.
  2. The secret values — either export each environment’s secret() values before deploying, or write the code with workspaceEnv("KEY") and store the value once in each workspace’s environment variables. The second is usually what you want for promotion: each workspace holds its own value under the same key, so pointing the same code at production is nothing beyond selecting the workspace.
Each workspace needs its own deploy state — a state records the workspace uuid, and a deploy refuses to run if it belongs to a different workspace than the one selected (it would orphan resources). Keep a separate checkout or --dir per workspace, and run cargo-ai project state create in the second one so the two have different pointers.
Secrets are never copied between environments through Cargo: deploy state records only uuids, hashes, and outputs — never secret values.