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
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.
- Invite. The authority creates a single-use invitation for an agent and requested label.
- Request. The device creates
node_idand node key, then returns a CSR containing its public key and RedFlag hardware public-key fingerprint. - Commission. The authority signs the tuple
(agent root, node_id, node public key, hardware fingerprint, label, issued-at, expiry/revocation policy). - Join. The device stores the commission, creates or receives its
nodes/<node_id>memory ref, and adds the federation endpoint. - 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
- Define versioned
NodeId, CSR, and commission structs plus round-trip/signature tests. - Add a local node-key store and
node status; do not connect it to deployment yet. - Implement invite/join and persist trusted commissions.
- Replace shared agent-key authentication for
reachwith node key plus commission verification. - Add memory remotes and
nodes/<node_id>divergence status. - 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.