Threat model
What arca protects against, honestly — and what it explicitly does not.
Protects against
- Plaintext in the repo — code and config carry
arca://refs, never values. There is nothing to leak in a commit, a diff, a paste, or a prompt context. - Plaintext in shell history / process listings — values never enter via CLI arguments (args land in
~/.bash_historyand/proc/*/cmdline).put/rotateread stdin or a hidden/dev/ttyprompt. - Plaintext in agent context —
arca runinjects values into the child's environment; the agent that spawned it sees the child's output, never the variable.[secrets]bounds the injectable surface so aruncan't dump the whole vault. - Accidental printing —
Secrethas noDisplay/Debugof the value (.expose()only),listis names + metadata only, and the TUI's posture is browse-never-spill (reveal is a deliberate two-step, audited). - Ciphertext at rest, everywhere — every vault impl stores age ciphertext. The local file is unreadable without the identity; the
cfremote store holds ciphertext over the wire and in D1 — a compromised worker yields blobs, not values. With[vaults.*] private_refs = trueit can't even see which secrets exist: storage keys are HMAC-derived hex and the real ref travels in an age-encryptednameblob. - Token leakage on the cf wire — scoped tokens (
ARCA_VAULT_TOKENS) bound a leaked credential to its{env, read, write}scope instead of every env forever, and ArcaHmac request signing means the token never travels at all (a ±60s window bounds replay). All auth compares are constant-time. - Silent misuse — every materialization, denial, and error emits an ndjson audit event on stderr, so capture is a wrapper away.
checkfails closed on a vault that won't decrypt, and on a mixed-mode cf vault (private config + plaintext rows).
Does NOT protect against
- A determined local actor —
ARCA_ACTORis self-claimed (attribution, not identity) and[guardrails]are advisory: anyone who can editarca.tomlor run the binary can bypass them. They prevent accidents, not adversaries. Real multi-actor policy and enforcement belong to peregrin. - The identity's host protection — the master key lives in the session keyring when one exists (ADR-0002), which protects it about as well as your login session. On headless/CI it is still
identity.txt(0600), raw key material — protect it like a private SSH key. Either way, UID-level compromise reaches it: the keyring raises the bar, it is not a TPM. - The child process — once
runinjects env vars, the child owns them (/proc/<pid>/environ, core dumps, the child logging its own env). arca's guarantee ends atexec. - Deploy sinks —
arca deploypushes plaintext into the sink by design (CF Secrets Store is write-only); a sink compromise is outside arca's boundary — it crosses exactly once, at push. - Access patterns —
private_refshides which secrets exist, not when they're touched: request timing and row counts are still observable to the store. Oblivious access is out of scope. - Host compromise in general — an attacker who can read your memory, ptrace the broker, or impersonate your UID already won; arca raises the bar for accidental and casual leaks, not root.
In one line
arca makes the default posture "values never appear in repos, prompts, args, logs, or agent context" — and records every time one still crosses.