arca is no longer maintained. The repository is archived and there will be no further releases or security fixes.
← All docs

Command reference

← README · related: Policies · Importing · MCP · Configuration

Command Purpose Key flags
init Create the store (reuse or generate an age key) --force
set NAME Add/update a secret (value from TTY or stdin) --tag --desc --rotate-after --ttl --expires-at --meta k=v --no-print --require-approval --canary --require-grant --rate N/D --allow-empty
generate NAME Create a secret with a random value -l/--length --charset --tag --desc --ttl --no-print --show --canary --require-grant --rate N/D
get NAME Decrypt and print one secret (records a read) -n (newline), --no-log
rotate NAME Replace value, keep created_at, log a rotation --rotate-after --ttl --expires-at --allow-empty
ls List secrets + metadata (no decryption) --tag, --no-rotation, --reads, --json
show NAME Show one secret's metadata (no value) --json
stale Secrets overdue/soon for rotation, or expired/expiring --within N, --json
rm NAME Remove a secret —
disable NAME Suspend a secret — refused on every access path — without deleting it or changing its value (a distinct kill switch, independent of expiry) —
enable NAME Re-enable a disabled secret (keeps any real expiry) —
edit NAME Edit a secret's value in $EDITOR (re-encrypted) —
rename OLD NEW Rename a secret, preserving metadata/history (alias mv) --force
annotate NAME Edit a secret's tags/description/metadata without changing its value (works on --no-print secrets) --tag --add-tag --rm-tag --desc --meta k=v --rm-meta
recipients List age recipients (with labels); add/rm/pin subcommands manage the set add --label name@machine
who-can-read [NAME] Show which recipients can decrypt the store (or one secret); flags high-privilege names —
exposure List secrets by blast radius (recipients that can decrypt each); flags master/admin-looking names --sensitive
doctor Security & health check of your setup, ranked by severity with a remedy per finding --json, --fix
reencrypt Re-encrypt every secret to the current recipient set —
import Bulk-load secrets from stdin (dotenv lines, or a JSON object) — see Importing --json, --dry-run, --overwrite, --allow-empty, --prefix P, --tag t
inject Resolve arca://NAME references on stdin → stdout —
exec -- CMD Run CMD with secrets injected as env (audited); injected values are redacted from its output --only a,b, --redact auto|on|off, --reveal
env Emit export … for eval "$(arca env)" --no-export
sync Replicate the store through an S3-compatible backend (see SYNC.md); envelope-encrypted, CAS-safe --pull, --push, --force, --admit-recipients; sync init URL, sync status, sync auto on|off, sync reset-escrow
signer show Print this machine's store-signing public key (headless-safe; generates the key on first use) —
signer add PUBKEY Trust another machine's store-signing public key here (additive; alias pin) — terminal-anchored --label NAME
signer list List the store signers this machine accepts (* marks its own key) —
signer rm PUBKEY Stop accepting a store-signing public key (refuses to empty the set) —
signer rotate Generate a new store-signing key for this machine and trust it — terminal-anchored; other machines then need signer add. The outgoing key stays trusted so this machine's escrow history still verifies —
log [NAME] Access history (agent/session/actor); --verify checks the log's integrity --limit N, --json, --verify, --require-signed, --anchor TOKEN, --remote, --print-anchor
canary [NAME] Plant a decoy secret (any use trips a signed alert), or list canaries and their trips --template, --list, --tag, --desc
grant SECRET Authorize a --require-grant secret for a command, a number of uses, and a window. --agent is advisory (env sniffing), not a containment boundary --command, --uses, --ttl, --agent
grants List active grants and their remaining uses —
revoke SECRET Remove the active grant for a secret —
handle create SECRET Mint an opaque capability handle an agent can use (via MCP) without the secret's name/value — operator-only (refused for a detected agent), and refused for a disabled secret --ttl, --command, --as, --override
handle ls / handle revoke ID List or revoke handles —
mcp Run an MCP server exposing arca to AI agents (stdio) — see MCP --strict (deny-by-default agent exposure)
agent allow/deny/ls NAME Manage which secrets a --strict MCP server exposes to agents —
version Print version, commit, build date, and toolchain (arca --version prints just the version) --json
completion SHELL Shell completion script (bash/zsh/fish/powershell) —

Values are always read from a TTY (no echo) or piped stdin — never passed as arguments.

An empty read is refused on every write path (set, rotate, and import --overwrite). The case this exists for is a pipe whose producer failed:

vault-cli read prod/key | arca set PRODKEY    # producer fails, prints nothing…

Without the guard, stdin closes empty, arca reports success, and the stored value is gone — the store keeps only the current value, so there is no undo. Pass --allow-empty when you genuinely mean to store nothing. Whitespace is a value, not an absence: a single space is stored, while "", a bare newline, and CRLF are refused.

The per-secret policy flags (--no-print, --require-approval, --canary, --require-grant, --rate) are documented in Policies.

Disabling a secret (fast, reversible kill switch)

disable NAME is the quickest way to take a secret out of service without losing it: the value and all metadata stay in the store, but every access path — get, exec, inject, env, and the MCP tools, including a capability handle minted before you disabled it — refuses it until you enable it again. Handles are made inert rather than revoked, so undoing a false alarm restores the pre-incident state exactly instead of forcing you to re-issue every handle you had handed out. It's a dedicated flag, independent of expiry, so a disabled secret shows as DISABLED in show / [disabled] in ls, the audit log records the disable/enable intent, and — unlike before 0.6.3 — enabling it keeps any real expiry the secret was carrying (disable/enable no longer touch expires_at).

arca disable GITHUB_TOKEN     # suspend it everywhere, keep the value + any expiry
arca enable  GITHUB_TOKEN     # bring it back (a real expiry, if any, is preserved)

Upgrade note: secrets disabled by an arca before 0.6.3 were suspended by stamping expires_at to now, so they read as EXPIRED (not DISABLED); clear that with rotate / set --expires-at.

This is a local kill switch, not revocation. Disabling stops arca from handing the value out; it does nothing to a copy that already leaked. On a suspected compromise, revoke the token at its issuer first (GitHub, AWS, …), then disable or rotate it here.

Note: env skips any secret it can't release — disabled/expired and --require-grant — instead of failing, so one suspended secret never blanks out eval "$(arca env)".

Accepting a recipient set (recipients pin)

arca records the recipient set each machine has been shown, in recipients.pin under that machine's state dir. The file never syncs: it is this machine's memory of what it expects, and a baseline that travelled with the store would be controlled by whoever controls the store.

Adding a recipient here is already guarded by an operator prompt. A recipient added on another machine and pulled in by sync is not, so the store can arrive carrying a key nobody here was ever shown. When that happens arca warns on every load and doctor raises its readership check to HIGH and names the key. Review it with arca who-can-read, then either:

The warning repeats until you do one or the other. Loading the store never accepts a key by itself, because a warning that silenced itself would report an injected key exactly once.

The baseline is established silently the first time a store is loaded, so a key that was already present at that point is accepted without comment. Reviewing arca who-can-read once, on a store that predates this check, is the way to close that gap.

Removing a recipient (recipients rm)

recipients rm KEY drops an age recipient (e.g. an ex-teammate's key) and re-encrypts every secret to the remaining keys in the same step, so the current store immediately stops being decryptable by the removed key. Use --no-reencrypt to defer the re-wrap to a later arca reencrypt.

Re-encryption is not revocation of what was already read. The removed key can still decrypt any copy it already had — local clones, backups, and every prior version of the store in git history. recipients rm prints this warning and lists the secrets to rotate. To truly deny the removed holder a secret, rotate its value (arca rotate NAME) so the old ciphertext decrypts to a dead value — and, as always, revoke the underlying credential at its issuer.