App icon: a red calculator
Android · v1.0

Messages that leave
no trace behind.

A closed-loop encrypted messenger. Direct device-to-device over WebRTC, paired in person by QR code. No servers, no accounts, no phone number — and on the home screen it looks like a calculator.

Version 1.0 Package com.utility.calculator Min Android 5.1 (API 22)

Zero trackers

No analytics, no telemetry, no third-party SDKs. The only network traffic is the direct link to the person you’re talking to.

One permission: the camera

Requested only to scan a pairing QR. No contacts, messages, storage, location, or microphone are ever touched.

No keystroke capture

A built-in keyboard that never reaches the Android IME — no autocorrect dictionary, no prediction, no learning, no clipboard.

Closed-loop encryption

Keys are generated on the device and never leave it. Two phones, one sealed channel, and nothing in the middle that could listen.

Built to hold nothing

Every design choice bends one way: leave as little behind as possible, on the wire and on the phone.

End-to-end encrypted

Ed25519 identity, Curve25519 ECDH, NaCl box (XSalsa20-Poly1305) with a fresh nonce per message. Keys never leave the device.

Truly peer-to-peer

A direct WebRTC data channel between the two phones over WiFi or cellular. No relay, no TURN, no cloud in the middle.

Paired in person only

QR codes are the entire signaling channel. You add a contact by scanning theirs, face to face. There is no way to add anyone remotely.

Ephemeral by default

Messages live in memory and self-destruct once read — on both phones. Choose to keep one and the sender is told, every time.

Secure keyboard

An in-app keyboard that never touches the Android IME — no predictions, no learning, no clipboard. Scramble mode randomises the keys.

Camouflaged

Launches as “Calculator” with a red calculator icon. A password gates every open; five wrong tries wipe the device clean.

How it’s built

An Android app with a web core — Capacitor + React + TypeScript — where the security lives in the protocol, not in a server you have to trust.

CapacitorReactTypeScripttweetnaclWebRTCIndexedDB
Identity & keys
One 32-byte Ed25519 seed per device. The Curve25519 key used for key agreement is derived from it through a domain-separated SHA-512, so the same bytes never serve two algorithms. The seed never leaves the phone.
Message encryption
NaCl box — Curve25519 ECDH with XSalsa20-Poly1305 — and a fresh random 24-byte nonce on every message. A frame that fails to authenticate is dropped silently; there is no error to probe.
Pairing & signaling
QR codes are the entire signaling channel. Each pairing payload is signed with the sender’s identity key, deflated, and split across animated QR frames that the scanner reassembles — binding the key-agreement key and the connection offer to one identity.
Transport
A direct WebRTC data channel over WiFi or cellular. STUN is optional and limited to Google’s public servers; there is no TURN relay, and relay candidates are stripped from every offer before it is shown.
Ephemeral messages
Messages live in memory only. Once read they self-destruct on both devices; choosing to keep one notifies the sender. No message body is ever written to storage.
At rest
Identity, contacts and settings are sealed with a key derived by PBKDF2-SHA256 (310,000 iterations). Only a verifier and salt are stored. Five wrong password attempts wipe the device’s copy of everything.
Platform hardening
FLAG_SECURE blocks screenshots, screen recording and the recent-apps thumbnail; allowBackup=false and explicit extraction rules keep the vault off cloud backup and device-transfer.

Wire protocol

Every frame below is sealed before it leaves the device.

FrameDirectionCarries
conn.requestinitiator → peernickname + timestamp — no content
conn.accept / conn.declinepeer → initiatorthe answer to a request
msgeitherid, body, timestamp
msg.readrecipient → senderid — sender then destroys its copy
msg.savedrecipient → senderid — sender is told a copy was kept
resume.propose / resume.ackeithertoken, STUN list, candidates, standby offer
bye / pingeitherteardown & keepalive

Installing the APK

It is distributed here directly, not through the Play Store, so Android will ask you to allow it once.

1

Download the APK

Tap Download APK above on the Android phone itself, or transfer the file to it.

2

Allow this one install

Open the file. Android will offer Settings → Allow from this source for your browser or file manager. Grant it, then go back.

3

Install and open “Calculator”

Confirm the install. It appears in the app drawer as Calculator. On first launch you choose a nickname and set a password.

4

Add a contact in person

Both people open the app and pair by scanning each other’s QR code. From then on you can connect directly, phone to phone.

Verify the download

SHA-256 of closed-loop-messenger.apk. Check it after downloading:

df1e0b77fa459e6cd61b6150da5030b1715e3f92011b2c9ce5f0f33118a8f7b7

sha256sum closed-loop-messenger.apk

⚠︎ Read before you rely on it

This is a release-signed build for sideloading. It has had no third-party security audit — review the source and verify the checksum before you rely on it.

It protects message content on the wire and at rest. It does not defend a rooted or compromised phone, and — with STUN enabled — a public STUN server learns your address. Turn STUN off in Settings to connect over a shared local network only.

How a conversation actually happens

Pairing does not open a chat. To talk, you tap Initiate Link on a contact — that sends a connection request carrying only a nickname, no message content. They see “[Nickname] wants to connect” and must accept before anything flows. Decline is reported back; thirty seconds of silence times out. Nothing is ever queued as a phone notification, so a message never sits waiting on a locked screen.