Dashboard: rules for contributors and agents
These rules add to the root AGENTS.md; read that first.
The dashboard is the same in every profile. For Solo, it will be embedded in the einsora binary
(ADR-0012), so it must work when served from 127.0.0.1 with no separate web server.
Layout
Section titled “Layout”src/api/types.ts mirrors controlplane/internal/api/json.go; change both togethersrc/api/client.ts fetch wrapper, ApiError (with requestId), auth headers, and file downloadssrc/workloads.ts pure UI rules (which actions apply to which state); unit-testedsrc/machines.ts how machine facts are shown (last seen, memory, GPUs, system); unit-testedsrc/pages/ one component per routesrc/styles.css plain CSS with light/dark tokens; no CSS framework- The API is the only backend. Never call Kubernetes, agents, IdP admin APIs, or anything else directly.
- Server state lives in TanStack Query. Use
useStatefor form and UI state only. Poll withrefetchIntervalonly while something is mid-transition (isSettling). - Don’t duplicate business rules. The UI may hide buttons, as
availableActionsdoes, but the API decides. Show the API’s error message to users verbatim. - Show what actually runs (ADR-0009, ADR-0010): always display a workload’s region and what it runs on with
classShown, which names the requested class whenever a fallback placed it elsewhere. Show the machine it runs on, and its driver (the Machines page lists your machines and whether each is online). Never hide a fallback. The launch form picks a region and offers only classes that can run the image there (launchableClasses); a machine picker and an explicit fallback opt-in come later. - Be honest about sharing (ADR-0014): before a user launches on someone else’s machine, show that the host can see their data. Show owners exactly what they’re sharing, and when.
- Nothing hidden (rule 14, ADR-0015): show every timeline reason in full, never truncated or summarized away. Keep Download full log on the workload page, and include anything new the API exports. Show the request ID with every error (
ApiErroralready appends it). Take download file names fromfilenameFromDisposition, which strips directories. Deleted workloads stay reachable: the workload page loads withinclude_deleted, and the list has Show deleted. Show notes (fromequalsto) as notes, viadescribeChange. - Build workload URLs with
workloadPath, which encodes the ID, so an ID can never point at a different API path. - No money anywhere (rule 12): no prices, plans, upgrade prompts, or payment screens. Usage pages show hours, never currency.
- Auth: in
client.tsonly. Local accounts sign in on/loginand get an HttpOnly session cookie, which the browser sends by itself; every request carriesX-Einsora-CSRF. A 401 from/v1/auth/mesends you to sign-in, andsafeNextkeeps the return path inside the dashboard. Dev mode addsX-Dev-Tenant. Planned: OIDC with PKCE (Datacenter). Never store tokens inlocalStorage; a new API token is shown once on the tokens page and kept only in memory. - Relative URLs only: call
/v1/..., never hard-coded hosts, so the embedded build works on any address. - Dependencies: keep the list short. Ask before adding a UI kit, state library, or CSS framework.
- Strict TypeScript:
anyneeds a comment explaining why.
Commands
Section titled “Commands”npm installnpm run dev # :5173, proxies /v1 to the API on :8080 (run `make dev` first)npm run typechecknpm testnpm run build