Skip to content

Dashboard: read devices, jobs, templates and schedules through the v1 client - #97

Merged
addisdev merged 3 commits into
developfrom
feat/dash-v1-client
Oct 16, 2026
Merged

addisdev merged 3 commits into
developfrom
feat/dash-v1-client

Conversation

@addisdev

@addisdev addisdev commented Oct 16, 2026 •

Copy link
Copy Markdown
Owner

LOOP-PLAN Phase 2 item 4. Part of #83 (Findings is not the only remaining exception, so this does not close it).

Summary

The dashboard no longer makes any request the deprecation headers mark. It reads devices, jobs, templates, schedules, the alert banner and health through the generated /api/v1 client (collector/dash/src/client.ts wraps collector/src/client/), and its Device, Job, DeviceDetail, JobDetail, DeviceList, JobList and Schedule types are the generated ones. They are re-exported from api.ts, not redeclared.

  • Transport. It sends what the dashboard sent before: X-Fleet-Token from localStorage, read on every call (not Authorization: Bearer), plus X-Fleet-Source: dash. A 401 on a change shows the same "paste a token" sentence mutate.ts uses. A strict-schema refusal lists each schema_errors path. The session cookie, /api/stream, <img> artifact links and the token banner work as before.
  • Schemas (schemas.ts) now state every field the dashboard reads: Beacon (mem_method, process_alive, busy), Device, DeviceDetail (counts, jobs, benchmarks), Job, JobDetail (locks, status_report, …) and Schedule (missed, next_run_in_s, template, …). Fields the handlers always send are marked required, so check-api-v1 fails if a handler stops sending one.
  • Route table, kept small. Two reads the dashboard needed were missing from v1, and the handlers already forward to routes that compute them. listDevices now returns facets {pools, platforms, kinds}, and listJobs returns facets {status_counts, workloads, pools} and takes has_error. openapi.json and api.gen.ts are regenerated, so expect a conflict with the findings PR on those two files.
  • Bug fixed on the way. forward() dropped X-Fleet-Token. With FLEET_DASH_TOKEN set and auth off, every /api/v1 cancel and retry came back 401 whatever was sent, because they forward to the guarded /api/jobs/:id/cancel|retry. The header is now carried, and inner() maps a 401 to unauthorized and a 403 to forbidden instead of internal.
  • Guard against going back. scripts/check-dash-requests.ts is a new suite step. It reads dash/src and fails on any request that matches SUCCESSORS in deprecations.ts (now exported). I checked that it fails on injected fetch("/api/devices"), useApi(`/api/jobs/${id}`), `/api/jobs${q}` and mutate("POST", "/api/jobs").

Changes you can see

  • The composer posts to POST /api/v1/jobs, so the spec is checked strictly. An unknown top-level field, or params outside the workload's manifest, is refused with the path named. The old POST /api/jobs accepted it and the job failed later. The curl the composer shows now names /api/v1/jobs.
  • Jobs enqueued from the dashboard record source: dash (it was null).
  • Enqueueing is no longer behind FLEET_DASH_TOKEN. /api/v1/jobs does not check the guard, the same as POST /jobs, which the guard's own comment says stays open. Cancel and retry are still guarded. This is a judgement call: say if you want the composer's enqueue guarded again. That would need the guard on /api/v1/jobs, which breaks fleetr and MCP against a guarded collector, because they send a bearer token.
  • docs/api.md, docs/dashboard.md, AGENTS.md and the CHANGELOG are updated.

Still on /api/*

Findings (left untouched on purpose, waiting for findings in /api/v1): GET /api/findings, /api/findings/:id, /api/findings/precision, POST /api/findings/:id/verdict (all in pages/Findings.tsx).

Not in v1 yet:

  • Reads: /api/overview, /api/results/{bench,vision,ui,thermal,cold-start,drain,soak,recent}, /api/evals*, /api/visual/*, /api/system, /api/locks, /api/executors, /api/events*, /api/enroll, /api/peers, /api/artifacts (list) and /api/artifacts/gc-candidates, /api/devices/:id/beacons (charts), /api/jobs/:id/mirror and /mirror/status.
  • The Alerts screen itself (GET /api/alerts): it shows per-state counts, whether a webhook is set and the thresholds, and listAlerts returns none of these. The banner moved to v1.
  • Live stream: GET /api/stream.
  • Session: POST|DELETE /api/session.
  • Changes: PATCH /api/jobs/:id (priority), POST /api/jobs/preview-targets, PATCH|DELETE /api/devices/:id, POST /api/devices/:id/release-lock, POST /api/templates, DELETE /api/templates/:id, PATCH|DELETE /api/schedules/:id, POST /api/schedules/:id/run, POST /api/alerts/:id/ack|snooze, POST /api/power/:pool/:state, POST /api/system/{sweep,scheduler-tick,retention}, POST /api/visual/baselines/accept, POST /api/artifacts/:sha/pin, DELETE /api/artifacts/:sha, POST /artifacts (upload).

None of these carry a Deprecation header.

Not verified

  • Not run against the lab brain (fleet-host is still on 0.7.2). Checked only against throwaway collectors on ultra.
  • npm run dev for the dashboard was not started. The server.fs.allow entry for ../src/client is from Vite's documented behaviour, not from running it.
  • Not checked with more than 500 devices (allDevices pages through, but no test covers more than one page).

Test evidence (ultra, Node 24)

  • collector: CI=true npm test → EXIT=0. Every step passed: dashboard typecheck and build, "the dashboard calls no deprecated route" (ok — 62 requests in dash/src, none deprecated), gen:api --check, auth ALL PASS, lifecycle ALL PASS, smoke ALL PASS, api v1 contract ALL PASS. That is 1284 ok lines and 0 FAIL. New checks:
    • ok listJobs has_error=true finds the cancelled job, and only jobs with an error
    • ok listJobs facets count every status, over the whole table
    • ok listJobs facets name the workloads
    • ok listDevices facets ignore the filter
    • ok its /api/v1 reads carry X-Fleet-Token too
    • ok as alice, via dash
    • FLEET_DASH_TOKEN with auth off: ok cancel without the token is refused, as unauthorized, ok cancel with the token works, ok and so does retry. Run with the forward() fix reverted, the last two FAIL with 401 X-Fleet-Token required.
  • fleet: npm test → tests 81, pass 79, fail 0 (2 skipped), EXIT=0.
  • A one-off Playwright script (not committed) ran against the built dashboard and a throwaway collector, once with the guard off and once with FLEET_DASH_TOKEN set. Both runs were ALL PASS. It covers:
    • the shelf and table views, including the pool facet under a filter
    • device detail, the jobs filters (has_error, status) and job detail
    • cancel and retry from their buttons
    • the composer: templates, the curl, a refused bad spec, an enqueue that lands on the new job with source: dash
    • schedules, alerts, findings and system
    • No response carried a Deprecation header. The v1 requests it saw were health, alerts, devices, devices/{id}, jobs, jobs/{id}, cancel, retry, templates, POST jobs and schedules.

🤖 Generated with Claude Code

addisdev and others added 2 commits October 15, 2026 22:47
… client

The dashboard called the deprecated GET /api/devices, /api/devices/:id,
/api/jobs, /api/jobs/:id and POST /api/jobs with hand-written response
types. It now uses the generated client (dash/src/client.ts) for every read
and change /api/v1 offers, so its types are the client's and a field the
server drops fails check-api-v1 instead of blanking a column.

- /api/v1 schemas state every field the dashboard reads (Beacon, Device,
  DeviceDetail, Job, JobDetail, Schedule), required where the handler always
  sends it.
- listDevices and listJobs return the facets their filter menus are built
  from; listJobs takes has_error.
- forward() carries X-Fleet-Token, so /api/v1 cancel and retry work on a
  collector with FLEET_DASH_TOKEN set (they were always 401 there).
- scripts/check-dash-requests.ts fails the suite if dash/src calls a route
  deprecations.ts marks.

Findings and every route /api/v1 does not offer stay on /api/*.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@addisdev
addisdev marked this pull request as ready for review October 16, 2026 02:55
@addisdev

Copy link
Copy Markdown
Owner Author

Self-review: v1 client transport keeps the dashboard's header behaviour; check-dash-requests guards against deprecated routes; the forward() X-Fleet-Token fix has a regression test. Composer enqueue via /api/v1 matches the documented posture (POST /jobs was never behind FLEET_DASH_TOKEN; FLEET_AUTH=tokens is the real gate). Checks green. Merging (LOOP-PLAN Phase 2.4, part of #83).

@addisdev
addisdev merged commit c6bc379 into develop Oct 16, 2026
24 checks passed
addisdev added a commit that referenced this pull request Oct 16, 2026
… findings list facets; deprecate the older findings routes

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant