Svault
The secret manager that knows an AI is asking.

Svault is an AI-aware secret access layer written in Rust. It sits between AI agents and your credentials — enforcing structured requests, detecting suspicious patterns, and making sure an agent has a real reason before it touches anything sensitive.
Why Svault? Every existing secret manager (1Password, Infisical, HashiCorp Vault) treats an AI agent the same as a human or a script. Svault doesn't. It knows the difference.
flowchart LR
H["Human"] -->|"svault secret get<br/>(passphrase)"| V["Svault"]
A["AI Agent"] -->|"svault get<br/>scope + reason"| P["Policy engine"]
P -->|"allow / deny + audit"| V
V --> E["vault.enc<br/>AES-256-GCM"]
Documentation
| Guide | What's inside |
|---|---|
| Installation | crates.io, from source, supported platforms |
| Interactive mode (TUI) | The full-screen dashboard and keybindings |
| Command reference | Every subcommand and flag |
| End-to-end walkthrough | Full flow: create → classify → judge → gated get, with real model output |
| Policy engine | The agent path — svault get, scopes, tiers, audit |
| Recovery & portability | Recovery code for a lost passphrase, export/import bundles |
| Daemon | Optional Unix daemon — keys in memory, auto-lock, daemon start/stop/status/doctor |
| Storage backends | Local today; cloud / self-hosted / S3 placeholders |
| Security model | Crypto, memory safety, what's safe to commit |
| Security review & audit | Independent review per release + the bulletproofing process |
| Architecture | How it works, on-disk layout, auth methods |
| Roadmap | Where Svault is headed |
| Changelog | What's shipped, version by version |
Quick start
# Install
# 1. Create an encrypted vault (interactive: storage, name, agents, auto-lock,
# default tier, AI judge, passphrase…). Prints a one-time recovery code — save it.
# 2. Add secrets — also classifies each one (scope + sensitivity tier) for the gate
# 3. Unlock for your session (derived key cached, not prompted again)
# 4. Use secrets without re-entering the passphrase
# 5. Lock when done
Or just run svault with no arguments for the interactive TUI.
⭐ Star us if you like the project!
Run svault with no subcommand to open the full-screen terminal UI:
Browse all vaults (with live lock state), c create, u unlock / l lock, s edit settings, and — once a vault is unlocked — a add, view, and d delete secrets. The TUI reuses the cached session key, so an unlocked vault is never re-prompted. Every subcommand still works for scripting.
Full keybindings → docs/tui.md
svault secret get is the human path — passphrase, no questions asked. svault get is the agent path: a structured request that an AI must justify. As of 0.9.0 it is enforced inside the daemon (the component that holds the key), not advisory — there is no unguarded read path, and every decision is audited with the connecting process's peer UID.
flowchart TD
REQ["svault get"] --> ID["Identify caller"]
ID --> RSN{"Reason valid?"}
RSN -->|no| DENY["Deny + audit"]
RSN -->|yes| CAP{"Caller holds scope<br/>& matches secret?"}
CAP -->|no| DENY
CAP -->|yes| RATE{"Within rate limit<br/>& no burst?"}
RATE -->|no| DENY
RATE -->|yes| TIER{"Tier?"}
TIER -->|low| ALLOW["Return value + audit"]
TIER -->|medium / high| JUDGE{"AI judge"}
JUDGE -->|allow| ALLOW
JUDGE -->|deny| DENY
AI judge (0.9.0): for medium/high-tier secrets, Svault asks a cheap, fast LLM via your OpenRouter account whether the stated reason plausibly justifies the request — the behavioural gate that makes Svault AI-aware. Per-secret classification (scope/tier + an optional description the judge weighs against the request's reason) lives in the signed meta.yaml (set with svault secret add --scope --tier --description); the committable svault.policy.yaml holds only caller definitions. The judge is off until you configure a key — store it with svault judge set-key (or $SVAULT_OPENROUTER_KEY), then try svault judge test.
Full pipeline, tiers, judge setup → docs/policy-engine.md
svault create prints a one-time recovery code — a 160-bit second key that resets a lost passphrase. It's shown once and never stored in plaintext; keep it in a password manager.
The bundle carries no machine-specific state and every byte is encrypted or signed — safe to move between machines (same major Svault version).
Recovery code + export/import → docs/recovery.md
| Backend | Status |
|---|---|
local |
Available (default) |
cloud |
Coming soon — Soluzy SaaS |
self-hosted |
Coming soon — your own server |
s3 |
Coming soon — S3 / MinIO |
The chosen backend is recorded in meta.yaml and shown as a storage:name prefix everywhere a vault is listed. Vault names must be unique.
Details → docs/storage-backends.md
| Property | Implementation |
|---|---|
| Encryption | AES-256-GCM |
| Key derivation | Argon2id (64 MB, 3 iterations) — GPU-resistant |
| Metadata integrity | HMAC-SHA256 — tampering with meta.yaml is detected |
| Memory safety | VaultKey + secrets derive ZeroizeOnDrop — wiped on drop |
| Session file | Atomic write, mode 0600 |
| Vault file | Safe to commit — encrypted at rest |
The passphrase is the only key.
Threat model + on-disk layout → docs/security.md
Every 0.x.0 release goes through an independent security review + bulletproofing pass — see docs/security-review/.
flowchart TD
U["AI Agent"] -->|"svault get (scope + reason)"| D["Svault daemon<br/>(enforced gate)"]
D --> POL["Policy checks<br/>reason → capability → rate limit · burst"]
POL --> TIER{"Sensitivity tier"}
TIER -->|low| OUT["audit (peer UID) → value"]
TIER -->|medium / high| JUDGE["AI judge (OpenRouter)"]
JUDGE --> OUT
OUT --> ENC["(.svault/<vault>/vault.enc<br/>AES-256-GCM encrypted)"]
Enforced-engine details, full layout → docs/architecture.md
Roadmap
| Phase | Status | What |
|---|---|---|
| Step 1 | Done | Local encrypted vault — AES-256-GCM + Argon2id |
| Step 1+ | Done | Interactive Ratatui TUI — forms, browsers, lock-aware secrets |
| Step 2 | Done | Policy engine — caller identity, reason, scopes, tiers, rate limit, audit log |
| Step 3 | Done | Recovery (code + export/import) and the Unix daemon (keys in memory, auto-lock). Extra auth methods (YubiKey, TOTP, Touch ID/Face ID) deferred |
| 0.9.0 | Done | Enforced policy engine (in the daemon, peer-UID-audited) + signed per-secret classification + AI judge (OpenRouter) |
| 1.0.0 | Planned | Final independent review + install channels, then the first stable release |
| 2.0.0 | Planned | Desktop GUI (Tauri) + system tray |
| 3.0.0 | Planned | MCP integration — Claude Code, Cursor, Copilot, VS Code, Aider |
| Cloud | Planned | Anomaly scoring via Claude Haiku — free tier + premium plans |
Full roadmap → docs/roadmap.md
Tests
99 tests (plus one #[ignore]d stress benchmark) covering: roundtrip encryption, wrong-key rejection, bit-flip authentication failure, distinct salts → distinct keys, key-from-bytes roundtrip, vault create/open, open-with-key, re-key, wrong passphrase, add/get/list/remove, persistence across reopen, tampered vault.enc rejected, truncated vault.enc errors instead of panicking, tampered meta.yaml rejected, session unlock/lock/lock-all, the session caching a derived key (never a passphrase), passphrase strength checks + entropy floor, owner-only file (0600) / dir (0700) permissions, audit record/read, rate-limit parsing, the policy engine (capability, tiers, rate limit, burst, unknown caller, fallback mode), recovery code write/unlock + wrong-code rejection, full recover-and-rekey roundtrip (old passphrase rejected, secret preserved, code still valid), export-bundle checksum integrity, build→import recreating an openable vault, import name-collision suffixing + rename re-signing meta, storage-backend metadata roundtrip, the daemon (protocol JSON roundtrip, client-derived-key unlock + bogus-key rejection, auto-lock idle/hard-max/active decisions, a unix unlock→get→lock→shutdown integration test, a concurrent-reads stress test, poisoned-mutex recovery, and connection-slot accounting), usage-log source stamping (event tagged with the current surface; old logs parse as unknown), TUI key dispatch (field navigation, the rate-limit space-toggle regression, paste handling, and help opening with h or ?), and the 0.9.0 enforced engine — the AI judge's JSON parsing + tier-dependent fail modes (with a fake transport, no network), the vault/secret descriptions reaching the judge prompt only when set, and the daemon's gated read path (policy allow/deny, high-tier fail-closed when the judge is unavailable, medium fail-open, peer-UID-stamped audit), and the OpenRouter key store (set-key/status/remove-key round-trip writes a 0600 file, trims the key, and resolves the source).
A heavier concurrency / pressure simulation runs on demand (cargo test --release daemon_stress_simulation -- --ignored --nocapture); methodology and a recorded run are in docs/security-review/stress/0.6.0.md.
CI runs the suite on Ubuntu, Fedora, macOS, and Windows on every push and pull request.
License
Apache 2.0 — see LICENSE.
Built by Soluzy.