Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/at6132/econ/llms.txt

Use this file to discover all available pages before exploring further.

Realm is built as three separable concerns that communicate through a single contract: simulation + services + clients. The simulation engine is the sole authority on game state. Every other component — web client, mobile app, AI agent, or player-authored Lua service — submits proposed actions and waits for the engine to validate and commit or reject them.
The simulation engine is the only thing that can mutate game state. All other parties submit proposed actions; the engine validates and commits or rejects.

Architecture diagram

The full system is organized into four horizontal layers: clients at the top, then the API gateway, then the three runtime services (simulation, agent, user-code), and finally the data layer at the bottom.
┌────────────────────────────────────────────────────┐
│                    CLIENTS                          │
│  Web (Next.js + React)   Mobile (React Native)      │
└──────────────┬───────────────────────┬──────────────┘
               │                       │
               ▼                       ▼
┌────────────────────────────────────────────────────┐
│                  API GATEWAY                        │
│  REST + WebSocket. Auth. Rate-limit. Validation.    │
└──────────────────────┬─────────────────────────────┘

        ┌──────────────┼──────────────┐
        ▼              ▼              ▼
┌──────────────┐ ┌────────────┐ ┌──────────────┐
│  SIMULATION  │ │  AGENT     │ │  USER-CODE   │
│  ENGINE      │ │  RUNTIME   │ │  RUNTIME     │
│  (auth.)     │ │  (Tier 1/2 │ │  (Lua sandbx │
│              │ │   /3)      │ │   per service│
└──────┬───────┘ └─────┬──────┘ └──────┬───────┘
       │               │               │
       └───────────────┼───────────────┘

┌────────────────────────────────────────────────────┐
│                  DATA LAYER                         │
│  Postgres (state)   Redis (hot)   S3 (history/blobs)│
└────────────────────────────────────────────────────┘

v1 vs v2+ stack

The v1 design collapses the full architecture into a single Python process and a Next.js client — no distributed systems required until multiplayer. v2+ splits each concern into its own service.
ConcernChoiceRationale
Web frontendNext.js 14 + React 18 + TypeScriptStandard, fast iteration, great tooling
2D map renderingPixi.js 8 (Canvas/WebGL)Performant 2D, works in browser, mature
Charts / data vizRechartsStandard for trading-style UIs
Simulation enginePython (FastAPI + uvicorn)Fastest design iteration; fine for solo scale
DatabaseSQLite per save fileOne save = one file; easy to copy and back up
In-process cacheIn-memory (Python dict)Solo doesn’t need Redis
MobileReact Native + Expo (later phase)Codeshare with web where possible
User-code sandboxLua via lupa (Phase 4)Sandboxable, familiar, fast
LLM agentsAnthropic API (Claude)Tier 3 agents; Claude Haiku by default
For v1 solo (50 plots, 15 agents, ~100 active orders), the Python engine ticks in under 50ms on a mid-range laptop. Don’t introduce Rust or Go prematurely.

The simulation engine

The engine is a deterministic tick-based system. It holds the entire world in memory (solo mode) and advances one tick at a time.

Tick loop

Each tick processes the world in a fixed, ordered sequence:
while world.running:
    tick_start = now()

    # 1. Process inbound actions (from players, agents, code)
    for action in pending_actions_queue:
        if validate(action):
            apply(action)  # mutates state in transaction
        else:
            reject(action)

    # 2. Run scheduled events (contracts triggering, decay, etc.)
    process_scheduled_events(world.time)

    # 3. Tick AI agents (Tier 1 every tick, Tier 2 every N ticks, Tier 3 on slower cadence)
    tick_agents(world.time)

    # 4. Tick user code services (those with `tick_every` schedules)
    tick_services(world.time)

    # 5. Run market clearing (match limit orders, update last-trade prices)
    clear_markets()

    # 6. Advance world time
    world.time += 1

    # 7. Persist snapshot if needed (every N ticks)
    if world.time % SNAPSHOT_INTERVAL == 0:
        persist_snapshot()

    # 8. Sleep until next tick
    sleep_until(tick_start + TICK_DURATION)
In the actual tick.py implementation, one simulation step runs: transit delivery → building decay → production → material spoilage → employment → Tier 1 agents → Tier 2 agents → Tier 3 LLM agents → clock advance → contract breach checks → financial contract processing → market history snapshot.

Tick rate

In public mode, 1 game-day = 1 real-hour, but ticks happen much more often (for example, 1 game-minute per tick = 1 real-second per tick = 1,440 ticks per game-day). This gives smooth real-time-ish gameplay without forcing the engine to tick at 60Hz. In solo mode, the tick rate is configurable per save: 1×, 2×, 4×, or paused.

Determinism

Same starting state + same inputs = same outputs. The engine enforces this rigorously:
  • All randomness derives from (world.tick, randomness_purpose) seeds — world.rng(purpose) returns a seeded random.Random instance.
  • Wall-clock time is never used inside game logic.
  • Agent decisions, price formation, and decay all use deterministic functions.
  • This makes replays, debugging, and offline simulation possible.

Data model

Every state change is a transaction. No direct mutations. The transaction layer enforces conservation laws (money and matter cannot be created or destroyed).
TableContents
worldsOne row per shard / save file
playersOne per human player
accountsFinancial accounts (player-owned, business-owned, system-owned)
plotsOne per plot: ownership, terrain, hidden subsurface
surveysSurvey results — who knows what about which plot
inventoriesMaterial holdings, indexed by owner + location
materialsMaterial catalog
production_unitsBuilt infrastructure on plots
labor_poolsRegional labor markets
employmentsActive employment relationships
ordersOpen buy/sell orders on order books
tradesHistorical executions
contractsActive contracts (template + parameters + state)
contract_eventsPayment, delivery, and breach events
messagesPlayer-to-player and agent-to-player messages
servicesUser-deployed code services
service_subscriptionsWho subscribes to what
service_callsCall logs for billing
agentsAI agent records
agent_memoriesTier 3 memory blobs
reputation_eventsAuditable reputation history

Real-time updates

The web and mobile clients react to changes via WebSocket:
  • New order on an order book → push to subscribers
  • Contract proposed → push to counterparty
  • Agent message → push to recipient
  • Price tick → push to chart subscribers
v1: A WebSocket connection from client to API gateway. The server pushes events on change. The client subscribes to topics it cares about (prices for material X, my contracts, my account). Mobile: Same WebSocket model, plus push notifications for high-priority events (new contract proposal, contract deadline approaching, large price move) — even when the app is closed.

User-code runtime

Each deployed Lua service runs in its own sandbox with:
  • A Lua interpreter (with a restricted standard library — no io, os, require, debug, or coroutine)
  • A connection to the simulation API (read and write actions)
  • A storage allocation (key-value store per service)
  • A CPU budget per tick or per call
  • An execution history for debugging and audit
When a service is called, the runtime instantiates a sandbox, validates the caller’s permission, runs the code with the call arguments, captures any actions the code attempts via the API, commits actions subject to engine validation, returns the result, and bills CPU usage to the service owner’s account. Services cannot access the file system, the network, or other services’ state. See User Code Layer for the full API surface and Phase 4 roadmap.

Persistence strategy

Save files are SQLite files. One save = one file. Easy to copy, share, and back up. Cloud sync is optional via account. Use POST /persistence/save and POST /persistence/load to write and restore saves from the engine API.

Performance targets

ModeScaleTarget
v1 solo50 plots, 15 agents, ~100 active ordersTick < 50ms on a mid laptop
v2 multiplayer (per shard)500 plots, 200 agents, 100 humans, 10K active ordersTick < 500ms on a single server
v3+ scale5K plots, 1K humansHot path likely needs Rust; multiple independent shards
For a deeper look at each layer, see Engine API Reference, Running Locally, and User Code Layer.

Build docs developers (and LLMs) love