Watch
1
0
Fork
You've already forked SouveraineOS
0
SouveraineOS/saf/federation/01-node-enrollment.md
Fimeg bde961c6f2 saf: one spine — device, state, and work under the index
PAF becomes saf/device (history kept), STATE.md dissolves
into saf/state.md with the dated era archived, the substrate
SAF moves up from souveraine, and every agreement points at
saf/INDEX.md and nowhere else. one map, nothing to remember
2026-08-18 09:47:30 -04:00

3.8 KiB

Node enrolment

Souveraine can already exchange signed events between configured peers. It cannot yet add a device as a node of an existing agent. Copying ~/.souveraine/agents/<id>/ to a phone is therefore a migration shortcut, not federation: it copies the agent's private key and gives two machines indistinguishable authority.

This document is the design-of-record for closing that gap. It narrows the existing federation sketch into a first implementation boundary; it does not claim that the ceremony exists today.

What a node is

One agent can have several independently operating nodes. They share an agent root identity but not a private signing key.

Concern Stored value Why
Being agent root public key Stable personhood across every fork
Node immutable random node_id Stable Git and protocol identity; never derived from a hostname
Hardware RedFlag device public-key fingerprint Attestation and revocation target
Display label such as phone or hearth Human-readable and renameable
Memory nodes/<node_id> Git ref Independent history for later reconciliation

machine-id and hostname are useful evidence and display hints, but neither is suitable as the durable node identifier. A reinstall can change the first; a rename can change the second.

The ceremony

The device being added creates its node key locally. Its private key never leaves it.

  1. Invite. The authority creates a single-use invitation for an agent and requested label.
  2. Request. The device creates node_id and node key, then returns a CSR containing its public key and RedFlag hardware public-key fingerprint.
  3. Commission. The authority signs the tuple (agent root, node_id, node public key, hardware fingerprint, label, issued-at, expiry/revocation policy).
  4. Join. The device stores the commission, creates or receives its nodes/<node_id> memory ref, and adds the federation endpoint.
  5. Operate. Each event carries the node signature and commission. Git moves memory; the event stream only says that a branch changed.

The future command shape is intentionally small:

souveraine node invite --agent Annie --label phone
souveraine node join --invite <one-time-token>
souveraine node status
souveraine node revoke <node-id>

The command names are provisional. The resulting wire formats and storage paths must be versioned before a phone depends on them.

What exists today

  • Machine and agent Ed25519 keys exist.
  • Federation transport signs each event with the machine key.
  • Peer keys are explicit configuration and inbound events now require one of those configured keys.
  • Git-backed memory commits locally, but has no push, fetch, branch status, or reconciliation commands.

What is deliberately not done

  • No root-key commissioning authority or CSR format.
  • No RedFlag hardware-key integration or revocation list.
  • No per-node key storage, commission validation, or event-chain validation.
  • No memory remote or branch-per-node sync.
  • No archivist reconciliation policy.

Until these land, packaging/deploy-phone.sh defaults to copying no private key. --clone-identity remains only for an explicit same-instance migration; it is not an enrolment workflow.

Implementation order

  1. Define versioned NodeId, CSR, and commission structs plus round-trip/signature tests.
  2. Add a local node-key store and node status; do not connect it to deployment yet.
  3. Implement invite/join and persist trusted commissions.
  4. Replace shared agent-key authentication for reach with node key plus commission verification.
  5. Add memory remotes and nodes/<node_id> divergence status.
  6. Let the archivist reconcile branch state according to Cloister policy.

This preserves the intended architecture: the phone is a real branch of the agent, not a thin client and not a copied private key.