security
A shell in a browser deserves paranoia.
You are considering exposing a terminal to the internet. That is a serious thing to do, so here is exactly how mtmux is built, what it protects against, and what it does not.
Everything on this page is implemented today in @repo/crypto. Where something is a design intention rather than shipped code, it says so.
Data path
tmux server
Unix socket, your uid. mtmux reads it as a normal client would.
mtmux CLI
Seals every frame for the paired browser. Config and token live at ~/.mtmux/config.json, mode 0600.
broker (untrusted)
Sees ciphertext, sizes and timing. No key, no plaintext, no pairing secret.
your browser
Derives the same keys from the pairing run, and keeps a device key to reconnect without a new code.
Guarantees
Pairing is a balanced PAKE
CPace over ristretto255, ciphersuite CPACE-RISTR255-SHA512, implemented against draft-irtf-cfrg-cpace-13 and checked over the draft's own test vectors.
The broker never sees the secret
The code splits into a 2-digit routing slot and a 6-digit secret. The slot routes; the secret is the PAKE password and is never transmitted, not even as a hash. Only the first two digits reach our servers.
One wrong guess ends it
Each side proves it derived the same key before anything is sent. A failed confirmation destroys the pairing, so an online guesser gets exactly one attempt at a million — and the machine says so, and slows down.
Keys are derived, never sent
An HKDF-SHA256 schedule over the CPace transcript produces one key per direction plus a confirmation key. Nothing derived from them crosses the wire.
Frames are sealed and counted
AES-256-GCM with a per-direction nonce and a monotonic counter, so a replayed or reordered frame fails authentication instead of being decrypted.
The QR keeps the code off the wire
The code rides in the URL fragment, which browsers never send to a server — so scanning it does not hand the code to whoever serves the page.
Nothing to port-forward
The CLI dials out to the broker. There is no firewall hole to open, no sshd exposed, and no reverse proxy to misconfigure.
Revocation forgets a key
Reconnecting a known browser is a signed Ed25519 challenge, so mtmux devices revoke on a device id is enough to lock it out permanently.
What we defend against
- A hostile or compromised broker reading or injecting session traffic.
- A broker that knows the whole code and tries to pair your CLI to its own browser.
- Passive network capture on hotel, café or cellular networks.
- Someone who watches you scan and captures the code after it is spent.
- Replay or reordering of captured frames, via the per-direction nonce counter.
- A lost phone: revoke its device id and its key is worthless.
What we don't
- A compromised machine. If your box is owned, so is the session.
- An unlocked, already-paired phone in someone else's hands.
- Traffic analysis — the broker can infer when you are typing from frame timing.
- Malicious browser extensions on a paired desktop.
- Anything your allow-list lets a paired browser read on disk.
- A weak or shared machine account: mtmux runs as you, with your permissions.
Don't trust the broker? Don't use it.
Two escape hatches. --local serves your LAN and loopback and contacts no broker at all — it is not a degraded mode, it is the same server without the tunnel. Or run the broker yourself and point the CLI at it.
Disclosure
Report anything you find to [email protected]. We acknowledge within one business day, ship fixes for critical issues as fast as we can, and credit reporters in the changelog unless asked not to.
- CLI & client source
- MIT, on GitHub
- Pairing suite
- CPACE-RISTR255-SHA512
- Transport
- AES-256-GCM
- Third-party audit
- none yet
- Session frames
- never stored
No independent cryptographic audit has been performed. The CPace implementation is verified against the draft's published test vectors, which proves the group maths and encoding are right — it does not prove the surrounding protocol wiring is. Read it yourself before you trust it with something that matters.