svault-ai 0.9.2

AI-aware secret access layer — enforces structured requests and detects suspicious patterns
svault-ai-0.9.2 is not a library.
Visit the last successful build: svault-ai-1.0.0

Svault

The secret manager that knows an AI is asking.

lint ubuntu fedora macos windows

crates.io downloads docs.rs license Rust

Svault Banner

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
cargo install svault-ai

# 1. Create an encrypted vault (interactive: storage, name, agents, auto-lock,
#    default tier, AI judge, passphrase…). 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 (derived key cached, not prompted again)
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.

Star us if you like the project!


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 judge (key, global on/off, model, thresholds, live test), 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

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.

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

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), caller rules, and the judge override all live AES-256-GCM encrypted inside vault.enc (0.9.2) — set classification with svault secret add --scope --tier --description; seed caller rules with svault policy init. Because the policy is encrypted at rest, an agent can't read it to plan a passing request, and a denied svault get returns only a generic message (the real reason is logged for you, never returned to the caller). 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.

svault recover                       # enter the code, set a new passphrase
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

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/&lt;vault&gt;/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)
0.9.1 Done Policy + judge in the TUIshift-J judge management, classification table + reclassify, judge enable/disable, audited
0.9.2 Done Policy encrypted at rest — classification, caller rules, access, judge overrides moved into the AES-256-GCM vault.enc (no read-to-bypass); denials are generic to the caller
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

cargo test

103 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), and the 0.9.1 TUI policy/judge surface (the classify form's tier cycle + require-reason toggle, and the judge screen's global toggle + masked key-entry sub-mode), and the 0.9.2 policy-at-rest guarantees (the policy round-trips through the encrypted payload, and the plaintext meta.yaml leaks no tier/scope/description/caller token at rest).

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.