Threat model

Who we defend against — and what we don’t.

A security product that only lists its strengths is marketing. This is the honest version: the adversaries we design against, the guarantees we make, and the things we deliberately leave out of scope.

This is the plain-English mirror of our internal engineering threat model.

Scope

What we do — and don’t — guarantee

In scope — guarantees this product makes:

  • Confidentiality of your vault contents (documents, notes, metadata) against a server operator with full database access.
  • Plausible deniability via the decoy vault — a coercer who obtains the decoy password cannot prove the real vault exists.
  • Recovery through secret-shared trusted contacts, without the server ever learning your recovery key.
  • Sharing to non-Inktally recipients without leaking the shared resource to the server.
  • Triggered release with server-assisted delivery: resources designated for release are held under our key between arming and delivery so recipients who have never used Inktally can still receive them. Every delivery is audit-logged; everything else in your vault stays end-to-end encrypted. (For fully server-blind delivery, the executor-assisted mode — coming soon — keeps our servers out of the loop entirely.)
  • A tamper-evident audit log that detects server-side edits to your history.

Out of scope — what we do NOT defend against:

  • An adversary who controls your device at the moment you unlock. If your endpoint is compromised, no server-side design can save the plaintext you just decrypted.
  • Side-channel attacks against your hardware (cache timing, Spectre/Meltdown, fault injection).
  • Quantum adversaries — XChaCha20-Poly1305 and X25519 are not post-quantum.
  • Availability — we are a confidentiality and integrity product, not a high-availability one. DoS and outages are not in this model.
  • Legal compulsion of the server to start logging new data going forward. Past data stays encrypted; future collection is a surface this product can’t close.

Adversaries

The four classes we design against

Ordered by capability — each tier can do everything the one before it can.

1. Passive network observer

Can: Sees your TLS-encrypted traffic to and from the server.

Defended by: TLS in transit, no plaintext personal data on the wire, and login responses that are constant-shape regardless of whether an account exists.

2. Honest-but-curious server operator

Can: Reads the database, storage, and logs in real time — but doesn’t inject rows or tamper.

Defended by: Zero-knowledge encryption: every sensitive field is ciphertext under a key derived on your device. Every account holds exactly two vault rows, so the operator can’t tell which password opens which. The audit chain detects after-the-fact edits.

3. Malicious server operator

Can: Injects arbitrary rows, modifies code paths, and — critically — can serve a backdoored client.

Defended by: Stored data stays useless without your client-derived key. But a backdoored web client that exfiltrates your key after derivation is NOT defended against in the current version — an inherent limit of any web-served crypto product without an out-of-band integrity check. We say so plainly (see limitations).

4. Coercive third party

Can: Physically or legally compels you to reveal a vault password — and may want proof you aren’t hiding a second vault.

Defended by: The decoy vault. You reveal the decoy password; the coercer unlocks a functional but separate vault and cannot distinguish it from 'the real one'. Limits are documented below.

Decoy invariants

What keeps the decoy believable

The decoy vault carries the highest-capability adversary, so it has the strictest invariants. Each is enforced in code and covered by tests:

  • Constant-shape login. The login endpoint always returns exactly two vault blobs, in randomised order — whether or not the email is registered, whether or not a decoy was set up, and whether or not it was later disabled. Verifier comparison runs in constant time against both rows.
  • No surface reveals the vault kind. No header, body field, status code, or URL differs between a real and a decoy session for the same operation. Cross-vault access collapses to a 404 (“not found”), never a 403 — a 403 would confirm the resource exists.
  • One-transaction disable. Disabling the decoy deletes every decoy-scoped resource in a single database transaction and resets the decoy row to a deterministic placeholder.
  • Manual-only triggers in a decoy session. A decoy session can’t configure automatic (dead-man’s-switch or scheduled) triggers — only owner-initiated ones — so the decoy can never auto-release and expose its own existence.
  • Indistinguishable timing. We continuously measure how long the server takes on the real-password path vs the decoy-password path; the medians must stay within 1.5× or the release is blocked. In our benchmarks the ratio is ~1.06×.

Known limitations

The honest caveats

These do not break the current security contract, but you deserve to know them. We’d rather state them than have you discover them.

  • 2FA and recovery contacts are shared across vaults. A coercer who unlocks the decoy can see that you have recovery contacts and 2FA — which hints a real vault exists behind it.
  • Notification preferences are user-scoped. Quiet-hours and channel settings are visible from either vault. They don’t reveal contents — only rough usage patterns.
  • Account existence can be enumerated by login timing. An unknown email is answered faster than a registered one. This confirms whether an address is registered — it does not leak vault contents or the existence of a decoy.
  • Storage cleanup after decoy disable is lazy. Orphaned encrypted blobs are swept by a background worker rather than deleted instantly. They’re already unreachable (no row, no envelope); the added surface is zero.
  • Client integrity. The web client is served from the same origin as the API, so a malicious or compelled operator could push a client that exfiltrates your key after you derive it. There is no out-of-band integrity check in the current version.

Quick surfaces

Recovery, sharing, and triggers

  • Recovery. Shamir secret sharing with a threshold you choose: any k of your n trusted contacts (k ≥ 2) can authorize a reset; fewer cannot, and neither can we. The server only ever stores ciphertext shares; the quorum coordinates out-of-band.
  • Sharing. The claim link is single-use, time-bounded, and stored only as a hash. The actual file key never sits in plaintext on the server — your client re-wraps it to the recipient’s public key; their client unwraps it with their private key.
  • Triggers. When a release fires, Inktally delivers a sealed envelope to the recipient — not plaintext. For owners who pre-armed escrow, the server briefly holds an encrypted copy of the resource key between arming and delivery, then re-seals it to the recipient's own key. After delivery only the recipient can decrypt; the server retains no useful key material. Manual shares from a living owner follow the same server-blind re-wrap pattern as ordinary sharing above.

Want the cryptographic detail?

The security whitepaper walks through the key hierarchy and primitives, and the security overview is the plain-English summary.