Skip to content

Latest commit

 

History

History
173 lines (128 loc) · 7.36 KB

development.md

File metadata and controls

173 lines (128 loc) · 7.36 KB

Development

This document gives an overview of the repository layout, the technologies in use, and how to run the tests.

Layout

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.

Technologies

  • 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.

Prerequisites

  • 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.

Installing dependencies

Install each package from its directory:

cd orchestrator && npm ci
cd proxy && npm ci
cd files && npm ci
cd dashboard && npm ci

shared/ has no dependencies and is imported as source.

The repository root has one more package.json, for Biome. npm ci there installs it.

Building

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 .

Running locally

Run the orchestrator in its container, per the shipped compose.yaml:

docker compose up -d

For 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 dev

Running the tests

Each 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 test

Type 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 check

Formatting 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 only

The 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 unit or --project e2e. The e2e project needs a Chromium; npx playwright install --with-deps chromium installs it, or set CHROMIUM_PATH to 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.

Smoke test

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.sh

The header of the script documents the environment variables for credential translation checks and optional probes.

Runtime conformance suite

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 -- docker

kubernetes.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

CI

.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.