Engine kernel

Follow into
Save into

Everything replays. That has been the standing invariant behind every entry in this log: restart the server and the world comes back byte-identical, roll a year of journal through the reducer and demand the same bytes out. This entry is a backfill, the first of four. The dev log opened at the fifth epic; these walk the record back to the beginning, and the beginning is the machine that makes the invariant true.
The engine kernel epic built the deterministic substrate and nothing else. No sectors, no ships, no credits: a command queue, a single-writer reducer loop, an append-only journal, snapshots with recovery, and a replay harness. Five phases, and at the end of them a game engine with no game in it, proven on a register machine because registers were the only state that existed.
The journal is the world
Every change to the world arrives as a command, is serialized through the single writer, and is applied by a pure reducer whose only inputs are the current state and that command. The journal of those commands is the sole source of truth; the live state is a cache the journal can rebuild at any time.
The journal record is a typed envelope: a sequence number starting at one, the receipt timestamp in nanoseconds, an origin that is exactly one of player, scheduler, or system, the player identity populated only for player origin, an opaque correlation id that ties a reply back to the request that caused it, and the command payload itself. The schema is declared a persistence contract rather than a wire surface, which is why it could stay private while the published protocol grew epic by epic on top of it.
On disk, each record rides a fixed frame:
| Bytes | Field |
|---|---|
| 4 | payload length, little-endian |
| 4 | CRC32C of the payload, Castagnoli polynomial |
| up to 1 MiB | the marshaled record |
The 1 MiB bound is a safety rail with a precise job: a length field above it is not believed, so a corrupted length reads as ordinary tail damage instead of commanding a gigabyte allocation. Opening a journal scans every frame, truncates at the first torn one, syncs the truncation, and reports the last valid sequence, so a crash mid-write heals to the last durable record on the next boot. The healing has a deliberate limit: a frame that passes its CRC but fails to unmarshal is not tail damage, it is real corruption, and the open refuses it loudly rather than silently discarding a record the checksum vouched for.
Writes are batched. An append stages every record of a batch in one file write, sync is the explicit group-commit point, and close never syncs, because the kernel owns the final commit. The read side is a scanner that can start after any sequence, applying the same discard rule the open applies.
The loop
The kernel is a bounded command queue drained by one goroutine. Commands enter through an ingress channel deep enough to absorb a burst while the loop commits, 1,024 slots by default. The loop drains up to 256 commands into one group commit: apply each through the State, stage the batch in the journal, fsync once, and only then release the replies and hand the events to the sink. Acknowledged means durable; no client ever holds a reply the disk could still lose.
flowchart LR C["command arrives"] --> E["enqueue stamps receipt time,<br/>the only wall-clock read"] E --> Q["bounded queue"] Q --> W["single writer stamps<br/>the sequence"] W --> A["Apply: pure reduction,<br/>events or deterministic rejection"] A --> J["journal append,<br/>one write per batch"] J --> F["fsync: the group commit"] F --> R["replies released"] F --> S["events to the sink"]
The State contract underneath that loop is small and strict, three methods. Apply takes one command and returns either events or a deterministic rejection, and on rejection the state is untouched, the random number generator included: replaying the same command against the same state must reproduce the same refusal. MarshalBinary returns the canonical serialization, and determinism there is the implementation's obligation stated in the contract itself, because protobuf does not promise stable bytes across versions, so states hand-order their own encoding. UnmarshalBinary restores what MarshalBinary produced. The generator is part of the state and rides its serialization, randomness is drawn only inside Apply, and so a replay draws the same numbers a live run drew. Nothing in a reduction may ask the operating system for anything.
Three clocks
The engine keeps three notions of time apart so that replay stays stable. The command sequence orders the journal and never consults a wall clock. The game day is a value inside the state, advanced only by a command. The real-world clock lives at the enqueue boundary, where the kernel stamps a command's receipt time exactly once; that stamp is data in the journal, and every later reading of it, in this epic or the sixth or the seventh, is a read of recorded fact rather than a fresh decision. An engine that asked for the time mid-reduction could never replay to the byte, so it never does.
The scheduled side of the world enters through the same door as everyone else. The kernel ships an injector, a deliberately policy-free periodic clock: on every tick it enqueues one scheduler-origin command and abandons the reply. An injected command is applied, journaled, acknowledged, and replayed exactly like player traffic, and multiple injectors serialize through the queue like any other enqueuers. No jitter, no catch-up, no calendar; the game's real scheduler arrived two epics later, as policy layered above this seam, which is precisely why the maintenance entry could say that a replay re-reads what the scheduler once did rather than re-deciding it.
Snapshots and recovery
A journal that is the sole source of truth would replay from genesis forever, so the kernel checkpoints. Every 1,024 applied commands, at a batch boundary and never inside one, it writes a snapshot: a twenty-byte fixed header carrying the format version, the last applied sequence, and a CRC32C of the state bytes, then the game id, then the state's own canonical serialization. The write is temp-file, fsync, rename, then an fsync of the directory, so a snapshot either exists whole or not at all. File names embed the sequence zero-padded to twenty digits, which makes lexicographic order numeric order, and pruning keeps the newest three.
Recovery is a ladder, and every rung is checked:
flowchart TD
O["open the game directory"] --> N{"newest remaining snapshot:<br/>version, game id, CRC,<br/>unmarshal all clean?"}
N -->|"clean"| T["restore it"]
N -->|"damaged"| D["discard, try the next older"]
D --> N
N -->|"none left"| G["construct the state from seed"]
T --> RT["replay the journal tail<br/>beyond the snapshot through Apply"]
G --> RT2["replay the whole journal<br/>through Apply"]
RT --> LIVE["live state"]
RT2 --> LIVE
A snapshot that fails any check, wrong format version, wrong game id, a checksum mismatch, a short read, is discarded and the next older one is tried; with none left the state constructs from its seed and the whole journal replays, which is exactly the path the replay harness walks, so the disaster path is also the most-tested path. Rejections replay too: a command the engine refused on Tuesday is refused identically in the recovery, which is the difference between a log and a ledger.

The harness
Determinism is not a nicety here, it is the fidelity contract. A faithful reimplementation has to prove it reproduces the original's behavior, and the only way to prove that at scale is to replay. The harness ships with the kernel rather than arriving later, in two instruments.
End-state replay feeds a whole journal, never a live scheduler, through a State's Apply from its seed construction, and returns the final canonical bytes, every event the commands emitted in application order with sequences stamped as the kernel stamps them, the last sequence fed, and a SHA-256 fingerprint of the state bytes. Two runs agree when their fingerprints agree, this year and next.
Lockstep runs two instances against the same feed record by record and localizes the first divergent sequence, and its rules are deliberately harsh. A rejection on one side and not the other is divergence, not noise, because deterministic refusals are recorded traffic like everything else. Two engines constructed differently are refused outright rather than compared, because a comparison that starts from different worlds can only produce an unlocatable disagreement later.
Both instruments carry a self-check on every state they touch: marshal twice, byte-compare, so a state whose serialization wanders is caught in the harness before it can masquerade as a divergence somewhere downstream.
The epic's acceptance ran all of it against a register machine, a toy state whose only verbs set and add registers, because the point of the kernel is exactly that it does not care. Everything this log has described since, the port economies, the daily maintenance, the mines and the escape pods, is a set of reducers bolted onto this loop and a set of commands written to this journal, and every one of them inherited the replay proof the day it was written.
Next
The Big Bang: a universe worth simulating. Thirty thousand sectors connected by construction, FedSpace seeded as a safe haven, landmarks seated, ports and planets scattered under recovered densities, and a golden universe pinned in test literals so generation can never drift.