# 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.
