Follow the message.
See the boundaries.
A visual guide to how Cryptico encrypts, delivers and stores — and to what it does not hide.
Only the endpoints can read a message.
Every box between two people carries sealed envelopes. Plaintext exists only on user devices.
L2. Encrypted attachments also travel through the Cloudflare proxy on their way to file storage. The server code does not contain the message-content format at all.
An account is a random number and 12 words.
No phone, no email, no real name on the account.
- Random ID — generated by the server
- Optional username — 3–32 letters, digits or underscore; unique
- 12 recovery words — generated on your device, never sent anywhere
- Phone number or SMS code
- Email address
- Real name on the account
- Address-book upload
- Recovery through support
L2. A deleted username is never issued again. A display name exists only inside the encrypted profile (section 19). Each linked device has a name that the server can see — on Android it is the phone model by default.
One phrase grows every key — on the device.
Each key has one job, so a key made for one purpose is never reused for another.
L2. The identity key is derived deterministically, so it is the same on every device of one account; each device still has its own device ID and its own prekeys.
Creating an account proves you hold a secret — without sending it.
The server receives a signature and public keys. The words stay on the phone.
Sign a challenge instead of sending a password.
Each challenge works once, so a signature cannot be reused.
L2. The same challenge-and-signature step protects linking a device, listing and removing devices, and deleting the account.
A message is sealed before it leaves the phone.
From “Send” to “Read”. The server only ever holds the sealed envelope.
L2. Delivery is at-least-once; duplicates are dropped by message ID. Chat order follows server time; a message that arrives much later than it was sent is marked late.
The outside of the envelope is not the letter.
Three nested layers — each with a different audience.
L2. Layer 2 is readable by anyone with access to the server’s database — it tells who sent a message to whom, never what it says. The server code cannot decode layer 3: the content format is not part of it.
Far more than the text is sealed.
Everything people associate with a conversation travels inside the encrypted envelope.
- Routing: sender, recipient, time, size
- A “wake the phone?” flag on each envelope
- In groups only: which members were @mentioned — so a mention can break through mute
L2. Envelopes are not padded to a fixed size, so the server sees approximate message size. Group membership is also known to the server (section 11).
Content is private. The fact of communication is not.
Every observer, every kind of data. Read across a row to see what one party learns.
| Observer | Text | Files | Profile | Contacts | Who ↔ whom | When | Size | IP address | Group members | Call media |
|---|---|---|---|---|---|---|---|---|---|---|
| Your device | Readable | Readable | Readable | Readable | Yes | Yes | Yes | Own IP | Your groups | Readable |
| Peer device | What you send them | What you send them | Once you have chatted, or on request in a shared group | Only contact cards you send | Your exchanges with them | Yes | Yes | Not in relayed calls; yes if a call falls back to direct | Shared groups | Calls with you |
| Cryptico server | Encrypted | Encrypted | Encrypted | Encrypted | Yes | Yes | Yes | Yes | Yes, with roles | Not carried; call set-up encrypted |
| Database | Ciphertext | File ID, size, owner | Encrypted | Encrypted | Yes — envelope headers, groups, blocks, mutes | Yes | Yes | Not stored | Yes | Not stored |
| File storage | Not held | Encrypted files | Not held | Not held | — | Upload time | File size | — | Not held | Not held |
| Cloudflare | Encrypted | Encrypted files in transit | Encrypted | Encrypted | Request metadata | Yes | Yes | Yes | Group requests | Not carried |
| Google FCM | No | No | No | No | No sender or chat; wake-up timing can hint | Wake-up times | No | Yes | No | Only that it is a call |
| Apple APNs | No | No | No | No | No sender or chat; wake-up timing can hint | Wake-up times | No | Yes | No | Only that it is a call |
| Web Push service | No | No | No | No | No sender or chat; wake-up timing can hint | Wake-up times | No | Yes | No | Not even the type |
| LiveKit (group calls) | No | No | No | No | Who is in which group call | Yes | — | Yes | Call participants | Encrypted frames |
| TURN relay (calls) | No | No | No | No | Callers’ account IDs and IPs | Yes | — | Both sides | No | Encrypted |
| Mini-app | No | No | Only an app-specific alias, if you set one | No | No | Its own backend: request times | — | Its own backend | No | No |
| Someone holding your phone (no app lock) | Readable — the app reopens by itself | Same | Same | Same | Same | Same | — | — | Same | — |
| Someone with your 12 words | New messages, plus contacts’ recent messages their apps re-send to a new device | Same as text | Yes | Yes | Your chat list | — | — | No | Your groups | — |
Your device5 readable · 5 visible
- Text
- Readable
- Files
- Readable
- Profile
- Readable
- Contacts
- Readable
- Who ↔ whom
- Yes
- When
- Yes
- Size
- Yes
- IP address
- Own IP
- Group members
- Your groups
- Call media
- Readable
Peer device4 readable · 6 visible
- Text
- What you send them
- Files
- What you send them
- Profile
- Once you have chatted, or on request in a shared group
- Contacts
- Only contact cards you send
- Who ↔ whom
- Your exchanges with them
- When
- Yes
- Size
- Yes
- IP address
- Not in relayed calls; yes if a call falls back to direct
- Group members
- Shared groups
- Call media
- Calls with you
Cryptico server5 visible
- Text
- Encrypted
- Files
- Encrypted
- Profile
- Encrypted
- Contacts
- Encrypted
- Who ↔ whom
- Yes
- When
- Yes
- Size
- Yes
- IP address
- Yes
- Group members
- Yes, with roles
- Call media
- Not carried; call set-up encrypted
Database5 visible
- Text
- Ciphertext
- Files
- File ID, size, owner
- Profile
- Encrypted
- Contacts
- Encrypted
- Who ↔ whom
- Yes — envelope headers, groups, blocks, mutes
- When
- Yes
- Size
- Yes
- IP address
- Not stored
- Group members
- Yes
- Call media
- Not stored
File storage2 visible
- Text
- Not held
- Files
- Encrypted files
- Profile
- Not held
- Contacts
- Not held
- Who ↔ whom
- —
- When
- Upload time
- Size
- File size
- IP address
- —
- Group members
- Not held
- Call media
- Not held
Cloudflare5 visible
- Text
- Encrypted
- Files
- Encrypted files in transit
- Profile
- Encrypted
- Contacts
- Encrypted
- Who ↔ whom
- Request metadata
- When
- Yes
- Size
- Yes
- IP address
- Yes
- Group members
- Group requests
- Call media
- Not carried
Google FCM4 visible
- Text
- No
- Files
- No
- Profile
- No
- Contacts
- No
- Who ↔ whom
- No sender or chat; wake-up timing can hint
- When
- Wake-up times
- Size
- No
- IP address
- Yes
- Group members
- No
- Call media
- Only that it is a call
Apple APNs4 visible
- Text
- No
- Files
- No
- Profile
- No
- Contacts
- No
- Who ↔ whom
- No sender or chat; wake-up timing can hint
- When
- Wake-up times
- Size
- No
- IP address
- Yes
- Group members
- No
- Call media
- Only that it is a call
Web Push service3 visible
- Text
- No
- Files
- No
- Profile
- No
- Contacts
- No
- Who ↔ whom
- No sender or chat; wake-up timing can hint
- When
- Wake-up times
- Size
- No
- IP address
- Yes
- Group members
- No
- Call media
- Not even the type
LiveKit (group calls)4 visible
- Text
- No
- Files
- No
- Profile
- No
- Contacts
- No
- Who ↔ whom
- Who is in which group call
- When
- Yes
- Size
- —
- IP address
- Yes
- Group members
- Call participants
- Call media
- Encrypted frames
TURN relay (calls)3 visible
- Text
- No
- Files
- No
- Profile
- No
- Contacts
- No
- Who ↔ whom
- Callers’ account IDs and IPs
- When
- Yes
- Size
- —
- IP address
- Both sides
- Group members
- No
- Call media
- Encrypted
Mini-app3 visible
- Text
- No
- Files
- No
- Profile
- Only an app-specific alias, if you set one
- Contacts
- No
- Who ↔ whom
- No
- When
- Its own backend: request times
- Size
- —
- IP address
- Its own backend
- Group members
- No
- Call media
- No
Someone holding your phone (no app lock)7 readable
- Text
- Readable — the app reopens by itself
- Files
- Same
- Profile
- Same
- Contacts
- Same
- Who ↔ whom
- Same
- When
- Same
- Size
- —
- IP address
- —
- Group members
- Same
- Call media
- —
Someone with your 12 words3 readable · 3 visible
- Text
- New messages, plus contacts’ recent messages their apps re-send to a new device
- Files
- Same as text
- Profile
- Yes
- Contacts
- Yes
- Who ↔ whom
- Your chat list
- When
- —
- Size
- —
- IP address
- No
- Group members
- Your groups
- Call media
- —
L2. “—” marks a cell this atlas does not assess. IP addresses are not stored in the database or in the web access log; some error logs can still contain them.
The first exchange starts a chain of fresh keys.
Bob can be offline: he left public “locks” on the server in advance.
L2. A new key for every message gives forward secrecy and recovery after compromise: a stolen key opens one message, not the ones before or after.
Group keys go to the members the server lists.
The server keeps the member list. The group name and the messages stay on devices.
- Group ID and creation date
- Members, roles (admin / member), join dates
- Invitation links and how often they were used
- Group name
- Messages
- Sender keys
L2. Up to 128 members. Alice’s own other devices get a separate “sent” copy, not the group copy.
The file key travels inside the encrypted message.
Storage holds a locked box; the key goes separately, sealed.
L2. Re-encoding covers camera formats (JPEG, HEIC). PNG, GIF and WebP images are sent unchanged, so check what such files contain before sending.
Call set-up is encrypted; media goes through a relay by default.
The relay hides the two callers’ IP addresses from each other — while it works.
L2. The relay sees both IP addresses and the callers’ account IDs, not the media. Relay credentials are temporary. The code is derived from the call and from both sides’ encryption certificates.
The media server never gets the room key.
Every frame is encrypted with a key the members hand to each other.
L2. Up to 8 video streams and screen sharing. Asked who is in a call, the server tells group members only the number of participants. The media server itself sees which accounts joined, when and from which IP — not the media.
A new device gets your account, not your history.
Up to 10 devices per account. Each one is vouched for by the account key.
- Profile
- Contacts
- Folders
- Some settings
- Chat list
- Blocks
- Mini-app data
- Your message history
- Attachments
- Call history
- Drafts
- “Verified” marks
L2. Contacts’ apps may automatically re-send their own recent messages to a new device. A forged device is caught once a contact remembers your account key; the very first sighting is trust on first use.
Check the first contact once — then a change raises an alarm.
The app remembers a key the first time it sees it. Comparing a number proves nobody is in between.
L2. Available on web, Android and iOS. The “verified” mark is not synced to your other devices.
A stolen phone shows locked files — if it has an app lock.
Data at rest is encrypted; the app lock decides who can open it.
| Mechanism | Web | Android | iOS |
|---|---|---|---|
| Chat database | Encrypted (AES-256-GCM) | SQLCipher · one file per account | SQLCipher · one file per account |
| Signal key store | Encrypted | SQLCipher | SQLCipher |
| Hardware key store | None | Android Keystore | Keychain (this device only); Secure Enclave for biometrics |
| PIN | Argon2id + AES-256-GCM | Same + hardware layer | Same + Keychain |
| Biometrics | WebAuthn | Fingerprint / face; invalidated when enrolment changes | Face ID / Touch ID; no passcode fallback |
| Auto-lock | Yes | Yes | Yes |
| Lock when the connection drops | Yes, with a PIN | Yes, with a PIN | Yes, with a PIN |
| Screen capture | Cannot be blocked | Blocked on the phrase and PIN screens only | Phrase and PIN hidden while recording; app hidden in the switcher when a PIN is set; screenshots cannot be blocked |
L2. A PIN is a convenience barrier; the strong secret is the 12-word phrase. In a browser there is no hardware key store, so someone with a copy of the browser’s data can try PINs offline.
Your settings sync, but the server sees only random names.
How contacts, chats and folders reach your other devices.
L2. This is not a message backup: history does not travel this way.
Your name and photo are locked with their own key.
The profile is encrypted; the key is handed out inside encrypted chats.
L2. The server sees that a profile exists, its size and when it changed. A username, if you set one, is separate from the encrypted profile.
A push wakes the app; it does not carry the message.
Google, Apple and browser push services learn that something happened and when — not what or from whom.
| Channel | What the push carries | Who draws the notification |
|---|---|---|
| Google FCM (Android) | Only “message” or “call” | The app itself, after decrypting · hidden on the lock screen |
| Apple APNs (iOS) | A fixed “New message” phrase, the same for everyone · a separate call signal | iOS, from that fixed phrase |
| Web Push (browser) | One fixed “wake” signal, the same for messages and calls | The open tab after decrypting, otherwise a generic “New message” |
- That the app is installed
- The exact time of every wake-up
- Message or call (FCM, APNs)
- IP address
- Message content
- Keys or the 12 words
- Who sent it
- Which chat
L2. New messages, calls and account notices (new device, added to a group) wake the phone; reactions, receipts and edits do not. Message previews are off by default. Muted chats are not woken — for that, the server keeps the list of chats you muted. Wake-up timing for two people can statistically suggest they talk.
Messages and files are short-lived on the server.
Maximum lifetimes on the server.
Queued messages are deleted as soon as they are acknowledged — 7 days is the ceiling, not the norm.
Stored metadata is part of the security model.
The kinds of records the server keeps — and what is deliberately absent from them.
| Record | Stored / reveals | Deliberately absent |
|---|---|---|
| Account | Random ID, optional username, public keys, creation date | Phone, email, name, password, phrase |
| Device | Device list, device name, last connection, endorsement · retired devices are kept | Private keys |
| Signal public keys | Identity and prekeys (public only) | Private keys, session state |
| Message queue | Recipient, envelope header with the sender, ciphertext, time | Readable content · deleted on delivery or after 7 days |
| Sessions | Hash of each refresh token and when it was issued — removed after a short grace period once expired | The tokens themselves |
| Group | Members, roles, join dates, invitation links with use counts | Group name |
| Contact links | Who created a link, its limits and how often it was used — removed after a short grace period once it stops working | Who used it |
| Push registration | Platform, device push token | Content, sender name |
| Attachment | File ID, size, owner, expiry | File key, content |
| Private storage | That a record exists, its size and change time | Record names, content |
| Profile | That it exists, size, change time | Name, photo (ciphertext only) |
| Block | Who blocked whom | Reason |
| Mute | Which chats or people you muted | — |
| Mini-app catalogue | Public catalogue | Which user uses which app |
| Deleted username | The username alone | Account ID, date |
L2. IP addresses are not stored in the database or in the web access log; some error logs can still contain them. Service logs exclude message content and tokens but can contain account and device IDs, so “no logs” is not claimed.
The server cannot read messages — end-to-end encryption, not a promise.
Automated checks in our development process flag server code that would cross a privacy boundary.
L2. These checks enforce the boundary in code. They are not an independent security audit.
Deletion is immediate and final — on the server.
Copies already on your contacts’ phones stay there.
L2. No waiting period and no restore.
A mini-app sees a stranger, not you.
Each app gets its own identity, unlinkable to your account and to other apps.
- Your account ID
- Your username
- Your avatar
- Your Signal keys
- IP address
- Request times
- Privacy Pass tokens: the server cannot tell who reported an app
L2. Reports use VOPRF tokens (RFC 9497).
A blocked person is not told they are blocked.
The server gives them the same answer as a successful send.
| Context | What happens | What the blocked person sees |
|---|---|---|
| 1:1 messages and calls | Not delivered | The same answer as a successful send |
| Shared group | Delivered without a wake-up; shown as a hidden placeholder | No block notice |
| Group call | Blocking does not apply — both are legitimately in the room | The call continues |
L2. Blocks are temporary: the app shows when a block will end, and you can renew it.
Every defence has an edge.
What each attacker can try, what stops them, and what is left.
| Threat | Defence | What remains |
|---|---|---|
| Server hacked / curious admin | End-to-end encryption | Metadata: who talks to whom, when, how much |
| Server tries to add a fake device | Device endorsements checked against a remembered account key | Trust in the very first contact |
| Traffic interception | TLS + end-to-end encryption | The TLS proxy (Cloudflare) sees metadata and session tokens |
| Man in the middle at first contact | Safety number / call code | Unprotected until compared |
| Future quantum computer vs recorded traffic | Kyber-1024 in the first key exchange | Covers session set-up only |
| One message key stolen | Double Ratchet | That one message |
| Guessing the phrase or PIN | Argon2id; on phones, limited PIN attempts backed by the hardware key store | A PIN is a convenience barrier — in a browser it can be guessed offline from a copy of the browser’s data |
| Lost or stolen phone | Encrypted storage, hardware key store, app lock, remote device removal | A phone with no app lock opens the app |
| Screen recording / shoulder surfing | Phrase and PIN screens protected | Ordinary screenshots |
| IP exposure in calls | TURN relay by default | Direct connection when the relay fails (with a warning) |
| Location in photos / videos | Camera photos re-encoded; video metadata neutralised | PNG, GIF and WebP are sent as they are |
| Malicious mini-app | Sandbox, per-app identity, revocation | Its backend sees IP and time |
| Google / Apple / browser push | Content-free push | Wake-up times and IP |
| Phrase leaked | Keep it secret | Full account access, including contacts’ recently re-sent messages |
Know where the protection ends.
Encryption hides what you say. It does not hide that you are talking.
The cryptography behind the boundaries.
One table for the technical reader.
| Purpose | Technology |
|---|---|
| Messaging | Signal Protocol via libsignal on every platform |
| Session set-up | PQXDH · Curve25519 + Kyber-1024 |
| 1:1 messages | Double Ratchet |
| Groups | Sender Keys · up to 128 members |
| Recovery phrase | BIP39 · 12 words · 128 bits |
| Key derivation | PBKDF2-HMAC-SHA512 → Argon2id (64 MiB) → HKDF-SHA256 |
| Sign-in | Ed25519 signature over a single-use challenge |
| Device endorsement | Ed25519 |
| Attachments | AES-256-GCM · key per file · SHA-256 |
| Local data | SQLCipher (phones) · AES-256-GCM (browser) |
| Private storage | HMAC-SHA256 names + AES-256-GCM |
| App lock | PIN · Argon2id + AES-256-GCM |
| Safety number | libsignal fingerprint · 60 digits |
| 1:1 calls | WebRTC DTLS-SRTP · TURN relay by default · 6-digit code |
| Group calls | LiveKit media server + end-to-end frame encryption (AES-GCM) · new key on join / leave |
| Mini-app reports | VOPRF (Privacy Pass, RFC 9497) |
L2. The system as a whole is not claimed to be post-quantum.