You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 5c3f3e0
Browse filesBrowse the repository at this point in the historyBrowse files
Copy file name to clipboardExpand all lines: docs/COMPUTE_CONTROL_PLANE.md
+8-1Lines changed: 8 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -55,7 +55,8 @@ Activation retires the previous policy for the same pool. Activating an earlier
55
55
`app/src/compute-agent.ts` is the previously-missing desktop half of this protocol — every piece above existed and was fully tested before it, but nothing in `app/` ever called any of it. It is opt-in (`AppSettings.computeAgentEnabled` + `computeNodeId`, both off by default) on top of an already-connected shared backend (`shared-backend-config-store.ts`/`shared-backend-auth.ts` supply the organization id and bearer token; `compute-agent-client.ts` reuses both rather than a separate login).
56
56
57
57
-**Identity**: `compute-node-identity.ts` generates one long-lived, self-signed EC (P-256) client certificate per install on first use, storing the private key via `secrets-store.ts` (OS-encrypted where available). The server only ever compares a certificate's SHA-256 fingerprint (`X509Certificate.fingerprint256` — the same value Node's own TLS layer reports for the live peer) against what an admin registered; it never validates a certificate chain, so a self-signed identity authenticates exactly as well as a CA-issued one would.
58
-
-**Enrollment stays an admin action**: registering a node (`POST /compute/nodes`) requires the admin-level `compute:manageNodes` permission, which this app never calls on its own behalf. The user copies this device's fingerprint (surfaced via the `computeAgent:getIdentity` IPC channel) to their organization's compute admin, who registers it and hands back a node id; the user enters that id into Settings as `computeNodeId`. From then on the agent only ever calls the agent-scoped endpoints (heartbeat, assignments, lease acknowledge/renew/release).
58
+
-**Enrollment stays an admin action**: registering a node (`POST /compute/nodes`) requires the admin-level `compute:manageNodes` permission, which this app never calls on its own behalf. The user copies this device's fingerprint to their organization's compute admin, who registers it and hands back a node id; the user enters that id in the desktop app. From then on the agent only ever calls the agent-scoped endpoints (heartbeat, assignments, lease acknowledge/renew/release).
59
+
-**UI: Runtime Manager's "Fleet" tab.** This did not exist for most of this feature's life — `computeAgent:getIdentity`/`getStatus` were built and tested at the IPC-handler level, but never bridged through `preload.ts`, and no frontend page ever called them, so the entire feature was inaccessible to an actual user regardless of how correct the backend was. Found by testing the app as a user would, not by any test (nothing tests preload bridging or checks that a built feature has a reachable UI entry point). Fixed: the fingerprint, a node-id field, and an enable toggle live in Runtime Manager (the compute-control-plane's own designated local node view) and save through the existing generic `settings:save` channel.
59
60
-**Loop**: a ~15s-jittered heartbeat reports live free CPU/RAM and per-device health/VRAM (from `resource-orchestrator.ts`'s own hardware snapshot), then polls assignments. An offered lease is only acknowledged after this node's own `ResourceOrchestrator.acquire()` actually grants the matching local capacity (at a new `"fleet-assigned"` workload kind, always `"background-compute"` priority regardless of the server-side request's own priority — fleet-delegated work must never contend with the person sitting at this workstation); a lease that can't be reserved locally is silently left un-acknowledged and reclaimed server-side rather than ever double-booking capacity. Acknowledged/running leases are renewed every cycle; a lease no longer present in the server's assignment list has its local reservation released.
60
61
-**Scope boundary, disclosed rather than assumed away**: this closes the admission/capacity-protection half of the protocol, not job dispatch — what actually *runs* inside a granted lease (e.g. serving a managed AI-gateway deployment to other users) is a separate, larger, unbuilt integration. The agent is also polling-based, not push-based: an offer landing just after a heartbeat cycle's poll could occasionally miss its 15s acknowledgment deadline and simply get reassigned. Certificate rotation is not implemented — there is no server-side "trust this new fingerprint for the same node" flow yet either.
61
62
@@ -80,6 +81,12 @@ Verified directly against the desktop code rather than assumed, since "enforceme
80
81
81
82
`admin-console/src/pages/ComputePolicies.tsx` covers signed resource policies: a full policy-version history per pool (every version, its status, and one-click activate — including activating a retired version, which is the rollback path), a draft composer for `hardLimits`/per-priority `workloadClassLimits` with a live effective-policy preview (the exact `{...hardLimits, ...workloadClassLimits[priority]}` merge the scheduler itself applies), a "download draft JSON" step, and a "paste the signed result and submit" step. Signing itself stays strictly offline — the Ed25519 private key never touches the browser or the server. `server/scripts/sign-compute-policy.js` is the tool that actually signs a draft (mirrors `app/scripts/sign-policy.js`'s pattern for the *different*, desktop-only central-policy system — verified end-to-end against the server's real `createComputePolicySignatureVerifier()` before shipping, including that a wrong organization id or a tampered payload both correctly fail verification).
82
83
84
+
**Two real, previously-undetected bugs were found and fixed by actually running the server + admin console through a real browser session** (a throwaway fake OIDC provider, in-memory stores — see the roadmap memory for the harness) rather than only `app.inject()`-based integration tests, which bypass CORS entirely, and admin-console unit tests, which mock `fetch` directly:
85
+
1.`@fastify/cors`'s own default `methods` is `GET,HEAD,POST` only — every `PUT`/`PATCH`/`DELETE` call the admin console makes (quota saves, this page's policy activation, break-glass policy, user/group/policy/service-principal updates, deletes) was silently failing CORS preflight. Fixed in `app.ts` by passing an explicit methods list.
86
+
2.`authorizedRequest()` unconditionally set `Content-Type: application/json` even on bodyless POST calls (policy activation, `createAccessReviewCampaign`, `verifyAiInferenceDeployment`) — Fastify correctly 400s an empty body declared as JSON. Fixed to only set the header when a body is actually present.
87
+
88
+
Both verified against the real fix, not just re-running the existing suite: a real quota save, and a real signed policy created via `server/scripts/sign-compute-policy.js` and activated, all through an actual browser session end to end.
89
+
83
90
**Compute audit events use the general Audit page, not a dedicated view** — deliberately not duplicated, since `listAudit`'s `action`/`targetType` filters are exact-match only (no prefix/wildcard support server-side), so a single "show all compute events" link isn't possible without a broader audit-search change this pass didn't make. Filter by `targetType` to browse a specific compute entity's history: `computeNode`, `computePool`, `computeRequest`, `computeLease`, `computePolicy`. Real action strings recorded: `computeNode.stateChanged`, `computePool.created`, `computeQuota.updated`, `computePolicy.created`, `computePolicy.activated`, `computeRequest.submitted`, `computeRequest.cancelled`, `computeLease.allocated`, `computeLease.acknowledged`, `computeLease.released`.
0 commit comments