Secrets on a device
Reference a credential that lives only on a device's own secret store, and get the device owner's acknowledgement it needs before an agent can run.
A device-side secret reference is a second way to give an agent a credential, alongside the workspace Secrets covered elsewhere. Instead of storing the value in CodeHerder, you leave it on the device itself and put only a reference to it in the agent’s launch config.
When to use this instead of a workspace secret
| Workspace secret + Credential ref | Device-side reference | |
|---|---|---|
| Where the value lives | Encrypted in CodeHerder | Only on the device, in its own secret store |
| Setup | Once, works on every device the agent runs on | Once per device |
| Best for | A credential you’re fine handing to CodeHerder | A credential you don’t want CodeHerder to hold at all |
See Secrets for the workspace-secret path. The rest of this page covers the device-side one.
How it works
In an agent’s launch config, an environment variable can hold a value of the form @secret:<key> instead of a literal value — for example, MY_SERVICE_TOKEN=@secret:my_service. CodeHerder stores that string as-is; it is a reference, not a credential. The device resolves it to the real value only at the moment a session starts, reading it from its own secret store. The value itself never reaches CodeHerder — not in the config, not in logs, not anywhere.
Because of this, the reference must live in the agent’s top launch config — the first one in its list (see Running more than one launch config in Agents and the CLI). CodeHerder builds the list of keys a device owner needs to acknowledge from that top config only, so a reference added to a lower config is never offered for acknowledgement and its session won’t start.
Step 1 — put the value on the device
Run this on the device itself, not from your own machine:
macOS:
security add-generic-password -U -s codeherder-device -a my_service -w
Leaving off a trailing value after -w makes security prompt for it (twice, to confirm) instead of taking it as an argument, so it never lands in your shell history. -U lets you re-run the command later to update the value.
Linux:
printf %s "$TOKEN" | secret-tool store --label codeherder service codeherder-device key my_service
Headless devices or CI: set an environment variable on the device server’s own process — CH_SECRET_<KEY> with the key upper-cased, for example CH_SECRET_MY_SERVICE. CodeHerder checks this before the operating system’s secret store. This is the simplest path on a machine with no interactive keychain, at the cost of the value sitting in the device server’s own environment instead of a dedicated secret store. Use a key made of letters, digits, and underscores so its upper-cased form is a valid environment variable name.
Device-side references only resolve on macOS and Linux. On any other platform, a session that needs one fails immediately.
Step 2 — reference it in the launch config
Web app: open the agent’s detail page, find the Agent templates panel, and edit the top config’s Env field — one KEY=value per line, using @secret:<key> as the value:
MY_SERVICE_TOKEN=@secret:my_service
CLI:
ch agent config set <agentId> --harness claude --env MY_SERVICE_TOKEN=@secret:my_service
--env is repeatable if the config needs more than one reference. config set always requires --harness (or --command) on every call, and it replaces the whole config — include every other flag you want to keep (see Configuring the agent template in Agents and the CLI).
Step 3 — get the reference acknowledged
Every key a config references with @secret: must be explicitly acknowledged before a session using that config can start. This is a deliberate checkpoint: it stops a config edit from quietly reaching into a device’s secret store for a credential nobody agreed to hand over.
Only the device’s owner or a workspace owner can give this acknowledgement — a workspace admin is not enough.
CLI (the reliable path):
ch agent approve <agentId> <deviceId> --secret my_service
Pass --secret once per referenced key. The set you pass must match the config’s references exactly — a missing key, or a key the config doesn’t reference, is rejected. The command’s error doesn’t list the keys it expected, so check the @secret: references in the config you set in Step 2 and re-run with exactly that set.
Web app: on Set up → Devices → [the device], the Approved to run here panel shows each pending assignment, including the command, arguments, and environment variable names the agent will spawn with. Tick the box next to each secret reference, then click Approve.
The web tick-box flow has one gap worth knowing about: CodeHerder hides the value of any environment variable whose name looks like it carries a credential — names ending in _TOKEN, _KEY, _SECRET, _PASSWORD, and well-known names like GITHUB_TOKEN or ANTHROPIC_API_KEY. A reference under one of those names shows no tick box at all, so ticking everything visible still won’t satisfy the requirement for it, and Approve reports that the acknowledged secrets don’t match. Since most credential-shaped variables are named exactly this way, treat the CLI’s --secret <key> as the dependable way to acknowledge a reference — don’t assume the web tick boxes alone got you there.
Adding a reference later: if you edit a config to add a new @secret: reference after it was already approved, the next session refuses to start until you approve the assignment again with the new key included.
When it goes wrong
- Session doesn’t start, and the assignment needs acknowledgement: approve again, listing every referenced key with
--secret. - Session doesn’t start, and a key can’t be found on the device: the failure reports that a secret reference couldn’t be resolved on that device, but it does not say which key. Check which
@secret:<key>references your top launch config uses, confirm each one is stored on that device (Step 1), and try again.
Related guides
- Secrets — the workspace-level alternative: CodeHerder holds the encrypted value itself.
- Agents and the CLI — launch configs, multiple configs and ordering, and assigning/approving an agent on a device.
- Managing your devices — device state and troubleshooting.
