Deployment Prep and Backend Growth
Kubernetes/K3s deployment configs, CI/CD scaffolding, schema-driven API generation, and the Star Trek BASIC backend.
On this page
The emulator hit the size where “it runs for me” stopped being evidence. If I needed to ship tomorrow, I wanted to already know the shape would hold. So today was scaffolding: K3s configs, pipeline wiring, schema-driven codegen, and a WASM race that made Star Trek BASIC fail without saying anything.
Multi-Environment Boundaries
The deployment plan splits dev and prod into separate namespaces with their own ingress, secrets, and quotas. Path-based routing keeps the services distinct:
/api/*goes to the API service (priority 1)./wsgoes to the WebSocket service (priority 2)./is the frontend catch‑all (priority 3).
That priority ordering is not optional. If the frontend catches /api/health first you get HTML where the caller expects JSON, and the failure reads like a logic bug rather than a routing bug. Cilium’s ingress rules make the ordering explicit.
CI/CD That Protects Time
Three workflows, each with its own time budget:
ci-cd.ymlfor the full mainline pipeline, lint through deploy.pr-validation.ymlfor fast feedback on pull requests, targeting under 15 minutes.nightly.ymlfor the long exhaustive run that catches slow regressions.
The sequence within each is deliberate: lint before tests, unit before E2E, security audits on every build. Cheap checks fail first so the expensive ones never run on code that was never going to pass. Caching Cargo, npm, and Docker layers should cut build times by 3–5x once the caches warm up.
Schema-Driven API Client
The frontend client is generated from JSON schemas in server/schemas/ via npm run codegen:ts, and the build regenerates it whenever the schemas change. The server owns the contract and the frontend inherits it, so the two can no longer drift apart quietly.
Star Trek BASIC and the WASM Gate
Star Trek BASIC gained a proper backend entry (555-1702), which immediately exposed a WASM timing issue: the program could load before the worker module was ready. Nothing crashed. The game simply did not run.
The fix is to gate on the worker’s READY message, then load the program, then run. The debugging took much longer than the fix, because a silent failure gives you nothing to grep for; adding logging through the initialization flow was what made the ordering visible.
CPU Core Memory Hygiene
I also tightened memory behaviour in the CPU cores. The 8088’s 1 MB of memory moved to Box<[u8]> so there is no Vec resize overhead, UART output swapped String::clone() for std::mem::take(), and buffers got explicit 64 KB caps to stop unbounded growth. This touched all five cores: Z80, Intel 8008, Intel 8080, Intel 8088, and MOS 6502.
None of it is visible in a demo. It reduces heap churn and keeps long sessions stable, which is the sort of thing you only notice when it stops being true. All 157 unit tests still pass.
Tomorrow: more hardware realism, now that the deployment skeleton can carry it. Viewport scaling still needs attention — the CRT layout on mobile falls apart when the keyboard opens.
Previous: 2026-01-27