Everything in one document: security, cryptography, threat model, platforms and setup. For forwarding or printing.
Your messages. Your server. Nobody else's. Enigma 3 is the chat you operate yourself: server, apps and data on your own hardware. Messages, attachments and calls are end-to-end encrypted — the server stores and relays only ciphertext it cannot read.
Why Enigma 3
Most messengers promise: “We protect your data.” That promise depends on trust — in the operator, the company, its employees. The most famous “unhackable” services in the world failed exactly there. The numbers below come from official Europol, Eurojust and prosecution announcements.
💥 EncroChat (2020)
A hardened “crypto phone” for some 60,000 users. Police broke into the vendor's servers — not the cryptography — and pushed an update to every device that captured the decryption keys. Result: 60 million messages read, €700 million seized, 6,500+ arrests by 2023 (Operation Sirus).
💥 Sky ECC (2021)
The provider's servers were seized and the founder arrested. The keys — held by the provider itself — reached the authorities through prosecution, not brute force. The ciphertext became readable after the fact. Hundreds more arrests across Europe (Operation LIMIT).
💥 ANOM (2021)
The “unwiretapable” device was a covert FBI operation (Trojan Shield). Every message was readable by investigators from the first day. Not a technical failure — the product itself was the backdoor.
🛡️ The common failure
All three failed at the same point: the operator was the weak spot, not the encryption. Whoever controls the server controls every device — without touching one. Enigma 3 removes that spot: the server runs at your place. There is nothing left to seize.
🔐 Security by default
You don't configure anything. All of the following applies from the very first message:
🔐 End-to-end encryption
Direct messages: X3DH + Double Ratchet (like Signal) with forward secrecy, self-healing and replay protection. Channels and groups: sender keys, rotated on every departure. Attachments: AES-256-GCM, filename and type inside the ciphertext. The server stores and relays ciphertext only — it cannot read anything.
📞 Encrypted calls
Voice and video with screen sharing, a media key per call, packed to the participants' device keys. New key at every participant change. Unconfirmed participants show as “without key”. Built on LiveKit E2EE.
⏱️ Self-destructing messages
Lifetime per message: off · 1 h · 7 h · 24 h · 3 · 7 · 30 days (default 7). The timer lives inside the encrypted content — the server cannot extend it. The client deletes at the earlier of its own timer or the server's time and shows deviations.
🔥 Irreversible deletion
Crypto-shredding: every message has its own key. Deleting or expiry destroys the key — copies on the server and in backups become unreadable. Deliberately no built-in backups or snapshots that would defeat deletion.
📱 Devices & safety numbers
A separate key per device, never leaving it. Review and revoke devices, alerts on new devices, optional auto-removal of inactive devices. Safety numbers for comparison rule out unnoticed eavesdropping.
🔑 Local vault
Biometrics or a PIN seal the keys on the device (Argon2id, AES-GCM). Android Keystore/StrongBox, iOS Secure Enclave, Windows DPAPI. Without the PIN, a seized device yields only ciphertext.
🔬 Cryptography in detail
Vetted standard primitives, no proprietary crypto: WebCrypto with NIST P-256 (ECDH, ECDSA), HKDF/HMAC-SHA-256 and AES-256-GCM. Private keys never leave the device.
🤝 Key agreement: X3DH
The sender fetches the recipient device's prekey bundle and verifies the signed prekey against the pinned identity signing key (trust on first use). The result is a shared secret known only to these two devices. Prekey retrieval is rate-limited against draining one-time prekeys.
🔁 Double Ratchet (DMs)
Every message gets its own key. The symmetric ratchet gives forward secrecy; the Diffie-Hellman ratchet gives self-healing after a compromise. Headers are encrypted. Seen base keys are rejected (replay protection). Each message is encrypted for every recipient device (up to 64) and your own other devices.
👥 Sender Keys (groups & channels)
Each device maintains a chain per room: mk = HMAC(ck, 0x01), ck' = HMAC(ck, 0x02). Every message is signed with the chain's signing key; the signature covers room and sender. Chains are distributed through the pairwise Double-Ratchet sessions to confirmed members only. Rotation: on any departure, device change, after 7 days, or before counter overflow. New members get the chain from the current state — older messages stay unreadable.
📞 Calls & attachments
Calls: LiveKit Insertable Streams. A leader generates random media keys per call and distributes them packed to each participant's device keys; new key at every change. Attachments: AES-256-GCM via the ratchet (DMs) or sender key (channels), bound to room and sender. EXIF, GPS and XMP metadata are stripped on upload.
Keys per device — every device generates its own, private keys never leave it:
| Identity key (ECDH P-256) | Device identity, basis of the safety number — permanent per device. |
| Signing key (ECDSA P-256) | Signs the signed prekey; pinned on first contact (TOFU). |
| Signed prekey | For the X3DH exchange — renewed regularly. |
| One-time prekeys | Single-use for X3DH — replenished after consumption, retrieval rate-limited. |
🚫 Enforcement: what the server refuses
Plaintext is rejected
CreateMessage, UpdateMessage, CreateUpload and image descriptions return e2ee_required without ciphertext — in DMs, groups and channels. This duty cannot be switched off in production, neither by setting nor environment variable. Old plaintext channel messages are destroyed at startup: key destroyed, storage securely overwritten.
No silent eavesdroppers
The client remembers the first seen member list. Members added later stay pending until you confirm them in the room or verify their safety number. The server can claim new members — but cannot hand out keys unnoticed. In DMs, sending pauses until you confirm a changed key.
Removed, not disabled
No bots — API keys, MCP, webhooks gone; existing bot accounts revoked at startup. No SSO/OAuth — password login always active. No link previews — the server would see the URL in plaintext. No telemetry, no crash reporting.
Hard defaults
Invitation-only registration · 12-character minimum password · login throttling with growing lockout · absolute session lifetime (default 12 h, not just idle) · HTTPS with HSTS · strict CSP, no embedding · optional auto-removal of devices inactive 30–365 days.
🛡️ Device security in detail
🔑 The vault (app lock)
The PIN or passphrase derives a key-encryption key with Argon2id (64 MiB, t=3; otherwise PBKDF2-SHA256 ≥600k iterations). It wraps a random 256-bit data key (DEK). Every vault entry is sealed with AES-GCM — unique nonces, record ID as authenticated data. Platform binding adds a second layer: Android Keystore/StrongBox, iOS Secure Enclave, Windows DPAPI. On lock, timeout, background and app exit the DEK is discarded from memory. On mobile, every return from background requires Face ID/Touch ID/fingerprint, PIN as fallback.
⛔ Attacks and answers
Wrong PIN: exponential backoff; optional vault wipe after N attempts (default selectable: 10). Forgotten PIN: local wipe and re-registration as a new device — server messages stay. Stolen database: without the PIN only ciphertext; hardware binding makes offline brute force expensive. Weak short PINs stay offline-bruteforceable — a long passphrase is recommended.
📱 Device management
Settings show all devices with message keys; each is individually revocable. A revoked device is logged out and gets no new keys. New sign-ins alert your other devices with name and time — “Was that me?” — with one-click revocation. Inactive devices can be auto-removed after 30, 60, 90, 180 days or a year, revoked completely along with keys, sessions and tokens.
✅ Safety numbers
Derived from both accounts' identity keys. Compare in person or via QR code — afterwards the person counts as verified. In groups and channels this works per member through the lock in the header, which also shows every member, their devices and the sender-key status. A key change on a verified device pauses sending until you confirm — in DMs, groups and channels alike.
🧭 Who sees what — the threat model
| Who | What they see | What they cannot do |
|---|---|---|
| Server operator / attacker with server access | Metadata: who with whom, when, which room, message sizes, IPs, device lists. | Decrypt content, attachments or calls; extend timers; slip in members or devices unnoticed (if you check safety numbers). |
| Network observer | A TLS connection. | Read content. |
| Stolen / seized device | With active PIN: keys sealed (Argon2id/PBKDF2 KEK, AES-GCM DEK). Without PIN: ciphertext only. | Reconstruct older ratcheted messages of other devices; hardware binding makes brute force expensive. |
| Push provider | Time and target device. By default no plaintext titles or sender names. | Read message content or sender names from the payload. |
| Investigator with server seizure | Memberships, timestamps, sizes, device lists, push tokens. | Without users' device keys: not a single readable message. |
What stays unencrypted — and why: metadata (inherent to any server architecture, stated
openly); room names, profiles and reactions (protect server, .env and backups accordingly);
system messages like “… joined” (server core, deletable but not E2EE payloads).
🕶️ Metadata protection — stage 1
Encryption hides content. Stage 1 also takes away the metadata the server does not need to operate — and makes the rest less precise. Experimental, default-on, no configuration needed.
🏷️ Encrypted channel names
The client encrypts the channel name and description with the room's sender keys; the server stores only ciphertext and a neutral placeholder (🔒 #4f2a9c01d7). The plaintext name never reaches the server — renaming a migrated channel in plaintext is rejected (fail closed). Members who can read messages can read the name; non-members and devices without the key see the placeholder. Old plaintext name records are deleted during migration.
📏 Size padding
Every encrypted message body and edit is padded to a size bucket before encryption (Padmé rule, ≥256 bytes, at most ~12 % overhead, invisible marker characters). The server cannot tell a short reply from a long sentence. Attachments already used size buckets.
⌨️ Typing opt-out
Settings → Security & privacy: “Send typing indicators” off means this device sends none — while you still see everyone else's. Your choice is per device and server, never stored centrally.
🧾 Minimal server logs
By default, process logs write no user IDs, login or display names, room names, e-mails or IPs — identity fields are [redacted]. Sign-in audit records keep no hash of IP or user agent unless the operator enables it — and then only with a key that rotates on a window (default 24 h), so hashes cannot be chained across time. The session list still shows the coarse device class (“Firefox on Windows”).
🎛️ Control: your account, your server — with the hard buttons
🔑 Passkey sign-in (WebAuthn)
Sign in without a password: the server stores only the public credential, the private key stays on your device and never leaves it. Passkeys are optional — password sign-in always stays available and cannot be disabled. Ceremonies are protected by a five-minute nonce that verifies at most once, even under concurrent requests. Adding or removing a passkey alerts your other sessions; removing one revokes established credentials like a password change. The relying party is derived from the public HTTPS URL — credentials can never be scoped broader than your server.
📡 Active sessions: who is online
Settings → Security lists every device signed into your account: device description, sign-in method, sign-in time, last use, an online marker and a “this device” marker. Online means a live realtime connection, tracked per session with 60-second heartbeats — not a guess. Every session except the current one can be signed out with one click. So a stolen laptop, a forgotten browser, or an ex's phone can be logged out precisely — without touching the account itself.
🗑️ Delete everything — the admin button
The server administrator finds a danger button in Admin → Security → self-deleting messages: “Delete all messages”. One action wipes every message in every room, thread and DM — with reactions, edits, attachments and link previews. The server reuses the expiry machinery: each message key is shredded, body events erased, attachment bytes removed, search/unread/notification projections updated, up to eight rooms in parallel as a background job with a progress report. Accounts, rooms, memberships, devices, keys and settings stay. The action is final and lands in the audit log — without any message content.
🧹 Delete all your messages
Users have the same power over their own data: Settings → Self-Destructing Messages has “Delete all my messages”. The server retracts every post you ever wrote — including rooms you left, thread replies and their echoes — newest-first, skipping what is already gone. Membership stays; content, reactions, pins, search entries, notifications, attachments and previews go. Crypto-shredded, not hidden.
📱 Devices & keys under your hand
Settings → “Devices with message keys” shows every device of your account; each is individually revocable. A revoked device is logged out, loses its sessions and tokens and gets no new sender keys — future messages skip it. New logins alert your other devices with name and time and the option “That wasn’t me” for one-click revocation. The operator can add auto-removal of devices inactive 30–365 days; abandoned devices are revoked completely, keys included.
⚙️ Admin → Security: inside the page
The admin menu has a dedicated Security page with three sections — everything observable, nothing hidden:
Access. Blocked usernames: names the server refuses at registration, with a pattern list. OAuth clients: every registered application with its origins and last authorization, each settable to default, trusted or blocked.
Retention. Delete all messages (danger button, background job with progress). System events: join/leave/call/room events — delete immediately (“clear all system events”, with count) or let them follow the message lifetime automatically; the server overwrites stored events securely.
Sessions. Session timeout: absolute lifetime from sign-in (minutes/hours/days, e.g. 12 h) — not just idle, applies overnight too. Inactive devices: auto-revoke devices inactive 30, 60, 90, 180 days or 1 year (off by default). Revoked completely: keys, sessions, tokens — the last device of an account included; a new sign-in then registers a fresh device. The panel shows how many devices are pending removal.
Plus: invitation links with expiry, password policy, login throttling, branding, audit log without message content, IP-watch diagnostics.
💬 Features
💬 Full-featured chat
Direct messages, groups and channels, threads, mentions, reactions, Markdown, pinned messages, emoji and file attachments — all end-to-end encrypted.
🖼️ Files & media
Images, documents, videos — transferred encrypted, EXIF/GPS/XMP metadata removed automatically. Filename and type inside the ciphertext.
📞 Conferences
Voice and video calls with screen sharing, encrypted per call, keys only for confirmed members.
🔔 Notifications
Push carries only sender and location — never your text. UnifiedPush/FCM/APNs/Web Push supported.
🛡️ Verification
Safety numbers per person, device overview, alerts on new devices, optional auto-removal of stale devices.
🧹 Controlled deletion
System messages deletable immediately or automatically — the server securely overwrites stored events, leaving only a content-free deletion proof.
📦 Downloads
| Platform | File | How to start |
|---|---|---|
| 🪟 Windows 10/11 (x64) | Enigma-3-Setup-<Version>.exe | Run. Replaces previous versions and keeps all data and keys. |
| 🐧 Linux x86_64 | Enigma-3-<Version>-x86_64.AppImage | chmod +x — then double-click. |
| 🐧 Linux arm64 | Enigma-3-<Version>-arm64.AppImage | chmod +x — then double-click. |
| 🤖 Android 7+ | enigma3-<Version>.apk | Signed release APK (CN=Enigma 3). Uninstall an existing debug APK first. |
| 📱 iPhone / iPad | — | Use the web app in Safari. |
| 🌐 Browser | — | Web app served directly from your own server. |
All downloads from the official repository: Current release page. Only ever one version is available — older releases are deleted so no outdated packages stay in circulation. Checksums for all files: SHA256SUMS.txt.
- Windows: the installer is unsigned. SmartScreen shows a warning — only confirm if this page was the source.
- Android: install via “unknown sources”; the release key differs from test APKs.
- Upgrades: install the new version over the existing one — all sessions, settings and keys are kept.
🔍 Verifying integrity
sha256sum -c SHA256SUMS.txt
The desktop app checks update notices automatically against these sums: without a matching checksum the app does not offer an update. Updates come exclusively from this repository — no tracking, no crash reporting, no background downloads.
⚡ Up and running in three steps
- Server — a Linux machine or VM with Docker Compose v2 (amd64/arm64). A single
setup.shsets up domains, Cloudflare, SMTP and Docker. Stack: SecureChat (server + web app), NATS (data), LiveKit (calls), Caddy or Cloudflare Tunnel (public access). You need your own domain and an SMTP mailbox. Details come from the server operator. - App — download and install the package for your platform. Registration works by invitation link only — the operator decides who joins.
- Start securely — enter the server address, register with the owner e-mail, send invitations. First message: compare the safety number, test the first call. If the call drops, the router ports are missing. Everything else is default.
📄 The brochure as PDF
Everything important in one document: security, cryptography, threat model, platforms and setup — to forward per messenger or to print. Always current with the latest version.
Brochure checksum: 03d05d781cc9036f85cf856414f6ad876b2f8840b5664b9c72843652e30d88af
SecureChat-App on GitHub
Release packages, checksums and issues.