Skip to content

Commit 5c3f3e0

Browse files
committed
docs: record the live-testing pass and the two real bugs it found
1 parent ee736a3 commit 5c3f3e0

1 file changed

Lines changed: 8 additions & 1 deletion

File tree

‎docs/COMPUTE_CONTROL_PLANE.md‎

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,8 @@ Activation retires the previous policy for the same pool. Activating an earlier
5555
`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).
5656

5757
- **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.
5960
- **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.
6061
- **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.
6162

@@ -80,6 +81,12 @@ Verified directly against the desktop code rather than assumed, since "enforceme
8081

8182
`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).
8283

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+
8390
**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`.
8491

8592
## Local performance profiles

0 commit comments

Comments
 (0)