Skip to content

Soluzy/Svault

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

98 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Svault

The principled way to give cooperative AI agents secret access.

lint ubuntu fedora macos windows

crates.io downloads docs.rs license Rust

Svault Banner

Svault is a secret access layer for AI agents, written in Rust. It sits between an agent and your credentials and makes every request structured, policy-gated, and audited: the agent must say which secret, in what scope, and why — and a sensitive request is scored by an AI judge before any value is returned.

The boundary, stated up front. Svault is built for cooperative and semi-trusted agents. It encrypts secrets at rest and gives you an enforced, tamper-resistant gate over agent access plus an audit trail. It is not a sandbox against a hostile process running as your own user — that process can read an unlocked session directly. Svault raises the bar for agents that mostly play by the rules and gives you the audit trail when one doesn't; it does not pretend to contain a determined local attacker. If that distinction matters to you, you're exactly who it's for. See the threat model.

Why not just 1Password / Infisical / HashiCorp Vault? Those treat an AI agent like any other client. Svault makes the agent path first-class — structured requests, per-secret policy and tiers, an AI judge for sensitive reads, and an audit record stamped with the caller's real (un-forgeable) UID.

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"]
Loading

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
MCP server svault mcp — gated secret access for AI agents (Claude Code, Cursor)
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
Architecture How it works, on-disk layout, storage and vault naming, auth methods
Security model Crypto, memory safety, what's safe to commit
Security review & audit Independent review per release + the bulletproofing process
Roadmap Where Svault is headed
Changelog What's shipped, version by version

Quick start

# Install
cargo install svault-ai

# 1. Create an encrypted vault (interactive: storage, name, agents, auto-lock,
#    default tier, AI judge). On first run you set one master passphrase — it
#    unlocks every vault. Prints a one-time recovery code — save it.
svault create

# 2. Add secrets — also classifies each one (scope + sensitivity tier) for the gate
svault secret add DB_URL --scope database --tier medium
svault secret add API_KEY --scope api --tier low

# 3. Unlock for your session — one master passphrase opens every vault
svault unlock

# 4. Use secrets without re-entering the passphrase
svault secret get DB_URL
svault secret list

# 5. Lock when done
svault lock

Or just run svault with no arguments for the interactive TUI.


Interactive mode (TUI)

Run svault with no subcommand to open the full-screen terminal UI:

svault

Browse all vaults (with live lock state), c create, u unlock / l lock, s edit settings, shift-J manage the AI judges (create or unlock the keyring, toggle the global on/off switch, add/edit/view judges with their model/thresholds/criteria, set the default judge, set/clear a judge's API key, live test, remove a judge), and — once a vault is unlocked — a add, c classify (tier/scope/reason/description), view, and d delete secrets, with each secret's classification shown inline. 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

Policy engine — the agent path

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, enforced inside the daemon that holds the key — not advisory. There is no unguarded read path, and every decision is audited with the connecting process's peer UID.

svault get DB_URL --scope database --reason "run nightly migration" --caller claude-code
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
Loading

The AI judge. For medium/high-tier secrets, Svault asks a fast LLM — via your OpenRouter account — whether the stated reason plausibly justifies the request. This is the behavioural gate that makes Svault AI-aware. You can define multiple named judges, each with its own model, thresholds, and free-text criteria, pick a default, and assign one per vault.

Everything that gates access is AES-256-GCM encrypted at rest — per-secret classification (scope/tier + an optional description the judge weighs against the reason) and caller rules live inside vault.enc; the judge registry and its API keys live in a separate encrypted keyring (.svault/keyring.enc). There are no plaintext config or key files. Because the policy is unreadable at rest, an agent can't study it to craft a passing request — and a denied svault get returns only a generic message, with the real reason recorded in the audit log for you.

svault keyring init          # create the encrypted keyring (one-time)
svault judge add reviewer    # name a judge: model, thresholds, criteria, key
svault judge enable          # turn the judge on globally

Full pipeline, tiers, judge setup → docs/policy-engine.md

Recovery & portability

svault create prints a one-time recovery code — a 160-bit second keyslot into the vault, used if you lose the master passphrase. It's shown once and never stored in plaintext; keep it in a password manager.

svault recover                       # enter the code, re-attach the vault to your master
svault export myvault --out vault.json   # portable, checksummed encrypted bundle
svault import vault.json                 # restore on another machine

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

Storage

Every vault is stored locally — an encrypted vault on this machine. The backend is recorded in meta.yaml as storage: local and shown as a local: prefix everywhere a vault is listed. Vault names must be unique.

Details → docs/architecture.md

Security model
Property Implementation
Encryption AES-256-GCM (authenticated)
Key derivation Argon2id (64 MB, 3 iterations) — GPU-resistant
Unlock One master passphrase wraps a random per-vault data key (keyslot model) — unlock once, every vault opens
Policy & judge config Encrypted at rest — the policy in vault.enc, the judge registry + API keys in keyring.enc. No plaintext config or key files
Metadata integrity HMAC-SHA256 — tampering with the public meta.yaml is detected
Memory safety VaultKey + secrets derive ZeroizeOnDrop — wiped on drop
Session / on-disk files Owner-only (0600), written atomically
Vault file Safe to commit — encrypted at rest, useless without the master passphrase

One master passphrase is the only key you type — it wraps each vault's random data key, so unlocking once opens everything. The recovery code is a second keyslot into a vault if you lose the master.

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/.

Architecture
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/&lt;vault&gt;/vault.enc<br/>AES-256-GCM encrypted)"]
Loading

Enforced-engine details, full layout → docs/architecture.md


Roadmap

Milestone Status What
Foundation Shipped Local AES-256-GCM vaults (Argon2id), the interactive Ratatui TUI, recovery code + encrypted export/import, and the Unix daemon (keys in memory, auto-lock)
Enforced policy + AI judge Shipped Daemon-enforced policy engine (peer-UID-audited) — reason, scopes, tiers, rate limit, burst — plus the AI judge (OpenRouter) gating medium/high-tier secrets
Everything encrypted at rest Shipped The whole policy surface in vault.enc and all global config + the judge registry (multiple named judges, with API keys) in keyring.enc — nothing abusable in plaintext; per-vault judge assignment; generic caller-facing denials
Unified unlock Shipped One master passphrase wraps a random data key per store (keyslot model); per-vault passphrases removed and the keyring brought under the master too; svault master init / rekey / status
Layered source Shipped Source split into a frontend-agnostic core plus cli / tui / daemon frontends (a library crate), with mcp / gui placeholders — structural only, so future frontends reuse core
Local MCP Shipped svault mcp — a local stdio MCP server exposing gated svault_get_secret / svault_list_vaults to AI agents; serves only unlocked state, never the passphrase, with a capability descriptor that advertises the request interface, not the decision criteria
Hardware-key unlock + hardening Shipped YubiKey (FIDO2 hmac-secret) unlock — an alternative keyslot over the master key (passphrase or touch, not 2FA); a 6-hour re-auth cap on every unlock path; first-run onboarding + an app-level TUI sign-in / logout; storage local-only
Conditional access + escalation Next Time-window / caller conditions in the encrypted policy; brute-force / anomaly seals a secret and escalates to a human (agents never self-clear)
Independent review + install channels Target A final independent review of the full agent-ready surface and install channels (script, Homebrew, Docker), then the first stable release
Desktop GUI Planned Desktop GUI (Tauri) + system tray

Detail for each milestone lives in the changelog and the full roadmap.

Full roadmap → docs/roadmap.md


Tests

cargo test

131 tests (plus an #[ignore]d concurrency stress benchmark) cover the crypto core and tamper detection, vault operations, the master keyslot model (wrap/unwrap a data key under the master for both vaults and the keyring, rekey, master recovery-code reset, wrong-master rejection), the policy engine and the enforced daemon gate (including peer-UID-stamped audit and high-tier fail-closed behaviour), the AI judge — run against a fake transport, so the suite never touches the network — and the encrypted-at-rest guarantees for both the policy (vault.enc) and the keyring (keyring.enc).

CI runs the full suite on Ubuntu, Fedora, macOS, and Windows on every push and pull request. A heavier concurrency 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.


License

Apache 2.0 — see LICENSE.

Built by Soluzy.

About

AI-aware secret manager | 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.

Resources

License

Security policy

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages