Skip to content
1Claw Academy
Curriculum/Working with Secrets4 minIntermediate · Lesson 7 of 10

Environment variables and bundles

Use first-class per-key env vars with production/preview/development scoping, and know when the older env_bundle secret still fits.

Since v0.51, 1Claw stores environment variables as first-class per-key entries on a vault, with Vercel-style environment scoping. Each entry targets a specific environment (production, preview, development, or a custom one you define), and is encrypted individually. This replaced the older pattern of stuffing everything into a single env_bundle secret.

  • Vault-level vars live in the vault's env_vars store, addressed by KEY plus environment.
  • Org-level shared vars can be linked to many vaults at once, so a value like SENTRY_DSN is defined once.
  • Branch overrides let a preview deployment on feat/checkout get a different value than other previews.
  • Precedence is three-tier and always resolves in the same order: shared < vault < branch override.
  • Sensitive write-only mode makes a value non-readable after creation, even for humans.
Tip

Vault-level vars with the same KEY and environment always beat org-level shared vars. That is the whole conflict rule; there is no priority field to reason about.

  1. 1

    Create a vault-scoped variable for production.

    bash
    curl -s -X POST https://api.1claw.co/v1/vaults/$VAULT_ID/env-vars \
      -H "Authorization: Bearer $ONECLAW_TOKEN" \
      -H "Content-Type: application/json" \
      -d '{"key":"DATABASE_URL","value":"postgres://prod-host/app","environment":"production"}'
  2. 2

    Add a different value for preview, so preview deploys never touch the production database.

    bash
    curl -s -X POST https://api.1claw.co/v1/vaults/$VAULT_ID/env-vars \
      -H "Authorization: Bearer $ONECLAW_TOKEN" \
      -H "Content-Type: application/json" \
      -d '{"key":"DATABASE_URL","value":"postgres://preview-host/app","environment":"preview"}'
  3. 3

    Resolve the final KEY=VALUE set for an environment. This is what a deploy or a runtime actually consumes.

    bash
    curl -s "https://api.1claw.co/v1/vaults/$VAULT_ID/env-vars/resolve?environment=preview" \
      -H "Authorization: Bearer $ONECLAW_TOKEN"
  4. 4

    Resolve with a branch override applied. If a branch-scoped value exists for feat/checkout it wins; otherwise you get the plain preview value.

    bash
    curl -s "https://api.1claw.co/v1/vaults/$VAULT_ID/env-vars/resolve?environment=preview&git_branch=feat/checkout" \
      -H "Authorization: Bearer $ONECLAW_TOKEN"
  5. 5

    Do the same from the CLI, which is what you would use in a deploy script. env add takes the key and the environment as positional arguments and prompts for the value.

    bash
    1claw env ls
    1claw env add DATABASE_URL production   # value is prompted
    1claw env environments ls
Concept

An agent connected over MCP calls the resolve_env tool to get the same resolved set, subject to its policies. It never sees values from an environment it is not scoped to.

The older approach, one env_bundle secret holding many KEY=VALUE lines, still works and still has a place. Use it when you want the whole set to move as a single versioned, rotatable unit, or when a consumer only knows how to read one secret. Reach for per-key env vars whenever you need environment scoping, shared org values, or branch overrides.

bash
# Legacy / single-unit pattern, still supported
cat .env | 1claw secret set app/env --type env --stdin
1claw env run -- node server.js
The env_bundle pattern: one secret, one version, injected as a process environment
Watch out

Cloud Runtimes merge resolved env vars into the container environment at start and rebuild. The combined set is capped at 64KB and a change needs a restart to take effect.

Where this goes wrong in practice. Environment variables feel like configuration rather than secrets, and they get handled accordingly.

  • Precedence is guessed. Resolution is shared, then vault, then branch override, and a value that appears not to apply is nearly always being shadowed by a more specific one rather than missing.
  • Sensitive mode is applied after the fact. A write-only value cannot be read back, so marking an existing variable sensitive removes your ability to verify what it holds.
  • The 64KB runtime limit is discovered at deploy. Resolved variables are merged into the container environment at start, and exceeding the cap fails the start rather than truncating quietly.
  • A restart is assumed unnecessary. Injection happens at start and rebuild, so a changed variable does nothing until the runtime is restarted.
  • Branch overrides accumulate. An override created for one feature branch stays after the branch is deleted, and later previews inherit a value nobody remembers setting.
Tip

When a variable resolves to something unexpected, read the resolve endpoint rather than reasoning about it. It returns the final set with precedence already applied, which is faster than guessing which tier won.

Check your understanding

3 questions
1

A vault defines DATABASE_URL for production, and an org-level shared var also defines DATABASE_URL for production. Which value wins?

2

What does the git_branch query parameter add to an env-vars resolve call?

3

When is the older env_bundle secret still the better fit?