Component · control-plane/
control-plane
The hub. A Fastify REST API in front of an orchestrator that drives the session lifecycle through a pluggable backend, mints and withholds credentials, records an immutable audit log, streams over SSE, and enforces org-scoped tenancy. Everything else is a client of this.
Role in the architecture#
The control plane is the single place session logic lives. It boots in src/index.ts, registers routes in server.ts, and delegates the lifecycle to orchestrator.ts; backends live under src/backends/.
control-plane/src/index.ts (boot) → server.ts (routes) → orchestrator.ts; backends in src/backends/ (backend.ts, simulator.ts, docker.ts, kubernetes.ts, factory.ts).REST routes#
Every session route is authenticated and scope-guarded. Full detail on the REST API page.
| Method | Path | Scope | Purpose |
|---|---|---|---|
| GET | /healthz | none | Liveness + backend reachability. |
| POST | /sessions | sessions:write | Create + run a session. |
| GET | /sessions | sessions:read | List sessions in the caller's org. |
| GET | /sessions/:id | sessions:read | Status — no token field, no brokered secret. |
| GET | /sessions/:id/result | sessions:read | Structured result. |
| GET | /sessions/:id/audit | sessions:read | Immutable audit trail. |
| GET | /sessions/:id/stream | sessions:read | Live SSE stream. |
| DELETE | /sessions/:id | sessions:write | Teardown. |
Orchestrator#
The Orchestrator owns the session lifecycle end to end. Every accessor funnels through an org-scoped ownedBy() check — the single tenancy enforcement point.
createSession(req: CreateSessionRequest, principal: Principal): Promise<Session>orgId/createdBy, and hands the LaunchSpec to the backend. No token is placed on the public Session.getSession(id, principal) · listSessions(principal)getAudit(id, principal) · teardown(id, principal)publicView(session): Session // = structuredClone(session)Session type, the clone structurally cannot leak it.SessionBackend#
The whole control plane is backend-pluggable behind one interface. The orchestrator builds a LaunchSpec and reports progress via a BackendEventSink — both backend-agnostic.
interface SessionBackend {
readonly name: string;
// Launch the session; returns once accepted, runs async via the sink.
launch(spec: LaunchSpec, sink: BackendEventSink): Promise<void>;
// Reclaim all resources for a session. Idempotent.
teardown(sessionId: string): Promise<void>;
// Optional readiness probe.
health?(): Promise<{ ok: boolean; detail?: string }>;
}
interface LaunchSpec {
sessionId: string;
code: string;
language: "python" | "node";
limits: Required<SessionLimits>;
egress: EgressPolicy;
serviceBindings?: string[]; // injected at the egress proxy, not in the sandbox
}
interface BackendEventSink {
phase(phase: SessionPhase, info?: { killReason?: KillReason }): void;
stdout(chunk: string): void;
stderr(chunk: string): void;
egress(host: string, allowed: boolean): void;
result(result: SessionResult): void;
}Credential broker#
For a private-repo git source, the broker mints a short-lived scoped git token. It is mounted only on the clone init-container, withheld from the workload pod, and never returned to the caller. No token is ever placed on the public Session.
issueGitToken(sessionId, scopes, ttlSeconds?): { token: string }Auth & tenancy#
An auth hook verifies a user JWT (HS256, ENCLAVE_JWT_SECRET) or an API key (introspected against console-api) and sets req.principal. Each route then calls authorize(req, reply, scope) before doing work, and the orchestrator filters by orgId. A dev bypass exists only when ENCLAVE_AUTH_DISABLED=1, is never the default, and is loudly logged.