Files
clawmates/migrations/0018_fleet_nodes.sql
T
Omar SobhandClaude Opus 4.8 2bdd0a23e8
ci / gates (push) Failing after 5s
ci / rust (push) Has been skipped
ci / sandbox-k8s (push) Has been skipped
ci / frontend (push) Has been skipped
ci / e2e (push) Has been skipped
Fleet P0: node registry + daemon + health + connect-host wizard
Users can connect their own local-hardware nodes into a fleet. Each node runs a
new Rust daemon that dials home over an outbound WebSocket, reports host health,
and runs commands we send.

Backend:
- migrations/0018_fleet_nodes.sql: nodes + node_health tables + agent_containers
  (node_id, workspace_id) index. cm-domain NodeId.
- cm-db repo/nodes.rs: create/auth/list+health/get/heartbeat/set_status/delete
  (unchecked sqlx, no .sqlx regen).
- cm-api fleet.rs NodeHub: live daemon channels (node_id→sender) + the WS channel
  runner (heartbeat→DB upsert, exec request/response framing). routes/nodes.rs:
  POST /pair, GET /nodes, SSE /nodes/live, POST /{id}/exec-test, DELETE /{id},
  WS /nodes/agent (token-auth). Wired into AppState + router.

Daemon (new crate crates/bins/clawmates-node):
- sysinfo host metrics (cpu/mem/pressure/swap/disk/load/containers), outbound WSS
  dial + reconnect, heartbeat loop, exec command handling, tailscale-ip probe.
  install.sh convenience installer.

Frontend:
- Fleet sidebar item + FleetOverview + LocalHardware node-health cards (live via
  /api/nodes, 3s poll) + ConnectHostWizard (install → verify connection →
  exec-test). InfraStage dispatches fleet/local; default selection = fleet.

Deferred: P1 (BYO Tailscale + network metrics), P2 (RemoteDriver + placement so
agents actually run on connected nodes).

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-06-24 08:09:14 -07:00

37 lines
1.9 KiB
SQL

-- Fleet: user-connected local-hardware nodes. Each node runs the clawmates-node
-- daemon, which dials home over an outbound control channel to report host health
-- and accept workload-placement commands. (Phase 2 of the multi-node node-pool;
-- agent_containers.node_id references the chosen node, 'local' by default.)
CREATE TABLE nodes (
id UUID PRIMARY KEY,
workspace_id UUID NOT NULL REFERENCES workspaces (id) ON DELETE CASCADE,
name TEXT NOT NULL,
status TEXT NOT NULL DEFAULT 'pending', -- pending | online | offline | draining
token TEXT NOT NULL UNIQUE, -- shared secret the daemon presents on the channel
agent_version TEXT,
tailscale_ip TEXT,
last_seen TIMESTAMPTZ,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX nodes_workspace_idx ON nodes (workspace_id);
-- Latest host-health snapshot per node (upserted by the daemon heartbeat).
CREATE TABLE node_health (
node_id UUID PRIMARY KEY REFERENCES nodes (id) ON DELETE CASCADE,
captured_at TIMESTAMPTZ NOT NULL DEFAULT now(),
cpu_pct DOUBLE PRECISION NOT NULL DEFAULT 0, -- 0..100
mem_total BIGINT NOT NULL DEFAULT 0, -- bytes
mem_used BIGINT NOT NULL DEFAULT 0,
mem_pressure DOUBLE PRECISION NOT NULL DEFAULT 0, -- 0..1 (used/total, or PSI if available)
swap_used BIGINT NOT NULL DEFAULT 0,
disk_total BIGINT NOT NULL DEFAULT 0,
disk_free BIGINT NOT NULL DEFAULT 0,
load1 DOUBLE PRECISION NOT NULL DEFAULT 0,
load5 DOUBLE PRECISION NOT NULL DEFAULT 0,
load15 DOUBLE PRECISION NOT NULL DEFAULT 0,
container_count INTEGER NOT NULL DEFAULT 0
);
-- Placement lookups (which containers run on which node, per workspace).
CREATE INDEX agent_containers_node_idx ON agent_containers (node_id, workspace_id);