Problem & my contribution
Changing phones often means depending on a phone number, an account operator or a separate backup. The goal is a portable identity and synchronized history without giving the server the keys to the content.
My contribution. I define account and recovery behavior, user journeys, client/server trust boundaries and the synchronization design. I own the product logic that explains what is protected, what remains visible and what losing the key means.
Identity is derived from a seed phrase. Clients encrypt records before upload; the server holds encrypted entities with revisions and cursors. The local database is a UI projection. WebRTC signaling stays separate from history storage.
Outcome
A browser client is available. The source implements portable identity, encrypted account sync, revisions, tombstone deletion and profile boundaries. Native builds add LAN messaging. The visual below shows the actual entry screen, not an end-to-end cryptographic audit.
Engineering highlights
Retry delivery, not the mutation
Idempotent mutations and cursors let a client recover after a connection loss. Signed requests and nonces address the separate problem of replay.
Deletion is synchronized state
A tombstone preserves the deletion. Otherwise another client can reload an old record and bring a deleted message back.
History and connectivity are separate
Signaling establishes peer connections; it does not become a second message database.
Architecture
| Layer | Technology & purpose |
|---|---|
| Frontend | React Native, Expo 55, TypeScript; web and native clients |
| Backend | Node.js, cloud-vault / sync, separate WebRTC signaling |
| Data | Client SQLite; encrypted entities, revisions and server cursors |
| Security | BIP39, did:key, @noble; client-side keys and encryption |
Trade-offs & lessons
Seed-based recovery removes dependence on a phone number, but the operator cannot reset a lost phrase. Encrypted records protect content, not all connection and synchronization metadata.
What the implementation taught
A core sync failure mode is a locally successful deletion that does not survive a later pull. Deletion is therefore record state, and the cursor advances after application. This is an architectural risk, not a fabricated incident story.
What I would improve now
I would prioritize reproducible two-device scenarios: concurrent edits, deletion while offline, recovery and device revocation. Evidence from those runs is more useful than a broad “secure messenger” claim.
Operations & data
Sync, signaling and notifications are separate services. Source includes log scrubbing and Sentry integration. Native clients require platform builds and dependencies; web has its own capability limits.
Data & migrations
A mutation carries an identifier, revision, owner and tombstone. sync_entity_heads tracks local record versions; sync_state stores the cursor and sync state. Schema ownership follows service boundaries rather than one shared database for every client.
| Entity | Stored state |
|---|---|
| Mutation | idempotency key, revision, owner, tombstone |
| sync_entity_heads | Local synchronized record version |
| sync_state | epoch, pull cursor, timestamps |
| Media | Encrypted bytes + reference; client holds the key |
Limits
Servers see metadata. Public-link posts are unencrypted. Browser key storage is weaker than native secure storage. Sync and calls require connectivity. No independent cryptographic audit is claimed here.
Evidence & source
Verified 24 Sep 2026: 71 sync/vault checks; local /health responds. These are targeted checks of the described decisions, not a full application audit.
Claims above are tied to public code and documentation. Evidence links point to the reviewed revision; CI status may change.

