Works offline over Bluetooth mesh. Reaches anyone in the world over Tor. No servers. No phone number. No account.
Protocol • Cryptography • Features • Architecture • Getting Started • Android • Security • Roadmap
⚠️ Alpha — not yet production-ready. Cryptographic primitives are sound, but the codebase has not been independently audited. Do not rely on it for life-critical anonymity.
TestFlight beta — coming soon · Mac: works today with a free Apple ID · Android
SophaxChat is an open-source, serverless, end-to-end encrypted messenger for iOS, macOS, and Android.
Nearby — it works over Bluetooth LE and WiFi Direct. No internet, no servers, no pairing. Messages relay across multiple devices automatically (up to 6 hops), so you reach people even when you're not directly connected.
Globally — enable TCP and connect through Orbot (Tor). Your traffic is anonymous, your IP is hidden, and you can reach anyone in the world with a .onion address. No configuration inside SophaxChat — Orbot handles everything.
In both modes: no phone number, no email, no account. Your identity is a Curve25519 key pair, generated on your device, never transmitted to any server. Every message uses the Signal Protocol (X3DH + Double Ratchet with Header Encryption).
Is it still "Anonymous, offline, end-to-end encrypted mesh chat"? Yes — and more. Local mode (BLE/WiFi) is fully offline and anonymous as always. With TCP enabled via Orbot/Tor, it becomes global: still anonymous (no account, no phone number), still end-to-end encrypted (same X3DH + Double Ratchet pipeline), still decentralized (no servers — direct peer-to-peer). TCP and Tor support are optional and off by default.
Journalists and activists — communicate without leaving traces tied to a phone number or account. Combine with Tor (Orbot VPN mode) for transport anonymity.
Disaster responders and field workers — no internet? Bluetooth and WiFi mesh still work. Devices relay messages across the network automatically, up to 6 hops.
Privacy-conscious users — tired of surrendering your phone number to chat apps. Here, your identity is a cryptographic key pair, generated locally, never shared with any server.
Developers and researchers — the full source is open. Cryptographic primitives (X3DH, Double Ratchet, Header Encryption, Sealed Sender) are implemented in ~3 500 lines of auditable Swift using Apple's CryptoKit.
SophaxChat is built around three principles that cannot be traded away:
-
No servers, ever. Servers are honeypots. Every central point is a single point of failure, surveillance, and censorship. SophaxChat communicates directly device-to-device — over Bluetooth, WiFi, and peer-to-peer TCP. The Tor network (via Orbot) provides internet routing without introducing any trusted server.
-
No identity leakage. No phone number, no email, no account. Your identity is a Curve25519 key pair, generated on your device, never transmitted to any server. The app doesn't even know your name unless you choose to set one.
-
Maximum cryptographic protection, not just "good enough". X3DH gives each session unique key material. Double Ratchet rotates keys every message. Header Encryption hides routing metadata from relay nodes. Sealed Sender hides who is talking to whom. These are not features — they are the baseline.
| Scenario | Signal | Telegram | bitchat | SophaxChat | |
|---|---|---|---|---|---|
| No internet connection (BLE/WiFi mesh) | ❌ | ❌ | ❌ | ✅ | ✅ |
| LAN auto-discovery (same WiFi, no pairing) | ❌ | ❌ | ❌ | ❌ | ✅ |
| Internet reach (TCP, peer-to-peer) | ✅ | ✅ | ✅ | ❌ | ✅ |
| Tor / anonymity network support | ❌ | ❌ | ❌ | ✅ | |
| No phone number required | ❌ | ❌ | ✅ | ✅ | |
| Signal-grade forward secrecy | ✅ | ✅ | ❌ | ✅ | |
| Per-session unique keys (X3DH) | ✅ | ✅ | ❌ | ✅ | |
| Header encryption (relay metadata) | ✅ | ❌ | ❌ | ❌ | ✅ |
| Sealed sender (hides sender from relay) | ✅ | ❌ | ❌ | ❌ | ✅ |
| No server dependency | ❌ | ❌ | ❌ | ✅ | ✅ |
| No metadata collection | ❌ | ❌ | ❌³ | ✅ | ✅ |
| End-to-end encrypted groups | ✅ | ✅ | ❌ | ✅ | |
| Open-source | ❌ | ✅ | ✅ | ||
| Multihop relay (mesh routing) | ❌ | ❌ | ❌ | ✅ | ✅ |
| iOS + Android cross-platform | ✅ | ✅ | ✅ | ❌ | ✅ |
| macOS support | ✅ | ✅ | ✅ | ❌ | ✅ |
¹ Telegram requires a phone number to register; usernames added in 2023 do not replace this requirement. ² Telegram's MTProto E2EE applies only to "Secret Chats" — regular chats, groups, and channels are server-side encrypted (Telegram holds the keys). ³ WhatsApp uses Signal Protocol for message content but collects extensive metadata: who you talk to, when, how often, your IP address, device fingerprint, and contact graph — all shared with Meta. ⁴ Telegram clients are open-source; the server is closed-source and proprietary.
SophaxChat occupies a specific niche: Signal-grade cryptography, zero infrastructure. Ideal for journalists, activists, protesters, disaster responders, or anyone in an environment where internet access is unavailable, monitored, or untrusted.
┌─ Alice ─────────────────────────────────────────────────────────── Bob ─┐
[1. DISCOVERY — MultipeerConnectivity (Bluetooth LE / WiFi Direct)]
Alice ←──── MPC peer found ────→ Bob
[2. HELLO — Immediate key exchange on connection]
Alice ──── Hello(PreKeyBundle_A) ────→ Bob
Alice ←─── Hello(PreKeyBundle_B) ──── Bob
[3. SESSION INIT — X3DH key agreement, first encrypted message]
Alice computes:
DH1 = DH(IK_A, SPK_B)
DH2 = DH(EK_A, IK_B)
DH3 = DH(EK_A, SPK_B)
DH4 = DH(EK_A, OPK_B) [if available]
SK = HKDF(DH1 || DH2 || DH3 || DH4)
Alice ──── InitiateSession(EK_A, usedKeyIDs, DR_msg_0) ────→ Bob
Bob derives SK from his keys
Bob inits Double Ratchet
[4. MESSAGES — Double Ratchet + Header Encryption]
Alice ──── SealedSender(encHeader, encBody) ────→ Bob
Alice ←─── SealedSender(encHeader, encBody) ──── Bob
[5. RELAY — If Alice and Bob not directly connected]
Alice ──── Relay(TTL=6, target=Bob, msg) ────→ Charlie ────→ Bob
(TTL=5)
└─────────────────────────────────────────────────────────────────────────┘
┌─ Alice (creator) ─────────────────────── Bob, Carol ─┐
[1. CREATE — Alice generates group and her sender chain]
Alice: senderChainKey_A = random(32 bytes)
[2. INVITE — Sent via per-peer Double Ratchet channel]
Alice ──── GroupInvite(groupID, senderChainKey_A) ────→ Bob
Alice ──── GroupInvite(groupID, senderChainKey_A) ────→ Carol
[3. JOIN — Each member generates their own sender chain]
Bob: senderChainKey_B = random(32 bytes)
Carol: senderChainKey_C = random(32 bytes)
Bob ──── SenderKeyDistribution(senderChainKey_B) ────→ Alice, Carol
Carol ──── SenderKeyDistribution(senderChainKey_C) ────→ Alice, Bob
[4. GROUP MESSAGE — Each sender uses their own KDF chain]
messageKey_n = HMAC-SHA256(chainKey_n, 0x01)
chainKey_{n+1} = HMAC-SHA256(chainKey_n, 0x02)
Alice ──── GroupMsg(ciphertext, iteration=n) ────→ Bob, Carol
(broadcast via mesh)
└──────────────────────────────────────────────────────┘
- Forward secrecy — each message uses a unique message key. Compromising key
nreveals nothing about keys< n. - Break-in recovery — after a compromise, DH ratchet steps generate fresh key material from new Curve25519 ephemeral keys. Each group member ratchets independently.
- Header encryption — ratchet headers (public key, counters) are sealed with rotating header keys. Relay nodes see only opaque ciphertext.
- Sealed sender — sender identity is hidden from relay nodes; only the intended recipient can learn who sent the message.
- Multihop relay — messages flood through the mesh with TTL=6, deduplicated via LRU cache.
- Offline queue — messages queued in memory when no peers are reachable; drained automatically on reconnect.
- Disappearing messages — optional
expiresAttimestamp; messages purged locally every 60 seconds. Supported in both 1:1 and group conversations.
All primitives come from Apple's CryptoKit — hardware-accelerated and independently audited by Apple.
| Layer | Operation | Algorithm | Key Size |
|---|---|---|---|
| Identity | Signing | Ed25519 | 256-bit |
| Identity | Key agreement | X25519 | 256-bit |
| Session init | Key exchange | X3DH | — |
| Session init | Key derivation | HKDF-SHA256 | 256-bit |
| Messaging | Symmetric ratchet | HMAC-SHA256 | 256-bit |
| Messaging | DH ratchet | X25519 | 256-bit |
| Messaging | AEAD encryption | ChaCha20-Poly1305 | 256-bit |
| Header encryption | AEAD | ChaCha20-Poly1305 | 256-bit |
| Sealed sender | Key derivation | HKDF-SHA256 | 256-bit |
| Sealed sender | AEAD | ChaCha20-Poly1305 | 256-bit |
| Group Sender Keys | KDF chain | HMAC-SHA256 | 256-bit |
| Group encryption | AEAD | ChaCha20-Poly1305 | 256-bit |
| Storage | At-rest encryption | AES-256-GCM | 256-bit |
| Identity verification | Safety numbers | SHA-512 | — |
All private keys and session states are stored in the iOS Keychain with kSecAttrAccessibleWhenUnlockedThisDeviceOnly:
- Not backed up to iCloud
- Not transferred to a new device
- Not accessible when the device is locked
- Wiped on device factory reset
| Feature | 1:1 | Group |
|---|---|---|
| Text messages | ✅ | ✅ |
| Image sharing | ✅ | ✅ |
| Voice messages (PTT, AAC M4A) | ✅ | ✅ |
| Reply to message (quoted bubble) | ✅ | ✅ |
| Message reactions (6-emoji) | ✅ | ✅ |
| Read receipts (blue tick) | ✅ | ✅ |
| Disappearing messages (30s–7d) | ✅ | ✅ |
| Forward message | ✅ | ✅ |
| Message search | ✅ | ✅ |
| Draft persistence (restored on re-open) | ✅ | ✅ |
| Feature | Status |
|---|---|
| Create group (multi-peer picker) | ✅ |
| Signal-style Sender Keys (per-member KDF chain) | ✅ |
| Group invite via Double Ratchet channel | ✅ |
| Sender Key Distribution to all members | ✅ |
| Per-message forward secrecy (per-member) | ✅ |
| Leave group | ✅ |
| Member list sheet | ✅ |
| Group push notifications | ✅ |
| Out-of-order message recovery (skipped key cache, 200 keys/sender) | ✅ |
| Re-keying on member leave (forward secrecy) | ✅ |
| Feature | Status |
|---|---|
| X3DH session establishment | ✅ |
| Double Ratchet + Header Encryption | ✅ |
| Sealed sender | ✅ |
| Ed25519 message signatures | ✅ |
| Safety Number verification (QR + manual) | ✅ |
| App lock (Face ID / Touch ID / passcode) | ✅ |
| Auto-lock on background | ✅ |
| App Switcher blur | ✅ |
| AES-256-GCM at-rest message storage | ✅ |
| Block peer | ✅ |
| TOFU key-change warning (Safety Number changed banner) | ✅ |
| Notification content hiding on lock screen | ✅ |
| Clipboard auto-clear (60s after copy) | ✅ |
| Keyboard privacy (autocorrect disabled, no learning) | ✅ |
| Screen recording warning banner | ✅ |
| Screenshot notification toast | ✅ |
| Screenshot prevention (screen appears blank) | ✅ |
| Duress PIN — decoy empty state on coercion | ✅ |
| Custom numeric lock PIN (alternative to biometrics) | ✅ |
Full encrypted export — identity + messages (.sxfe) |
✅ |
| File sharing (arbitrary files ≤ 2 MB) | ✅ |
| Feature | Status |
|---|---|
.onion address derived from identity key (no configuration) |
✅ |
| Orbot SOCKS5 support (route through Tor via proxy config) | ✅ |
Contact Card — QR code + sophaxchat:// deep link |
✅ |
| QR scanner for adding contacts | ✅ |
sophaxchat://add deep link handling |
✅ |
| Tor onboarding (first-run guide) | ✅ |
| Feature | Status |
|---|---|
| On-device AI (Apple Foundation Models, iOS 26+) | ✅ |
| No server, no API key, no data leaves device | ✅ |
| Conversation memory within session | ✅ |
| Graceful fallback on unsupported devices | ✅ |
| Feature | Status |
|---|---|
| Bluetooth LE transport (iOS) | ✅ |
| WiFi Direct transport (iOS / Android) | ✅ |
| LAN auto-discovery via mDNS/Bonjour (same WiFi) | ✅ |
| iOS ↔ Android cross-platform messaging (TCP wire protocol) | ✅ |
| TCP internet transport (peer-to-peer, no server) | ✅ |
| Tor / SOCKS5 anonymity (Orbot VPN mode or proxy) | ✅ |
| GMS-free Android (Wi-Fi Direct fallback, no Google Play Services) | ✅ |
| Multihop relay (TTL=6) | ✅ |
| LRU relay deduplication | ✅ |
| Offline message queue | ✅ |
| Rate limiting (20 relays / 10s per peer + 50 / 10s global cap) | ✅ |
| Relay hop indicator in UI | ✅ |
| Typing indicators | ✅ |
| Store-and-forward via relay peers (48h TTL, 300 items) | ✅ |
| macOS Catalyst support | ✅ |
SophaxChat/
├── Sources/SophaxChatCore/ # Core library — pure Swift, testable
│ ├── Crypto/
│ │ ├── CryptoTypes.swift # Types, constants, error definitions
│ │ ├── KeychainManager.swift # Keychain CRUD for all key material
│ │ ├── IdentityManager.swift # Ed25519 + X25519 identity lifecycle
│ │ ├── PreKeyManager.swift # X3DH prekey pool (SPK + 20 OTPKs)
│ │ ├── X3DH.swift # X3DH sender and receiver implementation
│ │ ├── DoubleRatchet.swift # Double Ratchet + Header Encryption (Signal spec §4.3)
│ │ ├── Keccak.swift # Minimal SHA3-256 (Tor v3 .onion checksum)
│ │ └── OnionAddress.swift # Tor v3 .onion derivation from Ed25519 key
│ ├── Group/
│ │ ├── GroupTypes.swift # GroupInfo, SenderKeyState, GroupCryptoVersion
│ │ ├── MLSGroupManager.swift # MLS actor — wraps mls-rs via UniFFI
│ │ ├── ChatManager+MLS.swift # MLS inbound/outbound handlers + dispatch router
│ │ └── GroupCryptoMigration.swift # Opt-in SKv2 → MLS migration scaffold
│ ├── MLS/
│ │ └── Generated/
│ │ └── sophax_mls.swift # Auto-generated UniFFI Swift bindings (mls-rs)
│ ├── Network/
│ │ ├── NetworkProtocol.swift # Wire message types + WireMessageBuilder
│ │ ├── MeshManager.swift # MultipeerConnectivity P2P transport (BLE/WiFi)
│ │ ├── TCPTransport.swift # Internet TCP transport (SOCKS5/Tor, port 25519)
│ │ └── RelayRouter.swift # Multihop relay with LRU dedup cache
│ ├── Storage/
│ │ ├── MessageStore.swift # AES-256-GCM encrypted at-rest storage
│ │ └── AttachmentStore.swift # Encrypted blob store for images/audio
│ └── ChatManager.swift # High-level coordinator (session + routing)
│
├── rust/ # mls-rs Rust crate (compiled to XCFramework via UniFFI)
│ ├── src/lib.rs # Stateless MLS API: create/add/remove/encrypt/decrypt
│ └── Cargo.toml # mls-rs 0.54, RustCrypto backend (no OpenSSL)
│
├── scripts/
│ └── build_mls_xcframework.sh # Cross-compile Rust → XCFramework for iOS/macOS
│
├── SophaxChat/ # iOS/macOS SwiftUI application
│ ├── App/
│ │ ├── SophaxChatApp.swift # App entry point, security overlays, deep links
│ │ └── AppState.swift # @MainActor observable state, SOCKS5/Tor config
│ └── Views/
│ ├── Onboarding/ # First-run username setup
│ ├── AI/ # Local AI assistant (Apple Foundation Models)
│ ├── Chat/ # Chat list, 1:1 and group bubbles, relay indicator
│ └── Settings/ # Safety number, app lock, Contact Card, Tor setup
│
└── Tests/SophaxChatCoreTests/
├── CryptoTests.swift # X3DH symmetry + Double Ratchet correctness
└── MLSGroupManagerTests.swift # MLS two-party, three-party, removal, epoch tests
macOS: The app runs on macOS 14+ via Catalyst.
AVAudioSessioncalls are guarded with#if !targetEnvironment(macCatalyst). Peer discovery works over WiFi on macOS.
SwiftUI View
│ sendMessage() / sendGroupMessage()
▼
AppState (@MainActor)
│
▼
ChatManager ← single coordinator, NSLock session mutex
│
├─ 1:1 path
│ ├─ buildOutboundWire() ← X3DH init (new session) or DR+HE encrypt
│ ├─ sealedSenderWrap() ← ECDH(ephemeral, recipientDH) → ChaChaPoly
│ └─ sendOrQueue()
│ ├─ tcp.send() if peer connected via TCP/internet
│ ├─ mesh.send() if peer directly connected (BLE/WiFi)
│ ├─ mesh.broadcast() relay via RelayEnvelope (TTL=6)
│ └─ pendingQueue[] offline; drained on reconnect
│
├─ group path
│ ├─ senderKeyRatchetStep() ← HMAC-SHA256(chainKey, 0x01/0x02)
│ ├─ ChaChaPoly encrypt ← with per-message key
│ └─ mesh.broadcast() ← GroupWireMessage to all members
│
└─ didReceiveMessage()
├─ .hello → store PreKeyBundle, drain queue
├─ .initiateSession → X3DH receiver, DR init, decrypt
├─ .message → DR+HE decrypt, send ACK
├─ .ack / .readReceipt → update message status
├─ .relay → process inner or forward (TTL-1)
├─ .groupInvite → store group, generate sender chain, distribute SKD
├─ .groupMessage → v2: ratchet peer chain; v1: shared key fallback
└─ .senderKeyDistribution → store peer sender chain state
The Android port uses the same wire protocol as iOS. Messages between iOS and Android are fully interoperable — same X3DH + Double Ratchet encryption, same JSON framing over TCP port 25519.
Auto-discovery on the same WiFi: iOS (Bonjour) and Android (NsdManager) both advertise _sophaxchat._tcp. They find each other automatically — no manual pairing, no IP addresses.
GMS-free: Works on GrapheneOS, CalyxOS, and any AOSP device. Google Play Services are detected at runtime; if absent, Wi-Fi Direct (android.net.wifi.p2p.*) is used instead. No GMS imports in the codebase.
Feature parity with iOS: The Android UI matches the iOS app on all key features — App Lock (BiometricPrompt), unread badges, typing indicators, message long-press menu (copy / delete / block / reply), reply-to-message with quoted preview, Safety Number screen, deep link confirmation dialog, onboarding slides, message reactions (6-emoji picker + reaction pills), disappearing messages (per-conversation timer, auto-cleanup), contact renaming, and per-conversation message search.
See android/README.md for build instructions and architecture.
| Requirement | Version |
|---|---|
| Xcode | 16.0+ |
| iOS deployment target | 17.0+ |
| macOS deployment target | 14.0+ (Catalyst) |
| Physical devices | 2× iPhone (MultipeerConnectivity requires real hardware) |
| XcodeGen | latest (brew install xcodegen) |
Note: The Swift core library (
SophaxChatCore) builds without Xcode viaswift build. Physical devices are required to test P2P connectivity; the simulator does not support MultipeerConnectivity peer discovery.
# 1. Clone
git clone https://github.com/sophaxtechnologies/SophaxChat.git
cd SophaxChat
# 2. Generate Xcode project
brew install xcodegen
xcodegen generate
# 3. Open in Xcode
open SophaxChat.xcodeprojIn Xcode:
- Select your Team under Signing & Capabilities
- Plug in your iPhone, select it as the destination
- ⌘R
Repeat on a second device to test messaging.
The app runs natively on macOS via Mac Catalyst — no iOS simulator needed.
xcodegen generate
open SophaxChat.xcodeprojIn Xcode:
- Select target SophaxChat → tab General → under Deployment Info enable the Mac checkbox → choose Mac Catalyst
- Set destination to My Mac (Mac Catalyst) in the toolbar
- Signing & Capabilities → select your Team (free Apple ID works)
- ⌘R
Free Apple ID: Mac Catalyst apps run on your own Mac without any developer account. No 7-day re-signing limit (unlike iOS sideloading).
What works on Mac:
- Full encrypted messaging (1:1 and group)
- Global reach via TCP + Tor (Orbot for Mac)
- Local AI assistant (macOS 26 + Apple Intelligence)
- QR Contact Cards (share / receive)
- All security features (App Lock uses Touch ID on Mac)
What differs on Mac:
- Peer discovery uses WiFi (no Bluetooth mesh on macOS Catalyst)
- Screen recording warning is disabled (screen recording is normal OS behavior on Mac)
- Camera picker uses the Mac camera
You can sideload SophaxChat on iPhone for free using AltStore — no Apple Developer account required.
- Install AltStore on your Mac and iPhone (altstore.io)
- Clone + generate the Xcode project (steps above)
- In Xcode:
Product → Archive(select Any iOS Device as destination) - In the Organizer: Distribute App → Save for Ad Hoc Deployment → save the
.ipa - In AltStore on your Mac: drag-and-drop the
.ipaonto your iPhone - AltStore re-signs automatically every 7 days when AltServer is running on your Mac
Free Apple ID limit: you can have 3 sideloaded apps active at a time. AltStore itself counts as one.
swift build# Run in Xcode: Product → Test (⌘U)Tests cover: X3DH key symmetry (with/without OTPK), Double Ratchet bidirectional messaging, out-of-order message delivery, session state persistence, associated data binding, and KDF distinctness.
See SECURITY.md for the full threat model, cryptographic primitive table, protocol details, and security review findings.
| Threat | Protection |
|---|---|
| Network eavesdropping | ChaCha20-Poly1305 E2EE; MPC transport encrypted (.required); TCP is a carrier of already-sealed WireMessages |
| MITM / impersonation | Ed25519 signatures on every message; Safety Number verification |
| Replay attacks | Unique nonce per message; message-ID deduplication at storage layer |
| Past message compromise | Forward secrecy via symmetric ratchet (per-message keys) |
| Future message compromise | Break-in recovery via DH ratchet (fresh Curve25519 ephemeral keys) |
| Header metadata leakage | Header Encryption — relay nodes see only opaque ciphertext |
| Sender identity leakage | Sealed sender — sender hidden from relay nodes via ECDH wrapping |
| Data at rest | AES-256-GCM encrypted message and attachment store |
| iCloud backup exfiltration | kSecAttrAccessibleWhenUnlockedThisDeviceOnly; keys never backed up |
| App Switcher screenshot | Content blurred on willResignActive |
| Unauthorized app access | App lock: Face ID / Touch ID / passcode, auto-lock on background |
| DoS via oversized messages | Attachment ≤ 512 KB; username ≤ 64 chars |
| Prekey exhaustion | OTPK pool auto-replenished when below 50%; SPK rotated every 7 days |
Each user has a Safety Number — a 60-digit fingerprint derived from SHA-512 of their identity keys. Both numbers are shown side by side in the verification screen: each party reads their own number aloud to the other. Scan via QR code or compare manually.
See SECURITY.md § Security Review Findings for full details. Key items:
| Finding | Severity | Summary |
|---|---|---|
| M-2 | MEDIUM | Sender Key Distribution may arrive after first messages in high-load scenarios |
| M-3 | MEDIUM | One-time prekey exhaustion window reduces X3DH entropy temporarily |
hiddenPreviewsBodyPlaceholder registered |
||
removeAllDeliveredNotifications() on lock |
||
toIntOrNull()?.takeIf { it in 1..65535 } |
||
.endsWith(".onion")^[a-z2-7]{56}\.onion$ |
||
initializeMemory(as: UInt8.self, repeating: 0) |
||
EncryptedSharedPreferences |
||
mk not zeroed after usemk.fill(0) after encrypt/decrypt |
||
precondition(count == 96 || count == 128) |
||
Log.e |
||
assert() in IdentityManager stripped in Release buildsprecondition() |
Do not open public issues for security bugs.
- Friendly invite code —
SXPH-XXXX-XXXXfingerprint in ContactCard + paste-link flow; no server - Mesh relay via Tor — relay envelopes forwarded over TCP/Tor peers, extends reach beyond local BLE/WiFi
- Avatar sync for group-only contacts — eager push in Hello handler, no DM required
- On-device diagnostic log — rotating local log, voluntary export from Settings; zero telemetry
- Duress mode — custom PIN shows empty decoy app; silently drops incoming messages
- Full export (
.sxfe) — identity + all messages in one PBKDF2-encrypted file - Sealed sender for groups — per-member SKv2 fan-out wrapped in ECDH envelope
- File sharing — arbitrary file attachments (≤2 MB) in DMs and groups
Full completed list (50+ items)
- X3DH session establishment
- Double Ratchet + Header Encryption
- Sealed sender (hides sender from relay nodes)
- Multihop relay (TTL=6, LRU dedup)
- Offline message queue + store-and-forward (48h TTL, 300 items)
- Disappearing messages (30s–7d, 1:1 and group)
- SPK rotation (7-day) + OTPK replenishment
- Safety Numbers (QR + manual)
- Rate limiting (20 relays/10s per peer, 50/10s global)
- Session initiation deduplication
- Voice messages PTT (AAC M4A, encrypted)
- Image sharing (encrypted, PhotosPicker + camera)
- Reply, forward, search, reactions, read receipts
- App lock (Face ID / Touch ID / passcode + custom PIN)
- Group messaging — Signal-style Sender Keys v2 (per-member KDF chains)
- Group re-keying on member leave
- Group skipped message key cache (200 keys/sender)
- macOS Catalyst support
- TOFU key-change detection
- Channel discovery (nearby groups broadcast)
- Tor global reach —
.onionv3 from Ed25519 identity key - Contact Card — QR +
sophaxchat://adddeep link - Orbot SOCKS5 support (route TCP traffic through Tor proxy)
- Clipboard auto-clear (60s)
- Keyboard privacy (autocorrect disabled)
- Screen recording warning + screenshot toast
- Local AI assistant (Apple Foundation Models, iOS 26+)
- Background BLE operation (
BGAppRefreshTask) - Pluggable transport adapter (
MessageTransportprotocol) - Android feature parity — App Lock, reactions, disappearing messages, safety numbers, search
- PrivacyInfo.xcprivacy (App Store privacy manifest)
- TestFlight public beta — applying for NLnet NGI Assure grant to cover Apple Developer Program and independent audit
- Independent third-party security audit — highest-priority before v1.0. Open a GitHub Security Advisory if you are interested in auditing
- DHT peer discovery — opt-in BitTorrent DHT announce of
.onionaddress; no server, no IP leak - Hardware security key binding — Secure Enclave identity key + FIDO2 external key (post-audit)
SophaxChat is applying for funding through NLnet Foundation / NGI Zero Core. NLnet supports open internet projects focused on privacy, security, and decentralization.
If this project receives a grant, the funds will be used for: Apple Developer Program membership (required for TestFlight distribution) and an independent third-party security audit.
Pull requests are welcome. Before contributing:
- Read SECURITY.md — especially if touching crypto code
- Open an issue to discuss significant changes before writing code
- All cryptographic changes require a corresponding test in
CryptoTests.swift - Run
swift buildbefore submitting
- UI/UX — the interface is functional, not polished
- Tests — relay dedup, group sender key, session initiation edge cases
- Localization — the app currently ships in English only
- LoRa / audio transport — implement
MessageTransportfor LoRa radio or near-ultrasonic audio channel (seeNetwork/MessageTransport.swift)
SophaxChat collects no data. See PRIVACY.md for the full policy (also usable as App Store privacy policy URL).
MIT — see LICENSE.
Copyright (c) 2026 SophaxChat contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.



