doritermend-to-end encrypted terminal

Your devices.
One terminal.

Every machine you own, linked through an end-to-end encrypted mesh.

Noise IK sessions · ChaCha20-Poly1305 · the relay holds no key

one terminal · 4 machines e2ee mesh
laptop macOS

~/api main $ bun test

✓ 412 pass  0 fail  3.1s

~/api main $

● cloud-box: api@4f2c healthy

mac mini OmO

mini:~/api $ omo

● Reading migrations/0042_keys.sql

! Needs you: apply to staging?

✓ Applied. 41 tests pass

relay ciphertext only
cloud-box ubuntu

ubuntu@cloud-box:~ $ tail -f deploy.log

pull api@4f2c  migrate  restart

api@4f2c healthy in 41s

phone iOS

attached to OmO on mac mini

! apply to staging?

› 1  yes, apply it

plaintext, only on your machines sealed with Noise in transit each machine keeps its own key

01App

Every terminal on every machine.
One sidebar for all of them.

Spaces on the left, agents grouped by whether they need you, are working or are done, and split terminals on the right. On a phone the same terminal gets a key bar with ctrl, esc and the arrows. What you see here is the app's own components, live, with sample data.

doriterm, studio app.doriterm.com

02Relay

The relay is a pipe.
It cannot read what it carries.

Your phone and your machine agree on keys with a Noise IK handshake, then seal every record with ChaCha20-Poly1305 and rekey every 220 messages. The relay, on Cloudflare Workers, matches the two ends by host id and copies bytes between them. It keeps no accounts and stores no messages.

phone you type
On the phone, you type git status.
~/app $ git status
server hc runs it
On the server, git status runs and prints that the branch is up to date.
~/app $ git status
On branch main
Your branch is up to date with 'origin/main'.
nothing to commit, working tree clean

What the relay can see

  • which host ids open sessions to which, and when
  • the addresses they connect from
  • the size and timing of encrypted records
  • with a typed code, a relay that answers as a fake host can read the new device's name; the fingerprint check then shows a key that is not your host's

What it never sees

  • your keystrokes and terminal output
  • the pairing secret in a QR code or link, and mesh records
  • nothing it learns lets it forge, replay or splice a record

03Pairing

Pair a phone in one scan.
The secret never leaves the link.

The pairing secret lives in the link's #fragment, which a browser never sends to any server. No camera handy? Type the 8-character code instead. CPace keeps the relay from testing guesses offline, and the host answers at most 10 tries per offer.

  1. Run one command on the machineIt prints a QR code, a link, a code and the host's fingerprint.
  2. Scan it with your phoneThe camera opens the pair page with everything filled in.
  3. Compare the fingerprintTen groups of four, from the host's Noise key. A changed key is refused unless you say so.
server expires in 10:00
~ $ doriface-host pair
linkhttps://doriface.omo.dev/pair#h=…&k=…&p=…
code7KQ4 M2XD
fingerprint 3FA94C0E81D2B7A50E6CD1932A7FC40B95E16D38
fingerprint matches phone paired with Noise IKpsk2

04Mesh

Machines that vouch for each other.
No ssh, no open port.

Put your laptop, a build box and a VPS in one mesh and each one shows the others in its sidebar. Membership is a set of records every member signs with its Ed25519 key and syncs over the same encrypted relay. The relay stores none of it and can forge none of it.

  1. 01Four machines share one mesh. Each one's hc lists the other three as machines.
  2. 02mesh pair on laptop invites a fifth machine; pi asks to join.
  3. 03You approve on laptop. The signed admit record syncs, and every member dials pi.
  4. 04mesh revoke vps --compromised: every member closes its sessions and refuses its keys.

Approved, then admitted

A machine joins after you approve it on a member, unless you opened the invite with --auto. For seven days every member shows who joined, who invited it and its fingerprint.

One sidebar, every machine

Each member adds every other member as a herdr machine on its own, so their spaces and agents appear under their names.

Revoke in one record

With --compromised, one record removes a member and every machine it admitted. Its sessions close and its keys are refused on every path.

05Platforms

Desktop, browser, phone.
The same terminal on each.

Terminals render with libghostty, built from Ghostty's own source and compiled to WebAssembly, so a pane on your phone draws the same cells as on your desktop.

Desktop app

Starts the host and herdr-core on this computer, installs herdr-core for you when it is missing, and offers this computer to your phone.

macOS, Windows, Linux

Any modern browser

Open app.doriterm.com, pair, and every pane of every machine is there. Nothing to install on a borrowed computer.

Chrome and Edge 119+, Safari 17.4+, Firefox 121+

Your phone

Install it from the browser as an app. A key bar puts ctrl, esc and the arrows under your thumb, so you can answer an agent from anywhere.

iOS 17.4+, Android with Chrome 119+
on your machine
$ doriface-host pair                     # a QR code, a link and a code for your phone
$ doriface-host mesh pair                # invite another machine into your mesh
$ doriface-host mesh join <code>         # on that machine: ask to join
$ doriface-host mesh revoke <member>     # remove a member everywhere

Read every line
before you trust it.

doriterm is open source under AGPL-3.0, and the wire format it is built against is published, relay section included.