A small REST service, written in Xi, that compiles submitted Xi source to
WebAssembly using the xc command-line compiler and returns the .wasm
module. It's built to sit behind a public playground website where developers try
the language in the browser.
The service is designed to be safe to expose publicly:
- It never executes user code. It only compiles it. The returned
.wasmruns later in the visitor's browser WASM sandbox — never on the server. - Admission policy (
SourcePolicy) rejects source that could influence the build itself:extern/include/link/unsafe, non-stdimports, externaldependencies, and oversized input — before the compiler runs. - Per-request isolation. Each compile runs in a throwaway working directory
(
Sandboxport) under CPU/fileulimits, and the dir is destroyed afterward. - The
SandboxandWasmCompilerports are seams: the temp-dir sandbox can be swapped for a container/nsjail adapter with no change to the service.
The strong OS-level isolation and rate limiting live in deploy/:
non-root pods, read-only rootfs, DNS-only egress (NetworkPolicy), and Traefik
rateLimit + inFlightReq middleware.
// 200 — compilation ran (check `status`)
{
"status": "success", // or "error" if the user's code didn't build
"exitCode": 0,
"diagnostics": "…compiler stdout/stderr…",
"wasmBase64": "AGFzbQ…", // base64 .wasm ("" on failure)
"js": "…emscripten loader…"
}- 400 — the request was rejected (empty source, unsafe name, or blocked by the source policy); the body is the reason.
{ "status": "ok", "service": "xi-playground-compile-server", "version": "0.1.0" }Layered, dependency-inverted — the service depends only on interfaces:
compile/
compile_request.xi domain: CompileRequest/CompileJob + validation (toJob)
compile_outcome.xi domain: CompileOutcome + CompileResponse mapping
source_policy.xi port: admission policy → default_source_policy.xi
sandbox.xi port: isolation → temp_dir_sandbox.xi
wasm_compiler.xi port: compilation → xc_wasm_compiler.xi
server_config.xi port: typed config
compile_use_case.xi port: the use-case → compile_service.xi
compile_service.xi application: orchestration only
web/
compile_controller.xi POST /api/v1/compile
health_controller.xi GET /health
app.xi composition root (module App + entry)
Requires the Xi toolchain (xc/xi) and Emscripten (emcc) on PATH.
xc app.xi # -> build/xi-playground
./build/xi-playground # serves on :8080 (see application.yaml)make build # xc app.xi
make run # build + run
make test # xi test test_app.xixi test test_app.xitest_app.xi is a test-only harness module that gathers the app code plus the
tests/ fakes; app.xi excludes both from the production binary.
Toolchain note (xc 0.0.85). Two
xclimitations shaped the layout, and are worked around here:
excludesonly honors"<dir>/**"or exact paths —**/*_test.xiglobs are silently ignored. Tests therefore live undertests/(excluded viatests/**) so their fakes/binds never leak into the production binary.- Running
xi testrooted at a*_test.xifile hits a codegen ordering bug, andxi test app.xiwould skip the (excluded) tests. Hence the dedicatedtest_app.xiharness root.The code itself avoids two more codegen pitfalls:
+-concatenating a struct-field/?-unwrapped string (use$"…"interpolation or extract with.value), and namespaced interfaces referencing compound types across files.
See deploy/README.md for the container image and Kubernetes
manifests (Traefik ingress, rate limiting, network isolation).