Nodes and the log
Keeps 7 promises
- Each node makes its own key, and the key never leaves it
- One node can be revoked without touching any other
- Peers' keys come from the log, never from a handshake
- The log merges in any order, and a revocation wins
- A grant gives one key rights on a scope, until it expires or is revoked
- The log crosses in every session, so a device added since is let in at once
- Every session carries both sides' log heads, and the difference crosses in it
A node per tab would fill the log, so a browser profile is one node. A router, a TV or an Arduino can't hold a key; a node reaches it on your behalf, and the grant is on that node. Every node keeps a copy of the log, and the holder of the data checks every caller against it itself, offline if it must; no server's word is needed. Two devices append at once, so the log is a grow-only set of signed, hash-linked records, not a chain, and a revocation wins any merge.
For now the whole log crosses: as a session opens, each side sends every
entry it holds as a control message (node/README.md), and the other
keeps what is new, checked as any entry is. Entries are a few kilobytes
each, so heads and the difference (log/heads-in-session) come before an
account has many devices. A machine checks a session's caller against its
copy: a key the log doesn't hold gets no answer, and a call needs may on
<machine key>/<service>, which an Admin device always has and a machine
(a Member) only by a grant.
Revocation, and where it breaks
Keeps 2 promises
A stolen device is a valid key until its revocation reaches the holder, and an offline node misses revocations. So chains are short-lived, every session carries log heads, and the relay holds entries for devices that are away (D-17); a relay can delay an entry but never forge one. The one peer you'd hear from may be the device being revoked, which won't pass on its own revocation, so a holder drains its mailbox at the relay before its first answer. A new device fetches the log from somewhere; a relay holding a copy sees the account's device list. Losing every device that can sign for the account is losing the account, so add a second one early.
Pairing
Keeps 8 promises
- A new node joins only from a device already trusted
- A pairing code lets in one key, before it expires
- A relay carrying a pairing learns nothing usable and can't change it
- Another browser of yours joins from a link the first one makes
- You choose how long a code works, and can type it
- A backup code written down adds a device with nothing else online
- Add a machine says which systems it runs on, and what each needs
- The install line checks what it downloads, and needs no root
The code carries 128 bits and names a pairing mailbox at the relay (D-11). The new node and the trusted one exchange keys through it, sealed under the code, so the relay sees ciphertext it can't change without failing the exchange. The code admits one key, once.
Today both must be online: the tab that made the code waits at the mailbox
with the relay's meet (spec/relay.md), and the new device meets it
there; each learns the other's key and the two frames of the exchange go by
to. The code ends in the relay, host/<account id>, so the new device
needs nothing else. A machine joins as a Member; another browser of yours,
from a link with the code in its fragment (#pair=…, never sent to a
server), as an Admin. On the machine, youros-node pair <code> keeps the
log and the relay beside its key, and youros-node run then needs no
flags. The installer is the same steps in a script: the build for the
machine's architecture, checked against the SHA-256 the script was served
with, pair, then a systemd user service. Same-origin integrity only: a
host that serves a different binary serves a matching script with it, which
is browser/checked-elsewhere's problem too.
Later, codes of words (not built). A live code is short only behind a PAKE, as magic-wormhole's are: without one the relay could test guesses against the mailbox the code names, so D-11 changes with it. The PAKE only authenticates: what crosses is sealed under an ML-KEM exchange it vouches for, so a recorded transcript stays closed (D-2). A backup code can't wait for an online Admin, so an Admin signs an invite into the log beforehand, naming a key derived from the words with Argon2id; holders accept the one add it signs, and the wait before it is an Admin is the veto that paper found in a drawer needs.
Three tiers of state
Keeps 4 promises
The OS's own state is docs like everything else, in three tiers, and each setting's schema says which tier it is in (D-22):
| tier | for example | kept |
|---|---|---|
| Account | workspaces, theme, keybindings, most settings | a doc per kind, synced to every device of the account |
| Device | zoom, which workspace each screen shows | a doc per device, synced too, so it's backed up and can be copied to another device; it overrides the account's value |
| Moment | focus, selection, scroll, who's here | presence (an ephemeral store), or nowhere |
A doc per kind, not one for all: a doc is the unit of sharing and of grants, and the theme and the layout change at different rates and by different hands. The shell reads and writes its state as typed objects mirrored onto the docs (Loro Mirror), so a setting gets undo and history like a page.