Guides
Using the secrets vault
Store API keys and tokens in the AgentKit secrets vault, let agents use them by reference, answer approvals, and audit every release.
ak secrets keeps small named values, such as API
keys, tokens, and certificates, in the OS keychain or an age-encrypted vault
file. Coding agents use a value through a ${secret:NAME} reference instead of
an unmanaged .env file or a shell export. This guide explains how the
commands fit together; each command's page has the full flags, defaults, and
exit codes. The Desktop app has its own Secrets page.
A grant trusts the agent with the value
The vault organizes and audits access. It does not seal a value away from an agent you granted it to. Read Limits before you grant a secret.
Commands
| Task | Command |
|---|---|
| Store or replace a value | ak secrets set, ak secrets rotate |
| See what is stored (never the value) | ak secrets list, ak secrets get |
| Remove a value | ak secrets delete |
Move a .env file into the vault | ak secrets import-env |
| Let an agent read a secret | ak secrets grant |
| Answer requests agents left waiting | ak secrets approve |
| See or remove grants | ak secrets grants, ak secrets revoke |
| Check or re-pin an agent's binary | ak secrets trust |
| Read the access ledger | ak secrets audit |
| Inventory the vault | ak secrets export |
| Manage the file backend's vault and recovery keys | ak secrets recovery |
Values are read from --value=- (stdin), --value-file, or a non-echoing
prompt, never from a literal flag value that would land in shell history.
ak secrets get prints [redacted] unless you pass --reveal in a terminal.
Choose a backend
| Backend | Use it when | Agents can resolve |
|---|---|---|
| Keychain (default) | The host has a working OS keychain: macOS Keychain, Windows Credential Manager, or a Secret Service on Linux | Yes |
| File | The host has no usable keychain | No: the vault passphrase is read only from a terminal, so an agent resolve fails (exit 7) |
| Memory | Tests and demos; nothing persists | Process-local only |
The backend comes from --backend, then AGENTKIT_SECRETS_BACKEND, then
backend in the secrets section of $AGENTKIT_HOME/config.yaml, and
otherwise the keychain. A host config that selects the file backend therefore
makes every agent resolve fail, even though no flag says so. Use the keychain
on any host where agents need secrets; the file backend suits a vault only you
read from a terminal.
The OS keychain may ask you to allow access the first time ak reads an item,
or after an upgrade changes the binary. That prompt is the keychain's own
access control.
Set up the file backend
ak secrets recovery init --label <label> # create the vault and one recovery key
ak secrets recovery add <age-public-key> --label <label> # enroll another recovery key
ak secrets recovery listrecovery init shows the recovery private key once and stores it nowhere.
Keep it where the host cannot reach it: if you lose the passphrase and every
recovery key, nobody can open the vault. If you forget the passphrase, run a
vault-reading command with --unlock-with-recovery in a terminal, paste the
recovery key, and choose a new passphrase.
The host remembers the newest vault version it has seen. If the vault on disk
is older (restored from a backup or synced over by a stale copy), commands that
open it fail. Find out why first; to keep the older vault anyway, run
ak secrets recovery accept-rollback.
Writes made after that older copy are lost.
Let agents use a secret
An agent writes a reference into a tool call:
curl -H "Authorization: Bearer ${secret:NAME}" <url>The core Kit's secrets-inject PreToolUse Hook substitutes the value in memory
just before the tool runs. In Claude Code it scans only the Bash command and
the WebFetch URL. For each reference it runs ak secrets resolve, which
identifies the calling agent by walking the process tree and hashing the
agent's binary; the caller is never taken from a flag. Any failure blocks the
tool call rather than passing the literal reference on.
When ak run starts a Codex or Grok agent, ${secret:NAME} references in the
environment it passes to that agent are resolved the same way, and the run
stops if a value cannot be released.
A resolve succeeds only when a grant matches four fields: the secret, the
project, the agent's binary fingerprint, and the purpose. An empty purpose is a
literal value, not a wildcard, and a block outranks any allow on the same
scope. Get an agent's fingerprint from
ak secrets trust <adapter> --json or from a
waiting request.
ak secrets grant --allow-host <host> restricts an allow to WebFetch URLs
aimed at the listed hosts. A restricted grant refuses any release whose
destination the Hook cannot check, including every Bash reference. The
restriction applies to the Hook's own substitution path only; see
ak secrets grant for the exact matching
rules.
Answer approvals
When no grant covers a resolve, it does not prompt. It fails closed (exit 8)
and leaves a request for you. Answer it with
ak secrets approve in your own terminal,
or in the approval inbox on the Desktop Secrets page:
| Answer | Effect |
|---|---|
| Deny | Refuses this request and stores nothing; the next resolve asks again |
| Allow once | Releases the secret exactly once |
| Allow for a while | An allow that expires on its own: 24 hours by default in the CLI (--ttl changes it); 15 minutes, 1 hour (default), or 8 hours in Desktop |
| Always allow (allow until revoked) | An allow with no expiry |
An agent that keeps asking without a grant is rate-limited per fingerprint and then held in a cool-down (exit 9). A job under a standing grant never reaches the limit.
grant and approve refuse to run (exit 6) when an agent sits above them in
the process tree, and Desktop refuses an answer when an agent started the app.
This stops an agent from approving its own request in the obvious way; it is a
speed bump, not a boundary.
Revoke
ak secrets revoke <ref> --agent sha256:<hash> # one scope, allow or block
ak secrets revoke --agent sha256:<hash> --all # every grant that binary holdsRevoking stops future reads. It does not recall a value the agent already read, so rotate the secret after a panic revoke.
After a release
A released value reaches the tool's process and arguments, and the runtime may
record the executed call, value included, in its session transcript. AgentKit
keeps only a hash and a length of each released value, in release manifests
under $AGENTKIT_HOME/diagnostics/manifests/. Two commands use them to remove
released values later:
ak diagnostics exportremoves them from a support bundle when it can read the manifests.ak sessions redactfinds them in registered Claude Code session files. It is a dry run unless you pass--apply --yes, and it backs up before rewriting.
Rotate
Each secret has a rotation window: 90 days by default, or the value you pass
with ak secrets set <ref> --rotate-ttl <days>d. ak secrets list flags
secrets past their window. Replace a value with ak secrets rotate <ref>; it
fails if the ref does not exist, so a typo does not create a new secret.
Audit releases
Every resolve with an identifiable caller writes one row to the secrets_audit
ledger, for a release and a refusal alike, before any value is returned. A row
records the ref, project, fingerprint, label, purpose, decision, source,
session, error code, and context, never a value. For a WebFetch reference,
the destination host is added to the context. A Desktop reveal requires a
reason, recorded the same way.
Read the ledger with ak secrets audit or on
the Desktop Audit log page. The ledger is kept until you change its
retention with ak data retention --class secrets_audit.
While Desktop is open, each release also shows an in-app notice with a Revoke this agent's grants button, and raises an OS notification titled Secret released while the window is unattended. Neither carries the value; see the Secrets page.
Export
ak secrets export --json # inventory on stdout, no values
ak secrets export --out ./inventory.json # the same, as a file only you can read
ak secrets export --include-values --out ./values.age # confirms, then writes an age-encrypted fileThe default export contains no values. --include-values needs an interactive
terminal and an --out path ending in .age; values never go to stdout. It
asks you to confirm and to choose a passphrase, and nothing on the host keeps
that passphrase, so losing it loses the export. Decrypt with the stock age
CLI, for example age --decrypt values.age | jq ., and treat any decrypted
file like the secrets it contains.
Move from .env
ak secrets import-env <path> # skips keys that already exist
ak secrets import-env <path> --force # overwrites them insteadThe command reports only counts, never values, and reads at most 1 MiB. Then
grant the agents that need each key, change the commands to use
${secret:NAME}, and remove the .env file.
Limits
These are deliberate boundaries of this version:
- A grant trusts the agent's model with the plaintext. A prompt-injected
agent can send a granted value to any host that
--allow-hostdoes not exclude. An unrestricted grant relies on the runtime's permission prompt, andAGENTKIT_SECRETS_INJECT_AUTO_ALLOW=1removes that prompt. - No wall after injection. Once a value is in a child process, its environment, or a transcript, AgentKit cannot contain it. Redaction and rotation are the remedies.
- Self-approval refusal is a speed bump. An agent running as your user can detach from the process tree or edit the grants file directly.
- Keychain access control is the OS's. Any process running as your user that the keychain lets in can read an item.
- Memory attackers. Anyone who can read the memory of
ak, the Hook, or the tool process can read the value while it is in use. - Static windows. Grant and rotation windows are fixed durations.