Commit Graph
4 Commits
Author SHA1 Message Date
Claude 0960418307 feat(cluster): root commands over the command channel
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
2026-09-25 14:40:01 +00:00
Claude 0ed24fc108 feat(cluster): token_rekey — recover a node whose tokens have all expired
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
2026-09-25 12:18:06 +00:00
Claude b1dc98d7c0 feat(cluster): server:enrol — enrol an existing LB in the cluster API over SSH
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
2026-09-25 11:40:10 +00:00
Claude d110d6fb47 feat(cluster): enrol new LBs in the cluster API at install, over SSH
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
2026-09-25 11:35:34 +00:00