the net routes around damage. so do we.
Tieroute is an open, federated protocol for end-to-end encrypted chat.
It starts from your contact list, not a public square: direct chats, groups of up to 25, and voice calls with people you've actually added. Your identity is a chain of entries you sign yourself. Groups run on MLS, and every key exchange is hybrid post-quantum, because recorded traffic doesn't expire. We build Reroute, a desktop app, and rerouted, the reference server. But the spec is the point.
Tieroute v1 draft. The v1 spec hasn't had its outside cryptographic review yet, and the wire format can still change. Don't rely on it for anything sensitive.
- Group encryption
- MLS (RFC 9420)
- Key exchange
- ML-KEM-768 + X25519
- Wire format
- JSON, with CDDL schemas
- Contact requests
- Invite code only
- Group chats
- Up to 25 people
- Calls
- Voice, up to 10 people
- Files
- 64 MiB, kept 30 days
- Phone or email
- Never asked for
Protocol
Written so someone else can implement it
We want someone who has never talked to us to be able to build a compatible server or client from the spec alone. So servers are kept deliberately dull: they move sealed bytes and check a short list of rules, and all the cryptography lives in clients. A lot of the design is a reaction to what we watched go wrong elsewhere, like servers that own your identity, direct messages the admin can read, loose schemas, unsigned content, and small servers drowning in fan-out.
- The server never sees inside MLS
- It only reads the Tieroute envelope we put next to the ciphertext: group, epoch, message type, recipients. That’s enough to route and enforce allowlists. The cryptography stays on the clients.
- §2.3
- Nothing new in the crypto
- MLS, SFrame, HPKE, ML-KEM-768 with X25519, and Ed25519, all published and reviewed by people who aren’t us. How we fit them together gets an outside review before v1 is frozen.
- §2.2
- Bring your own MLS library
- There’s no libtieroute. Any MLS library that supports the profile will do, which is why the profile sticks to features the mainstream libraries already have.
- §11.1
- Invite codes, always
- Nobody can even ask to be your contact without one. You hand codes out, or publish one on your profile if you want to be findable. Requests carry no text, so there’s nothing to spam you with. And a refused delivery gets the same 202 as an accepted one, so nobody can probe your allowlist.
- §9.1, §10.3
- No phone number, no email
- Your devices sign you in. If you lose all of them, 12 recovery words get you back; there’s no reset link, since there’s nowhere to send one.
- §6.1
- Plain JSON
- Every protocol object is JSON. A bit verbose, but you can read live traffic with curl, and what’s stored today stays readable long after whatever wrote it is gone.
- §4
Network
Alpha homeservers, checked live
rerouteholdings.llc
Checking
nocarrier.fail
Checking
Each server document is fetched and checked for a v1 API, at most once a minute.
Homeservers and federation
Anyone can run a homeserver. It carries sealed traffic for its own users and nobody else’s. Finding a server takes one JSON document, and every call between servers is a plain HTTPS POST with a signed body. No special transport, no persistent connections between servers.
Roles
- Homeserver
- Per user. Stores your identity chain, device list, MLS key packages, contact list and group allowlist, and an inbox with a read position per device. Authenticates your devices, enforces allowlists, and speaks server-to-server for you. Never sees plaintext.
- §8–10
- Hub
- Per group. Not a separate server, but a role a homeserver plays for one group: the creator’s to begin with, and another member’s if an admin hands it off later. It orders commits (first valid one per epoch wins), stores them for members who were offline, and signs receipts. Application messages never touch it.
- §11.6, §11.11
- TURN relay
- Each homeserver relays calls for its own users. Calls relay by default, so contacts don’t learn each other’s IP addresses.
- §15.1
- Device
- Each device has its own Ed25519 key, its own MLS leaf in every chat, and pulls its own copy of every delivery.
- §7
A message, end to end · §10.2
- 01 Encrypt The device encrypts with MLS and wraps the ciphertext in a delivery envelope: group, epoch, message type, and every recipient identity, from the MLS tree.
- 02 Submit POST send to its own homeserver, authenticated by a session token bound to the device key. Commits go to the group’s hub first and come back with a signed receipt.
- 03 Fan out The homeserver groups recipients by their current home, from cached chains, and makes one signed deliver call per destination server, with to narrowed to that server’s users.
- 04 Verify The receiving server checks the server signature, the clock, and the nonce, then that the sender’s chain names the calling server as home.
- 05 Admit Each recipient’s allowlists decide: chat traffic needs a group they joined, and a Welcome needs a contact. Refusals still answer 202.
- 06 Pull Every device of the recipient pulls the item from its inbox and acknowledges it. Once all devices have, it’s deleted; otherwise it expires after 30 days.
/.well-known/tieroute/server
- name
- The server’s DNS name. Never an IP address.
- §5.1
- roles
- "home" in v1. Readers ignore roles they don’t know, so later versions can add more.
- §5.1
- apis
- The client and server-to-server base URLs for each wire version. The only fixed path in the protocol is this document; everything else is found from here.
- §8.1
- keys
- Ed25519 signing keys, each with a key ID and a validity window. Readers keep old keys until they expire, so rotation never breaks signatures in flight.
- §5.1
- handles
- The domain this homeserver gives out free handles under, like tieroute.example for user.tieroute.example.
- §5.2
- registration
- "invite" or "closed". A server open to everyone publishes a public invite code, and closes by revoking it.
- §8.2
When things break
A server goes down
Everyone sending to it keeps their calls in a durable outbox and retries with backoff. For 72 hours. Then the sending device is told, so a message never just vanishes. §10.5
A device is stolen
If the thief removes one of your devices or moves your account, every device you still have raises an alarm right away. For the next 72 hours your recovery code can overrule anything they did, on every server. After that their changes stand, but you can still kick the device out. §6.4.1
You leave a server
Your app asks the new server first, and gets back a signed reservation for the move. Your old server won’t record the move without one, so a move can’t leave you between two servers. If the old one is gone, or decides to be difficult, sign the move with your recovery code instead. Every server accepts that, from anywhere. §6.6
Try it
Ask a real server
These go to the real alpha homeservers, through this site, as you press Send. They’re the protocol’s public read-only endpoints (the server document, a handle, an identity chain), so you can’t break anything by poking at them.
GET https://{server}/.well-known/tieroute/server
The one fixed path in the protocol. It names the server, lists its API base URLs for each wire version, its signing keys with their validity windows, the domain it gives handles under, and whether it takes new accounts.
Choose Send to run the request.
Identity
Identity is a chain you sign
Your identifier is a hash of the first entry in a chain that only you sign. A server can host the chain, but it can’t forge an entry, take your identifier, or keep you from leaving. Every device signs with its own key. The recovery key stays offline, and it outranks all of them.
- Identity chain
- A hash-linked list of signed entries. Ops: add-device, remove-device, set-home, set-handle, set-recovery. The first entry (genesis) is signed by your first device.
- §6.3
- tr:…
- Your permanent identifier: "tr:" plus the first 15 bytes (120 bits) of SHA-256 of the canonical (JCS) genesis entry, in lowercase base32.
- §6.2
- Handles
- Domain names: user.tieroute.example or your own domain. Resolved over HTTPS at /.well-known/tieroute/handle (a DNS TXT record counts only with DNSSEC), and only trusted if the chain names the handle back.
- §5.2–5.3
- Recovery code
- 16 random bytes shown as 12 BIP-39 words. Recovery keys derive from it with HKDF. Pre-rotation: the chain publishes only SHA-256 of the next recovery key, so no recovery public key is exposed before it’s used.
- §6.3
- Fork rule
- A recovery-signed entry may replace device-signed entries up to 72 hours older than itself. That’s how a stolen device’s changes are undone everywhere, even where the forked chain is shorter.
- §6.4.1
- Who vouches
- A device-signed entry only counts if the server that was home immediately before it appended it, so it went through that server’s clock check and alerts. A thief can’t have their own server push a backdated move to your contacts. Recovery-signed entries count from anywhere.
- §6.5
- Alerts
- A device-signed remove-device or set-home raises an alarm on every device of the account; add-device raises a notice. Chats show a line when a member’s devices change.
- §6.4.1
- Verification codes
- XXXX-XXXX in Crockford base32, 40 bits, from a commit-then-reveal round inside the direct chat, so an attacker gets one guess per round. The transcript includes both chain heads, so a match also proves you see the same chains.
- §6.7
Contacts and delivery
Contacts, and how messages travel
Nobody reaches you without an invite code. Even then, nothing gets to your devices that your allowlists didn’t let in. Between servers every call is signed, and when something can’t be delivered the sender hears about it.
- Invite codes
- 10 characters, created on your homeserver with an optional use count and expiry; the server stores only a keyed hash, under a secret outside its database. Paste @handle::code to send a request, or share it as a link: rerouteholdings.llc/@handle::code, a page that stores nothing. A decline blocks new requests for 30 days.
- §9.1–9.4
- delivery
- {id, kind, from, to, group, epoch, mls, receipt, ephemeral, data}. Servers read every field except data, which is a base64url MLS PrivateMessage or Welcome.
- §10.1
- s2s-request
- Every server-to-server call is a POST whose body is signed by a server key named in the sender’s /.well-known/tieroute/server document. Checked: destination, path, ±5 minutes, and a fresh nonce.
- §8.3
- Push, then pull
- Your homeserver pushes each delivery to the recipient’s homeserver; their devices pull from an inbox and acknowledge. Items are kept 30 days, and deduplicated by ID for 7.
- §10.2–10.4
- Durable outbox
- Failed calls are retried with exponential backoff and jitter, from 10 seconds up to hourly, on one schedule per destination server. 408, 429, and 5xx are retried; other 4xx are final. After 72 hours the sending device is told.
- §10.5
- Speaking for users
- A server may act only for identities whose chain names it as home. After a move, the old home gets 72 more hours for one thing, delivering what was already queued; it can’t claim key packages or send requests in your name. Fetched chains must extend the cached one or be a valid recovery fork; nothing else replaces a cache.
- §6.5, §6.6, §10.2
Live
Watch the server work
This is rerouteholdings.llc, live. Each row is a message, a group change, or an invitation, with everyone it reached. The server keeps the ciphertext to itself, and the group ID too, so all you get is the envelope it routes on: who, what kind, how big. Open a row to see what’s left of it.
rerouteholdings.llc is a public development server. Its traffic is shown here on purpose, including who talks to whom. Use another homeserver for anything you want kept between you and your server.
- Waiting for something to happen. The server replays its last 50 deliveries when you connect.
- kind, mls
- What the server routes: mls for chat traffic, and the MLS message type (app, commit, or welcome).
- from, to
- The sender’s identifier, and every recipient’s. Servers need them to enforce allowlists.
- group, epoch
- Which MLS group, and how many times its membership or keys have changed.
- data
- The MLS message, base64url. Only the recipients’ devices can decrypt it.
- receipt
- On commits: the hub’s signed receipt that it accepted the commit (§11.6).
Groups and devices
Group chats with MLS, ordered by one hub
MLS needs every member to apply commits in the same order, so something has to pick the order. We picked one server per group, the hub, and then made it hard for the hub to misbehave: only members can sign a commit request, and every commit it accepts comes back with a signed receipt.
- MLS profile
- RFC 9420 with MLS_128_MLKEM768X25519_AES128GCM_SHA256_Ed25519 (0x004F until IANA registers 0x0010). PrivateMessage only. A direct chat is a two-person group.
- §11.1
- tieroute_group
- GroupContext extension 0xF0A1: kind, hub, admins, since, replaces, and the add-approval bookkeeping, plus heads and basis, the chain history each change was checked against. Groups without it are refused.
- §11.2
- The hub
- One server orders each group’s commits. That’s the creator’s homeserver at first; after a hub handoff, it’s another member’s. Whichever it is accepts the first valid commit per epoch, answers later ones with 409 stale-epoch, and signs a receipt every client checks.
- §11.6, §11.11
- Epoch keys
- Ed25519 keys derived from MLS-Exporter("tieroute hub", group_id, 32). Only members can sign a commit request, and the hub learns nothing it could read.
- §11.6
- Admins
- Admins add their contacts directly, remove people, and grant or revoke admin. Other members propose an add; it goes through only with an admin’s signed approval, carried encrypted inside the commit and used once, so every member reaches the same verdict.
- §11.8
- Same verdict everywhere
- A change carries its own evidence: for each person it depends on, the chain entry it was checked against, by hash, and the few entries before it. Every member judges it against that same history, however old their own copy is, so nobody ends up stuck in an epoch the rest left. A member who can’t get the history waits, and sends nothing to the group meanwhile.
- §7.1
- Devices as members
- Every device is its own MLS leaf, with credential [identifier, device key]. When a chain changes, whichever member notices first commits it. A removed device goes at once, and until it’s out, members hold their own messages to that group. Replacing someone’s only device is one commit, so they never drop out in between.
- §7, §11.9
- Linking a device
- The new device shows a link code; a typed code is answered with an XXXX-XXXX confirmation that must be typed back and signed. The link bundle, sealed with hybrid HPKE, brings contacts, groups, and history.
- §7.2
- Limits
- At most 25 people and 250 devices per group, 10 per person. A stuck group can be re-created in one click; everyone else gets it as an offer, and decides whether to switch.
- §11.10–11.11
Privacy
What your homeserver can and can’t see
Passes through sealed
Messages
MLS, and each one signed by the sending device
Files
Encrypted before upload; 64 MiB, kept 30 days
Group name, topic, picture
Inside MLS, so only members see them
Typing and read receipts
Encrypted and never stored; receipts only when both sides agree
Calls
DTLS-SRTP plus SFrame keyed from the chat’s MLS group
What your server does see
Who talks to whom
The sender ID on each delivery, which allowlists need
Contacts and groups
Your homeserver keeps them as allowlists
Presence and status
Seen by your homeserver; shown only to contacts
Public profile
Display name, picture, bio, and any public invite code, to anyone
Timing and sizes
When deliveries happen and how large they are
So servers see who talks to whom, and never what’s said. Hiding the social graph too would take mixnets or private information retrieval, and we decided v1 can live without them; §18.1 lists everything a server can see. Moderation is report-only. Nobody scans your chats, and a report shares a decrypted copy only when the person reporting chooses to, signed by whoever sent it.
Calls
Voice calls between contacts
A call lives inside the chat you already have. Signaling goes through that chat’s MLS group and the media keys come from it, so a call is exactly as private as the chat it started in.
- Signaling
- Ringing, offers, answers, and ICE candidates are messages inside the chat’s MLS group, end-to-end encrypted like any other. A missed ring shows up later as a missed call.
- §15.2
- Media
- WebRTC between participants, with no media server: a mesh of up to 10 people, even in a 25-person group. No separate video calling: a camera is one more thing to cast.
- §15.2
- Media casting
- Proposed: anyone in a call can cast video, audio, or both, from a screen, a camera, or a film, over the call’s own connections and sealed with the same SFrame keys. Cast sound stays apart from voices, so people talk over it, and each listener sets its volume or mutes it.
- §15.3
- Sealed twice
- DTLS-SRTP between peers, plus SFrame keyed from the chat’s post-quantum MLS group, so recorded calls stay sealed against a future quantum computer.
- §15.2
- Relayed by default
- Media goes through each side’s own homeserver TURN, so neither side sees the other’s IP address. A setting allows direct connections.
- §15.1–15.2
- Community servers
- Self-hosted spaces with voice and text channels are planned for a later version. They aren’t part of v1; the draft design is with the spec pages.
- Later
Wire format
JSON on the wire, signed and checkable
- JSON
- I-JSON (RFC 7493): UTF-8, no duplicate names, integers only. Byte strings are unpadded base64url. Plain text can be inspected and preserved with ordinary tools.
- §4.1
- signed
- {t, b, k or kid, s}: a type tag, the body as JSON text, the signing key, and an Ed25519 signature over JCS ["tieroute/v1", t, b]. Verifiers check the exact text they received; nobody re-encodes a signed body. Verification is pinned exactly (S < L, no small-order points) so every implementation agrees on every chain.
- §4.3
- Schemas
- Every wire object is CDDL in the spec, extracted to one file and checked for duplicate and undefined rules.
- §4
- Test vectors
- Signed envelopes, Ed25519 edge cases, recovery derivation, a full identity chain, the fork rule, verification codes, device linking, formatting, and the group evidence checks, checked by both implementations and an independent script, which agree byte for byte.
- §19.3
- v1 core
- §3 to §11, the app-message envelope and text, the APIs, and the vectors. Every change is logged; v1 is frozen once the outside cryptographic review is done.
- §19
- MLS Group key agreement RFC 9420
- SFrame Call media, keyed from MLS RFC 9605
- HPKE Encrypting to one key: device link bundles RFC 9180
- Ed25519 Identity chains, devices, servers, every message RFC 8032
- I-JSON Encoding, with schemas in CDDL RFC 7493
- JCS Canonical JSON for signatures and hashes RFC 8785
- X-Wing ML-KEM-768 + X25519 hybrid KEM, inside MLS and HPKE IETF draft
Status
What exists today
There’s working code. rerouted, the reference homeserver, is Go and implements every endpoint in the v1 draft. Reroute is Rust and Svelte and implements the whole draft, calls included. Both are alpha, and we mean that.
- Contacts and chats
- Invite codes, requests, declines, and blocking. Direct chats with replies, edits, deletes, reactions, typing, and read receipts only when both sides agree.
- Stage 1
- Federation
- Signed server-to-server calls, chain pushes, a durable outbox, and a notice when a message can’t be delivered.
- Stage 2
- Devices and groups
- Linking by QR code or typed code, with a hybrid HPKE link bundle that carries contacts and history. Recovery with 12 words, verification codes, and group chats of up to 25 with admins, approvals, and re-create.
- Stage 3
- Recovery and backups
- Encrypted account data, a message backup locked with a passphrase that the recovery code also opens, moving between homeservers, and devices that rejoin after being offline too long.
- Stage 4
- Profiles and files
- Signed public profiles with pictures, presence with auto-away, files encrypted before upload, and reports that carry the sender’s signature as proof.
- Stage 5
- Voice calls
- One to one and in groups of up to 10: WebRTC between devices, signaled inside the chat, with SFrame keyed from its post-quantum MLS group, relayed by default so nobody sees anybody’s IP address.
- Calls
- Outside review
- An independent cryptographic review comes before v1 is frozen.
- Before v1
24
attacks on the server-side claims of §17.2, tried from outside against two servers and two rogue ones. Each one was refused.
3
implementations of the test vectors that have to agree: the Go server, the Rust client, and an independent JavaScript checker.
12
demo scripts in the repo that run the protocol end to end across federated servers, from the bare server to calls. A few attack it, and one upgrades data and clients from older releases.
Reroute is a desktop app for macOS, Windows, and Linux. The alpha homeservers register new accounts by invite code. rerouteholdings.llc is a public development server: its traffic, sealed but with who-sends-to-whom visible, is shown in the live feed above.
Company
Built so that it doesn’t depend on us
Reroute Holdings LLC is a small company that designs Tieroute and builds Reroute and rerouted.
We’d like to be optional. Your identity is a chain you sign, not an account we hold, and servers are interchangeable; anyone can run one. The spec and its test vectors are written so you can build a compatible server or client without ever asking us anything. If we disappeared tomorrow, your contacts would still have your keys and the servers would keep routing.
Open by design
Self-certifying identity and an open spec mean nobody can close the network later, including whoever owns this company by then.
No phone or email
Never asked for, so never depended on for your identity.
No scanning
Encrypted chats aren’t scanned on the server or on your device.
No directory
There’s no central list of people to search. The people you give a code to can reach you.