Why Tieroute works the way it does
This page explains the main design decisions behind Tieroute, the protocol, and the reasoning for each. The specification says what the protocol is. This page says why. Section numbers like §6.4 point into the Tieroute protocol specification.
It's written for people deciding whether to implement Tieroute, review it, or argue with it. If a decision here looks wrong to you, comment on the passage: that's what these drafts are for.
What Tieroute is for
Tieroute is a protocol for end-to-end encrypted (E2EE) chat between people who know each other. Version 1 covers contacts, direct chats, group chats of up to 25 people, voice calls, more than one device per person, recovery, encrypted backups, and moving between servers.
Private chat first
Most chat apps start from public spaces and add private messages later. Tieroute starts from a contact list. Private conversations are where encryption matters most, and they're where centralized apps hold the most power over people. Public, self-hosted spaces (community servers) are planned for after v1. They're designed separately, so they can't weaken the private layer.
Federated, with its own protocol
Anyone can run a homeserver and join the network, and people can move between servers. We didn't build on ActivityPub or Matrix. ActivityPub ties identity to a server, sends direct messages readable by the server, leaves its schemas loose, and makes small servers absorb fan-out and spam. Matrix solves some of that, but it's large and hard to implement from scratch. Tieroute aims to be small enough that one person can build a compatible server or client from the specification alone.
Servers never touch the cryptography
Homeservers store and route sealed bytes. Everything a server routes on (the group, the epoch, the message type, the recipients) sits in a plain Tieroute envelope next to the encrypted data. That lets someone write a correct server without learning Messaging Layer Security (MLS), the group encryption standard, and it means a server bug can't leak message contents (§2.3).
No official client library
There's no "libtieroute" that every client must use. Clients use any MLS library that supports the Tieroute profile. One shared library becomes a single point of failure, and a single point of control. To make this practical, the profile sticks to one ciphersuite and the features that mainstream MLS libraries already implement (§11.1).
Encryption can't be turned off
There's no plaintext message type. When there's no official library, the easiest correct implementation has to be the encrypted one, so the protocol has no unencrypted path to fall back to.
Your homeserver keeps the time
Dates are everywhere in the protocol: on identity changes, sign-ins, moves, and messages between servers, and the checks on them assume clocks within a few minutes of each other. Servers have to keep their clocks synchronized. Apps go by their homeserver's clock rather than the computer's, so a computer whose clock is wrong still works, and the app tells you to fix it. We didn't have apps ask a time server: ordinary time lookups aren't authenticated, so they don't stop an attacker, and they'd send everyone's address to the time service. Your homeserver's time already comes over an encrypted connection from a server your app trusts. When a clock is off, the error says by how much, and servers tell their operators when it's their own clock that looks wrong (§8.4).
Desktop first
Reroute, our app, is a desktop app. Desktops have real storage and a real keychain, and a desktop app's code doesn't change every time it loads. That makes them easier to make trustworthy than a website. Mobile apps can follow once the protocol settles.
Cryptography
No new cryptography
Tieroute only composes published standards: MLS (RFC 9420) for groups, SFrame (Secure Frame, RFC 9605) for call media, HPKE (Hybrid Public Key Encryption, RFC 9180) for encrypting to one key, Ed25519 for signatures, and JSON with canonical encoding for anything signed. Inventing a primitive, or a clever new way to combine them, is where secure messaging protocols usually go wrong.
Post-quantum from the start
Every key exchange is hybrid: ML-KEM-768 (Module-Lattice-Based Key-Encapsulation Mechanism) together with X25519. That covers the MLS ciphersuite, HPKE, the keys for calls, and TLS. Messages recorded today could be decrypted by a large quantum computer later, so the time to protect them is before that computer exists. Hybrid means an attacker has to break both algorithms, so a flaw in the newer one doesn't make things worse than X25519 alone.
Signatures stay Ed25519 for v1. A forged signature only matters while the signature is being checked, so signatures can move to post-quantum algorithms later without exposing anything recorded today.
The trade-off is a deadline. Once a large quantum computer exists, its owner could forge Ed25519 signatures: posing as your devices, adding devices to your identity, or posing as homeservers. Post-quantum signatures have to arrive first, in a new wire version.
We waited because they're large. ML-DSA-65 (Module-Lattice-Based Digital Signature Algorithm) signatures are about 3.3 KB, against 64 bytes for Ed25519, and Tieroute signs every message.
If the deadline is missed, your recovery code still works. Its key is never published, so a quantum attacker can't derive it.
A proposal under review would close this gap before v1 is frozen: hybrid ML-DSA and Ed25519 signatures everywhere Tieroute signs, at the cost of messages about ten times larger (§3.2).
A recovery key nobody has seen
The recovery key is the most powerful key you have, so its public half isn't published until it's used. Your identity chain stores only a hash of the next recovery key. Each use reveals one key and commits to the next. All of them come from the same 12 words. An attacker, quantum or not, can't target a public key that was never public (§6.3).
You can replace your recovery code with a new one, for example after using it. The new code takes over everything the old one did, including opening your account data and backup, and the old one stops working (§6.3).
Verification codes you can read aloud
To check that nobody is in the middle of a chat, two people compare a code like 7K4M-2QXP in person or on a call. It's letters and digits in Crockford base32, which leaves out characters that look alike. Codes come from a commit-then-reveal exchange, so an attacker gets one guess per attempt, not millions (§6.7).
Identity
No phone number, no email, no password
Identities have none of the three. A phone number or email address ties you to an identity in a way that can be sold, leaked, or demanded by a court, and a password can be phished or reused. Instead, your devices sign you in, and a 12-word recovery code gets you back if you lose them all.
Your identity is a chain you sign
Your permanent identifier is the hash of the first entry in a signed chain. Later entries add or remove devices, change your handle, or move you to another homeserver. Because the identifier comes from your own signed data, no server can forge your chain, take over your identifier, or stop you from leaving (§6).
There's no single identity key that every device shares. Each device signs with its own key, and the offline recovery key outranks all of them. Losing a device means removing that device's key. The rest of your identity stays as it is.
Handles are domain names
You go by a handle like @kat.chat.example, or a domain you own. Domain names are already a global system that people understand, and owning your domain means owning your name. Handles sit on top of the permanent identifier, so you can change your handle without losing your contacts (§5.2).
To use your own domain, you publish one small record: a DNS TXT record, or a file at https://{your domain}/.well-known/tieroute/handle. Either works on its own. The file is the safer of the two, because TLS protects it, while a plain DNS answer can be spoofed. We still accept DNS without DNSSEC, because most domains aren't signed, and requiring it would mean DNS-only handles almost never work. If both exist, the file wins (§5.3).
Undoing a stolen device
If someone steals a device and uses it to change your identity, your recovery code can override those changes for 72 hours. After that, device changes stand, but you can still undo them with new entries. Changes that could lock you out, like removing a device or moving servers, alert every one of your devices right away, so you notice within the window (§6.4.1).
The rule decides the same way whichever version of your identity a server or app saw first, so everyone ends up agreeing. If your recovery came more than 72 hours after a device change you never saw, the device change stands, and you use your recovery code again on top of it: it always outranks your devices.
The 72 hours balance two things: long enough to notice and react, and short enough that your contacts aren't left unsure which version of your identity is real.
Moving needs your own signature
When you move to a new homeserver, your old one stores the move in your chain and sends it to your contacts' servers. So a lot of servers have seen it, and the chain alone doesn't prove the person registering it at the new server is you. Your contact list and groups come along in a separate note signed by one of your devices, and the new server takes them only from that. Otherwise anyone who saw the move could register it first and decide who's allowed to reach you there (§6.6).
Moving asks first
Your old homeserver stops being your home the moment it stores the move. If the new server then refused you, because of a bad invite or a handle someone else took, your chain would point at a server with no account for you, and your messages would go there. So your app asks the new server first whether it would take you, and only then tells the old one. If something still interrupts the move after that, your app remembers it and finishes it the next time it signs in. The new server accepts a registration that comes late, because the old one already checked the time on the move when it stored it (§6.6).
Your old homeserver keeps delivering messages it had already queued for 72 hours after a move. That's all it can do in your name: it can't start anything new for you, like claiming your contacts' keys (§6.6).
Why identifiers are 120 bits
Identifiers are the first 120 bits of a SHA-256 hash. Taking over someone else's identifier would take about 2¹²⁰ work, which is out of reach for any attacker, quantum computers included.
Someone can find two of their own chains that share an identifier for much less: about 2⁶⁰ work, within reach of a well-funded attacker. All that gets them is showing different contacts different versions of their own identity. The owner of an identity can already do that far more cheaply, by signing two versions with their own devices. The protocol treats both as a fork, and contacts who have verified each other catch it. A longer identifier would make identifiers longer without adding protection.
History on more than one device
When you link a new device, your other device sends it your contacts, groups, and history in an encrypted bundle. You can also turn on an encrypted backup on your homeserver, locked with a passphrase. Your recovery code can open the backup too, so losing both your devices and your passphrase doesn't mean losing your history (§7).
Contacts
Nobody reaches you without an invite code
Every contact request needs an invite code: one you handed out, or one you chose to publish on your profile. Requests carry no message text. Open contact requests are how spam and harassment get in, and filtering them afterward never works well. With invite codes, you decide who can find you before they can reach you (§9.1).
A refused delivery gets the same answer as an accepted one, so nobody can use the protocol to find out whose allowlist they're on. A declined request tells the requester, so they don't keep trying. A block doesn't.
Invite codes are plain links
A code like @kat.chat.example::Kx82mPq1Za can be pasted into an app, or shared as an ordinary https:// link. The link page stores nothing: the whole code is in the link, and your app checks it with your contact's own homeserver.
Only contacts can add you to groups
Being in a group means receiving messages from everyone in it. So only people you've already accepted as contacts can add you to one.
Group chats
One hub orders each group's changes
MLS needs every change to a group (adding or removing someone, changing keys) applied in the same order by every member. Tieroute picks one server per group to keep that order: the homeserver of whoever created it. It accepts the first valid change for each epoch and signs a receipt that every member checks (§11.6). If the receipt gets lost on the way back, sending the identical change again gets the same receipt, so a dropped connection can't leave a change half done. A hub could still lie by signing two different changes for the same step, but each receipt is signed, so it can't do it without leaving proof; an app that sees two stops trusting that hub and offers to re-create the group.
A single orderer is simpler and easier to get right than a consensus protocol between servers. The hub only orders changes. It never sees message contents, and ordinary messages never pass through it.
Outsiders can't freeze a group
If anyone could submit a change, a malicious server could submit one that breaks the group for everyone. Members derive a signing key for each epoch from the group's own secrets, and the hub accepts only changes signed with the current key. Only members can sign. A member could still break a group, and for that case every group can be re-created in one step, with the same people and its history kept (§11.11).
A re-created group is an offer
A re-created group names the one it replaces, so apps can show the two as one conversation. But group IDs aren't secret, and anyone who can add you to a group can name any group there. If apps took that on trust, someone could slip a lookalike group with different people into one of your conversations, and you'd keep talking as if nothing had changed. So an app offers a re-creation only when everyone in it was in the old group, shows who's missing and whether the admins changed, and switches only when you say so (§11.11).
Everyone has to agree on every change
When a group changes, every member checks the change against the identity chains of the people in it. Members' copies of those chains can be days apart in age, so a check that depended on how new your copy is would let members disagree, and a member who rejected a change would be stuck. So every member asks the same question: was this device one of theirs at the moment the hub accepted the change? Each member answers it from a copy of the chain fetched after that moment, so they all see the same entries, and the entries' dates decide. Replacing someone's only device happens in one change, so they never drop out of a group in between (§7.1, §11.9).
If a member can't get a fresh copy, because that person's homeserver is down, it doesn't decide either way. Saying no would strand it while everyone else moved on, and saying yes on an old copy could miss a removed device. It waits and keeps trying, and can't follow the group meanwhile. After 24 hours, its app offers to re-create the group, the same as for a hub that's been unreachable that long. Most of the time the cause is someone else's server being down for a while, which is worth waiting out rather than splitting the group over (§7.1, §11.11).
Admins add, members ask
The group's creator is its first admin. Admins add their contacts and remove people. Other members can ask to add someone, and an admin approves. Without this, anyone in a group could pull in anyone they know, and a group of friends could fill up with strangers (§11.8).
Up to 25 people
Group chats hold up to 25 people, and calls up to 10. Tieroute is for people who know each other. Small groups also keep every member's device count, key updates, and call mesh manageable without a media server.
The group's name is private too
A group's name, topic, and picture are encrypted like its messages, so only members see them. A server knows a group exists. It doesn't know what it's called.
Messages, presence, and calls
Encrypt what matters, not everything
Messages, files, typing indicators, read receipts, and group details are end-to-end encrypted. Presence and public profiles aren't, because their job is to be seen. Your homeserver needs your presence to share it with your contacts, and your public profile is meant for anyone.
Markdown, without hidden links
Messages support a small Markdown subset: bold, italic, strikethrough, code, quotes, and links. Links always show their real address. Formats like [text](url) let link text hide where a link goes, which is how phishing works, so they aren't supported.
Link previews come from the sender
When you send a link, your app may fetch the page to build a preview, and the preview travels inside the encrypted message. People you send it to never fetch anything. If every recipient's app fetched the page, the site would learn who received it and when, and a busy group could flood a small website.
Read receipts only when both people agree
The sender asks for a read receipt, and the reader decides whether to send it. Typing indicators are on by default, and both are encrypted and never stored.
Files are for chat, not storage
Files can be up to 64 MiB each, and your homeserver keeps them for 30 days. These limits keep small homeservers affordable to run, and Tieroute is meant for chat, so it isn't built to store files long term. Files are encrypted before they leave your device.
Calls between contacts
Voice calls work one to one and in group chats of up to 10 people, with no special server. Call setup travels inside the chat's encrypted group, and call media is encrypted twice: by the standard WebRTC (Web Real-Time Communication) encryption between devices, and by SFrame keys that come from the chat's post-quantum group. By default, calls go through each person's own homeserver relay, so nobody learns anybody else's IP address (§15).
Everyone in a call sees who's muted. Each device says so itself when it mutes or unmutes, and says it again when someone joins, so nobody has to guess whether a quiet person is muted. The message is a general state rather than a mute switch, so later things like turning video on can use it too. It's only a label: what a device sends is what everyone hears (§15).
Video and screen sharing aren't in v1, and the protocol leaves room to add them.
Moderation and metadata
Moderation by report
Nobody scans encrypted chats, on servers or on devices. Scanning on devices breaks the promise of end-to-end encryption. Moderation is by report: if someone sends you something you need to report, you choose which messages to share with your server's admin. Each one carries the sender's signature, so the admin can confirm who sent it (§16).
Servers decide for themselves
A homeserver can stop federating with another, and its users are always told when it does. There are no shared blocklists: each admin decides on their own, so one list maintainer can't cut a server off the whole network.
What your homeserver can see
Your homeserver sees who you talk to and when, because it needs that to deliver messages and enforce your allowlists. It never sees what's said. Hiding the social graph from servers too would need techniques like mixnets or private information retrieval, which are much harder to build and run. For v1 we accepted this trade-off, and the specification lists exactly what each party can see (§18.1).
Not in v1: community servers
Community servers will be small, self-hosted places for a community, with voice and text channels. They aren't part of v1, and their design gets its own review before anything is built. A few choices are already made:
- Members only. You join with a code from the server before you see anything except its preview.
- No public directory. Nobody runs a central list of servers.
- Voice is end-to-end encrypted. Channel text isn't: the server's admin can read it, and the app labels it clearly. Pretending otherwise for a large group would be worse than being honest about it.
Still open
These questions aren't settled yet:
- Hybrid signatures in v1. Should signatures go hybrid now, before v1 is frozen, instead of in a later version? The proposal is in §3.2, with its costs.
- Handles that are no longer used. When someone renames their handle or moves servers, should their homeserver free the old name? If so, should it wait, for example 30 days, before someone else can take it, so contacts notice the change first?
- Detecting a forked identity without a verification round. Group members could share what they see of each other's chains, so a fork shows up on its own.
- Junk from removed group members. Someone removed from a group can still send it deliveries that are dropped, since group IDs aren't secret. Rate limits contain this today. Signing each delivery with a key that only current members have would stop it, at the cost of more complexity.