NodeActions (reboot, update, service restarts) become signed node.root
commands for nodes with the COMMANDS flow whose root-owned pin of the
panel key is in place; others keep the signals table.
- RootPin: /etc/xc_vm/cluster/{main_sign.pub,node,root.seq}, trusted only
when root-owned and not group/other writable
- cluster:exec hands node.root to root through config/cluster/root-inbox
- cluster:root (root cron, 58 s watch) verifies against the pin, raises
its own high-water before running, runs RootSignalsCronJob actions and
writes results without following planted links
- cluster:pin-root <panel_fp> pins a code-enrolled node; LbInstallFlow
pins over SSH at install
- cluster_nodes.root_ready (migration 039) from the agent's heartbeat;
the Cluster Nodes page shows it and the panel fingerprint
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DcNdxHieZuUh3EnMUKt7zf
An active node with intact keys gets a new epoch without SSH:
GET challenge?cn=, then a node-signed POST token_rekey (epoch 0, no MAC)
whose body is SEALed to the panel box key under the request context.
MAIN checks the signature before recording anything, then the nonce,
state=active, a once-a-minute limit, the single-use challenge
(NonceStore::consume, atomic via a used marker), the instance_id
attestation (a mismatch quarantines) and the licence (the mint). The
reply is panel-signed `pre` and bound to the node and request nonce.
TokenService::rekey drops every other epoch row.
The install data now carries panel_box_pub; the test fake gets a real
box key, openSealed and licence-gated granting signatures.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DcNdxHieZuUh3EnMUKt7zf
Reruns provisionCluster on a serving node. Trust on first use is refused:
the host key must match --expect-hostkey or the one stored at install. The
node must already run this release. A failure never marks the live node
failed, and a re-enrolment stops its running agent before replacing the
node's identity (generation + 1).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Es8UhyPS8MNjuYTagqfGWi
provisionCluster pushes the agent, runs keygen on the node (keys never
leave it), has the node verify MAIN's signed health before any token is
minted, then installs epoch 1 and starts the agent, which completes with
enrol_complete. The API off, no extension or no agent binary leaves the
node legacy; only an extension refusal or an unreachable API fails the
install. A keepalive in service boot and the root cron runs the agent on
enrolled nodes, and stops for good when MAIN has stopped the node.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Es8UhyPS8MNjuYTagqfGWi