Agent Vault is designed around a single principle: Agent Vault never reveals vault-stored credentials to agents. Credentials are encrypted at rest, decrypted only in memory at proxy time, and injected into outbound requests server-side. No credential value stored in the vault is ever returned in an API response or written to a log.
This page covers the cryptographic primitives, token model, network protections, and operational safeguards that make this possible.
Passthrough services are an operator-chosen
exception: they allowlist a host without storing or injecting any credential,
so the client’s own request headers flow through unchanged. The guarantees
below apply to credentials stored in the vault — a credential the client
holds outside the vault is not protected by Agent Vault, but the host
allowlist, netguard, TLS interception, and audit
logging still apply.
Encryption at rest
All credentials stored in a vault are encrypted using AES-256-GCM, an authenticated encryption scheme that provides both confidentiality and tamper detection.
Agent Vault uses a KEK/DEK key-wrapping architecture:
- A random 256-bit DEK (Data Encryption Key) is generated on first boot. This key encrypts all credentials and the CA private key.
- If a master password is set, Argon2id derives a KEK (Key Encryption Key) that wraps the DEK. The KEK is never stored — it is derived in memory, used to unwrap the DEK, then wiped.
- If no master password is set (passwordless mode), the DEK is stored in plaintext in the database. Security depends on filesystem access controls — suitable for PaaS platforms where the platform manages volume isolation.
| Parameter | Value |
|---|
| Data encryption | AES-256-GCM |
| Key wrapping | AES-256-GCM |
| KDF (for KEK) | Argon2id |
| Time cost | 3 iterations |
| Memory cost | 64 MiB |
| Parallelism | 4 threads |
| DEK length | 256 bits (random, crypto/rand) |
| KEK length | 256 bits (Argon2id output) |
| Salt length | 128 bits (random per setup) |
Each encryption operation uses a fresh random nonce generated from crypto/rand. Credential values are decrypted in memory only at proxy time to resolve auth headers, then discarded.
Password changes are lightweight: changing the master password re-wraps the DEK under a new KEK — a single database update with zero credential re-encryption. You can also add a password to a passwordless instance or remove a password entirely using agent-vault master-password set|change|remove.
The master password is never stored on disk. If set via the
AGENT_VAULT_MASTER_PASSWORD environment variable, Agent Vault unsets it from the process
immediately after reading. The KEK derived from the password is wiped from
memory after unwrapping the DEK.
Password hashing
User passwords are hashed with Argon2id using the same parameters as master key derivation. Each user gets an independent random salt. Password verification uses constant-time comparison (crypto/subtle.ConstantTimeCompare) to prevent timing attacks.
KDF parameters are stored per user, so Agent Vault can upgrade hashing parameters in the future without invalidating existing accounts.
Changing a password (agent-vault account change-password or POST /v1/auth/change-password) invalidates all existing sessions and issues a fresh token.
Token model
All tokens are generated from crypto/rand (256 bits of entropy) and hashed before storage. The raw token is returned to the caller exactly once; Agent Vault only stores the hash.
| Token | Prefix | Storage hash | TTL | Notes |
|---|
| Session | av_sess_ | SHA-256 | Configurable (or no expiry) | Vault-scoped or user-global |
| Agent token | av_agt_ | SHA-256 | Configurable (or no expiry) | Instance-level agent identity |
| Invite | av_inv_ | SHA-256 | 15 minutes | Single-use, burned on redemption |
| Approval | av_appr_ | SHA-256 | 24 hours | Grants read-only access to a proposal |
| Vault invite | av_uinv_ | SHA-256 | 48 hours | For onboarding human users |
All tokens use SHA-256 for fast lookup.
Network security
Agent Vault includes a network guard that validates every outbound proxy connection at the IP level, preventing agents from using the proxy to reach internal infrastructure.
Modes
| Mode | Behavior |
|---|
public (default) | Blocks private/reserved IP ranges and cloud metadata endpoints |
private | Blocks only cloud metadata endpoints (for local/trusted networks) |
Set via AGENT_VAULT_NETWORK_MODE=private for local deployments.
Always blocked
Cloud provider metadata endpoints are blocked in both modes to prevent credential theft via SSRF:
169.254.169.254/32 (AWS, GCP, Azure instance metadata)
fd00:ec2::254/128 (AWS IMDSv2 IPv6)
Blocked in public mode
RFC-1918 and other reserved ranges are additionally blocked in public mode:
10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16 (private)
127.0.0.0/8, ::1/128 (loopback)
169.254.0.0/16, fe80::/10 (link-local)
fc00::/7 (IPv6 unique local)
100.64.0.0/10 (carrier-grade NAT)
0.0.0.0/32
DNS rebinding protection
Agent Vault resolves hostnames to IP addresses, validates every resolved IP against the block list, then connects directly to the validated IP. This prevents DNS rebinding attacks where a hostname resolves to a safe IP during validation but a different (internal) IP during connection.
Transport security
All proxied requests are forwarded over HTTPS, regardless of how the agent connects to Agent Vault locally. The agent’s Authorization header (containing its Agent Vault token) is stripped before forwarding; real credentials are injected server-side based on the vault’s services.
This means:
- Agents authenticate to Agent Vault with a token that grants no direct access to credentials.
- The agent’s token never reaches the target service.
- Credential values only exist in the outbound request headers, never in the agent’s address space.
Access control
Agent Vault enforces access at two independent layers. For full details, see Permissions.
- Instance roles (
owner, member) control who can manage users, vaults, and server settings. Both users and agents have instance-level roles. Owners can see and join any vault, but must explicitly join to access vault contents.
- Vault roles (
admin, member, proxy) control who can approve proposals, manage agents, and use the proxy within a specific vault.
- Agents are instance-level entities that can access multiple vaults via the
X-Vault header. Vault-scoped sessions (from vault run) are limited to a single vault.
- Proposal approval requires a valid user session. Read-only approval tokens let you view the proposal, but submitting an approval (providing credentials, clicking “Allow”) requires logging in.
- Email domain restrictions — Owners can restrict signups to specific email domains (e.g.,
@acme.com). This applies to email/password registration, Google OAuth signup, and vault invite acceptance. The first user (owner) is exempt. Configure via Manage > Settings in the web UI or agent-vault owner settings set --allowed-domains.
OAuth security
When Google OAuth is enabled, Agent Vault applies several security measures:
- No automatic account linking — OAuth callbacks never link to existing accounts from unauthenticated contexts. If a Google email matches an existing email/password user, sign-in is rejected with a generic error. This prevents pre-hijack attacks.
- Safe linking from settings — Authenticated users can connect OAuth providers from account settings, where identity is already proven.
- PKCE (S256) — All OAuth flows use Proof Key for Code Exchange to prevent authorization code interception.
- CSRF state parameter — 32-byte random state, SHA-256 hashed in the database, single-use, 10-minute expiry.
- ID token validation — RS256 signature verified against Google’s JWKS (cached 1 hour), with issuer (
accounts.google.com), audience (client ID), and expiry checks.
- No token storage — Google access and refresh tokens are not stored. Only ID token claims are used at login time.
- Generic error messages — OAuth errors do not reveal whether an email is registered.
- Timing attack prevention — Login attempts for OAuth-only users run a dummy KDF hash to prevent timing-based auth method detection.
- Owner protection — The first user must register with email/password. OAuth login is rejected when no users exist.
Rate limits
Built-in limits prevent abuse and resource exhaustion:
| Resource | Limit |
|---|
| Pending proposals per vault | 20 |
| Pending agent invites per vault | 10 |
| Pending user invites per vault | 50 |
| Active sessions per agent | 10 |
| Rules per proposal | 10 |
| Credential slots per proposal | 10 |
Database security
Agent Vault uses SQLite with the following hardening:
- File permissions: Database file is created with mode
0600 (owner read/write only).
- Parameterized queries: All queries use prepared statements, preventing SQL injection.
- WAL mode: Write-ahead logging for crash resilience.
- Foreign key cascades: Deleting a vault automatically cleans up all associated credentials, proposals, agents, and sessions.
Sensitive data handling
A summary of how each category of sensitive data is stored:
| Data | Storage format | Readable after storage? |
|---|
| Credentials | AES-256-GCM ciphertext + nonce | Yes, by vault members and admins via CLI (credential get, credential list --reveal). Proxy-role agents cannot read values. |
| User passwords | Argon2id hash + salt + params | No |
| Session tokens | SHA-256 hash | No (raw returned once at creation) |
| DEK (data encryption key) | Wrapped by KEK (password mode) or plaintext (passwordless mode) in master_key table | Yes, in memory while server runs |
| Master password | Not stored | N/A (KEK derived in memory, wiped after DEK unwrap) |
Key material is zeroed after use via WipeBytes() on a best-effort basis. Sensitive byte slices are never converted to strings (Go strings are immutable and cannot be wiped from memory).