> ## Documentation Index
> Fetch the complete documentation index at: https://docs.getcargo.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Secrets & environments

> How secret() and env() differ, the CARGO_* environment variables the CLI reads, and how to promote the same CDK code to a second workspace.

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, …) |

```ts connectors/hubspot.ts theme={null}
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
  },
});
```

<Warning>
  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.
</Warning>

### 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](/deploy/state-and-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`:

```bash theme={null}
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.

## Promoting code to a second workspace

The same CDK 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 cdk deploy` resolves the right target.
2. **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.

```bash theme={null}
# 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
```

<Warning>
  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.
</Warning>

Secrets are never copied between environments through Cargo: `cargo.state.json`
records only uuids, hashes, and outputs — [never secret values](/deploy/state-and-drift).
