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

68 lines
3.8 KiB
Markdown

# 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:
```text
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.