have it
The link is the first key.
The room secret lives after the # in the URL. Browsers keep that part of the URL local. Our server sees a hash of the room. It never sees the key.
protocol
Encryption happens in your browser, before a single byte leaves. The key never reaches our server. The history never touches our database. Lose your link, and there is nothing for us to recover.
Mainstream chat apps ship accounts, contact discovery, group invites, forwarding, and cross-device history. Each of those is a surface that lets you be found, followed, and correlated. We do not ship them. What you trade for the rough edges is a chat you actually own.
A channel can ask for three things to let someone in: the link you have, the password you know, and the knock that says who you are. For two friends, only the link is required. For an agent pairing, all three are.
have it
The room secret lives after the # in the URL. Browsers keep that part of the URL local. Our server sees a hash of the room. It never sees the key.
know it
Add a password and the message keys are derived from the link and the password. Someone with just the link cannot read or speak.
prove it
Knocking sends a short introduction. A person sees it and lets you in. For an agent, the knock is a hidden session secret — the phone has to type it correctly before the agent auto-approves.
Human rooms can skip the password and the knock. Agent pairing uses both: the CLI prints a fresh code and asks for a hidden knock, then only approves a phone that holds all three.
When you create a channel, your browser generates a secret and writes it into the part of the URL after the #. Browsers do not send that part to a server. Our server gets a hash of the room. It never gets the key.
When you tap send, your browser encrypts the message with a key derived from the channel secret and, if you set one, the password. The server receives an opaque blob and forwards it. The other browser — or the bridge on your own machine — uses the same inputs to open it.
Messages use AES-256-GCM. Keys are derived from the channel secret and the optional password with HKDF-SHA256.
A new person opens the link and knocks. Someone already inside sees the knock and approves. For an agent, the local bridge joins from your machine, decrypts the knock with the pairing code, and matches the body against a hidden session message before it lets the phone in. There is no global identity to search, follow, invite, or leak.
Our database holds a hash of each channel, a hash of each participant token, and timestamps. Enough to deliver an encrypted event to the right place. Not enough to read it. Your readable message history lives in IndexedDB on your own device.
on our server
Just enough to know which encrypted event belongs where. Not the text of the message.
on your device
Every message you received. Clear your browser storage and that local copy is gone.
A channel can opt into encrypted offline catch-up. The server stores ciphertext only, capped by retention rules — newest messages first, old ciphertext expires instead of becoming permanent cloud history.
We protect the contents of your messages from our server. We don't fix bad link-sharing habits. We don't hide your network metadata from your ISP. And we don't decide whether an agent command is safe to run. Those choices stay with you.
git push is correct.If a link gets shared too widely, close the room and create a new one. That is the clean escape hatch.
The whole repository is intentionally small. Read the server, the client crypto, and the deployment files without signing an NDA.