0003 — TypeScript everywhere
Date: 2026-11-09 · Status: accepted
Context
Galaxy generation and combat resolution are deterministic functions that must produce bit-identical results on client and server (ARCHITECTURE.md §1.1). Two implementations of a deterministic function is a bug factory (ARCHITECTURE.md §2). The client needs to generate regions locally, and the balance work runs the same maths in a simulator (AGENTS.md §3, ARCHITECTURE.md §4).
Decision
One language, TypeScript, client and server, with a shared core package used verbatim by client, server and balance simulator: galaxy/, combat/, economy/, rules/ (ARCHITECTURE.md §2, §4).
- Runtime: Bun — TypeScript with no build step, built-in test runner, bundler, Postgres and SQLite drivers; fewer moving parts is the brief.
[OPEN]— production maturity versus Node 26 is to be confirmed with evidence before locking. Node is the fallback and the port is trivial (ARCHITECTURE.md§2, §7). - HTTP: Hono — runtime-agnostic, so the Bun bet stays reversible (
ARCHITECTURE.md§2). - Database: PostgreSQL 17 — one store, boring, already the house pattern on the prod box (
ARCHITECTURE.md§2).
Consequences
- Exactly one implementation of determinism, shared by the client, the server and the simulator.
- Types and constants are shared too (
rules/), so the deterministic core has no serialisation boundary with the HTTP layer. - The runtime question stays open without blocking anything: Hono keeps it from leaking into the rest of the server.
- Testing concentrates on the core — determinism, combat resolution, tax and economy arithmetic; plumbing gets smoke tests (
ARCHITECTURE.md§4,AGENTS.md§3). - The stack is one language end to end; there is no second toolchain to keep in sync.
Alternatives rejected
- Rust or Go server. Either one means the galaxy function and combat resolver exist twice — once in TypeScript for the client, once in the server language — which is exactly the bug factory the decision avoids (
ARCHITECTURE.md§2).[INFERENCE: ARCHITECTURE.md states the one-implementation requirement and the TypeScript choice; it does not contain a separate Rust/Go evaluation.]