In short
- A random 256-bit master key encrypts each user's data. The master key is never persisted. Only wrapped copies of it are stored.
- A user passphrase wraps one copy. Neither the passphrase nor the trustee mechanism ever touches the documents directly, so changing either means re-wrapping a small key blob instead of re-encrypting everything.
- Posthumous access runs through a second wrapped copy whose unwrapping key is XOR-split between a trustee and Ninebarc, so neither side can unlock alone.
- Release passes four independent gates. The default outcome of any uncertainty is that nothing unlocks.
- The trust boundary sits at rest, deliberately, and I can tell you exactly where it sits and what I gave up to put it there.
The constraints
Two requirements pulled in opposite directions, and each had a concrete failure mode.
| Requirement | What failure looks like |
|---|---|
| Documents unreadable by anyone, including Ninebarc, for years | A breach, a subpoena or a curious insider exposes a family's estate |
| Documents released reliably to the right people after death | Beneficiaries permanently locked out of what they are legally entitled to |
| Release triggered by an event no system can observe directly | Data released while the user is alive, or never released at all |
| Neither the user nor the data outlived by the company | An acquisition, a migration or a shutdown strands the key material |
The design had to make each of those structurally hard and not merely unlikely.
Threat model
Stating this explicitly is what makes the later claims checkable.
In scope
- Full database dump, including backups and replicas
- A malicious or compelled employee, me included
- The cloud provider
- A court order or government request for stored data
- An acquirer's engineers after a change of control
- A single malicious trustee
- A beneficiary who wants early access
- A forged death certificate
Out of scope
- A compromised user device or browser
- A user coerced into revealing their own passphrase
- An attacker holding production deploy rights (see below)
- Collusion between a Ninebarc insider and a named trustee
The key model
On account creation each user got a random 256-bit master key. That key encrypted the user's documents with AES-256-GCM. The master key itself was never written to disk. What the database held were wrapped copies of it, and a wrapped copy is worthless without the secret that unwraps it.
Wrapping a key rather than the data is the decision the rest of the system rests on. Because the passphrase protected a 32-byte blob and not a gigabyte of PDFs, changing a passphrase, adding a trustee, revoking one, or introducing a new recovery path all became a matter of re-wrapping, not re-encrypting. Systems that encrypt data directly under a user secret end up unable to change anything about that secret, and then they grow a backdoor to cope.
For a user who set a passphrase, the stored state was ciphertext plus a copy of the master key locked under a secret Ninebarc had never seen. Accounts that declined the passphrase got a weaker arrangement: the master key wrapped under a server-held key, which protects against a stolen database and nothing else. Those are two genuinely different guarantees and I kept them visibly different in the product rather than averaging them into one marketing claim. Everything below about zero-access refers to the first tier.
Where the trust boundary sits
This is the decision I get asked about most, so I want to be precise:
Key derivation and decryption ran on the server, in request scope. I considered doing both in the browser and decided against it. Browser crypto does not remove trust from the server, it moves it from the running process to the JavaScript bundle that same server ships on every page load. A compromised or compelled server can push code that captures the passphrase, and the user cannot tell. Meaningfully closing that gap requires signed, reproducibly built native clients, which was not a commitment a solo engineer could make credibly. Alongside that, the browser would have cost me Argon2id at server-grade parameters (WebCrypto offers PBKDF2, and WASM Argon2 gets tuned down to whatever the weakest phone tolerates), server-side malware scanning of documents that beneficiaries would open years later, and a workable multi-beneficiary delivery path.
Having chosen the server, I spent the budget on making the server hold as little as possible for as short a time as possible:
- The master key was never cached. It was derived, used and discarded within the request that needed it. No session-scoped master key, in process memory or anywhere else.
- The passphrase lived in the session encrypted under a rotating key, so the session store on its own was inert. Dumping it yields ciphertext whose key was never in that store and has since rotated.
- Passphrase fields were excluded from request logging and from APM traces, and the app tier produced no crash dumps.
- No server-side rendering, indexing, thumbnailing or full-text search of decrypted documents. Plaintext existed in a request and nowhere else, which cost the product features I would otherwise have liked to ship.
So the honest claim is a scoped one, and it is still the claim that matters:
The database, the backups, the replicas, the session store, the cloud provider, the acquirer and a subpoena all hit a structural wall. An attacker holding production deploy rights does not, and no design running in a browser we served would have stopped that one either.
Trustees and the split
A dead user cannot supply a passphrase, so posthumous access needs a second path that does not depend on them.
When a user added a trustee, they authenticated with their passphrase, and the system generated a fresh random 256-bit recovery key, wrapped a second copy of the master key under it, then XOR-split the recovery key into two halves. One half went to the trustee. The other stayed with Ninebarc in the cloud secret store, deliberately outside the application database that held the wrapped master-key copies, under separate credentials. A dump of either store alone yields nothing: the halves are a two-of-two one-time-pad split, so one half reveals exactly zero bits of the other.
Worth separating two things that sound alike. The cloud secret store was custody for one half, not the cryptography. No wrapping operation ever called a cloud KMS, which is why the whole scheme stayed portable and why the migration later cost what it cost.
Ninebarc holding a half was not an accident of convenience, it was the point. It is what binds the cryptographic unlock to the procedural gates below. A trustee with a forged death certificate still cannot decrypt anything, because the server only releases its half once the gates have passed. Cryptography enforces the policy instead of documenting it.
With several trustees, each got an independently wrapped copy under their own split recovery key, so any single trustee could complete an unlock. That was an availability choice with a cost I accepted: security against a rogue trustee is the weakest of the n, not the strongest. One unreachable trustee should not permanently lock a family out of a will.
The release flow
A system that guards data perfectly and releases it carelessly has only moved the risk. Four independent gates stood between an inactive account and any released byte.
Gate 1 was an inactivity trigger the user configured, in the simplest case a periodic email check-in with retries and exponential backoff before the account was treated as unresponsive. Crossing the threshold decrypted nothing. It only opened a case and started outreach.
Gate 2 contacted the account holder first on every channel on file, then the trustees and beneficiaries. Any "still alive" response was a hard abort. A false positive from a bounced mailbox died here.
Gate 3 required a genuine Sterbeurkunde, the death certificate issued by the German Standesamt, reviewed by a person, followed by a fixed objection window during which every named party was notified. Silence never auto-released. With no certificate, the case simply stalled and the documents stayed encrypted.
Gate 4 enabled the trustee login, released Ninebarc's half of the recovery key and accepted the trustee's. Every state transition was written to an append-only, tamper-evident audit log, and every transition notified every named party, so no release could happen quietly.
In production the flow never released an account in error.
What it defended against
Mapped back to the threat model:
- Database breach. An attacker who dumped the database got ciphertext and wrapped key blobs. Nothing in that dump is usable without a secret held somewhere else.
- Insiders, including me. No single person could unwrap a passphrase user's master key, and no single store held both a recovery-key half and the copy it unwraps.
- Legal and government requests. Ninebarc could not produce plaintext it had no path to. A request for a passphrase user's documents hit a structural wall rather than a policy one. Under GDPR that is also the difference between a reportable exposure of personal data and a breach of opaque ciphertext.
- A rogue beneficiary or a forged certificate. Neither yields a key. Gate 4 is cryptographic, not procedural.
The one thing the product sold was that you could hand a company your estate and it would still be yours. That property was not a feature of the product, it was the product.
Known limits
Four, stated because a reader will find them anyway, each with what actually contains it.
- Lost passphrase with no trustee is unrecoverable. There is no override, because an override is precisely the backdoor the system exists in order not to have. Recoverability and zero-access pull against each other and I chose zero-access as the property that could not bend. It cost real support conversations, and I would have every one of them again rather than ship a master override.
- An active session is the soft spot. The master key was never cached, but for a logged-in user the passphrase existed server-side, encrypted under a rotating key. That is a much smaller window than caching a master key and it survives a session-store dump, but it is not nothing, and it is the reason the trust boundary section is scoped the way it is.
- The master key is briefly in the clear during trustee-add. It is unwrapped, re-wrapped under the new recovery key, and discarded, never written to disk. Given limit 2 this is one moment among several rather than a unique exception, which I would rather say than imply the key never existed in cleartext anywhere.
- Release is all-or-nothing. One trustee unlock releases every document to every beneficiary. Estate documents are not uniformly sensitive, so this is the design's real product limitation and not a security one.
Death certificate review was also human, and humans can be socially engineered. It sits behind the inactivity trigger and the liveness veto, and even a perfect forgery gets an attacker nowhere without a trustee's half.
Outside scrutiny
The design went through three rounds of external review in two years.
The first was the technical due diligence ahead of the 2023 acquisition by a German publishing group. I walked their engineers and their counsel through the key model directly, and the questions that took longest were the two you would expect: what happens to a user who forgets their passphrase, and what the company could produce if a court asked. Both answers were the same answer, which is the point of the design.
Then two penetration tests, run by different firms. Neither produced a change to the key model or the release flow.
The third round was the least expected and the most useful. After the acquisition the platform migrated from AWS to Azure, which forced a full architecture review. Because the wrapping and splitting were built on standard primitives rather than a managed key service, the scheme was cloud-agnostic by construction: the migration moved opaque ciphertext, wrapped key blobs and one secret from Secrets Manager to Key Vault. At no point was any plaintext or master key materialised. The only things that changed were where the encrypted key material rested and the least-privilege configuration around it. A design that had used a cloud KMS for the wrapping itself would have turned that migration into a re-encryption project across every user's data, with a window during which somebody had to hold the keys.
What I would build differently now
- Per-document data keys wrapped by the master key, instead of encrypting everything under the master key directly. It fixes limit 4, and it buys real product capability: different documents to different beneficiaries, per-document rotation, streaming uploads.
- Escrow for business continuity. The product promises a release that may be decades out, and the company was acquired inside three years. Ninebarc's halves should have had a notarised escrow path and users a downloadable recovery kit, so the guarantee outlives the company.
- Key rotation as a first-class operation. If a trustee half leaked, the only real remedy was rotating the master key, which meant re-encrypting everything. I never built the tooling, and I should have built it before I needed it.
The part that generalises is the one I would keep: decide where the trust boundary sits before you write any crypto, say it out loud, and then make the boundary do the enforcement, so that no policy, no promise and no future owner of the company has to.