Who holds the key
End-to-end encryption is a claim about the operator rather than a cipher. The honest question underneath it is key custody, and by that test our own vault fails.
Thomas
Founder · Forward-Deployed Engineer
“Encrypt it end to end and then you can’t read it.” Buyers say it and mean something reasonable by it. It is a claim about the operator rather than a cipher, so test it as one. Can the operator, holding the servers and the database, produce the plaintext? In a web application the operator also serves the code that does the encrypting, so the operator can ship code that hands the plaintext back.
Define end-to-end encryption by the test that fails it. RFC 9750, writing about messaging systems, puts the test plainly: “end-to-end” captures the notion that users enjoy some level of security even in the face of malicious actions by the operator of the messaging system.1 The operator is the adversary the phrase exists to name. What buyers want is key custody, meaning nobody else holds the key that opens their data. Two earlier pieces took the other layers, jurisdiction in May,2 then the premises in July.3 This one is the key, the layer where adding cryptography is usually the wrong move.
The browser is the operator’s code
Browser-delivered encryption has a delivery problem that no cipher fixes. The server holding your ciphertext also ships the script that encrypts it, fresh on every page load, at whatever version the operator shipped that day. That script can change tonight. E2EE in a tab is a promise about every deploy.
The Web Cryptography API warns authors that script injection is conceptually equivalent to remote code execution in other operating environments, and that hostile injected script may exfiltrate keys or data.4 Cloudflare, describing how it verifies the code WhatsApp Web serves, poses the defect directly: if the same website providing the download also provides the hash, a malicious actor could replace both, and the check would still pass.5 That moves the trust. It does not remove it.
If the operator serves the code that does the encrypting, the operator can serve code that does not.
The malicious-server case has been analysed rather than left hypothetical. MEGA states that its client-side encryption protects users from MEGA itself or an adversary controlling its infrastructure. In 2022 researchers at ETH Zurich analysed that setting and presented five attacks giving full compromise of file confidentiality, plus an integrity break that inserted files passing all client-side authenticity checks.6 The encryption was real. The threat model failed.
Key custody is the question with honest answers
Drop the acronym and ask about custody. Who holds the key material, on whose hardware, and what happens when they lose it? Three arrangements have shipped at scale, each with a published price.
- 01
Device custody puts the key on hardware the user already owns. With Advanced Data Protection on, the user’s trusted devices retain sole access to the encryption keys for the majority of iCloud data, and because keys are protected by iCloud HSMs, deletion of Apple’s copies of the service keys is immediate, permanent, and irrevocable.7
- 02
Hold your own keys is AWS’s name for the external key store pattern. KMS never interacts directly with your external key manager and cannot create, view, manage, or delete your keys, and your key material never leaves it.8 AWS then prices it: a major departure from the shared responsibility model whose availability and latency risk will, for most customers, exceed the perceived security benefits.8 A vendor arguing customers out of its own security feature is worth reading twice.
- 03
A secret the operator never receives costs the most, and Signal prints the bill in one sentence: because Signal does not have access to your keys or your data, your PIN is not recoverable if you forget it.9 No reset link exists, because a reset link needs a key somebody else holds.
Each of these three still leaves someone else writing the code that touches the key. A fourth arrangement moves the machines to you, though someone still writes the code, and its price is hardware you buy and staff who run it.3
Real end-to-end encryption takes features away
Proton cannot decrypt emails on its servers and therefore cannot search their content, so content search runs against a locally built index whose build time varies considerably with mailbox size.10 Now apply that to a research platform. Server-side search across a cohort goes. So does aggregate reporting over the encrypted fields. Retrieval over the corpus goes too, because server-side embeddings need plaintext to compute from. Password reset stops being a support ticket and becomes data loss. Name the fields that must be unreadable by the operator, then budget custody separately.
Ordinary server-side search over a field means the server reads that field.
The statutes are written around who applied the protection
The law reaches custody too. Obligations imposable by a UK technical capability notice include, at section 253(5)(c) of the Investigatory Powers Act 2016, the removal by a relevant operator of electronic protection applied by or on behalf of that operator.11 Australia’s Part 15 regime opens its list the same way: the first act a notice may specify is removing electronic protection applied by, or on behalf of, the provider.12 Section 317ZG bars a notice requiring a systemic weakness in electronic protection, and the OAIC records that terms such as “whole class of technology” are ambiguous enough that the limitation’s effect is still uncertain.13 Both statutes turn on one phrase, applied by, or on behalf of, the provider. Whether a script an operator serves to a browser counts as protection applied on that operator’s behalf is the kind of question the OAIC says is still unsettled,13 so moving the cipher into the client settles less than it looks.
Our own vault fails the test this piece proposes
Here is where we do encrypt. One table holds identifiers for study participants: public.study_identifier_vault, one ciphertext column, encrypted_pii bytea, one row per participant. The cipher is pgcrypto, pgp_sym_encrypt in and pgp_sym_decrypt out. The key is 32 random bytes generated at migration apply time into Supabase Vault under the name study_identifier_vault_key, where pgp_sym_encrypt takes it as a passphrase, so it appears in no git history, application code, or environment variable. The accessor private.study_vault_key() is granted to nobody.
Inside the database, plaintext exists only transiently, within two SECURITY DEFINER functions, decrypt_participant_pii and set_participant_pii, which return it to the caller. Each checks the caller’s role inside the function against data_controller and research_lead, reading roles from auth.jwt() app_metadata, wrapped in coalesce so a null claim denies rather than permits, and raising errcode 42501 otherwise. Every access writes a PII-free audit row from inside the function: actor, action, entity, entity id and a fixed reason string, no identifiers. Decrypt is deliberately VOLATILE rather than STABLE, because a STABLE function may not write and the audit insert is not optional.
The table carries exactly one RLS policy, a ciphertext-only select for those two roles, and no insert, update, or delete policy, so every write goes through the RPC. ENABLE and FORCE row level security are set on the vault and on the cohort and participant tables beside it, participants carry a pseudonym_code and no identifier columns, and a dependency-cruiser rule named no-ai-to-identifiable-vault keeps AI and embedding code out of the vault data access layer. Our architecture record ranks the layers: “Structural RLS isolation is the PRIMARY guarantee; encryption is defence-in-depth behind it.” The bound is narrow: “A PII leak now requires three independent layers to fail at once: the RLS policy, the DAL role gate + null-strip, and the in-function role check on the decrypt RPC.”
Now apply the test. A definer function reads the key inside the database and returns plaintext to a caller holding the right role. We hold the servers and we hold the database, so we can produce the plaintext. What we built is server-side encryption under a server-held key, and it is not end-to-end encryption.1 It defends against a wrong role. It was never scoped against a hostile operator, so by the test at the top our own vault fails.
The limits belong beside the claims. Key rotation is a documented future seam and is not implemented. pgsodium transparent column encryption was considered and deferred. Encryption covers exactly one PII surface, so candidate identifiers in the talent zone sit in plaintext columns behind row-level security alone. The per-client ingest HMAC secret is plaintext at rest in a private table with no grants, and that migration says production should wrap it with Supabase Vault or pgcrypto. Both WebCrypto calls in the codebase hash rather than encrypt, and they are one routine written twice, in the application and in its Edge Function mirror: SHA-256 over a normalised recovery code of 10 random bytes, 16 Crockford base32 characters, about 80 bits, digest only.
We hand over the machines rather than add a cipher
For a client who needs our infrastructure out of the path, a sovereign build moves the system onto hardware you own. You hold root on every machine, every credential, and the model weight files on your own disks. The repository, the CI pipeline and the infrastructure accounts are in your name from the first commit, and we work inside them. There is no Cogvera account your system depends on, and no credential we hold that you do not.
The platform we run today is different. Your data plane runs in-country: Postgres, auth and file storage in AWS Sydney. Two things sit outside it and we name them rather than round them off. This website is served from Vercel US compute, and model calls go to a US provider under zero-retention terms. No training on your data, no retention by model providers. Inference runs and leaves nothing behind. A sovereign build removes both of those exceptions.
SOC 2 and ISO 27001 are on our roadmap, not our wall. We have not yet delivered a client system into production, sovereign or otherwise, and we would rather write that down than imply a history we do not have. Capacity is one engineer and one build at a time. A cipher we serve from our own servers does not change who can produce the plaintext. Handing over the machines, on credentials you can revoke, does.
References
- 01Beurdouche, B., Rescorla, E., Omara, E., Inguva, S., and A. Duric, “The Messaging Layer Security (MLS) Architecture,” RFC 9750, RFC Editor (IETF), April 2025. Link ↗ ↩
- 02Cogvera Labs, “Onshore by default for Australian AI” (2026). Link ↗ ↩
- 03Cogvera Labs, “AI that never leaves the building” (2026). Link ↗ ↩
- 04W3C, “Web Cryptography API,” W3C Recommendation, World Wide Web Consortium, 26 January 2017. Link ↗ ↩
- 05Silverlock, M., “How Cloudflare verifies the code WhatsApp Web serves to users,” The Cloudflare Blog, 10 March 2022. Link ↗ ↩
- 06Backendal, M., Haller, M., and K. G. Paterson, “MEGA: Malleable Encryption Goes Awry,” IACR Cryptology ePrint Archive, Report 2022/959 (2022); published at the 44th IEEE Symposium on Security and Privacy (2023). Link ↗ ↩
- 07Apple Inc., “Advanced Data Protection for iCloud,” Apple Platform Security, 7 December 2022. Link ↗ ↩
- 08Amazon Web Services, “External key stores,” AWS Key Management Service Developer Guide (accessed 11 August 2026). Link ↗ ↩
- 09Signal, “Introducing Signal PINs,” Signal Messenger, 19 May 2020. Link ↗ ↩
- 10Martinoli, M., “Behind the scenes of Proton Mail’s message content search,” Proton, 31 August 2022. Link ↗ ↩
- 11Investigatory Powers Act 2016 (UK) c 25, s 253(5)(c), legislation.gov.uk. Link ↗ ↩
- 12Telecommunications Act 1997 (Cth) s 317E(1) item 1, Compilation No 115 (compilation date 4 April 2025), Federal Register of Legislation. Link ↗ ↩
- 13Office of the Australian Information Commissioner, “Telecommunications and Other Legislation Amendment (Assistance and Access) Act 2018: submission to the Independent National Security Legislation Monitor review,” OAIC, 1 October 2019. Link ↗ ↩