Architecture and contracts
Three components and one contract repository.
Control plane
Section titled “Control plane”Go. Accounts, authentication, the HTTP API, WebRTC signaling, scheduling, admin state and Postgres migrations. It decides where a session runs, and holds no GPU, media pipeline or per-host GPU state. Don’t merge it with the node agent.
Node agent
Section titled “Node agent”Rust. Owns one GPU host: reports capacity, launches app containers, creates virtual input devices, runs the compositor and encoder, and holds the WebRTC connection to the client. Media never goes through the control plane.
Web client
Section titled “Web client”One React app serving /app and /admin. It talks only to the control plane;
media goes directly to the node agent.
Admin access is enforced by middleware on each admin endpoint. The /admin route
gating in the client is presentation only; never make a client-side role flag the
enforcement.
Protocol submodule
Section titled “Protocol submodule”protocol/ is a frozen interface, shared with photon, the native client. Changes
need explicit human sign-off and the review level in CLAUDE.md. Generated client
schemas must match protocol/openapi.yaml.
Push a protocol/ commit to its own remote before pushing anything that pins it.
Control-plane API reference
Section titled “Control-plane API reference”Control-plane API is generated from
protocol/openapi.yaml at build time. A control-plane test fails if registered
routes and the schema disagree.
The reference doesn’t show:
- Which endpoints are admin-only. Most are, marked
x-required-role: adminin the schema. The authoritative list isprotocol/control-api.mdunder Authorization. - That
agentanddevtags aren’t operator API.agentis the node agent’s channel;devexists only withQUASAR_DEV_AGENT_AUTH=1, which production refuses. - That a 403 doesn’t mean admin-only. Some endpoints return 403 for ownership.
Load-bearing boundaries
Section titled “Load-bearing boundaries”- The control plane schedules; the node agent runs sessions.
- App containers are untrusted workloads, never part of the control plane.
- WebRTC is one transport behind the media-pipeline seam; keep it out of the compositor and encoder.
- Host identities, ports, container names and storage roots are configuration, not constants.
- Migrations only move forward. A binary older than the applied migration crash-loops at boot; recover by redeploying the commit that has the migration.
Rationale: CLAUDE.md and docs/architecture-and-plan.md in the checkout.