Expand description
Two members of one collective reconcile its logs over iroh (QUIC, TLS with raw ed25519 keys).
iroh authenticates the peer’s key during the handshake; a node then checks that key against its verified membership before it sends a single byte of the collective. One session is a sequence of swaps on one stream (the dialler speaks first, the listener answers after it has checked what it heard). The order is the one PLAN.md “Reconnect” fixes, trust first:
-
Hello: which collective (the listener refuses a name that is not its own), and the port the dialler listens on. The listener remembers it in peers.json at the IP QUIC saw the dialler come from, so a node bound to 0.0.0.0 is still dialled back at an address that works. -
Membership: both nodes’ whole history of signed versions; each resolves it from its own root (membership.rs: every step signed by an admin of the previous one, forks resolved the same way everywhere). The peer must still be a member under the result, and revocations are applied (quarantine) before any data moves, also when the held version did not change but the logs were never checked against it (origin::apply_if_new). -
lessons.jsonl,stats.jsonl,outcomes-team.jsonl,playbooks.jsonl,constraints.jsonl,messages.jsonl,absorbed.jsonl(lines.rsSYNCEDand the removed playbooks’ step,PLAYBOOKS; a 0.4.0 to 0.6.x peer’sborg/mesh/13or/14session stops before the absorbs, a 0.3.0 peer’sborg/mesh/12session before the messages), each in turn: range-based set reconciliation (recon.rs), cost ∝ difference: one fingerprint when nothing changed. ThenLines: what the other side lacks, each line with its signed origin (origin.rs). A line is stored only if the peer announced it, its origin verifies, the membership admits its origin key at its stamp time, and it may travel under that key (lines.rstravels: a message or seen line names its origin key as its node, and is fresh; an absorb or release has the shape borg’s write path gives it). Messages also travel only within theirwindow(borg msg.rswithin_windows: what any sender’s window, msg.rswindow_for, can have let through to one recipient, decided by no earlier line, so pruning never takes a held line out of it), on offer and on receipt, and the log stays bounded assendbounds it (msg.rsbound, bytes per member and class): a new line the bound would cut is refused, so it is not taken again. What the log takes is decided before its lock and written under it (stored,Store::store_messages), so a hook’s acknowledgement never waits on it. Aborg/mesh/13peer is offered only what 0.5.0 takes (v1_window). A pause, resume or veto from another node counts only once admitted under its node’s role (steer.rsadmit), when it is stored.The playbooks are removed, but their step is still spoken so a 0.3.0 or 0.4.0 peer keeps its order: this node offers nothing there, follows the peer’s reconciliation and stores nothing (
skip), as for a hive goal below.3b.
Goals: the removed hive’s step, still spoken so a 0.3.0 peer keeps syncing onborg/mesh/12. This node names no goal; for each valid goal id the peer names it follows the peer’s reconciliation of that goal’sLEGACY_GOAL_LOGSlogs and stores nothing (skip). What a peer gave is remembered in memory (Node::legacy), so it is not sent again every session.
One Lines frame carries at most MAX_GIVE bytes of lines; the rest go at the next session (the
sets still differ, so its reconciliation finds them again).
4. reasoning.jsonl (git-note excerpts, notes in the borg crate): not reconciled with everyone.
Each author offers its own excerpts to the k = 2 members that rendezvous hashing assigns to
each excerpt (replicas), so the reasoning survives the author being offline without every
member holding all of it.
5. Ask: the commits whose reasoning a member wants (borg notes fetch); any node that holds
an admitted excerpt for one (or a link record with it on either side) answers with it, author
or replica, so the “why” can be read back while its author is offline.
6. Live: ephemeral, never reconciled into a log: presence (live.rs: which drones run where,
latest per member and session, a day at most). A 0.3.0 peer’s hive beats and signed steering
directives in it are ignored: steering travels as messages, admitted when stored (steer.rs).
A completed session, dialled or served, is recorded on each side by when it started, per member
(mesh/synced.json, borg presence.rs synced_path), and by what its protocol says the member reads
(reads, mesh/reads.json): send refuses a text the member’s sessions could never be shown.
Addressing is by key (addr.rs): a peer is dialled at its node id, with direct addresses and relay URLs as optional hints. How the endpoint reaches it across NAT is the collective’s relay setting: n0’s relays and address lookup (default), a self-hosted relay, or direct only.
Joining (invite.rs) is a second ALPN: a node that is not a member yet presents a signed
invite to the admin that issued it, and gets the membership history back (join, admit).
Structs§
- Node
- One node of one collective: its key, its store and an iroh endpoint.
- Place
- Where a node finds its collective. Kept instead of a fixed
Routeso every session re-reads the config: setting the collective toshare: local, adding the repo tooffor breaking the file stops a runningserveat its next session, not at its next restart. - Report
- What one session moved.
- Signed
- A line and where it came from.
- Store
- One node’s store as a session reads and writes it: where it is, the lock its writes take, and the
key that stamps its lines.
Nodestamps and merges through it alone; built over a store directly, it does the same without an endpoint, for simulation and tests (tests/two_nodes_cli.rs), so they run the merge a session runs.
Constants§
- ALPN
- The session protocol. 15:
absorbed.jsonljoined the synced logs, last (lines.rsABSORBED). 14: messages v2 (borg msg.rs: texts kept whole up toWIRE_TEXT,seenlines, the acknowledgement window instead of 0.5.0’s rate). 13:messages.jsonljoined the synced logs (lines.rsSYNCED). The logs reconcile in a fixed list, so a peer on another list would reconcile another file at the same step; 11 addedconstraints.jsonland refused 10 at the handshake for that reason. - JOIN_
ALPN - Protocol: the join handshake, an invite for the membership history (
Node::join,Node::admit). - LEGACY_
ALPN - 0.3.0’s protocol, still spoken whole: the session of
V13_ALPNwithout the messages (synced). 12 already outlived the hive: its steps (Goals, the beats ofLive) are spoken empty. - REPLICAS
- Baseline: how many members hold a replica of each reasoning excerpt besides its author (two, so an excerpt outlives one member going away; design-chosen).
- V13_
ALPN - 0.4.0 and 0.5.x’s protocol, still spoken whole:
V14_ALPN’s steps, but such a peer reads only v1 message lines and keeps only what 0.5.0’s window takes (V1_RATE_MS,V1_KEEP), so it is offered only that (v1_window): no v2 text, no seen line, one tell or ask per sender and recipient per rate slot. - V14_
ALPN - 0.6.x’s protocol, still spoken whole: the session of
ALPNwithout the absorbs (synced). Such a peer reads every message line this node writes.
Functions§
- frame
- The
Linesframe a session gives of a log after its reconciliation (step 3): the lines ofset(offered) named byhashes, the ones the peer lacks (Recon::give), each with its signed origin, as many as the frame’s budget takes (within_budget). Public for simulation and tests, which hand it to the peer instead of a stream. - gate
- The gate. A collective’s data may leave this machine only if
collective::may_syncsays so. - offered
- The lines of the log at
paththis node offers atnow:travelling, and for the messages those within theirwindow; to aV13_ALPNpeer (v1) only what 0.5.0’s window takes of them (v1_window). Public for simulation and tests: it is the set a session reconciles (step 3). - replicas
- The members (other than
author) that hold a replica of excerpth: theREPLICAShighestblake3(h, key)(rendezvous hashing: every node computes the same choice, and a membership change moves only the excerpts of the members it touches).