Skip to content

Architecture and contracts

Three components and one contract repository.

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.

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.

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/ 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 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: admin in the schema. The authoritative list is protocol/control-api.md under Authorization.
  • That agent and dev tags aren’t operator API. agent is the node agent’s channel; dev exists only with QUASAR_DEV_AGENT_AUTH=1, which production refuses.
  • That a 403 doesn’t mean admin-only. Some endpoints return 403 for ownership.
  • 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.