Skip to content

Overview

The arena service is the execution host for the adversarial test type’s head-to-head play: matches, which pit two controllers and return a replay immediately, and tournaments, which run every pair in a field while streaming live per-match progress. Running those matches is CPU-bound, in-process wasm, so it lives in its own service rather than in the single-replica control-plane backend.

The arena is a data-plane peer of the backend. The backend owns the data (controller inputs, published tournaments, stored replays) and the arena owns the execution. A console posts a match or tournament to the arena and streams a tournament’s live progress from it, while arena reads stay on the backend. The backend reports the arena’s public base URL (TCAB_ARENA_PUBLIC_URL) via GET /config, and the console fetches it for those actions.

RoutePurpose
GET /matches/controllersList the controllers a case can pit
POST /matchesRun one head-to-head match and return its replay
POST /tournamentsSubmit a tournament, driven in the background
GET /tournaments/{id}Read a tournament job’s status
GET /tournaments/{id}/eventsStream a tournament’s live progress

These endpoints are unauthenticated behind the private-network boundary, and their CPU-bound execution is bounded by the capacity guard. The arena has no Kubernetes API access and only talks HTTP to the backend.

The arena holds no database and no disk. It fetches every controller input from the backend over HTTP: a resolved test-case version, a baseline’s references/<id>.wasm, a pushed run’s controller.wasm, and the pushed-controller listing. It persists a finished tournament and its per-match replays back to the backend.

Two controller kinds are resolvable in this topology: committed baselines, checked against the arena’s opponent allowlist, and pushed-run controllers. A run-local controller is resolved from a host’s own run output directory, which a stateless service does not have, so the arena rejects one with a 400.

The in-flight tournament registry and its live progress channel are in-memory and per-pod, so the arena runs as a single replica. Scale its throughput with its CPU and the concurrency cap.

A semaphore (TCAB_ARENA_MAX_CONCURRENT, default 2) caps how many matches and tournaments run their wasm at once. At capacity the arena rejects with 503 and a warn log rather than queueing, so a flood of submissions cannot pile up unbounded blocking tasks and stall the pod. A match holds one permit for its single blocking execution, and a tournament holds one across its whole background drive, including publishing. Its Kubernetes Deployment carries CPU requests and limits to match.

The arena service is the test-cabinet-arena crate (crates/arena), an Axum server reusing the shared match-play engine from the core and the foray-host wasm sandbox it runs matches in. Its configuration is entirely environment variables, documented in crates/arena/src/config.rs.

It binds all interfaces by default (0.0.0.0:8791), because the console reaches it over the cluster network, and the deployment fronts it with the same private-network boundary as the other services. It is deployed as a single-replica Deployment with a Service and its own ServiceAccount, with no API access and no volume. See Kubernetes: staging & prod.