Commit Graph
3 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 775362f99e feat(cluster): Cluster Nodes admin page and cron:cluster housekeeping
Servers → Cluster Nodes (permission `servers`) is the admin side of the
cluster CLI, via Domain\Cluster\ClusterAdmin: enrolled nodes with state,
liveness, epoch, token expiry, last heartbeat and agent version; code
enrolments waiting for a SAS (approve / reject); issuing an enrolment
code (shown once); and revocation. The deciding admin is recorded.
Strings in all eight languages.

cron:cluster (enabled by migration 037) deletes expired epochs (erasing
their z), the replay cache and used challenges, unused expired codes and
decided requests after a day. It returns at once on load balancers and
while the API is disabled.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DcNdxHieZuUh3EnMUKt7zf
2026-09-25 12:39:13 +00:00
Claude 2cdadf017a feat(cluster): break-glass enrolment by code
For an LB MAIN cannot reach over SSH. `cluster:enrol-code <id>` issues a
one-time code (MAIN URL, panel key hash, 128-bit secret; 30 min). MAIN
keeps only K_req/K_res, sealed to the machine under a row MAC.

The node sends enrol_code (sid:<n>, epoch 0; MAC'd with K_req, signed by
the key it asks to enrol, body SEALed to the panel box key). Nothing is
minted until `cluster:enrol-approve <id> <SAS>`: the admin types the SAS
the node shows, which binds its keys; five wrong entries reject. The
node then collects epoch 1 via enrol_code_status (MAC'd with K_res,
panel-signed `pre`) and finishes with enrol_complete.

A wrong code MAC records nothing; other keys under a used code are a
signed ENROL_CONFLICT, the same keys again are accepted. Migration 036
keeps the request's per-epoch key until approval. Both commands are
MAIN-only (stripped from the LB archive).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DcNdxHieZuUh3EnMUKt7zf
2026-09-25 12:32:59 +00:00