Kamo

Threat Model

Written to be honest rather than reassuring. If you are evaluating whether to trust this service, the interesting sections are §3 and §4 — the things it does not protect.

1. The guarantee

Every phone number belonging to someone other than the account holder is encrypted before storage, under a key this server does not possess.

That covers:

DataStorageServer can read it?
Caller's real number in your call historysealed to your public keyNo
Protected-contact labelssealedNo
Protected-contact numberssealedNo
Alias↔caller mappingsealedNo
Your own handset numberplaintextYes — we must dial it
Alias presented on a callplaintextYes — it's ours, not personal
Call time, duration, coarse origin cityplaintextYes — needed for billing
Never-mask listblind index (see §3)Only by brute force

How

ECIES over P-256: ephemeral ECDH → HKDF-SHA256 → AES-256-GCM.

  1. Your browser generates a P-256 keypair at signup. The private key never leaves it in usable form.
  2. The private key is wrapped twice — once under PBKDF2-SHA256 (210,000 iterations) of your PIN, once under your recovery code. We store two opaque blobs.
  3. When a call arrives, Twilio hands us the caller's number in plaintext. We immediately seal it to your public key with a fresh ephemeral keypair and store only the sealed blob. The plaintext is never written to disk.
  4. Your browser decrypts it after you unlock.

The repository includes browser interoperability checks and a database sweep designed to detect plaintext caller numbers. Current verification status is maintained with each release.

2. What this defends against

3. What this does NOT defend against — read this part

The never-mask list is brute-forceable

It is a keyed hash (HMAC-SHA256 with a server-side pepper and per-user salt), not sealed data. It has to be, because the server must match an inbound caller against it while your browser is closed.

There are roughly 10^10 North American numbers. Anyone holding both the database and the application pepper can enumerate that space and recover the list. Treat the never-mask list as obscured, not encrypted. Put your bank on it; do not put anything you would be harmed by on it — put those in protected contacts, which are genuinely sealed.

Twilio sees everything in real time

Masking happens in Twilio's network, so Twilio necessarily knows who called you and when. You have moved trust from your carrier to Twilio, not eliminated it. Nothing in this design can change that, and any claim otherwise would be false.

Calls are traceable by lawful process

Carrier records exist independently of us. This service does not remove lawful carrier records and must never be marketed as though it does.

A compromised browser defeats everything

Malware, a malicious extension, or a hostile person with your unlocked phone can read decrypted data on screen. End-to-end encryption protects data in transit and at rest, not a device someone else controls.

Forgotten PIN plus lost recovery code is unrecoverable

This is the honest cost of holding no key. There is no reset. Your history becomes permanently unreadable — including to us.

Metadata is real information

Even sealed, the pattern "this account received 14 calls from the same unidentified party between 11pm and 1am" is meaningful. Sealing identities does not seal patterns. Retention limits (7/90/365 days by plan, actually deleted) are the mitigation.

4. Operational requirements

These are not optional; the guarantee is void without them.

  1. BLIND_INDEX_PEPPER must be set or the server refuses to start. Generate once with openssl rand -base64 32. Store it in the secret manager, not in the repo, and never in the same place as database backups — together they defeat §3's blind index.
  2. Never add a plaintext column for a third party's number. The test suite sweeps every column for exactly this and will fail. That test is the guardrail protecting the product's central claim; do not weaken it.
  3. TLS everywhere. WebCrypto requires a secure context, so the dashboard will not function over plain HTTP anyway.
  4. VALIDATE_TWILIO=true in production. Without it, anyone who discovers the webhook URL can make a subscriber's phone ring.
  5. Do not add analytics, session replay, or error trackers that capture DOM or network bodies to the dashboard. A session-replay tool would silently undo the whole design.
  6. Back up the database freely — it's ciphertext. Do not back up the pepper alongside it.

5. Reporting

Security issues: security@yourdomain — please allow 90 days before disclosure. Abuse: the /abuse-report endpoint, reviewed on every business day. Credible reports of unlawful use result in suspension pending review.