Skip to content

World Engine — the central generator, clock, and event bus the programs are lenses over

  • ../gameplay-framework.md — the gameplay map this is the world-substrate for (the programs are the “tools, always on” row; this engine is what their :world reads)
  • ../programs/enrichment-contract.md — the lens-over-simulated-world frame, the single shared mission world-region, the sanctioned dossier-commit write path (§1–§6) this generator installs into and the programs read
  • ../programs/dossier-data-model.md — where a discovered key durably lands (profiles / fact-nodes / links); the engine emits into this, never raw-writes
  • ../programs/bzbx.md · conduit.md · kommander.md · ripsaw.md · keyring.md · kn9.md — the six recon/data programs; each program’s :world section is a lens-slice of the one world this engine generates
  • recon-mode.md — the player-elected casing mode + the shared heat economy the World Clock’s wall-clock “heat” feeds
  • mission-control.md — generates the contract whose archetype seeds this world; the Mission Board is a feed off this engine (§7)
  • ADR-0043 — the objective model whose :hold / TTL / heat the World Clock owns and evaluates each tick
  • ADR-0042 — first-party programs; carts enrich, never gate (the lens contract)
  • ADR-0046 — knEmacs as the program engine (the programs are modes that read this world)
  • ADR-0049 — the launch-app opaque :world handle, dossier-commit / dossier-has?, the run-state mission-data region (the contract this spec’s region is delivered through); the live :world handle the tick-reactive cases need is its forthcoming amendment (Open / deferred)

One-sentence shape: the cart hands the engine a world-archetype; the engine generates one coherent world from seed × archetype and installs it into the mission’s single run-state world-region; every program is a lens that materializes and reads its own slice of that one world; the World Clock advances it (move-driven by default, wall-clock “heat” when a cart opts in); a typed-event bus tells the programs what changed so they re-render. The world is reactive + scheduled, not an autonomous NPC sim.


1. Why there is one engine, not per-app generation

Section titled “1. Why there is one engine, not per-app generation”

Six of the first-party programs (bzbx, CONDUIT, Kommander, RIPSAW, Keyring, kn9) each read a :world — a network, a host, a filesystem, an org graph, key material, a message store. The naïve model is that each program (or each cart-per-program) generates its own. That model breaks coherence: bzbx’s host must be the same box CONDUIT rides, owned by the same entity RIPSAW maps, holding the same file Kommander stages, unlocked by the same lock Keyring cracks, briefed by the same dead-drop kn9 delivers. Six independent generators cannot agree on that without a shared source of truth.

So there is one World Engine and the programs are lenses, never generators (enrichment-contract §1). The engine generates one coherent cross-referenced world from the seed and the cart’s archetype, installs it into the single shared mission world-region (run-state, one region per mission — enrichment-contract §4), and each program materializes and reads its own slice on demand. The slices agree because they are facets of one generated world, not six parallel worlds. This is the load-bearing invariant the per-program :world sections all assume (“coherent with the rest”).

Programs never generate; they read + verb + emit. A program is (a) a lens onto its slice of the world, (b) a provider of domain verbs over that slice, and (c) a source of dossier-commit discoveries. Generation is the engine’s; durable consequence is the engine’s sanctioned-write path’s. A program in between is stateless about world creation.


2. The generator — seed × archetype → one coherent region

Section titled “2. The generator — seed × archetype → one coherent region”
InputFromRole
cipher_seed + relevant deck state (reputation tier, cartridge_history)Universal Deck State, via the accepted contract (mission-control §3)the entropy — the same seed reproduces the same world; reputation sets threat density, history pre-reveals known nodes
world-archetypethe cart (the generator seam — §6)the shape — what kind of world (a 16-node shell-company net, a hardened single host, an offshore org tree), its parameters (depth, host count, shell count, where the real owner hides, the conspiracy-signature), and which facts are tagged discoverable keys

The world is a deterministic function of seed × archetype. No wall-clock, no RNG outside the seeded stream (the existing LFSR primitives, per ADR-0049 §5). Determinism is what makes the device offline, authorable, and replayable.

2.2 Output: one cross-referenced world in the mission world-region

Section titled “2.2 Output: one cross-referenced world in the mission world-region”

The generator builds a single graph of in-world entities whose cross-references are the coherence:

  • a host (bzbx’s wire / CONDUIT’s box) is owned-by an entity (RIPSAW’s org graph), located-at a mount (Kommander’s filesystem), gated by a lock (Keyring’s key material), referenced by a message (kn9’s store).
  • the edges (:owns · :controls · :routes-to · :employs · :located-at, the canonical edge-verb set — dossier-data-model §5) are generated once, in the region, so every lens sees the same connections.

This installs into the mission world-region: volatile run-state, never UDS, reset at the mission-instance boundary like the rest of run state (enrichment-contract §4; ADR-0049 §2). The cart installs it; launch-app hands each program an opaque FE_TPTR handle to it (ADR-0049 §2); a program reads/navigates through program-side accessors, never by walking the handle’s internals.

The whole world is not generated eagerly into the region. The region holds the seed + archetype + a sparse spine; each program materializes its slice on demand, deterministically, the first time that slice is touched:

  • bzbx materializes the network nodes when it scans; RIPSAW materializes the org subtree when it expands a node; CONDUIT materializes the host’s cmd-response table on connect; Kommander materializes a mount’s fs-nodes on enter.
  • Because materialization is a pure function of seed × archetype × address, two lenses that touch the same underlying entity get the same materialization — the host bzbx revealed is the host CONDUIT connects to. The slices agree by construction, not by synchronization.

This keeps the arena bounded (a 16-node shell ledger is never fully resident — dossier-data-model §1, the Xiki fetch-on-expand discipline) while preserving cross-lens coherence. A node a lens reveals (sets :revealed?) stays revealed in the shared region, so the next lens sees the reveal live — the bzbx → CONDUIT invariant.

Per-program world schemas are owned by the program specs, not here. This spec fixes how a world is generated, installed, materialized, and ticked; the exact node shape of each slice (network nodes, ledger rows, org entities, fs-nodes, key material, message store) is per-program design (the :world tables in bzbx / conduit / kommander / ripsaw / keyring / kn9), exactly as enrichment-contract §9 reserves.


The world advances on a World Clock. The clock is hybrid, and the default is deliberately not real-time:

By default the world ticks per operator action — a move, not a wall-clock second. This matches the ADR-0043 move-budget pressure model: the run’s cost is measured in moves, the cart’s pacing is turn-like, and a cold case (Black Ledger’s forensic audit, a static org tree) advances only when the operator does something. Move-driven is the floor: every world ticks at least on operator action, and a cart that wants no live pressure gets exactly that — a world that holds still between the operator’s moves.

3.2 Wall-clock “heat” — opt-in, per cart

Section titled “3.2 Wall-clock “heat” — opt-in, per cart”

A cart opts a case into wall-clock pressure by declaring it live (the wall-clock heat hook). Then, on top of move-driven ticks, the clock also fires time-based events — a trace advancing toward the operator while they linger on a host, a remote mount about to drop, a crack racing a closing window. This is the heat the per-program specs reference (“on a cart’s wall-clock heat”): live cases get a clock that doesn’t wait for the operator; cold cases don’t.

The two are one timeline, not two clocks: heat events and move ticks both advance the same world state and feed the same pressure machinery. Wall-clock heat reconciles with the existing trace mechanic and the shared heat economy (recon-mode §4) — recon-heat is the operation’s starting trace baseline; the World Clock is where that baseline advances over real time when a case is live.

Wired (GWP-632). The opt-in heat hook is driven off real elapsed time by the world_heat driver, which rides the one monotonic virtual clock the runtime already pumps each frame — the ADR-0045 background scheduler (sched.c) — exactly the way the Fe idle-timer registry rides its own per-frame pump. No second clock. A live case arms heat (world_heat_arm); the driver advances world_clock_heat with the scheduler’s real delta, so a scheduled trace beat fires as wall-clock time crosses its threshold and publishes trace-advanced on the bus. Power-off catch-up coalesces an offline gap into one fat-dt beat for free (sched_catchup). Move-driven stays the floor and is untouched; until a case arms heat the heat clock never advances. CONDUIT is the first consumer — its TRACE readout reads the live trace meter. The exact cadence/thresholds remain mission-layer tuning (see Open questions).

3.3 The Clock owns the ADR-0043 TTL / holds / heat

Section titled “3.3 The Clock owns the ADR-0043 TTL / holds / heat”

The World Clock is the single evaluator of the time/pressure predicates the objective model defines (ADR-0043):

  • :hold predicates — engine-evaluated each tick, never polled by the cart (ADR-0043 Decision-4). A :hold that watches “trace below threshold” is checked by the clock on every move tick and every heat tick.
  • TTL / contract decay — a contract’s time-to-live and the case clock (mission-control) tick on this clock; a live case’s TTL burns on wall-clock heat, a cold case’s on moves.
  • heat — the meter the wall-clock hook feeds; the clock advances it and crosses the recon-mode §4 thresholds (low / mid / high → defender focus).

Programs never own the clock. A program observes clock events and re-renders; it never advances the clock, never evaluates a :hold, never burns TTL. The clock is the engine’s; the program is a reactive reader. This is the consistent stance across every program spec (“the engine watches the :hold / TTL; <program> just keeps reporting”).


4. The typed-event bus — how a program observes and re-renders

Section titled “4. The typed-event bus — how a program observes and re-renders”

The world is reactive: when the generator’s world or the clock changes state, the change is published as a typed event on a bus; programs subscribe and re-render their lens on the events that touch their slice. This is what makes “CONDUIT re-renders on the event; it never drives the clock” a real mechanism rather than a figure of speech.

The canonical event types (the vocabulary the program specs already speak):

EventFired whenCarriesPrimary observers
transfer-posteda ledger posting lands (live financial case)the posting (accounts, amount, date)AmberCalc; RIPSAW (if it names an account)
node-onlinea host / node comes online in the network slicethe node id + reveal statebzbx (the top table updates); CONDUIT (a :routes-to host appears)
trace-advancedthe defender trace moves (a wall-clock heat tick, or a noisy move)the new trace position / threshold crossedCONDUIT, bzbx, Keyring (the pressure); knSALK (the defensive side)
mail-arriveda feed item arrives on its :arrives-at tickthe message / contractkn9 (the feed); the Mission Board (§7)
mount-droppeda remote mount’s CONDUIT session is traced / killedthe mount idKommander (a copy in flight can be cut off)
registration-fileda new filing / registration appears (live org case)the new entity / edgeRIPSAW (a new node in the org tree)

The set is extensible by archetype — a cart’s world-archetype may register additional typed events for its domain (a domain-specific heat beat), the same way the profile-type set is extensible (dossier-data-model §3). The above are the canonical floor every program is built to observe.

4.2 How a program observes (the reactive loop)

Section titled “4.2 How a program observes (the reactive loop)”
  1. A program (a knEmacs mode — ADR-0046) reads its :world slice through its accessors and renders it.
  2. It subscribes to the event types its slice cares about (bzbx → node-online, trace-advanced; kn9 → mail-arrived; Kommander → mount-dropped).
  3. The World Clock ticks (move or heat) → the engine mutates the shared world-region → it publishes the resulting typed events on the bus.
  4. Each subscribed program re-renders its lens on the next event (the top table reflows, a new mail row appears, a mount greys out). A feed arrival is a tick event, not an animation (kn9 scope-line); a re-render is a data-driven redraw, not a real-time loop.

The event bus is the seam between the world and the lenses: the world changes → an event is published → the lens that cares re-renders. No program polls the world; no program drives it. This is the “reactive” half of the engine’s stance.

4.3 Discoveries flow back through the sanctioned write path, not the bus

Section titled “4.3 Discoveries flow back through the sanctioned write path, not the bus”

The bus carries world → program notifications (what changed). The reverse direction — a program reporting a discovery — does not go on the bus and does not raw-write. It routes through (dossier-commit <key>), the engine’s sanctioned capture → refile path (enrichment-contract §5; dossier-data-model §4; ADR-0049 §3). Nothing a program does to the world-region touches durable state; the only durable consequence is a sanctioned key emission. The bus is for re-render; dossier-commit is for consequence. The two are kept separate on purpose — it is what keeps the run-state world volatile and DOSSIER the single durable record.


5. Reactive + scheduled — NOT an autonomous sim

Section titled “5. Reactive + scheduled — NOT an autonomous sim”

The world is authored and verb-driven, not alive on its own. This is the explicit design stance, and it bounds the engine:

  • Scheduled — events fire on an authored timeline (a kn9 brief at mission start, a dead-drop after phase 1 — the cart’s archetype seeds the arrival timeline; a live case’s filings on a wall-clock cadence). The schedule is data the cart authored, not behavior the world improvised.
  • Reactive — the world also responds to operator verbs: a scan reveals a host, a crack yields a key, an expand materializes an org subtree, a noisy move advances a trace. The operator’s actions move the world; the world does not move itself between them (except a live case’s authored heat schedule).
  • NOT autonomous — there is no free-running NPC simulation, no agents pursuing goals, no emergent economy of in-world actors taking turns. The world has no behavior the cart did not author or the operator did not trigger. A cold case is static; a live case follows an authored heat schedule plus the operator’s verbs. Determinism (§2.1) requires this: an autonomous sim is non-replayable and unbounded; an authored-timeline + verb-driven world is both.

This keeps the engine inside the Pi Zero 2 W arena budget and inside the offline/authorable contract, and it is why the per-program specs can promise a deterministic, headlessly-testable lens: the world only changes on a tick the engine controls, carrying an event the program can re-render against.


A cart touches the World Engine at exactly two seams — and nowhere else. Everything else (generation, the clock, the bus, the region, the write path) is the engine’s.

6.1 The world-archetype (the generator seam)

Section titled “6.1 The world-archetype (the generator seam)”

The cart supplies a world-archetype — the generator’s shape input (§2.1): the kind of world, its parameters, the seeded-procgen knobs (depth, host/shell count, where the real owner hides, the conspiracy-signature, the message arrival timeline), and which facts are tagged discoverable keys vs. flavor (enrichment-contract §4–§5). The cart authors the world’s bones; the engine generates the body from seed × archetype. The cart never generates the world itself and never installs per-program worlds — it installs one archetype, and the engine makes one coherent world from it. (Generation is procedural; playing the world is grammar — ADR-0049 §5.)

6.2 The verb-set overlay (the domain seam)

Section titled “6.2 The verb-set overlay (the domain seam)”

Each program ships a generic verb floor (bzbx’s PING/PROBE/TRACEROUTE, CONDUIT’s DIAL/PULL, RIPSAW’s QUERY/EXPAND/FOLLOW, Kommander’s MARK/COPY/MOVE, Keyring’s GENERATE/CRACK, kn9’s READ/REFILE — the bare-launchable floor). A cart overlays domain verbs on top of that floor (per-protocol decoders, exploit/escalate, staging/exfil-routing, crack archetypes, auto-DOSSIER-capture). The overlay is verbs over the same generated world — it adds what the operator can do, not what the world is. The world-archetype seam is generation; the verb-set seam is domain interaction. Two seams, cleanly separated: a cart enriches the world (seam 1) and the verbs over it (seam 2), and never gates the program (ADR-0042) — bare-launch a program with no cart and it reads its empty/default world with its generic floor.


7. The Mission Board and kn9 share a feed engine

Section titled “7. The Mission Board and kn9 share a feed engine”

A second insight the program specs surfaced: the Mission Board and kn9 are the same kind of thing — a feed. kn9 is a message store whose items arrive over time on mail-arrived ticks; the Mission Board is a feed of contracts that arrive the same way (a handler offering work is a mail-arrived/contract event — kn9 objective-graph ties). Both render as a knEmacs tabulated-list (folders + unread / contracts + status) — identical in shape (kn9 engine reuse).

So the World Engine’s event bus has one feed mechanism that both consume:

  • kn9 subscribes to mail-arrived for its message store (briefs, dead-drops, LINK traffic).
  • the Mission Board subscribes to the same feed for contract arrivals (the board is a feed off this engine, seeded by deck state — mission-control §3).

This is a rule-of-three extraction (ADR-0046 build-then-extract): kn9’s group buffer + the Mission Board’s contract feed are the pair that proves a shared feed-reader / tabulated-list library (with bzbx’s capture table, RIPSAW’s attribute table, Kommander’s panels, Keyring’s key list as the other tabulated-list consumers). The feed engine is the World Clock’s mail-arrived stream + one shared tabulated-list renderer; both the board and kn9 are thin consumers of it. Building each program’s table first, then extracting the one shared library when the second consumer lands, is the discipline — not a blocker for this spec.


graph TD
  subgraph Seeds
    UDS["Universal Deck State<br/>(cipher_seed, reputation, history)"]
    ARCH["cart world-archetype<br/>(seam 1: shape, params, key-tags, timeline)"]
  end

  UDS -->|entropy| GEN["World Engine — Generator<br/>(seed × archetype, deterministic LFSR)"]
  ARCH -->|shape| GEN

  GEN -->|installs one coherent world| REGION["Mission world-region<br/>(single, run-state, never UDS,<br/>resets at mission-instance boundary)"]

  REGION -->|opaque FE_TPTR :world handle<br/>via launch-app| LENSES

  subgraph LENSES["Programs = lenses (knEmacs modes) — lazy per-lens materialization"]
    BZBX["bzbx — network slice"]
    CONDUIT["CONDUIT — host slice"]
    KOMM["Kommander — filesystem slice"]
    RIPSAW["RIPSAW — org-graph slice"]
    KEYRING["Keyring — key-material slice"]
    KN9["kn9 — message-store slice"]
  end

  CLOCK["World Clock (hybrid)<br/>move-driven default + opt-in wall-clock heat<br/>owns ADR-0043 :hold / TTL / heat"]
  CLOCK -->|ticks, mutates| REGION
  CLOCK -->|publishes typed events| BUS

  BUS["Typed-event bus<br/>transfer-posted · node-online · trace-advanced<br/>mail-arrived · mount-dropped · registration-filed"]
  BUS -->|observe + re-render| LENSES

  KN9 -. shared feed engine .- BOARD["Mission Board<br/>(contract feed — §7)"]
  BUS -->|mail-arrived / contracts| BOARD

  LENSES -->|verb-set overlay<br/>seam 2: domain verbs| LENSES
  LENSES -->|discovery: dossier-commit<br/>sanctioned capture → refile| DOSSIER["DOSSIER<br/>(durable record — profiles / facts / links)"]

  classDef engine fill:#1a1a1a,stroke:#E6A020,color:#E6A020;
  classDef store fill:#1a1a1a,stroke:#888,color:#ddd;
  class GEN,CLOCK,BUS engine;
  class REGION,DOSSIER,BOARD store;

Reading the diagram: the seeds (deck state + cart archetype) feed one generator; the generator installs one coherent world into the single mission world-region; each program gets an opaque handle to that region and materializes its slice lazily; the World Clock ticks the region (move-driven by default, wall-clock heat when a cart opts in) and publishes typed events; programs observe the events and re-render; durable consequence flows out one sanctioned path (dossier-commit → DOSSIER), never the bus, never raw. The cart touches exactly two seams (archetype + verb overlay); the Mission Board and kn9 are two consumers of one feed mechanism.


9. Invariants (the contract this engine holds)

Section titled “9. Invariants (the contract this engine holds)”
  1. One world, one region, one source of truth. One generated world per mission, installed in the single shared run-state world-region; programs are lenses, never generators (enrichment-contract §1, §4).
  2. Deterministic. The world is a pure function of seed × archetype; lazy materialization is a pure function of seed × archetype × address. Same seed ⇒ same world ⇒ same slices. No RNG outside the seeded stream.
  3. Run-state, never durable. Nothing in the world-region touches UDS or DOSSIER. The region resets at the mission-instance boundary (ADR-0049 §2). Durable consequence is only dossier-commit (sanctioned) (ADR-0040).
  4. The clock is the engine’s. Programs observe ticks; they never advance the clock, evaluate a :hold, or burn TTL. The World Clock is the single evaluator of ADR-0043 pressure (ADR-0043 Decision-4).
  5. Reactive + scheduled, not autonomous. Events fire on an authored timeline + operator verbs; there is no free-running NPC sim. A cold case is static; a live case follows an authored heat schedule.
  6. Two cart seams only. A cart enriches via the world-archetype (generation) and the verb-set overlay (domain verbs); it never gates the program (enrich-never-gate, ADR-0042).
  7. The opaque :world handle. Programs hold an opaque FE_TPTR handle and read through program-side accessors; they never walk the region’s internals (ADR-0049 §2).

  • The opaque live :world handle. The shipped v1 :world transport is copy-by-value (a serialized snapshot rebound in the program context — Black Ledger’s launch-app → AmberCalc handoff). True live re-sync — a node revealed in the run-region mid-mission seen by the next lens without a fresh launch, and tick events landing in an already-open program — depends on the opaque live handle, tracked as a forthcoming ADR-0049 amendment (the live-handle work the bzbx / CONDUIT / Kommander / RIPSAW / Keyring / kn9 specs all flag in their Open sections). v1 fallback: commit reachability/state as a durable key (host:<id>:reachable), then gate on it; the static cold-case path works today.
  • The shared tabulated-list / feed library. The rule-of-three extraction (§7) — bzbx’s capture table, RIPSAW’s attribute table, kn9 + Mission Board’s feed, Kommander’s panels, Keyring’s key list — is engine work scheduled by build-then-extract (ADR-0046); not a blocker for this spec.
  • One region or many. Whether a mission installs one shared world-region or one per program/phase is the enrichment-contract §9 open item; this spec assumes one shared region per mission (the coherence-by-construction model of §1–§2). A multi-phase mission’s region lifecycle (does the region survive a hot-swap, or regenerate per phase from the carried phase_chain) finalizes with the engineering spike.
  • Per-program world schemas. The exact node shape of each slice is per-program design (the program specs’ :world tables), not this spec (enrichment-contract §9).
  • The event-bus mechanism + extensible event registration. The concrete in-runtime bus (subscription model, event-struct shape, the per-archetype event-registration API) finalizes with the engineering spike; this spec fixes the taxonomy and the observe-and-re-render contract, not the C struct layout.
  • Wall-clock heat cadence + numbers. The exact wall-clock tick rate, trace-advance rate, and heat thresholds are mission-layer tuning (recon-mode §8), not fixed here.
  • Bare-launch default world. What an un-enriched program shows (empty vs. a sandbox) is the per-program bare-launch policy (enrichment-contract §3; each program’s “Bare-launch default world” section); the engine’s part is only “no archetype ⇒ empty/default world, still runs.”