cryptoscreen

Transparency

What the database sees

cryptoscreen is designed so the hosted service stores encrypted payloads only. A database row alone, or a database row plus the PIN alone, is not enough to decrypt a message.

Text messages

The app encrypts the message on the sender's device before upload. The server receives fields shaped like this:

{
  "id": "message uuid",
  "ciphertext": "encrypted message bytes",
  "nonce": "AES-GCM nonce",
  "tag": "AES-GCM tag",
  "salt": "per-message salt",
  "pin_verifier": "server-peppered PIN proof",
  "read_policy": "app_only or web_allowed",
  "expires_at": "30 day maximum"
}

That row does not contain the plaintext, raw PIN, link secret, sender identity, recipient identity, contacts, or account profile.

What can decrypt

  • Database row only: cannot decrypt.
  • Database row plus PIN: cannot decrypt because the link secret is missing.
  • Database row plus link secret plus PIN: can decrypt locally in the app.

Images

Images follow the same boundary. The app encrypts the image before upload. R2 stores encrypted image bytes. Neon stores the object key, encrypted file-key bytes, size, content type, and expiry metadata. The raw image and raw image key are not stored by the service.

One-time opening

After a correct PIN proof, the database function returns the encrypted payload and deletes the normal message row in the same locked operation. The third wrong PIN destroys the row. Unopened links expire after 30 days.