What Wundervault doesn't do
Every security product has a boundary. This page is ours — the intentional trade-offs, the things explicitly out of scope, and the things that simply aren't built yet. It is maintained alongside the code, not written for marketing.
When Wundervault is the wrong tool
- You need the model to reason about the secret itself. Wundervault's entire value is that plaintext never reaches the model. If your agent has to read, parse, or transform the credential in its own context, this is the wrong architecture.
- You need a fully air-gapped deployment. The vault is a hosted service. The client components are open source and auditable, but there is no self-hosted or offline server build today.
- You need a compliance attestation right now. There is no SOC 2, no HIPAA certification, and no completed third-party security audit. See Compliance & audit.
- You need dynamic, short-lived credentials. Wundervault stores and injects the credentials you give it. It does not mint short-lived database or cloud credentials the way a dynamic-secrets engine does.
- You need guaranteed uptime or geographic redundancy. See Architecture. Wundervault is not run redundantly and makes no contractual uptime commitment.
Agent usage — what the protections do and don't do
Four things get called "the agent" and they are not the same. The model is what reasons and what a prompt injection talks to. The MCP daemon is our code, running on your machine under your user account. The child process is the command the daemon spawns. The credential holder is anyone in possession of the agent's API key.
The real, durable boundary is that secret plaintext is never returned to the model: the daemon decrypts it and hands it to a child process (as an environment variable, on stdin, or via an askpass helper), then scrubs it from the output the model reads. So the model can use a secret without ever holding its value. What that does not mean is that the model cannot cause the secret to go somewhere — it chooses the command, and the command runs with the credential attached. Everything below is a layer around that boundary, not the boundary itself.
- Shell-escape and file-write blocking is a speed bump, not a wall.
vault_execrejects commands matching$(, a backtick,bash -c,sh -c, orevalbefore the secret is decrypted, and separately rejects>,>>,tee,dd,install, andsed -ias file-write vectors. These are literal pattern matches. They stop casual leakage and naive prompt injection. What they do not stop is the child process, which does hold the plaintext: a command the model chooses can write it to disk throughnode,python, or any interpreter. Treat it as defense-in-depth, not containment — and expect occasional false rejections of legitimate commands that happen to contain those tokens. - Nothing inspects intent, and nothing blocks network egress.
vault_execexists to run the commands an agent asks for. An injected instruction that produces an ordinary-looking command — one that sends the credential to a remote host in a request header — matches no blocklist, because the patterns above are literal string matches and there is no sandbox underneath them. Treatvault_execas a shell on your machine with a credential attached, and scope entries and tiers on that basis. - Output scrubbing is best-effort. The MCP server replaces literal occurrences of the plaintext in stdout and stderr with
[SECRET_REDACTED]. A command that base64- or hex-encodes, splits, or otherwise transforms the secret defeats that redaction. Do not rely on scrubbing to keep a transformed secret out of the output. - The signed directive is advisory. The directive shipped to an agent ("burn after reading, never store…") is integrity-verified, but it is guidance. A compromised or adversarial agent can ignore it. Its value is integrity and intent, not enforcement.
mcp_onlyis advisory, not enforceable. It checks for a staticX-Via-MCPheader that the MCP client sets; nothing binds that header to the daemon. A holder of the agent's credentials can replicate it on a raw REST call. It is a deterrent and an audit signal — a header cannot constrain a credential holder, because anything the daemon can compute, a credential holder can too.- A tier-1 secret in an agent's vault can be used by that agent at will. No further approval is asked for, and there is no per-secret capability inside a vault — scoping is the choice of which secrets you send the agent in the first place. The value still is not returned to the model, but "the model never sees it" and "the model cannot spend it" are different claims, and only the first one is ours.
- Tier-2 approval gates the request, not what happens after it. Marking a secret tier 2 makes every retrieval require your approval, granted per (agent, secret) pair as one use, a 15-minute window or a 60-minute window, and enforced server-side. Inside an open window the agent can retrieve as often as it likes, and no approval can be withdrawn from a secret already released — revoking a window stops the next call, not the copy in a running process.
- Agent credentials do not auto-expire or rotate. A leaked agent credential works until it is manually revoked. Rotation is manual — re-onboard or repair the agent.
- Revocation cannot reach what has already been used. Revoking an agent stops the next request. It cannot claw back a secret already loaded into a running process or written somewhere by the agent.
- There is no agent-scoped burn-after-read secret. The agent-facing one-time endpoints were removed on 7 September 2026 — they had never worked (every call authorised on columns nothing has ever set, and the retrieval path returned the ciphertext it had just erased), and the shape was wrong besides. An agent can still open an ordinary one-time link the way a person would, but be clear what burning buys there: it destroys the link, not the copy that is now in the agent's context, its transcript, and whatever the model provider retains. Single use earns its keep for a credential that is spent on arrival — agent onboarding works exactly that way — and not for one the agent will use again.
Encryption scope
- All secret content is encrypted client-side. One-time secrets, persistent vault secrets, and agent-vault entries are encrypted in the browser (or by the MCP client) before transmission. The server stores
{ciphertext, salt, nonce}plus a one-way verifier, and is architecturally unable to decrypt any of it. API calls attempting to send plaintext secret content are rejected with400. - The one non-zero-knowledge path was removed on 2026-08-11. Until then, agent onboarding uploaded discovered context files and held them server-side in plaintext for owner review. Worse than we had documented: those rows were deleted only when onboarding completed, so an abandoned run retained the plaintext indefinitely. That wizard and its endpoints are gone, and the plaintext table it wrote to is emptied and VACUUMed with no writer. The encrypted agent-context feature that replaced it was retired in turn on 2026-09-08: its endpoints, model and migration are gone, so no code path reads or writes context files any more, and the table itself is dropped when an instance starts. Every content path that remains encrypts client-side. Read that as it is meant: it is a claim about where encryption happens, resting on the honest-client assumption in the browser-trust bullet below — not a proof that a hostile server could never obtain plaintext.
- Metadata is visible to the server. Secret existence, name, TTL, creation timestamp, and access patterns are not encrypted. Only content is.
- Browser trust is the fundamental limit. Client-side encryption depends on the JavaScript the server serves. A malicious or compromised server could ship script that captures plaintext before encryption. Mitigations: the browser crypto and MCP server are published open source (AGPL-3.0), and you can verify the zero-knowledge claim yourself at your own network boundary with an intercepting proxy. That test cannot rule out targeted malicious JavaScript served only to specific users.
- Once revealed, a secret can be captured. A recipient can screenshot, copy, or photograph a revealed secret before it burns.
Authentication & multi-tenancy
- No self-service API key management for human accounts. The dashboard uses email/password login with optional WebAuthn (Face ID / Touch ID). Agent access uses per-agent bearer tokens, hashed at rest, with immediate revocation — but there is no multi-key management, per-key revocation, or per-key audit trail for human accounts.
- Account access depends on a device-local secret. The Account Secret lives in browser localStorage. A recovery code is required to restore access on a new device or after clearing browser data.
- The contact form has no user system. Messages go to an email address. No ticket tracking, no status updates, no submitter dashboard.
Secrets
- No edit or extend on one-time secrets. TTL and max-views can be patched on non-delete secrets; the unlock code is never changeable. Vault secrets can be renamed and re-encrypted.
- You can get your data out, in the form we hold it. Settings → export downloads every vault secret as JSON with the values still AES-256-GCM encrypted, decryptable with your vault passphrase, and the agent access log exports as CSV. Neither export is a plaintext dump, because we could not produce one — and both keep working if you decide to leave.
- No cross-instance portability. A secret created on one Wundervault instance cannot be moved to another — the encrypted payload is instance-specific.
- No bulk operations. No batch create, batch retrieve, or batch delete. Each secret is handled one at a time.
- Soft delete on burn — and this page was wrong about it until 7 September 2026. When a secret burns, the sealing read now nulls the ciphertext, salt and nonce in the same atomic statement, and the row remains with its metadata for audit. Until that date burning set a flag and nothing else, so every secret ever burned still held its ciphertext — seven of seven in production, back to the first in May, cleared by a migration. Scope it accurately: the blob is encrypted in the browser and the server never holds the passphrase, so a database dump still exposed only ciphertext. What was untrue is that burning destroyed anything. The same fix closed a race in which two readers opening one link at once could both be handed the payload.
Compliance & audit
- Not SOC 2 or HIPAA certified, and no third-party audit yet. The architecture reduces what the server can expose, but organizations in regulated environments should evaluate their obligations independently. We publish a security white paper, this page, and a reproducible verification procedure in the meantime.
- Anonymous one-time secrets are deliberately untraceable. There is no account and no requester identity on the anonymous path — no record of who retrieved a one-time secret. That is a feature of the anonymous product, not an oversight.
- Agent and vault access is logged and tamper-evident, not immutable. Every agent access is recorded with declared purpose, timestamp, and outcome, HMAC-chained with a server-side audit key. Each chain also carries a signed record of how far it runs, so removing its newest or oldest rows contradicts a signature that cannot be recomputed without the key. A server-side attacker holding that key could still rewrite history. Rows written before the chain shipped are unsigned.
- Entries written before 8 September 2026 have weaker coverage. They were signed under the earlier format, which does not cover which account or chain a row belongs to, and they have no extent record — so for those rows we can still detect an edit, but not a deletion at the ends. They age out within a year and nothing new is written to them.
- Deleting an account's log entirely, extent record and all, is not detectable from the database alone. Every check we run is a check of the database against itself, so an attacker who removes both the evidence and the record of what the evidence should look like leaves nothing to disagree with. Catching that needs an anchor kept somewhere we do not control — a digest mailed out, or written to storage with different credentials. We have not built that yet, and would rather say so than let "tamper-evident" imply it.
- The declared purpose is stored in plaintext and the server can read it. It is metadata, not secret content, and it has to be readable for the log to answer "why was this used" — a hash can only confirm a guess. Do not put secret material in a purpose string.
- The
accessed_attimestamp is not covered by the HMAC. Our storage layer does not round-trip timezone-aware timestamps byte-for-byte, which produced false tamper reports. Row ordering is protected by the chain instead, but an individual timestamp could be altered without invalidating a signature.
Architecture
- No redundancy, and no uptime guarantee. Wundervault is not run across multiple regions or replicas, and we make no contractual availability commitment. If it is down, it is down until we bring it back — plan for a credential path that can tolerate that, or keep an exported copy. What we can offer instead of a promise is the record: uptime is monitored from outside our infrastructure and published publicly, bad days included.
- Recovery is to the last snapshot, not the last transaction. Data is protected by scheduled snapshot backups. A failure between snapshots can lose the writes since the most recent one.
- Rate limiting is per-IP, not per-key. Users behind shared NAT share rate-limit buckets, and a determined attacker can rotate IPs. There is not yet a per-secret failed-attempt cap on unlock validation.
- We keep your agent activity for a limited time, and you set how long. Routine access — an agent read a secret, an agent ran a command, and the purpose it declared — is kept 30 days by default, adjustable to 60, 90 days or a year. Security events (Tier 2 decisions, revocations, anything blocked) are kept 365 days whatever you choose, because a 30-day window would discard the forensics on the day you need them. Export the log if you want a longer record of your own; what we no longer hold cannot be exposed.
- Webhooks are effectively instance-global. Each secret stores a callback URL, but it is copied from a single operator-configured environment variable when the secret is created. There is no way to set a different callback per secret or per user, and changing the global does not affect secrets already created.
Licensing & source availability
- The Wundervault platform is proprietary. The hosted service and server code are closed-source commercial software.
- The code that touches your secrets is open. The MCP server and local daemon (wundervault-mcp) and the browser crypto (wundervault-crypto) are published under AGPL-3.0, so you can audit exactly what runs on your machine and what leaves it.
Discussed, not built
Reasonable future work that is not currently implemented — listed here so it is never mistaken for a shipped feature.
- Per-secret webhook URL
- Per-secret failed-attempt cap and a server-side entropy floor on custom unlock codes
- Multi-key API auth with per-key revocation for human accounts
- Hard delete (permanent row removal)
- TTL extension on a burn-on-read secret (TTL and max-views can already be patched on a
delete=falsesecret) - Batch secret operations
- Payment-specific policy for agent wallets — spend caps and payee allowlists
- Sandboxed execution: running
vault_execcommands in a container or VM, with default-deny network egress and a destination allowlist - Agent credentials that expire on their own, and binding a token to a device or workload