Skip to content

Architecture, animated

A message in TalX always goes through the same five steps on the device itself – and then looks for the first path that carries. The diagram shows both. Pick a delivery path above to see when it applies.

TalX architecture: two devices, four delivery pathsTwo Android devices encrypt every message on the device itself and exchange the resulting ciphertext over four possible paths: Bluetooth and Wi-Fi Direct via Nearby, a direct TCP connection on the local network, a direct WebRTC connection over the internet, and as a fallback a store-and-forward relay that only ever sees ciphertext.Device APlaintextmessage, photo, fileDEFLATEbefore encryptionDouble Ratchet+ hybrid ML-KEM-1024a fresh key per messageAES-256-GCMauthenticatedChunks of ~18 KBoutbox, one ACK per recipientDevice BChunkscomplete? otherwise waitGCM checkone wrong bit = discardRatchet stepthe key is spentself-healing on gapsSQLCipherAES-256, key in the keystoreDisplayed+ ACK sent back1 · Bluetooth / Wi-Fi DirectNearby Connections · no internet2 · Local networkmDNS · direct TCP connection3 · WebRTC P2Pstraight over the internet · DTLSRelayciphertext only4 · Relay, store-and-forwardholds ciphertext up to 7 days · push only wakes, carries nothingEvery path carries finished ciphertext and nothing else

1 NearbyBluetooth + Wi-Fi Direct, no internet at all

Google Nearby Connections spans a P2P_CLUSTER mesh. Two devices within radio range exchange chunks directly – no router, no mobile network, no server. Call signalling travels this way too.

  • no internet needed
  • range ≈ Bluetooth/Wi-Fi Direct
  • same ciphertext as everywhere
The animation shows the route of the data, not its speed. Anyone who has reduced motion (system setting) gets the same graphic without the moving dots.

Whichever path is chosen, what leaves the device is already finished ciphertext. No transport holds a key, none can read anything, and none can alter anything unnoticed.

  1. Plaintext is created in the app. Text, photo, video, file, voice message – all of it is bundled into one content envelope. Files sit inside it as Base64.

  2. Compress, and do it first. DEFLATE runs before encryption, because ciphertext is random and cannot be packed any further. That saves 60–80 % of radio volume on text and around 25 % on media. Anything that would grow through compression is sent uncompressed and marked accordingly.

  3. Derive a key. For one-to-one chats the Double Ratchet supplies a fresh message key – a new one for every single message. In groups it comes from the sender’s own sender-key chain. An ML-KEM-1024 secret is additionally mixed into the root key.

  4. AES-256-GCM. Encrypt and authenticate in one pass. A single altered bit makes the check fail – the message is then discarded rather than half displayed.

  5. Cut into pieces and queue. The ciphertext travels in chunks of around 18 KB – at most 8192 of them per transfer, which caps the receive buffer. The entry stays in the encrypted outbox until a confirmation comes back from the recipient.

The tick on your own message does not appear when you send it, but only once the other device has confirmed receipt. Until then it says “waiting for delivery” – even if the bytes left the radio long ago.

In groups there is a separate queue entry per recipient. The tick arrives only once no entry is left open, meaning everyone has confirmed. That is deliberate: previously the very first confirmation from any single member set the tick – which looked like “everyone has it” and hid genuine losses.

The relay is the last fallback. It accepts messages even when the recipient is offline, holds them for up to seven days and delivers them on the next connection. Without it both devices would have to be online at the same time.

What the relay receives What it can do with it
Ciphertext (AES-256-GCM) nothing – no key, no reading, no undetected tampering
Sender marker in the chunk changes per transfer – two messages from the same sender are not recognisable as related by third parties
Registered connection the operator sees which identifiers communicate with each other, and when
Push wake-up carries no content – the app fetches the message itself, encrypted

The sender marker is an HMAC over the transfer identifier, computed with the shared key. Only the intended recipient can attribute it. That protects against third parties – a stranger’s device within radio range, someone reading the payload at the relay.

Against the relay operator it does not help: to receive messages at all a device has to be registered there, and every delivery is tied to that registration. Anyone who wants to rule that out too runs their own relay or uses TalX purely offline over the short-range paths.

Location Protection
Messages, contacts, groups SQLCipher (AES-256); the passphrase lives only in the Android keystore
Attachments in app storage AES-256-GCM with a key of its own
Identity, prekeys, ML-KEM pair derived from a hardware-wrapped master secret
Master secret Android keystore, StrongBox preferred, TEE otherwise
Cloud backup AES-256-GCM, key derived from the passphrase with Argon2id, second factor required to restore

A copied data folder, a backup or a stolen switched-off device are therefore worthless: without the secure element of that specific device the master secret cannot be unwrapped – and without it, none of the others can either.

Originally eight secrets sat individually in the hardware keystore. On StrongBox devices a single keystore operation costs roughly two seconds; the two that are unavoidable at start-up alone added up to about 4.4 seconds of a 7.8-second cold start.

Today exactly one master secret stays hardware-wrapped. All the others are derived from it via HKDF and wrapped in software – one StrongBox operation per process instead of one per secret. The hardware binding is fully preserved: anyone who could invoke the wrapping key in the keystore could already open every individual secret before.

Every chunk carries its sender’s protocol version. TalX remembers the last version it saw from each contact and uses that to decide which features may be used. While the version is unknown it sends conservatively, in the oldest format.

The promise: sending and receiving stay compatible across at least ten app versions. New features are only available between devices that both know them; older counterparts automatically get the variant they can read. Unknown content types are silently ignored on receipt rather than shown as an empty message.