VaultProof Documentation
Everything you need to store, protect, and proxy your API keys with zero-knowledge security.
What is VaultProof?
VaultProof is a security layer for API keys. It splits your keys using Shamir Secret Sharing, encrypts the shares separately, and reassembles them only for the milliseconds it takes to make a proxied API call. Your raw API key is never stored whole on any server.
It is built for developers who work with third-party API keys -- OpenAI, Anthropic, Google AI, Together.ai, and more. If you have ever committed a key to GitHub, emailed a key to a teammate, or stored a key in plaintext in a .env file on a server, VaultProof solves that problem.
The problem is real: In 2024, GitHub detected 39 million leaked secrets in public repositories. API keys are the number one attack vector for credential theft. One leaked OpenAI key can cost you thousands of dollars in minutes.
Split and encrypt API keys with Shamir Secret Sharing. No one -- including us -- can see the full key.
Make API calls through VaultProof. The key is reconstructed in memory for ~100ms, then destroyed.
Every proxied call is logged with timestamp, endpoint, status code, and latency. Full audit trail.
Quick Start
Three ways to get started. Pick the one that fits your workflow.
Path A: SDK (for applications)
npm install @vaultproof/sdk
import VaultProof from '@vaultproof/sdk' // Initialize with your developer API key const vault = new VaultProof(process.env.VAULTPROOF_API_KEY) // Store an API key (split + encrypted automatically) const key = await vault.store('sk-openai-key-here', 'openai', 'Production') // Make a proxied API call (key reconstructed for ~100ms, then zeroed) const res = await vault.proxy(key.id, '/v1/chat/completions', { model: 'gpt-4', messages: [{ role: 'user', content: 'Hello!' }] })
Path B: CLI (for terminal and CI/CD)
npm install -g @vaultproof/cli
# Authenticate vaultproof login # Store a key (you'll be prompted to paste it securely) vaultproof store --provider openai --label "Production" # Make a proxied API call vaultproof proxy --key KEY_ID --path /v1/models --method GET
Path C: Dashboard (for manual management)
- Go to vaultproof.dev/app and create an account or log in
- Navigate to Keys and click Add Key
- Paste your API key, select the provider, and add a label
- Click Test Key to verify it works through the proxy
- Go to Settings → API Keys to create a
vp_live_developer key for SDK use
How It Works
A deep dive into the cryptographic pipeline that protects your keys.
3.1 Key Storage Flow
When you call vault.store(), here is exactly what happens:
You call vault.store('sk-openai-key', 'openai', 'Production'). The SDK receives the raw key in local memory.
The SDK uses Shamir Secret Sharing over GF(256) with polynomial interpolation to split the key into exactly 2 shares. Each share alone is cryptographic garbage -- indistinguishable from random bytes. Neither share reveals any information about the original key.
Share 1 is sent to the VaultProof server, where it is encrypted with AES-256-GCM using the server-side VAULT_ENCRYPTION_KEY. A unique IV and salt are generated per encryption. The ciphertext and auth tag are stored in PostgreSQL.
Share 2 is encrypted with AES-256-GCM using a key derived from your vp_live_ developer API key. This encrypted share is also stored server-side, but it cannot be decrypted without your developer key.
The original API key is zeroed from memory immediately after splitting. It never leaves your machine whole. From this point forward, the key only exists as two separately encrypted shares.
The server returns a keyId (UUID). You use this ID for all future proxy calls. The SDK caches Share 2 locally for subsequent requests.
Key point: At no point does the VaultProof server possess enough information to reconstruct your API key on its own. The server holds encrypted Share 1 (locked with the server key) and encrypted Share 2 (locked with your developer key). Both decryption keys are needed.
3.2 Proxy Call Flow
When you call vault.proxy(), the key is temporarily reconstructed to make the API call on your behalf:
You call vault.proxy(keyId, '/v1/chat/completions', body). The SDK sends your developer key in the X-API-Key header over HTTPS.
The request hits Cloudflare's edge network. The edge worker HMAC-signs the request with HMAC-SHA256 and forwards it to the backend. Unsigned or rate-limited requests are rejected at the edge.
The backend retrieves the encrypted Share 1 from PostgreSQL and decrypts it using VAULT_ENCRYPTION_KEY with AES-256-GCM. The auth tag is verified to detect tampering.
The backend decrypts Share 2 using a key derived from your vp_live_ developer key (sent in the request header). Both shares are now in plaintext in server RAM.
The two shares are combined via Shamir polynomial interpolation to reconstruct the original API key. The full key now exists in RAM.
The server makes the HTTP request to the provider (OpenAI, Anthropic, etc.) using the reconstructed key in the appropriate auth header. Streaming (SSE) is supported for LLM APIs.
Immediately after the API call completes, the reconstructed key and both plaintext shares are overwritten with zeros in memory. The key existed in RAM for approximately 100ms. It never touches disk, logs, or persistent storage.
The provider's response is forwarded directly to your application. For streaming responses, SSE events are piped through in real time.
The call is logged with: timestamp, endpoint path, HTTP method, response status code, latency (ms), provider, and key ID. The request/response body is never logged.
3.3 What Happens in a Breach
VaultProof is designed so that compromising any single component does not expose your API keys. Here is what an attacker gets in each scenario:
| Scenario | What Attacker Gets | Can They Reconstruct Keys? |
|---|---|---|
| Database breached | Two encrypted blobs per key (AES-256-GCM ciphertext). Without either decryption key, this is indistinguishable from random data. | No |
| VAULT_ENCRYPTION_KEY leaked | Can decrypt Share 1. But Share 2 is still encrypted with the user's vp_live_ key. One share alone reveals nothing (Shamir property). |
No |
| vp_live_ key leaked | Can decrypt Share 2. But Share 1 is still encrypted with VAULT_ENCRYPTION_KEY. One share alone reveals nothing. |
No |
| Database + VAULT_ENCRYPTION_KEY | Decrypted Share 1. Encrypted Share 2 (still locked). | No |
| Database + vp_live_ key | Encrypted Share 1 (still locked). Decrypted Share 2. | No |
| All three compromised | Both shares decrypted. Shamir reconstruction possible. | Yes -- requires all three |
Three independent secrets must all be compromised simultaneously to reconstruct any API key: (1) the database contents, (2) the server-side VAULT_ENCRYPTION_KEY, and (3) the user's vp_live_ developer key. These are stored in different systems with different access controls.
3.4 Shamir Secret Sharing Explained
The simple version
Think of it like tearing a photo in half. Each half looks like random noise -- you cannot tell what the photo is from either piece alone. Only when you put both halves together do you see the picture. VaultProof tears your API key into two pieces of cryptographic noise. Each piece is then locked in a separate safe with a separate key. To see the photo again, you need to open both safes.
The technical version
Shamir Secret Sharing (SSS) works over a finite field -- in VaultProof's case, GF(256) (the Galois Field with 256 elements, where each element fits in one byte).
For each byte of the secret, the algorithm constructs a random polynomial of degree k-1 (where k=2 is the threshold). The constant term of the polynomial is the secret byte. The shares are evaluations of this polynomial at distinct non-zero points in GF(256).
With a 2-of-2 threshold scheme, this means: a random polynomial f(x) = a1*x + s is constructed for each byte s of the key, where a1 is a random coefficient. Share 1 = f(1), Share 2 = f(2). Given only one share, the attacker has one equation with two unknowns -- every possible value of the secret is equally likely.
Reconstruction uses Lagrange interpolation over GF(256) to recover the original polynomial from 2 evaluation points, then reads the constant term (the secret byte).
3.5 Zero-Knowledge Proofs
For advanced security, VaultProof uses zero-knowledge proofs (ZK proofs) written in Noir and verified with Barretenberg. The ZK circuit proves three things without revealing any sensitive data:
You own the key slot you are requesting access to. The proof verifies your vault commitment hash matches without revealing the key material.
The requesting application has been granted access to this key slot. The proof checks the app grant without exposing the grant chain.
The request is fresh and not replayed. Each proof includes a unique nullifier that is stored server-side. If the same nullifier appears twice, the request is rejected.
Note: When using the SDK, ZK proof generation is handled automatically. You do not need to interact with the Noir circuit directly. The SDK generates proofs client-side before each proxy call.
SDK Reference
The JavaScript/TypeScript SDK for storing keys and making proxied API calls.
Installation
npm install @vaultproof/sdk # or yarn add @vaultproof/sdk # or pnpm add @vaultproof/sdk
Constructor
new VaultProof(apiKey: string, apiUrl?: string)
| Parameter | Type | Required | Description |
|---|---|---|---|
apiKey |
string |
Yes | Your vp_live_ or vp_test_ developer API key |
apiUrl |
string |
No | Custom API URL. Default: https://api.vaultproof.dev |
import VaultProof from '@vaultproof/sdk' // Production const vault = new VaultProof(process.env.VAULTPROOF_API_KEY) // Custom API URL (self-hosted or staging) const vault = new VaultProof('vp_test_...', 'https://staging-api.vaultproof.dev')
Methods
vault.store(apiKey, provider, label?)
Splits an API key using Shamir Secret Sharing, encrypts both shares, and stores them on the VaultProof server. The raw key is destroyed after splitting.
| Parameter | Type | Required | Description |
|---|---|---|---|
apiKey |
string |
Yes | The raw API key to store |
provider |
string |
Yes | 'openai' | 'anthropic' | 'google' | 'together' |
label |
string |
No | Friendly name (e.g., "Production", "Staging") |
Returns:
{ id: "uuid", provider: "openai", label: "Production" }
vault.proxy(keyId, path, body?, method?)
Makes a proxied API call through VaultProof. The key is reconstructed in server RAM for approximately 100ms, then zeroed. The provider's response is returned directly.
| Parameter | Type | Required | Description |
|---|---|---|---|
keyId |
string |
Yes | Key slot ID returned from store() |
path |
string |
Yes | API endpoint path (e.g., '/v1/chat/completions') |
body |
object |
No | Request body forwarded to the provider |
method |
string |
No | HTTP method. Default: 'POST' |
Returns:
{ status: 200, data: { /* provider response */ }, ok: true }
vault.keys()
Lists all stored keys associated with your developer API key.
Returns:
[
{ id: "uuid", provider: "openai", label: "Production", createdAt: "2026-03-24T..." },
{ id: "uuid", provider: "anthropic", label: "Staging", createdAt: "2026-03-22T..." }
]
vault.revoke(keyId)
Permanently destroys both encrypted shares on the server. This is irreversible. The key can never be reconstructed after revocation.
| Parameter | Type | Required | Description |
|---|---|---|---|
keyId |
string |
Yes | Key slot ID to revoke |
Returns: void
Supported Providers
| Provider | Value | Base URL | Auth Header |
|---|---|---|---|
| OpenAI | 'openai' |
api.openai.com |
Authorization: Bearer |
| Anthropic | 'anthropic' |
api.anthropic.com |
x-api-key |
| Google AI | 'google' |
generativelanguage.googleapis.com |
x-goog-api-key |
| Together.ai | 'together' |
api.together.xyz |
Authorization: Bearer |
CLI Reference
Command-line tool for managing keys from your terminal or CI/CD pipelines.
Installation
npm install -g @vaultproof/cli
Global Flags
| Flag | Description |
|---|---|
-h, --help | Show help for any command |
-v, --version | Show CLI version |
--json | Output results as JSON (works on all list commands) |
--api-url <url> | Override API URL for this command |
$ vaultproof -h # overview + quick start guide $ vaultproof store -h # detailed help for store command $ vaultproof keys --json | jq # pipe JSON output to jq
Authentication
vaultproof login
alias: l
Log in to your VaultProof account. Stores JWT token at ~/.vaultproof/config.json. Token expires after 7 days.
| Flag | Description |
|---|---|
-e, --email <email> | Email address (skips prompt) |
$ vaultproof login $ vaultproof login -e you@example.com $ vaultproof l
vaultproof logout
Log out and clear stored credentials from ~/.vaultproof/config.json. Does not affect your VAULTPROOF_API_KEY env var.
vaultproof register
alias: signup
Create a new VaultProof account. Password must be at least 8 characters. Automatically logs you in after registration.
| Flag | Description |
|---|---|
-e, --email <email> | Email address (skips prompt) |
vaultproof whoami
alias: me
Display the currently authenticated user's email and creation date.
vaultproof status
alias: st
Check connection, authentication, and API key status at a glance.
$ vaultproof status VaultProof Status API: https://api.vaultproof.dev (vaultproof-edge) Auth: Logged in as you@example.com API Key: vp_live_abc1...ef89 Config: ~/.vaultproof/config.json
Key Management
vaultproof store
alias: add
Store an API key with Shamir splitting. The key is split locally -- the server never sees the full key.
| Flag | Description | Required |
|---|---|---|
-p, --provider <name> | API provider (openai, anthropic, google, etc.) | Yes |
-l, --label <text> | Label for this key | No |
$ vaultproof store -p openai API Key: ************************ Key stored: vk_8f3a2b1c (openai) Key was Shamir-split locally. Server never saw the full key. $ vaultproof store -p anthropic -l "Production Claude" $ vaultproof add -p google -l "Vertex AI"
vaultproof keys
alias: ls
List all stored API keys with provider, label, and creation date.
| Flag | Description |
|---|---|
--json | Output as JSON for scripting |
$ vaultproof keys ID Provider Label Created ------------------------------------------------------------ vk_8f3a2b openai Production GPT-4 2d ago vk_c91ef4 anthropic Claude Sonnet 5h ago 2 keys total $ vaultproof keys --json | jq '.[].provider'
vaultproof revoke <keyId>
alias: rm
Permanently revoke a key. Both encrypted shares are zeroed on the server. This cannot be undone.
| Flag | Description |
|---|---|
-y, --yes | Skip confirmation prompt |
$ vaultproof revoke vk_8f3a2b1c Permanently revoke key vk_8f3a2b1c? This cannot be undone. (y/N) y Key vk_8f3a2b1c revoked. Both shares destroyed. $ vaultproof rm vk_8f3a2b1c -y # skip confirmation
Proxy Calls
vaultproof proxy
alias: call
Make a proxied API call. The server decrypts both shares, reconstructs the key for ~100ms, makes the call, then zeros everything.
| Flag | Description | Required |
|---|---|---|
-k, --key <keyId> | Key ID from vaultproof keys | Yes |
--path <path> | API path (e.g., /v1/chat/completions) | Yes |
-m, --method <method> | HTTP method (default: POST) | No |
-d, --body <json> | Request body as JSON string | No |
-f, --file <path> | Read request body from a JSON file | No |
# GET request $ vaultproof proxy -k vk_8f3a2b1c --path /v1/models -m GET # POST with inline JSON body $ vaultproof proxy -k vk_8f3a2b1c --path /v1/chat/completions \ -d '{"model":"gpt-4","messages":[{"role":"user","content":"Hello"}]}' # POST with body from file $ vaultproof proxy -k vk_8f3a2b1c --path /v1/chat/completions -f request.json # Using the alias $ vaultproof call -k vk_8f3a2b1c --path /v1/models -m GET
Access Logs
vaultproof logs
View recent access logs for all proxied API calls.
| Flag | Description | Default |
|---|---|---|
-k, --key <keyId> | Filter by key ID | All keys |
-n, --limit <n> | Number of entries | 20 |
--json | Output as JSON | -- |
$ vaultproof logs $ vaultproof logs -n 50 $ vaultproof logs -k vk_8f3a2b1c $ vaultproof logs --json | jq '.[] | .endpoint'
Developer API Keys
Developer keys (vp_live_ / vp_test_) authenticate your SDK and CLI requests. They are different from the API keys you store in the vault.
vaultproof dev-key create
parent alias: dk
Create a new developer API key. The full key is shown once -- save it immediately.
| Flag | Description | Default |
|---|---|---|
-l, --label <text> | Label for this key | "default" |
-m, --mode <mode> | live or test | live |
$ vaultproof dev-key create Developer API key created: vp_live_a8Kj3mNp9xRt2wQz7vBc... Save this key -- you won't see it again! Write to .env file in current directory? (y/N) y Written to .env $ vaultproof dk create -l "CI/CD" -m test $ vaultproof dk create -l "Production" -m live
vaultproof dev-key list
alias: dk ls
List all developer API keys (masked).
| Flag | Description |
|---|---|
--json | Output as JSON |
vaultproof dev-key revoke <id>
Revoke a developer API key. All SDK/CLI calls using this key will immediately stop working.
| Flag | Description |
|---|---|
-y, --yes | Skip confirmation prompt |
Configuration
vaultproof config
Show current CLI configuration: API URL, auth status, developer key, and config file path.
vaultproof config set <key> <value>
Update a config value.
$ vaultproof config set api-url https://api.vaultproof.dev $ vaultproof config set api-url http://localhost:3333
vaultproof config reset
Clear all configuration (token, email, preferences). Asks for confirmation.
Environment Variables
| Variable | Description | Default |
|---|---|---|
VAULTPROOF_API_KEY |
Developer API key -- required for store, keys, proxy, revoke, and logs commands | -- |
VAULTPROOF_API_URL |
Custom API base URL (overrides config file and default) | https://api.vaultproof.dev |
# 1. Create an account and log in $ vaultproof register $ vaultproof login # 2. Create a developer API key $ vaultproof dev-key create # writes VAULTPROOF_API_KEY to .env # 3. Load the env var $ export VAULTPROOF_API_KEY=vp_live_... # 4. Store an API key $ vaultproof store -p openai -l "GPT-4 Production" # 5. Make proxied calls $ vaultproof proxy -k vk_8f3a --path /v1/models -m GET # 6. Check everything is working $ vaultproof status
Dashboard
Web interface for managing keys, viewing logs, and monitoring usage at vaultproof.dev/app.
Overview Page
Your home screen after login. At a glance you see:
- • Total keys stored -- active key count across all providers
- • API calls this month -- total proxied requests with trend indicator
- • Usage chart -- daily call volume for the past 30 days
- • Key health -- per-key error rates and last-used timestamps
- • Recent activity -- last 10 proxy calls with status
Keys Page
Full key management interface:
- • Add Key -- paste a key, select provider, add a label. Shamir splitting happens in-browser.
- • Test Key -- one-click test that makes a lightweight proxy call to verify the key works.
- • Rotate -- replace the stored shares with a new API key without changing the key slot ID.
- • Revoke -- permanently destroy both shares. Irreversible.
- • Code snippets -- copy-paste SDK and cURL examples for each key.
Logs Page
Complete audit trail of every proxied API call:
- • Search -- filter by key, provider, status code, date range
- • Details -- timestamp, endpoint, method, status, latency (ms)
- • Export CSV -- download logs for external analysis or compliance
Settings Page
Account and security configuration:
- • Profile -- email and account details
- • Password -- change password (requires current password)
- • Developer API Keys -- create, list, and revoke
vp_live_keys - • Plan -- view current tier (Free/Pro/Team/Enterprise) and usage limits
API Reference
Base URL: https://api.vaultproof.dev
All requests go through Cloudflare's edge network (330+ cities). HMAC-signed and rate-limited. All request and response bodies are JSON (Content-Type: application/json).
Authentication
/api/v1/auth/register
Create a new account.
// Request { "email": "you@example.com", "password": "min8chars" } // Response (200) { "token": "eyJ...", "user": { "id": "uuid", "email": "you@example.com" } } // Error (409) { "error": "Email already registered" }
/api/v1/auth/login
Login and receive a JWT token (valid 7 days).
// Request { "email": "you@example.com", "password": "yourpass" } // Response (200) { "token": "eyJ...", "user": { "id": "uuid", "email": "you@example.com" } } // Error (401) { "error": "Invalid credentials" }
/api/v1/auth/me
Auth required
Get current user profile.
// Headers Authorization: Bearer <jwt_token> // Response (200) { "id": "uuid", "email": "you@example.com", "plan": "free", "createdAt": "..." }
/api/v1/auth/password
Auth required
Change password.
// Request { "currentPassword": "oldpass", "newPassword": "newpass8+" } // Response (200) { "status": "updated" }
/api/v1/auth/account
Auth required
Delete account and all stored keys. Irreversible.
Key Management
All key endpoints require Authorization: Bearer <jwt>.
/api/v1/keys/store
Store a Shamir-split API key.
// Request { "provider": "openai", "label": "Production GPT-4", "share1": "base64...", // Shamir Share 1 "vaultCommitment": "hex...", // Hash commitment for ZK "appId": "my-app", // optional "appName": "My App" // optional } // Response (201) { "keySlotId": "uuid", "grantId": "uuid", "status": "stored" }
/api/v1/keys/list
List all keys with app grants.
// Response (200) { "keySlots": [{ "id": "uuid", "provider": "openai", "label": "Production GPT-4", "status": "ACTIVE", "createdAt": "2026-03-24T...", "appGrants": [{ "id": "uuid", "appId": "my-app", "appName": "My App" }] }] }
/api/v1/keys/revoke/:keySlotId
Revoke a key. Destroys Share 1 (zeroed in DB). Irreversible.
/api/v1/keys/:keySlotId/grant
Grant an application access to a key slot.
// Request { "appId": "new-app", "appName": "New App" } // Response (201) { "grantId": "uuid", "status": "granted" }
/api/v1/keys/:keySlotId/revoke-app
Revoke an application's access to a key slot.
// Request { "appId": "old-app" } // Response (200) { "status": "revoked" }
/api/v1/keys/:keySlotId/logs
Get access logs for a specific key.
// Query params: ?page=1&limit=50 // Response (200) { "logs": [{ "id": "uuid", "endpoint": "/v1/chat/completions", "method": "POST", "statusCode": 200, "latencyMs": 847, "createdAt": "2026-03-24T..." }], "total": 1284, "page": 1 }
/api/v1/keys/:keySlotId/rotate
Rotate the stored shares with a new API key. The key slot ID stays the same.
// Request { "share1": "base64...", // new Shamir Share 1 "vaultCommitment": "hex..." // new commitment } // Response (200) { "status": "rotated" }
/api/v1/keys/:keySlotId/logs/export
Export access logs as CSV for compliance and external analysis.
SDK Endpoints
Authenticated with X-API-Key: vp_live_... header.
/api/v1/sdk/store
Store an API key via the SDK. The server handles Shamir splitting and encryption.
// Request { "apiKey": "sk-...", "provider": "openai", "label": "Production" } // Response (201) { "keyId": "uuid", "status": "stored" }
/api/v1/sdk/call
Make a proxied API call. Key is reconstructed ephemerally.
// Request { "keyId": "uuid", "share2": "base64...", "path": "/v1/chat/completions", "method": "POST", "body": { "model": "gpt-4", "messages": [...] } } // Response: provider's response forwarded directly
/api/v1/sdk/keys
List all stored keys for this developer API key.
/api/v1/sdk/revoke
Revoke a stored key.
// Request { "keyId": "uuid" } // Response (200) { "status": "revoked" }
Developer Key Management
Authenticated with Authorization: Bearer <jwt>.
/api/v1/dev-keys/create
Create a new developer API key.
// Request { "label": "Production", "mode": "live" } // Response (201) -- key shown ONCE { "id": "uuid", "key": "vp_live_abc123...", "label": "Production", "createdAt": "2026-03-24T..." }
/api/v1/dev-keys/list
List all developer API keys (masked).
// Response (200) { "keys": [{ "id": "uuid", "maskedKey": "vp_live_abc1...f456", "label": "Production", "lastUsedAt": "2026-03-24T...", "createdAt": "2026-03-24T..." }] }
/api/v1/dev-keys/:id/revoke
Revoke a developer API key. All SDK calls using this key stop working immediately.
Proxy
/api/v1/proxy/call
Make a proxied API call with ZK proof authorization. Supports SSE streaming.
// Request { "keySlotId": "uuid", "share2": "base64...", "zkProof": "hex...", "nullifier": "hex...", // unique per request "appId": "my-app", "targetPath": "/v1/chat/completions", "method": "POST", "stream": false, "body": { "model": "gpt-4", "messages": [{ "role": "user", "content": "Hello!" }] } } // Response: provider's response forwarded directly // If stream: true, SSE events are piped through
Note: The SDK handles ZK proof generation, nullifier creation, and Share 2 management automatically. You only need to use this endpoint directly if you are building a custom client.
Stats & Analytics
Authenticated with Authorization: Bearer <jwt>.
/api/v1/stats/overview
Dashboard overview stats.
// Response (200) { "totalKeys": 5, "callsThisMonth": 1284, "errorRate": 0.02, "activeApps": 3, "recentActivity": [...] }
/api/v1/stats/usage?days=30
Daily call volume for charting.
// Response (200) { "usage": [ { "date": "2026-03-24", "calls": 47 }, { "date": "2026-03-23", "calls": 62 }, ... ] }
/api/v1/stats/by-key
Per-key usage breakdown.
// Response (200) { "keys": [{ "keyId": "uuid", "provider": "openai", "label": "Production", "totalCalls": 892, "errors": 12, "lastUsedAt": "2026-03-24T..." }] }
Error Codes
| Code | Meaning | Common Cause |
|---|---|---|
400 |
Bad Request | Missing or invalid fields in the request body |
401 |
Unauthorized | Missing, expired, or invalid JWT / API key |
403 |
Forbidden | ZK proof verification failed, app not authorized, or nullifier replayed |
404 |
Not Found | Key slot ID does not exist or has been revoked |
409 |
Conflict | Email already registered, duplicate nullifier |
429 |
Too Many Requests | Rate limit exceeded (60/min edge, 100/min backend) |
500 |
Internal Server Error | Server-side failure. Contact support. |
502 |
Bad Gateway | Provider API returned an error during proxy call |
All error responses follow the format: { "error": "Human-readable message" }
Security Architecture
Eight layers of defense between your API keys and an attacker.
Shamir Secret Sharing
API keys are split into 2 shares using polynomial interpolation over GF(256). Each share alone is information-theoretically secure -- no amount of computation can extract the secret from a single share. This is not encryption (which can be brute-forced); it is mathematically impossible.
AES-256-GCM -- Share 1 Encryption
Share 1 is encrypted at rest with AES-256-GCM using the server-side VAULT_ENCRYPTION_KEY. Each encryption uses a unique random IV and salt. The GCM auth tag detects any tampering with the ciphertext.
AES-256-GCM -- Share 2 Encryption
Share 2 is encrypted with AES-256-GCM using a key derived from the user's vp_live_ developer key. This means even with full server access, Share 2 cannot be decrypted without the user's key.
HMAC-SHA256 Edge Signing
All requests are HMAC-SHA256 signed at Cloudflare's edge network before reaching the backend. The backend rejects any request without a valid HMAC signature. Direct access to the backend is blocked -- all traffic must flow through the edge.
JWT Authentication
User sessions use JWT tokens with 7-day expiry. Developer API keys (vp_live_) are hashed with bcrypt and stored in the database. The raw key is never stored.
Nullifier Replay Prevention
Every proxy call requires a unique nullifier (a random hex string). Used nullifiers are stored in the database. If an attacker intercepts a request and tries to replay it, the server rejects it because the nullifier has already been consumed.
Rate Limiting
Two layers of rate limiting: 60 requests/minute per IP at the Cloudflare edge, and 100 requests/minute per user at the backend. Brute-force and DDoS attempts are throttled before they reach the application.
Noir ZK Proofs
Zero-knowledge proofs (written in Noir, verified with Barretenberg) cryptographically prove key ownership, app authorization, and request freshness without revealing any sensitive data. Proof verification happens on every proxy call.
Memory Zeroing
After every proxy call, the reconstructed API key and both plaintext shares are explicitly overwritten with zero bytes in memory. The key exists in server RAM for approximately 100ms. It is never written to disk, swap, or logs.
TLS Everywhere
All traffic between the client, Cloudflare edge, and backend uses TLS 1.3. All traffic between the backend and API providers uses TLS. No plaintext transmission anywhere in the pipeline.
Plan Limits
| Tier | Price | Key Slots | Proxied Calls/mo | Support |
|---|---|---|---|---|
| Free | $0 | 3 | 1,000 | Community |
| Pro | $12/mo | 20 | 50,000 | |
| Team | $39/mo | 100 | 200,000 | Priority |
| Enterprise | Custom | Unlimited | Unlimited | Dedicated |
Frequently Asked Questions
Common questions from developers evaluating VaultProof.
"Can VaultProof see my API keys?"
No -- and this is by design, not by policy. Your API key is split into two shares using Shamir Secret Sharing before it is stored. Share 1 is encrypted with a server-side key. Share 2 is encrypted with your developer key. Neither share alone contains any information about the original key (this is an information-theoretic guarantee, not a computational one). The only time the full key exists is during a proxy call, when both shares are decrypted and combined in RAM for approximately 100ms, then immediately zeroed. There is no "admin mode" that can view stored keys.
"What if I lose my vp_live_ developer key?"
Create a new developer key from the Dashboard (Settings → API Keys). You will need to re-store any API keys that were encrypted with the old developer key, because Share 2 was encrypted using that key and cannot be decrypted without it. This is a security feature -- it means a stolen developer key can be rotated and the old one becomes useless.
"What if VaultProof goes down?"
If the VaultProof server is unavailable, proxied API calls will fail because the key cannot be reconstructed. However, your API keys at the provider (OpenAI, Anthropic, etc.) are completely unaffected. VaultProof does not modify, rotate, or delete your provider keys. You can always use your original API keys directly if needed. We recommend keeping a backup of critical keys in a secure location for disaster recovery.
"Is this open source?"
The Noir ZK circuit is open source so the cryptographic proofs can be independently audited and verified. The SDK and CLI are distributed as npm packages. The backend server and edge proxy are proprietary.
"What providers are supported?"
Currently: OpenAI, Anthropic, Google AI (Gemini), and Together.ai. Each provider has its own base URL and auth header format, which VaultProof handles automatically. More providers are coming -- contact us if you need a specific provider.
"How is this different from 1Password or HashiCorp Vault?"
1Password and HashiCorp Vault store secrets and give them back to you in plaintext when you need them. Your application then uses the key directly, meaning it exists in your app's memory, potentially in logs, and definitely in network traffic. VaultProof never gives you the key back. Instead, it makes the API call for you using the reconstructed key, which only exists in VaultProof's RAM for ~100ms. Your application never touches the raw key after initial storage.
"What about HIPAA / SOC 2?"
Enterprise tier with compliance certifications is coming. The current architecture (encryption at rest, TLS in transit, audit logging, access controls) is designed with compliance in mind. Contact us at team@vaultproof.dev for enterprise inquiries.
"Does VaultProof add latency to my API calls?"
Yes, but it is minimal. The overhead is the time to decrypt both shares and reconstruct the key (a few milliseconds) plus one additional network hop (Cloudflare edge to backend). For most LLM API calls, which take 500ms-5s to complete, VaultProof adds less than 50ms of overhead -- typically under 2% of the total request time.
"Can I use VaultProof in production?"
Yes. VaultProof runs on Cloudflare's edge network (330+ cities worldwide) with multi-region database replication. The SDK uses vp_live_ keys for production and vp_test_ keys for development/staging environments.