This document gives an overview of the repository layout, the technologies in use, and how to run the tests.
Each top-level directory is a self-contained package with its own lock file.
| Directory | Contains |
|---|---|
orchestrator/ |
The backend service. Node.js and Fastify, written in TypeScript. Serves the REST API, the dashboard bundle and the /ws gateways, drives Docker through dockerode, stores state in SQLite (better-sqlite3). Request and response bodies are validated with zod. |
dashboard/ |
The frontend. A React 19 single-page app with React Router, zustand for state, assistant-ui (shadcn-style, sources committed under src/components/assistant-ui/) for the chat, Tailwind CSS 4 for styling, xterm.js for the terminal, shiki for syntax highlighting. |
proxy/ |
The egress proxy. Node.js and TypeScript, built on mockttp. The only route out of a box network; swaps placeholder tokens for real credentials on the wire. |
files/ |
The file service boxes-files. The orchestrator reads and writes the files of a box through it. Node.js and TypeScript, no runtime dependencies. |
shared/ |
Plain TypeScript modules imported by the orchestrator, the dashboard and the file service: the ACP vocabulary, the REST types, the terminal subprotocol, the file service protocol. There is no build step; the consumers import the sources directly. |
box-image/ |
The Dockerfile and support files of the box images, the base image and the full image. |
tests/ |
smoke-test.sh, the security smoke test (see below). |
doc/ |
Topic documentation. |
.github/workflows/ |
The CI pipeline. |
- Language: TypeScript (ESM) throughout. The images and CI use Node.js 26.
- Build: Vite. The orchestrator, the proxy and the file service are bundled in SSR mode (dependencies stay
external, the runtime images install them); the dashboard is bundled as a browser app. Every package also
type-checks with
tsc --noEmit(npm run check). - Tests: Vitest everywhere, Playwright for browser tests.
- Formatting and lint: Biome, set up once at the repository root (
biome.json). CI fails on a formatting difference or a lint error. - Containers: Docker with Compose v2 for deployment; the orchestrator talks to the daemon directly through the Docker socket.
- Protocol: The dashboard and the orchestrator speak the Agent Client Protocol (ACP) over WebSocket as JSON-RPC.
- Node.js, any recent release (the images and CI use 26).
- Docker with the daemon running. The orchestrator needs the Docker socket to create box containers, and it pulls the box image on first boot.
Install each package from its directory:
cd orchestrator && npm ci
cd proxy && npm ci
cd files && npm ci
cd dashboard && npm cishared/ has no dependencies and is imported as source.
The repository root has one more package.json, for Biome. npm ci there installs it.
Each package builds from its directory:
cd orchestrator && npm run build # bundle in dist/
cd proxy && npm run build # bundle in dist/
cd files && npm run build # bundle in dist/
cd dashboard && npm run build # static app in dist/The images build from the repository root, because the Dockerfiles copy shared/. The two box images are the targets
base and full of one Dockerfile:
docker build -f orchestrator/Dockerfile .
docker build -f proxy/Dockerfile .
docker build -f box-image/Dockerfile --target base .
docker build -f box-image/Dockerfile --target full .Run the orchestrator in its container, per the shipped compose.yaml:
docker compose up -dFor frontend work, the dashboard's dev server proxies the API, /healthz and /ws to a running orchestrator
(ORCHESTRATOR_URL, default http://localhost:3000):
cd dashboard && npm run devEach package runs its own suite. Run them from the package directories:
cd orchestrator && npm test
cd proxy && npm test
cd files && npm test
cd dashboard && npm testType checking is separate, also run from each package directory:
cd orchestrator && npm run check
cd proxy && npm run check
cd files && npm run check
cd dashboard && npm run checkFormatting and lint cover the whole repository. Run them from the root:
npm run check # reports formatting differences and lint findings, like CI
npm run format # rewrites the files in the Biome style
npm run lint # lint onlyThe orchestrator, proxy and file service suites are plain Vitest unit tests; Docker interactions are faked, no daemon is needed. The orchestrator tests run the real file service in process, on a temporary directory.
The dashboard suite has two projects, both run by npm test:
- unit — tests under
src/, run in Node. - e2e — tests under
e2e/, which build the production bundle, start a real orchestrator in-process against a fake Docker daemon and a stub ACP gateway, and drive it in Chromium through Playwright. To run only one project:npx vitest run --project unitor--project e2e. The e2e project needs a Chromium;npx playwright install --with-deps chromiuminstalls it, or setCHROMIUM_PATHto an existing binary.
Each box image has its own checks: the Dockerfile has a test stage between the build and the shipped stage of each target, so a failed property fails the image build.
tests/smoke-test.sh is not a unit test. It runs against a live deployment on a Docker host (docker compose up -d),
creates throwaway boxes through the API and asserts the isolation properties from inside their containers:
API_BASE=http://localhost:3000 ./tests/smoke-test.shThe header of the script documents the environment variables for credential translation checks and optional probes.
orchestrator/src/runtime/conformance.ts checks the rules of the runtime against a real runtime and the
real box image. docker.conformance.ts runs it against the Docker daemon of the host. It needs the box image that
BOX_IMAGE names and a running egress proxy, and no orchestrator on the same daemon, because the orphan sweep removes
the boxes of the suite. It must run as root:
docker compose up -d egress-proxy
cd orchestrator && sudo env "PATH=$PATH" BOX_IMAGE=... npm run test:conformance -- dockerkubernetes.conformance.ts runs it against the cluster and the namespace of the kubeconfig context that
BOXES_CONFORMANCE_CONTEXT names. Without this variable, the suite is skipped. The namespace needs the egress proxy from
k8s/egress-proxy.yaml, a box image that the nodes can get, and no orchestrator:
kubectl --context kind-boxes apply -f k8s/namespace.yaml
kubectl config set-context kind-boxes --namespace=boxes
kubectl --context kind-boxes apply -f k8s/egress-proxy.yaml
cd orchestrator && BOXES_CONFORMANCE_CONTEXT=kind-boxes BOX_IMAGE=... BOX_IMAGE_PULL_POLICY=IfNotPresent \
npm run test:conformance -- kubernetes.github/workflows/publish.yml runs the four test suites in parallel and builds the four images. It runs the runtime
conformance suite against Docker and against a kind cluster, and the smoke test against the built images. It publishes
to GHCR on pushes to main.