---
sidebar_position: 4
title: Per-agent env vars
---

# Per-agent env vars

Each deployed agent has its own encrypted env-var bag. The cloud runtime injects those vars into the Fargate task that runs the agent — they show up in `process.env` like any other env var.

Use this for credentials that are **specific to one agent** — different `ANTHROPIC_API_KEY` per agent, an agent-only `DATABASE_URL`, an external webhook secret. Project-wide secrets stay on the project record (set in dashboard); agent env wins on conflict.

## The fast path: `--env` at deploy

Most users want to ship a `.env` alongside the agent:

```bash
zibby agent deploy my-agent --env .env
```

The CLI deploys the agent as usual, then syncs the `.env` into per-agent env vars. You'll see:

```
✔ Deployed my-agent (v1)
✔ Bundle ready (78s)
✔ Synced 4 env vars from .env
```

Multiple files merge with later wins (mirrors how dotenv libraries usually layer):

```bash
zibby agent deploy my-agent --env .env --env .env.prod
```

## Manage after deploy: `zibby agent env`

Four verbs, all keyed by the agent UUID (from `zibby agent list` or `.zibby-deploy.json`):

```bash
zibby agent env list <uuid>                       # show key names (no values)
zibby agent env set <uuid> ANTHROPIC_API_KEY=sk-…  # add or rotate one
zibby agent env unset <uuid> OLD_KEY               # remove one
zibby agent env push <uuid> --file .env [--file .env.prod]  # bulk replace
```

`set` is for surgical updates — leaves every other key alone. `push` is the deploy-flow `--env` exposed as a standalone command for when you want to update env without redeploying the agent itself.

`list` only returns key names, never values. Once you set a value, the only place it surfaces is inside the running container.

### Examples

Rotate one key:
```bash
zibby agent env set 1a255ded-9f57-44ad-81cf-70726b13d653 ANTHROPIC_API_KEY=sk-ant-rotated
```

Wipe all env on an agent (push an empty file):
```bash
zibby agent env push <uuid> --file /dev/null
```

CI rotation (GitHub Actions):
```yaml
- run: |
    echo "ANTHROPIC_API_KEY=$ANTHROPIC_KEY" > .env.cd
    npx @zibby/cli agent env push $WORKFLOW_UUID --file .env.cd
  env:
    ANTHROPIC_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
    WORKFLOW_UUID: ${{ vars.WORKFLOW_UUID }}
    ZIBBY_API_KEY: ${{ secrets.ZIBBY_API_KEY }}
```

## Resolution order at trigger time

When a Fargate task starts, env vars come from three layers (later wins):

```
built-in container vars  →  project secrets (DDB)  →  agent env (DDB)
```

So if both your project record and your agent env define `ANTHROPIC_API_KEY`, the agent's value is what `process.env.ANTHROPIC_API_KEY` returns inside the run.

## Validation

| Field | Rule |
|---|---|
| Key name | Must match `^[A-Z_][A-Z0-9_]*$` (uppercase letters, digits, underscores; can't start with a digit). Bash-compatible. |
| Value | Any string. Empty string is allowed. |
| `.env` files | Standard dotenv syntax — comments, quoted values, blank lines, `KEY=value` per line. |

The CLI validates locally before the API round-trip, so `zibby agent env set <uuid> lowercase=v` fails fast with a readable message instead of a 400.

## Use case: multi-vendor agents

Different nodes in the same agent may want to call different model vendors:

```js
// graph.mjs
graph
  .addNode('plan',      { prompt, outputSchema: Plan,   agent: 'claude' })  // uses ANTHROPIC_API_KEY
  .addNode('implement', { prompt, outputSchema: Diff,   agent: 'cursor' })  // uses CURSOR_API_KEY
  .addNode('verify',    { prompt, outputSchema: Result, agent: 'codex'  }); // uses OPENAI_API_KEY
```

Drop the three keys into `.env`, deploy:

```bash
cat > .env <<EOF
ANTHROPIC_API_KEY=sk-ant-...
CURSOR_API_KEY=key_...
OPENAI_API_KEY=sk-...
EOF

zibby agent deploy multi-agent --env .env
```

Each agent reads its own env var when invoked. No prompt-stuffing keys, no per-node config.

## Security model

- **Encryption**: KMS envelope encryption with per-account keys. The plaintext never lands in CloudWatch, DDB, or Lambda logs.
- **Access control**: every env command needs project-level access on the agent's project — checked on every request.
- **Audit**: KMS logs every decrypt operation. Cross-reference with `WORKFLOW_JOB_ID` to see exactly which run pulled which secret.
- **Rotation**: `zibby agent env set` with a new value — the next triggered run picks it up. Currently in-flight runs keep using the value they decrypted at start time.

## What this is *not*

- **Not for the project's primary credentials** — those live on the project record (dashboard → Project → Secrets). Use agent env for *agent-specific* overrides.
- **Not a values-readable store** — `list` returns key names only. If you need to retrieve the value, you'll have to push it again. This is intentional.
- **Not 1Password / Vault** — no rotation policies, no version history. Plain CRUD.

## HTTP API (advanced / scripting)

If you can't use the CLI (e.g. inline server-side trigger, similar to `triggering.md`'s webhook section), the routes are exposed directly:

```
GET    /workflows/{uuid}/env
PUT    /workflows/{uuid}/env       body: {"env": {"KEY": "value", ...}}
PATCH  /workflows/{uuid}/env/{key} body: {"value": "..."}
DELETE /workflows/{uuid}/env/{key}
```

Auth is `Bearer` (session JWT or `ZIBBY_API_KEY`). Host is `https://api-prod.zibby.app`. There is no `/v1` prefix.

For everything else, use the CLI — it's the supported public surface and handles auth + UUID resolution + error messages for you.
