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
68 lines
3.8 KiB
Markdown
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.
|