Update this file as tasks complete. Check off each item the moment it is done, not in batches.
-
internal/config/config.go— Config struct, Load(), env overrides (GOBOXD_*) -
configs/config.yaml— default server config (port, jail dir, max_concurrent, limits)
-
internal/registry/language.go— Language struct (id, name, build, run, smoke_cmd, limits) -
internal/registry/template.go— Expand(args, vars): {{source}}, {{artifact}}, {{flags}} -
internal/registry/registry.go— Load YAML, startup validation, Lookup(id), All() -
configs/languages.yaml— py3 and cpp entries
-
internal/validation/filename.go— reject path separators,.., leading dot, length cap -
internal/validation/flags.go— per-language allowlist, glob match for-std=*, return 400 on reject -
internal/validation/limits.go— MergeLimits(request overrides, language defaults)
-
internal/sandbox/sandbox.go— Sandbox interface, BuildJob, RunJob, BuildResult, TestResult types -
internal/sandbox/nsjail/nsjail.go— NsjailSandbox: argv builder, os/exec run, output cap with [truncated] marker -
internal/sandbox/mock/mock.go— MockSandbox: host exec, no isolation (tests only)
-
internal/runner/concurrency.go— Semaphore via chan struct{}, Acquire blocks (never rejects) -
internal/runner/workdir.go— SafeWorkDir (os.MkdirTemp + defer Remove), StartupSweep (orphans > 10 min) -
internal/runner/pipeline.go— build step → per-test run steps → status roll-up logic -
internal/runner/runner.go— Runner struct, Run(ctx, RunRequest) RunResponse, wires all above
-
internal/stats/stats.go— atomic counters: InFlight, JobsTotal, JobsFailedInternal, LastErrorAt, DiskFree
-
internal/handlers/health.go— GET /healthz → 200 {"status":"ok"} -
internal/handlers/readyz.go— GET /readyz → nsjail binary check + per-lang smoke probe, 30s cache -
internal/handlers/info.go— GET /info → build_info, nsjail version, languages, limits, stats -
internal/handlers/run.go— POST /run: MaxBytesReader, decode, validate, runner.Run, encode
-
cmd/goboxd/main.go— wire config → registry → sandbox → runner → handlers → server
- Fix
cmd/goboxdbuild path (currently broken per CLAUDE.md) - Install py3 and g++ in runtime stage
- Verify
make buildandmake runsucceed
-
tests/unit/filename_test.go— table-driven: traversal, dot-prefix, absolute paths -
tests/unit/flags_test.go— allowlist pass/reject, glob-std=* -
tests/unit/limits_test.go— merge override logic, zero-value fallback -
tests/unit/status_test.go— roll-up: build_failed, first non-accepted, all accepted -
tests/unit/truncation_test.go— output cap, [truncated] marker present -
tests/integration/run_test.go— end-to-end: py3 hello world, cpp hello world (build tag: integration)
-
README.md— what it is, how to run, where the docs are; no filler -
docs/api.md— full API contract with request/response examples -
docs/security.md— 7 holes listed, each with file:line where it is closed
No Go code changes. YAML edits and Dockerfile installs only.
-
configs/languages.yaml— add: c, java, bash, node, verilog -
Dockerfile— install: gcc, openjdk, bash (already present), nodejs, iverilog (bash + iverilog viascripts/lang_install/) - Smoke-test each new language via
/readyz -
tests/integration/run_test.go— add one test per new language (bash, verilog done; c/java/node pending) -
docs/languages.md— per-language notes: filename rules, flag allowlist, limits
No Go code changes. Verification and documentation only.
- Verify all 7 security holes are closed (they should be from Stage 1)
- Run load tests at 1, 10, 50, 100 concurrent clients (hey or vegeta)
-
docs/benchmarks.md— p50, p95, p99 results from a clean Docker run -
Makefile— wiremake loadtarget -
docs/architecture.md— enough detail that a new engineer can orient on day one
5xxis only for server failures. User-code crashes, timeouts, OOMs all return200with structured status.- Adding a language in Stage 2 must take under 30 minutes: one YAML block + one Dockerfile install line.
- Security holes are implemented in Stage 1 so Stage 3 is verification, not new work.
- Concurrency pool is always on;
max_concurrentdefaults toruntime.NumCPU().