This is section 12 of the Kontaks architecture document, published unedited. It is written for a reader who wants the engineering, and it includes what Kontaks does not defend against. Cross-references such as §6.1.3 point at other sections of documents we have not published in full; they are left in place rather than removed.

12. Security & Threat Model

Written for Sam. Every claim here must be one we would defend in a public audit.

What Kontaks defends against

ThreatDefense
Unsolicited contact / spam / robocallsThere is no address to acquire. Reaching a device requires a route the recipient issued and a key held in that sender's hardware
Contact-list resale or breachWe hold no contact lists, names, numbers, or emails. There is nothing to resell
A contact leaking your address to a third partyThe routing handle is inert without the paired private key, and the key is non-extractable
Server reading messages or hearing callsDouble Ratchet for messages; DTLS-SRTP with pair-signed fingerprints for media. Keys never reach the server
Server MITM of a callThe DTLS fingerprint is signed with the pair key; a substituted fingerprint fails verification
Server MITM of a pairingAn 8-digit PIN the server never sees, over a slow-hashed lookup key and a deliberately expensive bundle KDF (CRYPTO §6.1, §6.3) — or an in-band DIK fingerprint carried in the QR. Holding the entire invite table does not let the server complete a pairing at any permitted TTL
Push provider learning who talks to whom by namePayloads carry a route id and ciphertext only
Stolen or seized device (locked)Local database is SQLCipher, keyed from the Device Root Key; the DRK is enclave-bound, non-extractable, and gated on unlock + biometry
Device backup extraction (iCloud, iTunes, adb, forensic tooling)The DRK is ThisDeviceOnly and non-extractable, so it is absent from every backup. A restored backup yields a device that can decrypt nothing and is not on the roster
Rogue device enrollmentA roster entry must chain to a key your contacts already trust, or be ARK-signed — which is visibly discontinuous, alerted on every surviving device, and delayed 24 h (§6.1.3)
SIM swapNot applicable. Kontaks never uses a phone number for anything (§6.2)
Social engineering of supportStructurally impossible. Support is advisory, never operative (§5.9, §6.15) — there is no account action a support agent can be talked into taking, because none exists. Someone impersonating you to our support gets the same thing you would: an explanation
Database dumpYields: device push tokens, opaque route ids, verify keys, ciphertext ≤ 7 days old, opaque roster blobs. No names, no numbers, no plaintext, and no way to link the two directions of a pair
Harassment by an accepted contactUnilateral, instant, irreversible route destruction with no notification and no appeal

12.2 What the addressing claim does and does not cover

What we can and cannot say about contacts

"We never know your contacts" is not a claim this product can make. The precise position:

TrueNot true
We do not store your contact list. No such field exists in any collection"We never know your contacts"
We never see names. The name you present travels only inside end-to-end encryption, to the one person or room you chose; renames never leave your device; no collection has a field for either"Names are never transmitted" — they are, end-to-end, when you pair
We cannot read your messages or hear your calls"We have no contact graph" as an absolute
We do not know who your 1:1 contacts are — routes are unlinked, and only timing correlates them

The exception is groups, and it is structural. A group post is one batched request carrying an envelope for every member's every device. That batch is the membership list, delivered to us in a structured request, every time anyone posts. Sending 87 separate requests instead would cost battery and latency and they would still arrive on one connection within the same second — so group membership exposure is the price of groups working at all, not a defect we have left unfixed.

Kontaks removes the reusable address. It does not make you anonymous to the people you talk to. Inside a room, members necessarily see one another. The honest inventory:

Exposed inside a roomReusable outside it?
The name a member chose to present to that roomNot as an address — unresolvable, not a lookup key anywhere. But it is pre-filled from a local default (SPEC §6.3), so an unedited one repeats across that person's rooms and 1:1 relationships and is recognizable to anyone in two of them. Recognition, not reach — D23, D25
Who else is in the roomNo, but it is a real social-graph fragment revealed to every other member
Message content, and writing style with itUnavoidable
Invite provenance — "Tyler · admitted by Jess"A relationship assertion, deliberately visible (§6.8.b)
Voice, if they send voice messages or take callsBiometric. Unavoidable in a voice product
An avatar, if they set onePotentially their face

None of it grants reachability, and all of it dies when they leave — but "no identity" would not be a defensible summary of that table, and this document should never make one.

12.2.a What the server can infer about who talks to whom

The schema stores no contact list: routes are one-way rows with no field linking the two directions of a relationship, and no field grouping one person's routes (§17). That is a true statement about storage and a much weaker one about practice.

What it gives an observer with server access
Group sends are batched — one request carrying an envelope for every member's every deviceThis is the significant one. A batch of 87 envelopes arriving together is the group. Not an inference from timing — a direct, structured observation delivered in a single request
Timing correlation between two routesStrong evidence that they are the two directions of one relationship
Push tokensTie routes to devices, and devices to a person's hardware
Route creation in burstsA group forming, and roughly who is in it
Device revocation burstsThe second significant one. Killing a device issues ~750 signed deletes in a short window (SPEC §6.1.1, which owns the figures; §6.1.2 is the user-facing flow). The revoke_pubkey field is not a correlator at rest — every value is distinct — but the arrival pattern groups those routes as one account. Rare and conditional, unlike group batching; and it happens at exactly the moment the user is under duress. Mitigated by prioritising recent contacts and spreading the tail over an hour, which is weak cover at small user counts
Load-balancer IP logs (30 days, §13)Network-level correlation of devices and sessions

On batching, specifically — this was an implicit choice and it should be an explicit one. The alternatives are real and none is free:

Cost
Batch (current)The server observes group membership with near-certainty
Separate requests per envelope87 HTTPS requests per group message. Battery and latency on mobile
Batch with padding, shuffling and jitterPartial mitigation, real complexity, and still correlatable

We batch, and we say so. Pretending otherwise would be worse than the exposure. The honest claim is "we do not record a contact graph" — never "we cannot determine one."

12.2.b This document is not the cryptographic specification

CRYPTO.md is authoritative for protocol. This PRD is the product view.

The protocol is necessarily described across several sections here — key inventory in §6.1.a, pairing in §6.2, primitives and novelty in §12.3, the inventory below, and wire-level records in Appendix A. A specialist cannot review a protocol scattered through a product document, and an implementer working from these sections alone will fill gaps by guessing.

So: write CRYPTO.md before the crypto code (§12.4), make it self-contained, and treat it as the single reviewable artefact. Where this document and CRYPTO.md differ on protocol, CRYPTO.md wins and the PRD is the defect.

12.2.c What Kontaks does not defend against — stated because pretending otherwise is worse than the gap

  1. A contact behaving badly. They can screenshot, relay, or read your messages aloud. They are a legitimate recipient; cryptography has nothing to say about it.
  2. Recognition by the people you have paired with or share a room with. The name you presented, your voice, your avatar, your writing, the fact of your membership, and who vouched for you are all visible to the person or room you presented them to — and to a determined observer there, that may be enough to identify you in the ordinary sense. What they cannot do is turn any of it into a way to reach you, find you, or link you to another room (§12.2). Reusing one name and avatar everywhere creates that link, and the product pre-fills a default name, so reuse is the path of least resistance rather than a deliberate act (SPEC §6.3, D23, D25). The link is legible only to someone already in both relationships; it is still not a lookup key and still yields no route to you.
  3. Traffic-analysis metadata, and group membership in particular. The server sees which device is awake, envelope sizes, and timing per route, and group sends arrive batched, which reveals membership directly (§12.2.a). The two directions of a pair are stored as unrelated rows — we do not record the link — but we do not claim it cannot be determined.
  4. A compromised endpoint, or a device stolen while unlocked. Malware, a jailbreak, or a snatched unlocked phone reads everything on that device. The app lock and panic wipe (§6.1.2) shrink the window; nothing closes it. What we do bound is the blast radius: it is one device on your keyring, revocable in seconds, with no action required from any contact — unlike a SIM swap, which takes every relationship at once and cannot be undone.
  5. Your recovery secret. It is the one bearer credential in the system. We minimized what it can do (sign roster entries only — it cannot read, send, or decrypt history) and made using it slow and loud (§6.1.3), but someone who obtains your 12 words, or your Apple/Google account together with a device passcode, can enroll a device. Tier 4 (no recovery at all) exists for users who would rather lose their contacts than carry that risk.
  6. A pre-compromised push token. Apple and Google know a device received a push and when.
  7. Coerced disclosure of your own device. Rubber-hose applies as it always does. There is no duress mode; the panic wipe (§6.1.2) is a wipe, not a plausible-deniability mode.
  8. A malicious build. Reproducible builds and a published binary transparency log are worth doing and are not achievable for an iOS app today. Decided: we publish build provenance instead — signed GitHub Actions attestations and published artifact hashes for every release, so a build is tied to a commit even though it cannot be byte-reproduced. Sam will ask; that is the honest answer.

Cryptographic inventory

PurposePrimitive
Messaging sessionlibsignal — the Signal Protocol library itself (PQXDH + Double Ratchet; PQXDH is X3DH plus a Kyber-1024 KEM), not a reimplementation
Identity signingEd25519 (software) / ECDSA P-256 (Secure Enclave). Algorithm tagged on the wire
Route authorizationEd25519 signature over the request. Not novel — a signature check
Device rosterAppend-only log of signed entries. Same shape as a certificate chain
Pairing authenticationArgon2id + HKDF + AEAD over the full invite token (12-letter Part A + 8-digit Part B), with a slow-hashed lookup key (CRYPTO §6.1)
MediaDTLS-SRTP, fingerprint signed by the pair key
Local storageSQLCipher (AES-256), key sealed under the Device Root Key, WhenUnlockedThisDeviceOnly
Device root keySecure Enclave (P-256) / StrongBox / WebAuthn PRF extension on the web. Non-extractable; seals all other on-device key material
Recovery escrowiCloud Keychain / Google Block Store (both E2EE, HSM-rate-limited by the platform), or BIP-39 phrase → Argon2id
Roster backupXChaCha20-Poly1305; key in iCloud Keychain / Block Store, or from a BIP-39 phrase via Argon2id
Invite lookupArgon2id(Part A), t=2, m=32 MiB (CRYPTO §6.1) — slow on purpose; Part A is never stored
TURN credentialsHMAC-SHA1 ephemeral (coturn use-auth-secret), 5-minute validity

12.3 Novelty is deliberately minimal

The probability of being broken, and the cost of reviewing it, both scale with how much of the cryptography is ours. Almost none of it is.

ComponentWhat it is
1:1 messaging sessionlibsignal — the Signal Protocol library itself, not a reimplementation. Groups are pairwise fan-out of 1:1 sessions, not Signal's group protocol. No sealed sender
Route authorizationAn Ed25519 signature over the request, checked against a stored public key
Device rosterAn append-only log of signed entries — the same shape as a certificate chain
PairingArgon2id + HKDF + AEAD over the full invite token, slow-hashed lookup key. No PAKE
Call mediaDTLS-SRTP with fingerprint verification — the standard WebRTC pattern

What remains to review is protocol logic, not cryptographic mathematics: whether the roster chain really stops a rogue device, whether the recovery flow has a hole, whether the invite entropy budget is right, whether the fingerprint binding really prevents a server MITM. A strong security engineer answers those in a few days.

For a first version, being boring buys more risk reduction than being audited does. An unaudited app built on libsignal with no hand-rolled ratchet and no PAKE is far less likely to be broken than an audited app full of original constructions.

12.4 External review — optional, staged, and not a launch gate

Nothing gates us. No store, regulator, or law requires a third-party cryptographic audit for a consumer messaging app. Signal, Telegram, Threema and Session all shipped before one. We can write CRYPTO.md, build, launch, reach 10k users and take revenue with zero review spend.

"Optional" means nobody will stop you, not that there is no downside. If the protocol is broken we find out when someone else finds it, and for a privacy product that failure is terminal rather than recoverable. The natural trigger is affordability, not principle: at ~1,000 subscribers the product takes ~$3,000/month and a review is two to three months of revenue — at which point skipping it is a choice, not a constraint.

WhenWhatCost
Before writing crypto codeWrite CRYPTO.md. The highest-value action in the entire plan. Most protocol bugs are caught by the author while writing a precise spec, because you cannot hand-wave in a document that must say exactly which key signs what and what the recipient checks$0
Before writing crypto codePublish it and invite critique (IETF CFRG list, the places Sam reads). Take free scoping calls with two or three firms — they walk the design to quote it, and will say if something is obviously wrong$0
Before writing crypto code (optional)2–3 days of one experienced security engineer on the spec. Not a firm engagement$3–5k
MVP + private betaTesters told plainly it is unreviewed$0
Public launch, anti-spam marketingStill no outcome guarantees (§12.5)$0
Only when making security claims publicly, or at real scaleFull design + implementation review$6–10k, from revenue

Timing note: the crypto is not the first code written. App shell, pairing UX, call plumbing and push infrastructure come first — months of work. "Before you write the crypto code" is realistically month three or four, by which point there may already be beta signal on the thing that actually determines viability.

12.5 What we may and may not claim publicly

The core pitch — "no spam calls, because there is no number to leak" — is a claim about architecture, verifiable by anyone who opens the app. No cryptographer needs to certify it. The line is describe what you built; do not guarantee an outcome.

Safe without a reviewRequires care
"There is no phone number and no username.""No one can ever read your messages."
"Nobody can contact you unless you added them.""Unbreakable" / "bank-grade" / "military-grade"
"Delete someone and they can never reach you again.""Independently audited" (when it is not)
"We do not store your contacts.""More secure than Signal."
"Messages are deleted from our servers the moment they are delivered."Any guarantee about attacker capability
"1:1 sessions use libsignal, the Signal Protocol implementation.""Uses the Signal Protocol" unqualified — it implies Signal's whole audited system, including a group protocol and sealed sender we do not have
"You're anonymous." / "Nobody knows who you are"
"Nothing you share here can be used to reach or find you anywhere else.""No identity." / "Untraceable"

The encryption line is fine unreviewed if it is true — deception requires a false statement, not an unverified one. Exposure comes from guaranteeing outcomes, not from describing an implementation.

Be precise about libsignal, because over-claiming here invites exactly the criticism we least want. We use libsignal for 1:1 sessions. Group messaging is pairwise fan-out of those 1:1 sessions — it is not Signal's group protocol, and there is no sealed sender. Saying "uses the Signal Protocol" without qualification reads as "audited like Signal," which is not a claim we can support.

And anonymity is not one of our claims. People in a room see each other's chosen names, voices and avatars (§12.2). Marketing may say that nothing shared in Kontaks can be used to reach or find someone elsewhere; it may never say that users are anonymous or untraceable, because inside a room they are neither.

Two disclosures we cannot opt out of: the Apple Privacy Nutrition Label / Play Data Safety forms (see §13 — "Data Not Collected" is very likely not the right answer; do not tick Play's independent-security-validation badge), and US encryption export paperwork, which is free but must be answered correctly or a submission is blocked.