Vault
The local, sealed store for your agents' wallets and secrets.
The vault is a local-first store on your machine for wallets and secrets, encrypted at rest. Agents never read from it directly — they act through a warrant.
Unlock modes
Set with warden init --mode:
password— a scrypt-derived key; fully offline, no chain needed.password+seal— password and the Seal factor (two-factor; the Seal factor is owner-gated on-chain).seal— passwordless; the owner signs at open.
Seal modes (password+seal, seal) register the vault on-chain and recover the Seal factor via an
owner-signed cert dance — which is also what makes the on-chain kill-switch bite.
Owner
Every vault has an owner Sui address, set at init with --owner — required for every mode, with
no silent default. It's the one identity that:
- revokes the vault or a warrant on-chain (the kill-switch),
- approves high-risk actions (owner-wallet approvals verify against it), and
- for
password+seal/seal, signs the unlock.
Warrants inherit it. Use --owner keystore (or self) for your sui keystore active address, or pass
an explicit wallet/zkLogin address. An owner-wallet-offchain approver can differ from the vault owner
via config set --approval-owner, which overrides it for approvals only.
warden ui. An owner you don't control can never unlock the
vault.Vault file structure
The vault is a directory of plain files (default ~/.warden/). Public metadata is readable; all key
material is sealed.
~/.warden/
├─ config.json public · vault-key signed (kdf_salt, owner, approval, rpc)
├─ vault_key.json the vault's ed25519 key — seed sealed under the KEK
├─ wallets/<id>.json wallet index + sealed_passphrase (sealed under the KEK)
├─ wallet_keys/wallets/ OWS keystore — the mnemonic, encrypted under the wallet passphrase
├─ secrets/<id>.json master_dek (under the KEK) + payload (the credential, under the DEK)
├─ warrants/<hash>.json per-token key copies + scope, policy hash, on-chain refs
├─ policies/<hash>.json content-addressed policy documents
├─ pending/ approvals/ approval markers
└─ audit/ logs/ audit trail
<hash> is sha256(token) for warrants and the content hash for policies; <id> is a uuid.
How it's encrypted
Everything sensitive is sealed under a KEK (key-encryption key) derived from your unlock factors:
password→scrypt(password, kdf_salt)— OWS Keystore-v3 params (N=2¹⁶, r=8, p=1).password+seal→H(scrypt(password) ‖ seal_factor);seal→H(seal_factor). The Seal factor is recoverable only via the owner-gated on-chain decrypt, so an on-chain revoke destroys the KEK.
Wallets are two layers. The mnemonic lives in the OWS keystore under wallet_keys/, encrypted
under a per-wallet passphrase (AES-256-GCM, scrypt). That passphrase is itself sealed under the KEK in
the wallet's index file (sealed_passphrase). To sign: KEK → unseal passphrase → decrypt mnemonic →
sign in-process. Only the public chain_id + address per account are stored in the clear.
Secrets use an envelope. A random data-encryption key (DEK_master) encrypts the credential
(payload); DEK_master is then wrapped under the KEK (master_dek). To read: KEK → unwrap
DEK_master → decrypt payload. Only name, type, and host_pattern are public.
How secrets are contained
An agent never has your password or the KEK. When you issue a warrant,
Warden writes a second copy of just that warrant's scoped keys into warrants/<hash>.json, each
wrapped under HKDF(token) instead of the KEK:
wallet_tokens— the wallet passphrase, per scoped wallet.secret_deks— theDEK_master, per scoped secret.
So the bearer token alone opens exactly its slice of the vault, and nothing else. Revoke the warrant and that copy is gone — the agent holds the token, never the KEK, a passphrase, or a raw key.
A credential is decrypted only for the instant it's used. The gateway
resolves it from the token (no KEK), injects it into the upstream request, and relays the response
— the agent, which holds only the token, never sees the credential. The decrypted bytes (and the
intermediate DEK_master) live in mlocked memory — never swapped to disk — and are zeroized the
moment they drop. Nothing decrypted is ever logged or returned by the local API, which exposes only
name, type, and host_pattern. The process disables core dumps and debugger (ptrace) attach so
the material can't be lifted out of memory; the long-lived gateway also zeroizes any cached keys on
SIGTERM / SIGINT / panic before it exits.
How wallets are contained
Warden custodies the signing key in-process (via the OWS signer): the mnemonic is never written
decrypted and never handed to the agent. To sign, Warden decrypts the keystore in memory, signs
in-process, and returns only the signature — the seed never leaves the process. The wallet
passphrase and the derived 32-byte seed are held in mlocked, zeroize-on-drop buffers and wiped the
moment the key is built. The same hardening applies: no core dumps, no ptrace, and the gateway
zeroizes cached keys on exit. Short-lived CLI commands like warden sign cache nothing — their key
material is transient and wiped on drop.