Security
Designed so we can’t betray you, even if we tried.
If our database leaked tomorrow, the attacker would get ciphertext. If we were subpoenaed, we’d have nothing to hand over but ciphertext. That’s not marketing — it’s the architecture.
This page is the human-readable version. Engineers can read the full whitepaper and the open threat model.
Pillar 1
End-to-end encryption
When you sign up in zero-knowledge mode, your password is run through a deliberately slow function called Argon2id inside your browser. The output never leaves your device. What does leave your device is a verifier — a one-way hash of that output — plus your vault key, wrapped in a separate cipher that only your password can unwrap.
On our end, we store two things: the verifier (which is useless for decrypting anything) and the wrapped vault key (which is useless without your password). The actual document and note contents live as XChaCha20-Poly1305 ciphertext that we cannot inspect.
When you log back in, the same Argon2id derivation runs in your browser, produces the same output, unwraps your vault key locally, and you can read your data. We never see the master key. We never see the contents. We never see your password.
Pillar 2
Decoy mode and plausible deniability
For people who might be physically coerced to unlock their vault — domestic-abuse survivors, journalists, dissidents — a single password isn’t enough. You need to be able to give up a password that opens something believable, while the real vault remains invisible.
Every account in zero-knowledge mode has two vaults: a real one and a decoy one. They look identical on the wire. Logging in with either password opens its respective vault. The server cannot tell which is which — both passwords produce a verifier of the same shape, both vaults store the same kind of wrapped key, and the login response is byte-for-byte indistinguishable between them.
We also enforce a timing invariant: we continuously measure how long the server takes to validate the real password vs the decoy password. The two medians must stay within 1.5× of each other. A bigger gap would be a measurable signal an attacker could use, so we treat it as a release-blocking regression — a build that fails this check never ships.
Server-side mode (for people who don’t want to manage a password with no recovery) does not provide this same level of deniability, and we say so plainly during signup. See the decoy guide for when this matters and when it doesn’t.
Pillar 3
Recovery without us
Zero-knowledge has an honest cost: if we don’t know your password, we cannot reset it for you. The standard answer ("you’re out of luck") is unacceptable for the kind of data people put in Inktally, so we use Shamir secret sharing.
At setup, your recovery key is split into shares held by trusted contacts — chosen by you, named by you (we don’t see any of them). You decide how many contacts there are and how many of them are required to act together — for example, any two of three. Below that threshold no one can reset your password: one contact alone cannot, and neither can we.
The contacts don’t need to be technical. They receive an email with a one-click approval link when you ask for recovery. They never see your data. They’re the human equivalent of a safe deposit box that needs two keys to open.
Pillar 4
Tamper-evident audit log
Every action — login, upload, share, trigger fire — writes a row to a per-user audit log. The rows form a hash chain: each row carries the SHA-256 of all the data in the previous row, so if anyone modifies a row mid-chain, the link to the next one breaks and the whole chain stops verifying.
You can verify your own chain at any time from Settings → Audit log → Verify chain. We also re-verify every chain on our side around the clock and flag any divergence.
Decoy vault and real vault have separate chains, and the chain hash includes which vault the row belongs to. An attacker with database write access can’t shuffle rows between chains to hide what happened.
Pillar 5
Trigger delivery — what actually happens years later
When a trigger fires — a date passes, a check-in lapses, or you initiate manually — your recipients get email invites. But how does someone who wasn’t there when you set things up actually open the content, especially if they’ve never had an Inktally account?
This is the question the first four pillars don’t answer, so here is the honest version. Inktally supports two delivery modes, each with a different privacy trade-off. You choose which to arm.
Server-assisted (Track C). At arming time, you provide your password so your browser can re-seal each shared resource’s Data Key to Inktally’s sealing public key. We hold an AES-256-GCM-wrapped copy of that key — so yes, in this mode, Inktally holds something that can open your content after a trigger fires. The window is scoped: we exercise that key only after a trigger fires and a recipient verifies their email. The moment we re-seal it to the recipient’s own key, we discard ours. Every delivery is logged in your audit trail. If you never arm escrow, this window never opens.
Executor-only (Track B). Instead of the server, you seal each Data Key to a designated executor’s X25519 public key. We store those sealed bytes but cannot open them — only the executor’s private key can. When a trigger fires the executor is notified; they sign in, their client opens the sealed keys, and re-seals them to each recipient. The server never reads the Data Keys throughout. This mode trades convenience (the executor must actively respond) for a stronger privacy guarantee: we cannot deliver anything, with or without a subpoena.
Both modes use the same X25519 sealed-box scheme. The difference is whose keypair sits at the centre of the trust chain — ours, or your executor’s.
Verify, don’t trust
The things you should be able to check yourself.
- Read the threat model. We publish what we aren’t trying to defend against alongside what we are — coercion of trusted recovery contacts, malicious endpoints under your control, etc.
- Inspect the client. The encryption happens in your browser. Open devtools, watch the network tab, check that ciphertext is what we receive.
- Verify your audit chain. One button, no technical knowledge required. If it ever says broken, that’s a signal worth investigating.
More questions?
The help center has plain-English walkthroughs of every security feature on this page. Or read what happens if we go away.