Files and environment variables are explicit boundaries.

Skarbiec separates the encrypted vault, audit journal, keyring, acquisition state, capability routes, and consumer token files so each can be owned and backed up independently.

There is no configuration file#

Skarbiec has no configuration file of its own. Almost every setting is an environment variable read by the process that needs it, plus a handful of owner-only state files whose defaults derive from the vault path. Defaults are stated wherever the product has one. The one exception is the vault's own policy section, below.

Administrative policy#

The vault carries a policy section holding administrative rules applied before an operation. It is written with policy set and read back with policy get, and it is an enforcement surface rather than an open bag of operator metadata. The supported keys are a compiled registry, and a key the registry does not carry is refused rather than stored, because a rule an operator believes is in force and is not is worse than no rule at all. The registry checks the value as well as the name: a value the rule's reader could not consume would leave the rule visible and unenforced, which is the same defect.

Exactly one rule is registered today. A refusal names every supported key and the shape it demands, because withholding the allowed set only moves the guessing one step along.

Registered policy keys
KeyValueMeaning
min_generated_lengthA whole numberMinimum character count a generated value is expected to reach. policy check decides a candidate read from standard input against it and reports required, actual, and ok for the rule. With no minimum configured the rule reports configured false and passes. generate does not consult it, so a short value is produced and then checked rather than refused at generation.

Vault and state#

Vault, journal, and side-state paths
VariableMeaning
SKARBIEC_VAULT_FILEPath of the encrypted vault, for one process. It is read exactly as the operating system passes it, so a path that is not UTF-8 selects that file, and it disables owner routing in the same way. Unset, the product opens the vault Stado declares for this machine — secrets.skarbiec.vault_file in Stado's config file ($STADO_CONFIG, then stado.config.json in the working directory, ~/.config/stado/config.json, ~/.stado/config.json) — and only a machine nothing declares for falls back to ~/.local/share/skarbiec/skarbiec.vault.json. A machine whose Stado declares secrets.skarbiec.url and no vault file reads the fleet vault on its owner and holds none itself: there the fallback file is a leftover copy, and opening or writing it unasked is refused with 'this machine holds no Skarbiec vault: Stado reads the fleet vault through secrets.skarbiec.url (…), so … is a copy and is neither read nor written'. When Stado's config file exists but cannot be read or is not JSON, the fallback file is refused the same way, naming the config and its cause, because whether this machine holds a vault cannot be told; doctor's selection check reports the HOME fallback with that cause. A vault named explicitly with this variable is still opened. doctor's selection check names which of the three chose the vault. An operator-route request may override it for exactly that request through its body's vault member.
SKARBIEC_AUDIT_FILEPath of the append-only, hash-chained audit journal. Default ~/.local/state/skarbiec/audit.jsonl. audit verify always names the journal it read, because the default and an override are different files.
SKARBIEC_ACQUISITION_FILEOwner-only acquisition state: issued bearer hashes, their bindings, and expiry. Default is the vault file name with acquisitions.json appended, beside the vault.
SKARBIEC_CAPABILITY_FILECapability broker state: each issued capability and each seen nonce. A capability record is kept for two hours past its expiry, so a late redemption is refused as expired, and a nonce for two hours after it was seen; every read of the file drops older entries, so the file holds only the live window. The audit log keeps what was issued and redeemed. Default is the vault file name with capabilities.json appended, beside the vault.
SKARBIEC_CAPABILITY_ROUTES_FILEThe capability routes table. Default capability-routes.json beside the capability state file; routes help prints the path that resolves.
SKARBIEC_SYNC_DIRGit synchronization working directory. Default ~/.skarbiec-sync. Like every path variable in this table, it is read exactly as the operating system passes it, so a path that is not UTF-8 is still the one used; only an unset or blank variable selects the default.

Unlocking a protected key#

Unlock phrase sources
VariableMeaning
SKARBIEC_UNLOCKUnlock phrase for a passphrase-protected recipient key, for a single invocation. Handed to gpg over stdin, never on argv and never written to disk. A value that is set but not UTF-8 is refused naming the variable, instead of being taken as no phrase.
SKARBIEC_UNLOCK_FILEOwner-only file holding the same phrase, for a persistent service; unset, ~/.stado/skarbiec-unlock is used when it exists. A default file that exists but cannot be inspected is refused with its cause rather than read as no phrase. With neither source an unprotected key decrypts normally and a protected key fails closed, without an interactive prompt.

Consumer files#

A service reads its bearer from an owner-controlled token file or its own environment. Tokens are never passed as command arguments. Workload-bound acquisition identities keep the private signing key with the workload and register only the public key.

Server surfaces#

The MCP server keeps its compatibility resolve path disabled until all three of its variables are set in the server process's own environment — never as tool arguments, so no bearer lands in a transcript, a log, or a child's argv.

MCP server and browser native host
VariableMeaning
SKARBIEC_MCP_CONSUMERConsumer identity the server gates by.
SKARBIEC_MCP_TOKEN or SKARBIEC_MCP_TOKEN_FILEThat consumer's scoped grant, inline or from a file. A grant file that is named but cannot be read refuses the resolve with its path and cause.
SKARBIEC_MCP_OUT_DIRRequired absolute directory for emitted mode-0600 env files. A relative path is refused, so a launch directory cannot place files inside a repository.
SKARBIEC_BROWSER_TOKEN_FILEOwner-private token file of the browser consumer. Default ~/.local/state/skarbiec/browser-host-token.
SKARBIEC_BROWSER_CONSUMERConsumer name the native host presents. Default skarbiec-browser-host.

Credential lifecycle#

Canonical endpoint and bridge
VariableMeaning
STADO_FORWARDS_DIRDirectory of forward files. Default ~/.stado/forwards. Remote credential calls resolve their endpoint from the skarbiec.local forward in that directory; the bridge resolves Weles admission from weles-admission.local beside it.
SKARBIEC_CREDENTIAL_TOKEN_FILEOwner-only file holding the bearer for remote credential calls, the alternative to naming a token file on the command line.
SKARBIEC_WELES_CREDENTIAL_COMMANDAbsolute, owner-controlled, non-symlink executable implementing the skarbiec.credential-operation.v3 bridge. Anything else is refused before an operation starts.

Diagnostics, receipts, and bounds#

Evidence and resource limits
VariableMeaning
SKARBIEC_WORM_RECEIPT_DIR and SKARBIEC_WORM_CHECKPOINTEnable write-once receipt checking in doctor. With either unset, doctor reports not_configured, which is deliberately not a failure.
SKARBIEC_OPENSSLPath of the openssl binary when the preferred build lives somewhere unusual.
Request threadsEvery accepted HTTP connection is served on a thread of its own; no request waits behind a worker count or is refused because a queue is full. When the system refuses a thread, that request is answered 503 infra_down with the system's reason.
SKARBIEC_READINESS_ITEMSComma-separated additional item ids that readiness must decrypt.
SKARBIEC_GPG_DAEMON_MEMORY_LIMIT_MBRequired for the daemon ceiling: physical footprint — resident, compressed and swapped pages together — above which skarbiec maintain replaces keyboxd or gpg-agent. No default; without it maintain and doctor name the missing setting. gpg never stops its daemons and keyboxd grows with every lookup. A replacement is logged and journalled as daemon-recycle. skarbiec maintain runs the daemon ceiling, the readiness proof and one pull of every pulled bond once, and fails naming every failed step; a Stado schedule pinned to the host runs it: stado schedule create --pinned-host HOST --cron '* * * * *' 'skarbiec maintain'.
SKARBIEC_RELEASE_URI and SKARBIEC_RELEASE_COMMITBuild-time stamps reported by version. A source build reports them as null rather than guessing a coordinate.

Synchronization#

Bonds move ciphertext documents and public metadata; they do not widen recipient sets or make a destination key capable of decrypting an item.

External tools#

Skarbiec requires gpg for per-recipient encryption, decryption, owner signatures, and key custody, and it runs only through a bounded executor with a deadline and guaranteed child reaping. Everything else runs in process: bearer, proof and capability digests and the audit journal compute SHA-256 in process, breach checking computes its own SHA-1, and bearer values are drawn from /dev/urandom. Spawning shasum or openssl children for those would take slots in the same bounded pool the decryptions use, so a grant metadata call that decrypts nothing would wait behind verifier sweeps. openssl is still used to verify Ed25519 workload proofs, and oathtool only by totp.