Security model
ApiVault is built for developers who read the security model. This page covers encryption, authentication, session management, and best practices across all integration surfaces.
Encryption
All API key values are encrypted at rest before storage. ApiVault supports two encryption modes, configured in Settings → Encryption Key.
Default mode
Secrets are encrypted using server-managed keys. No vault key is required to reveal keys — your authenticated session (or CLI/MCP token with appropriate scope) is sufficient.
Custom mode
You set a personal custom vault key that ApiVault never stores. Secrets are encrypted with a key derived from your vault key. Decrypt operations require the vault key via:
- Web UI vault key prompt
- CLI:
--vault-key,APIVAULT_KEY, configvaultKey, or interactive prompt - MCP:
vault_keyparameter onreveal_key,add_key,update_key
You can switch between modes, change your vault key, or disable custom encryption from the encryption settings page. Changing or disabling custom encryption requires your current vault key and re-encrypts existing keys atomically. New or rotated custom keys use a unique salt and a memory-hard derivation. New custom keys must be at least 16 characters; legacy custom vaults remain readable with their existing derivation until you explicitly rotate them.
Authentication
The web app supports multiple authentication methods:
| Method | Description |
|---|---|
| Google OAuth | Sign in with your Google account. Manage linked accounts in Settings → OAuth Connections. |
| Email & password | Traditional credentials with email verification and OTP password recovery. Set or change password in Settings → Security & MFA. |
| Email OTP Verification | 6-digit one-time password sent via email to verify account ownership and facilitate secure password resets. |
| Passkeys | WebAuthn passkeys for passwordless sign-in (Touch ID, Face ID, Windows Hello, hardware keys). |
| TOTP 2FA | Time-based one-time passwords via an authenticator app. Can be required after initial setup. |
Passkeys can also serve as a second factor when MFA is enabled. The sign-in flow verifies the factor you actually use without exposing whether a particular email address has TOTP or passkey enrollment.
Email verification & OTP recovery
To prevent unauthorized account registration and enable secure self-serve password recovery, ApiVault implements cryptographically secure 6-digit One-Time Passwords (OTPs):
- SHA-256 Hashed Storage: Plaintext OTP codes are never stored in the database. Only SHA-256 hashes are persisted at rest.
- Time-Bounded Validity: OTP codes expire automatically after 10 minutes.
- Rate Limiting & Anti-Spam: A strict 60-second cooldown is enforced between code dispatch requests.
- Brute-Force Protection: Verification records automatically self-destruct after 5 failed attempts.
Sign-in attempt limits
Failed authentication attempts are counted per account and per source address, so a stolen password cannot be searched for and a six-digit second factor cannot be guessed exhaustively. A completed sign-in clears the counter, so ordinary use — including mistyping a password a few times — never reaches a limit.
| Attempt | Limit | Counted per |
|---|---|---|
| Password sign-in | 10 failures per 15 minutes | Account and source address |
| Authenticator (TOTP) code | 5 failures per 15 minutes | Account and source address |
| Custom vault key (web, CLI, and MCP) | 10 failures per 15 minutes | Signed-in user and source address |
| Email OTP code | 5 failures, then the code is destroyed | Issued code |
Vault-key attempts are counted per user rather than per project, so one member of a shared project cannot exhaust the allowance for their team-mates. Requests that exceed a limit are answered with 429 and a Retry-After header.
The vault-key limit applies wherever a vault key can be supplied — the web vault-key prompt, the CLI's --vault-key flag and APIVAULT_KEY, and the MCP reveal_key, add_key and update_key tools. All three resolve vault keys through the same throttled code path, so no client has a faster route to guessing one. A correct key clears the counter, so a scripted apivault run or env export that decrypts many secrets in a row is never throttled.
Sessions & tokens
ApiVault uses three independent credential types. Revoking one does not affect the others:
| Credential | Used by | Revoke via |
|---|---|---|
| Browser session | Web dashboard, settings | Settings → Sessions & Devices, or sign out |
| CLI token | apivault commands | apivault logout or Settings → Sessions & Devices |
| MCP OAuth token | Cursor, Claude, MCP clients | Settings → MCP Connections |
CLI tokens issued by the browser approval flow expire 90 days after they are minted, so a token that leaks has a bounded lifetime even if nobody notices. The expiry date of each connection is shown in Settings → Sessions & Devices, where a token can also be revoked at any time before it expires. Reconnect with apivault login to obtain a fresh token.
Browser request protections
Cookie-authenticated state-changing requests are restricted to the app's own origin. CLI and MCP integrations use bearer tokens instead, so they do not depend on browser cookies or cross-site request behavior.
Anonymous CLI connection requests are limited to five per source address per ten-minute window. Production reverse proxies must overwrite, rather than append to, forwarded-IP headers before forwarding requests to ApiVault.
Content Security Policy
Every response carries a Content Security Policy in which scripts execute only if they carry a nonce — an unguessable value generated fresh for that single response. A script injected into a page through stored content, such as a key name, a project name or a note, has no valid nonce and the browser refuses to run it. strict-dynamic extends that trust to the bundles those scripts load and makes origin allowlists inert, so there is no host an injected script can be served from instead.
Because a nonce only exists once a request is being served, every page is rendered per request rather than prebuilt. Responses also set Strict-Transport-Security, so browsers refuse to reach ApiVault over plain HTTP, alongside X-Frame-Options, X-Content-Type-Options, Referrer-Policy and a Permissions-Policy that denies camera, microphone and geolocation access.
CLI security
- Browser-based pairing — CLI auth uses a one-time browser approval flow. No passwords are typed in the terminal.
- Local token storage — tokens are stored in
~/.apivault/token.jsonwith owner-only permissions on Unix. - Dotenv isolation —
apivault runtemporarily hides local.envfiles so injected vault secrets take precedence. - No secrets in shell history — prefer
config set vaultKey(hidden prompt) orAPIVAULT_KEYover typing vault keys inline.
apivault run to inject secrets into a process instead of writing decrypted values to a .env file on disk. Fewer files means fewer leak paths.Service tokens
A pipeline cannot complete a browser approval, so it authenticates with a service token instead — a credential owned by a project rather than a person.
- Hashed at rest — only a SHA-256 hash is stored. The raw value is shown once at creation and cannot be recovered or re-displayed.
- Scoped — listing keys, revealing their values and modifying them are three separate grants, capped by the role of whoever created the token.
- Optionally pinned — to one environment, and to a set of IP ranges. A request whose source address cannot be determined is refused when an allowlist is set.
- Always expiring — between 7 and 365 days, defaulting to 90. There is no never-expires option.
- Confined to key endpoints — project settings, members, invitations, encryption configuration and token creation itself all reject them, so a leaked CI credential cannot add a collaborator, rotate your vault key, or mint a replacement for itself.
- Separately audited — every request is logged under its own
servicesource with the token's name, including refusals.
MCP security
- Scoped OAuth — agents receive only the permissions you approve (
keys:read,keys:write,keys:reveal). - Unverified clients are labelled — MCP clients register themselves, so the name and icon shown during consent are self-reported and are marked Unverified. The consent screen displays the redirect origin the authorization code would be sent to; verify it matches the tool you started the connection from before approving, especially for
keys:reveal. - Isolated token boundaries — MCP OAuth tokens are strictly isolated to the
/mcpendpoint and cannot be used to authenticate general REST APIs, enforcing cryptographic least privilege. - Least privilege — grant read-only scopes when agents only need to browse keys; avoid
keys:revealunless necessary. - Short-lived access tokens — MCP access tokens expire after 1 hour; refresh tokens last 30 days.
- Revocable connections — each MCP client appears separately in Settings → MCP Connections.
keys:reveal scope lets an agent read raw secret values. Only grant it when the agent truly needs plaintext keys, and revoke the connection when the task is done.See MCP Integration → Security practices for agent workflow guidance.
Best practices
- Never commit
~/.apivault/, exported.envfiles, tokens, or vault keys - Add
.envand.env.localto.gitignore - Use custom encryption mode for an extra layer of zero-knowledge protection
- Enable 2FA and passkeys on your account
- Review and revoke stale CLI and MCP connections periodically
- Use
apivault runinstead of exporting secrets to disk when possible - Do not paste raw secrets into chat, commits, logs, or issue trackers
- Request minimum MCP scopes for AI agents
.env, .env.local, and any exported dotenv files to .gitignore before your first commit. A single accidental push exposes every key in that file permanently in git history.Data storage
ApiVault stores encrypted key values, metadata (name, service, environment, notes), and account data in a MySQL database via Prisma. Secret values are never stored in plaintext. The CLI and MCP server communicate with ApiVault over HTTPS only — they never access the database directly.