DEV Community

kh.vibecoding
kh.vibecoding

Posted on Originally published at habr.com AI-assisted

We open-sourced our vault's encryption code — and wrote honestly about what it doesn't guarantee

We build proxykey, a credential proxy: real API keys sit encrypted on our side, and apps and AI agents talk to us using revocable virtual tokens instead. We recently wrote about the scheme itself. Underneath that post and in DMs, one question kept coming up — the right question to ask any service like this:

"Can you read my keys yourselves?"

Short honest answer: technically, yes. Instead of hiding that behind marketing fog, we did two things: published the crypto module in full, and wrote a threat-model page where this point is first and in bold. This post is about why, and exactly what we opened up.

Why "zero-knowledge" is impossible here by design

A proxy has to decrypt the key to put it into the request to the provider — that's the entire job. A password manager can be zero-knowledge: the server never needs your password. A credential proxy can't be: at the moment of the request, the plaintext key exists in the process's memory.

That leads to an uncomfortable but unavoidable conclusion: a process with full access — and therefore the server operator — can in principle obtain the plaintext. This is a property of the architecture, not a bug. It's the same for every hosted solution in this category — secret managers, proxies, cloud vaults. The only difference is who says so out loud.

What the encryption actually protects against

Against a set of concrete, and much more likely, scenarios:

Scenario Protected?
Database dump leak Yes — secrets are encrypted, the master key isn't in the database
Backup theft Yes — same ciphertexts, no key
Log leak Yes — no keys or auth headers in logs
Database-only compromise Yes — without the KEK from the process environment, ciphertexts are useless
Virtual token leak Yes — IP binding, rate limits, one-click revocation
Full server compromise / malicious operator No — honestly, no

The full version of this table is on the security page.

What we open-sourced

The proxykey-crypto repo is exactly the envelope-encryption module that encrypts secrets in our production system. 450 lines of TypeScript with tests, no dependencies besides node:crypto:

  • every secret gets its own DEK; the plaintext is encrypted with AES-256-GCM using AAD = the secret's id (a ciphertext can't be silently swapped into another record);
  • the DEK is wrapped with a master key (KEK) that lives only in the process environment — it's never in the database;
  • decryption happens in memory for the duration of a single request; the DEK and the plaintext copy are zeroed in finally on every code path, including exceptions;
  • virtual tokens in the database are stored only as SHA-256 hashes;
  • 18 tests: roundtrip, ciphertext/tag/wrapper tampering, wrong AAD, wrong key version.

What the open code does NOT guarantee

Also stated plainly, because this is the other half of being honest:

  1. Open code is not proof. You can review the design, but a repository can't cryptographically prove that this exact code is what runs on the server. It's a transparency gesture, not an attestation.
  2. fill(0) is best-effort. V8 may have already copied buffers internally as part of its own operations, and a decrypted key stored in a JS string is immutable — you can't overwrite it, it lives until garbage collection. We narrowed the window; we didn't close it — and we're saying so.
  3. We validate the KEK's length, not its entropy. There's no KDF, on purpose: the master key is required to be a random key (openssl rand -base64 32), not a password.

The repo's README has a "Design notes" section answering the standard reviewer questions — the bounds on the random nonce for GCM, why the DEK wrapper isn't bound to the AAD (to allow future KEK rotation), and so on. If you find something to pick apart beyond that, file an issue — that's the best possible contribution to everyone's security.

How to reduce risk even against "the operator"

A practical takeaway for anyone using any hosted key vault, not just ours:

  • create scoped keys, not master keys: an OpenAI project key with a $20 budget instead of a key for the whole account — then even a full vault compromise costs you $20, not everything;
  • set limits on the provider's side — a second line of defense after the vault's own limits;
  • rotate the originals — reissuing at the provider zeroes out the value of anything that may have leaked earlier;
  • if you don't trust any of this — that's a rational position: the design is reproducible, the crypto module is open, go build your own instance.

Disclaimer

We're the people behind proxykey (proxykey.org). The service is free. This post isn't "trust us, we're secure" — it's the opposite: here's the boundary up to which it's secure, here's the code, and here's what's left resting on trust. We think that's more honest, and more useful for anyone deciding where to keep their keys.

I build ProxyKey at Hikmah Labs. You can try the vault at proxykey.org.

Top comments (0)