# Variables

Set environment variables and sealed secrets at group, workspace, device, or agent scope, and see which one an agent's session actually gets.

Source: https://codeherder.com/docs/variables/

Set environment variables and sealed secrets at group, workspace, device, or agent scope, and see which one an agent's session actually gets.

A **variable** is a key/value pair CodeHerder injects into an agent’s process environment when a session starts. You set one at whichever scope makes sense — a group, a workspace, a device, or a single agent — and every session that spawns underneath it picks it up automatically, no launch config edit required.

A variable can hold a plain value or a **secret** one. Either way it’s the same kind of record; secret just means CodeHerder never shows the value back to anyone once it’s saved.

## The four scopes, and which one wins

You can set a variable at:

- **Group** — reaches every workspace nested underneath it.
- **Workspace** — reaches every device and agent that runs tasks for that workspace.
- **Device** — reaches every agent that runs on that device.
- **Agent** — reaches only that agent’s own sessions.

If more than one scope sets the same key, CodeHerder resolves a single winner per session: an **agent** -scoped value wins over a **device** -scoped value, which wins over the **workspace** chain (checking the workspace itself first, then its parent group, and so on up the tree). That’s normal, not a conflict to avoid — set a sensible default at the workspace or group, then override it lower down for the one device or agent that needs something different.

One thing outranks all of it: if the agent’s own launch config already sets that key directly (its own **Env** field), that value always wins, no matter what any variable says. Variables are for values you want to manage centrally without touching every launch config; a value hard-coded in a launch config still takes priority over anything set here.

## Setting one from the web app

Open **Settings → Variables**. Use the **Scope** filter to switch between Workspace, Device, and Agent — picking Device or Agent shows a second picker to choose which one. Check **This scope only** to see just the rows set directly there, without anything inherited from above.

Click **New variable** and fill in:

- **Key** — uppercase letters, digits, and underscores, starting with a letter or underscore (for example `DEPLOY_ENV` or `MAX_RETRIES`).
- **Value** — the value CodeHerder should inject.
- **Secret** — turn this on to store the value sealed. See [Secret variables](https://codeherder.com/docs/variables/#secret-variables) below.

A row inherited from a group or a parent workspace shows an **Inherited** badge — hover it to see which scope it came from. Click **Override here** on that row to set your own value at the current scope instead — the override wins from then on, per the ranking above. Anyone in the workspace can see the list; changing it needs a workspace owner or admin — or, for a device, that device’s own owner. Without one of those roles, the page opens but every write control is hidden.

## Setting one from the CLI

`ch variable` (short form: `ch var`) is the CLI equivalent:

```
ch variable list                                  # the effective chain for your current workspace
ch variable list --scope device mydevice          # a device's own rows
ch variable show DEPLOY_ENV                       # one key, at the default (workspace) scope
```

`--scope` accepts `group`, `workspace`, `device`, or `agent`, and defaults to workspace. `--device <ref>` and `--agent <ref>` widen a group or workspace listing to also show that device’s or agent’s own rows layered on top — handy for checking exactly what one agent’s next session will see. Add `--strict` to a workspace or group listing to show only the rows set directly there, with nothing inherited.

Setting a value always goes through a file or standard input, never a plain argument — anything typed directly on the command line can end up in your shell history or in a process list other users on the same machine can see:

```
ch variable set DEPLOY_ENV --from-file value.txt
printf %s "staging" | ch variable set DEPLOY_ENV --from-file -
```

Both forms work the same way; use whichever is easier to script. `ch variable unset <KEY>` removes the row you set at that scope (both `set` / `unset` also accept `edit` / `delete` as aliases).

## Secret variables

Turn on **Secret** — or add `--secret` on the CLI — and CodeHerder seals the value instead of storing it as plain text. From that point, nobody can read it back: the web app shows a **Secret** badge and dots instead of a value, and `ch variable show` prints `<secret>`. If you need to change it, you write a new value; there’s no “reveal” anywhere.

```
printf %s "sk-live-…" | ch variable set STRIPE_KEY --from-file - --secret
```

Whether a variable is secret is fixed the moment you create it — you can’t flip an existing plain variable to secret or back. To change your mind, delete the key and create it again with the flag set the way you want.

A secret variable only reaches a session if the **device it’s about to run on has been told to expect it**. This is the same acknowledgement a device owner gives for a [device-side secret reference](https://codeherder.com/docs/device-secrets/) — set it once with:

```
ch device secrets set <deviceId> --secret STRIPE_KEY
```

This replaces the device’s whole acknowledged list, so include every key you want it to keep, not just the new one — see [Secrets on a device](https://codeherder.com/docs/device-secrets/) for the full command and how to check the current list. Until the device owner acknowledges a secret variable’s key, a session that would need it refuses to start rather than run without it.

## What’s refused, and why

CodeHerder turns down a handful of key/value combinations before they ever get saved:

- A key must be uppercase letters, digits, and underscores, and start with a letter or underscore.
- A key starting with `CH_` is reserved for CodeHerder’s own use.
- A key whose name reads like a credential must be saved with Secret turned on, whatever its value — for example anything ending in `_TOKEN`, `_KEY`, `_SECRET`, `_PASSWORD`, or `_CREDENTIALS`, or a well-known name like `DATABASE_URL` or `GITHUB_TOKEN`. This is keyed off the name, not the value: `DEPLOY_TOKEN` set to the harmless value `production` is still refused as plain text, since the point is to protect the class of key, not to guess whether any one value looks sensitive.
- Separately, a value that carries an inline credential in a connection string — `scheme://user:password@host` — must be Secret too, regardless of what the key is called.
- A handful of keys that could redirect what a session runs or which server it talks to are refused at every scope, no exceptions — for example, keys that control which executable a shell finds or which libraries a process loads.
- A value in the form `@secret:<name>` is refused here — that syntax belongs in a launch config’s own Env field, where it references a [device-side secret](https://codeherder.com/docs/device-secrets/) directly. Use the Secret flag on this store instead.

If a save is refused, the error names which rule it hit — adjust the key or value and try again.

## Limits

A single scope — one group, one workspace, one device, or one agent — can hold up to 100 variables, and a single value can be up to 4096 bytes.

## Who can manage variables

Anyone in the workspace can see the workspace’s or group’s effective chain, including a view narrowed onto one device’s or one agent’s rows — useful for checking exactly what a session will get. Looking at a device’s or an agent’s own rows on their own, separately from that chain, needs the same role that can change them there.

Changing a group, workspace, or agent variable needs a workspace owner or admin. Changing a device’s own variables needs that device’s owner or a workspace owner. An agent can never write its own variables through its session credential — only a signed-in human can.

## How this fits with secrets and credential refs

Variables are the current, one-stop way to hand an agent a value at runtime, whether it’s a plain setting or something sensitive. Two older mechanisms still work and reach the same place a different way:

- **[Secrets](https://codeherder.com/docs/secrets/)** covers a workspace secret wired to a launch config through a Credential ref — the two-step version of a secret variable. If you have existing secrets set up this way, they keep working; a new one is easier to manage as a secret variable instead.
- **[Secrets on a device](https://codeherder.com/docs/device-secrets/)** covers `@secret:<name>` references in a launch config’s own Env field, resolved from a device’s local secret store rather than from CodeHerder. Reach for that when you specifically don’t want CodeHerder to hold the value at all.

## Related guides

- [Secrets](https://codeherder.com/docs/secrets/) — the workspace-secret-plus-Credential-ref path, and what still uses it
- [Secrets on a device](https://codeherder.com/docs/device-secrets/) — device-side references and the acknowledgement they require
- [Agents and the CLI](https://codeherder.com/docs/agents-and-cli/) — launch configs, where a config’s own Env values come from
- [Why isn’t my task moving?](https://codeherder.com/docs/task-not-moving/) — diagnosing a session that refuses to start
