Cargo resolves credentials and configuration from your environment — both inside
define* resources (secret() / env()) and for the CLI itself (CARGO_*
variables). This page covers both, and how to deploy the same code to more than
one workspace.
secret() vs env()
Both read a value from process.env at deploy time, but they differ in one
important way — whether the value is tracked in the content hash:
| Helper | Resolved | In the content hash & cargo.state.json? | Use for |
|---|
secret("NAME") | at apply time | No — only a reference is stored | Credentials: API keys, tokens, refresh tokens |
env("NAME") | at load time | Yes — the value enters the spec | Non-secret config you want tracked (region, base URL, …) |
import { defineConnector, secret, env } from "@cargo-ai/cdk";
export const hubspot = defineConnector("hubspot", {
integration: "hubspot",
config: {
method: "privateApp",
accessToken: secret("HUBSPOT_API_KEY"), // never hashed or stored
region: env("HUBSPOT_REGION"), // tracked in the content hash
},
});
Always use secret() — never env() — for credentials. secret() is
excluded from the content hash and from cargo.state.json; env() bakes the
value into the hash, so rotating it reads as drift and the value is written to
state.
How each behaves
secret("NAME") returns a deferred reference (SecretRef). At apply time
the CDK reads process.env.NAME, wraps it in Cargo’s encryption envelope, and
sends it to the API. If the variable is unset at deploy, the deploy throws.
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 cdk deploy --refresh.
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 cdk plan.
See State & drift for how the content hash drives
what a deploy considers changed.
CLI environment variables
The cargo-ai CLI (and therefore cargo-ai cdk) reads three environment
variables. They take precedence over the saved credentials file
(~/.config/cargo-ai/credentials.json):
| Variable | Purpose | Default |
|---|
CARGO_API_TOKEN | API token used to authenticate every request | — (falls back to the saved credentials) |
CARGO_WORKSPACE_UUID | The workspace to operate on | — (falls back to the token’s workspace) |
CARGO_BASE_URL | Cargo API base URL | https://api.getcargo.io |
In CI or an AI coding agent, set these instead of running cargo-ai login:
export CARGO_API_TOKEN=<your-api-token>
export CARGO_WORKSPACE_UUID=<workspace-uuid> # optional; pins the target workspace
cargo-ai whoami # confirms the active token, workspace, and source
whoami reports the source as environment when a CARGO_API_TOKEN is set,
or credentials-file when it’s reading the saved login.
The same CDK code can deploy to multiple workspaces (e.g. staging → production).
Two things change per workspace; the code does not:
- Which workspace you target — set
CARGO_WORKSPACE_UUID (or log in to that
workspace), so cargo-ai cdk deploy resolves the right target.
- The secret values — provide each environment’s
secret() values via its
own environment variables. Because secret() resolves from process.env at
deploy time, pointing the same code at production keys is just a different set
of exported variables.
# Deploy the same repo to the production workspace
export CARGO_WORKSPACE_UUID=<prod-workspace-uuid>
export HUBSPOT_API_KEY=<prod-hubspot-key>
cargo-ai cdk deploy --dir ./prod # keep prod's cargo.state.json separate
Each workspace needs its own cargo.state.json — it records the workspace
uuid, and a deploy refuses to run if the state file belongs to a different
workspace than the one selected (it would orphan resources). Keep a separate
checkout or --dir per workspace so their state files don’t collide.
Secrets are never copied between environments through Cargo: cargo.state.json
records only uuids, hashes, and outputs — never secret values.