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.

The user-code layer is what turns Realm from an economy sim into a platform. Players write Lua services that run on the engine, read public and private game state, place orders, propose contracts, and send messages — and can sell those services to other players as subscriptions. A player who automates their inventory management writes a script. A player who notices others have the same problem polishes it, adds a UI, and offers it as a SaaS. Other players subscribe, and a real software business exists entirely in-game, with revenue, customers, and reputation.
The full user-code layer (in-browser IDE, block editor, service marketplace) ships in Phase 4. The current API endpoints are stubs for static validation and source deployment. The simulation engine already exposes all the actions that scripts will eventually call.

What code can and cannot do

Before writing any Lua, it helps to understand the hard boundaries the runtime enforces.

Permitted

  • Read public world state: current tick, map and plot data, market order books, price history
  • Read your own private state: your inventory, accounts, and contracts
  • Place market orders and cancel them
  • Propose and accept contracts
  • Send in-game messages to parties
  • Call other players’ published services (Phase 4)
  • Move money between your own accounts

Forbidden

The runtime enforces these at the sandbox layer — not by convention.
  • Cannot read another player’s private inventory, account balances, or contracts
  • Cannot impersonate another party
  • Cannot create money or materials from nothing (all transactions go through the engine’s conservation-law enforcement)
  • Cannot bypass the contract system
  • Cannot make other players’ code execute
  • Cannot interact with external networks (no real-world HTTP)
  • Cannot use real randomness — only the simulation’s (tick, purpose) seed
  • Cannot consume more CPU, memory, or storage than budgeted
  • Cannot use forbidden Lua globals: require, dofile, loadfile, io, os, debug, package, coroutine, getfenv, setfenv, string.dump, collectgarbage, newproxy, module

Resource metering

Code costs CPU, memory, and storage, all billed in in-game currency. CPU budget: Each player has a base monthly allocation (free for everyone). Beyond that, you buy more at a market rate. Each deployed service executes within a hard instruction budget per call — exceeding it fails the call rather than stalling the simulation. Memory / storage: Code can persist data in a per-service key-value store. Storage costs in-game money per byte per game-day. Determinism budget: Code must execute within a fixed instruction count per call. Over budget = the call fails. This is what prevents one player’s badly-written code from stalling the tick loop for everyone else.

API endpoints

GET /code/status

Returns the user-code capability advertisement. Use this to check whether a Lua runtime is available, what the size limits are, and what phase the sandbox is in.
curl http://localhost:8000/code/status
Example response:
{
  "phase": "stub",
  "lua_runtime": false,
  "lua": { "available": false },
  "max_source_bytes": 256000,
  "eval_requires_env": "REALM_LUA_EVAL=1",
  "eval_max_bytes": 8192,
  "message": "Deterministic Lua sandbox and deploy pipeline are Phase 4; the engine API already exposes the same actions scripts will call."
}

POST /code/validate

Performs static validation on a Lua source string: size and shape only, no execution. Use this from an IDE or CI pipeline to check whether a source is within limits before deploying. Request body:
{
  "source": "<lua code>"
}
curl -X POST http://localhost:8000/code/validate \
  -H "Content-Type: application/json" \
  -d '{"source": "local x = 1 + 1\nreturn x"}'
On success:
{
  "ok": true,
  "bytes": 24,
  "lines": 2,
  "chars": 24
}
On failure (e.g. source over 256,000 bytes):
{
  "ok": false,
  "reason": "source exceeds 256000 UTF-8 bytes"
}

POST /code/deploy

Validates and stores a Lua source string for a party in world.deployed_lua_sources. Validated size only; execution is handled separately and gated. This is the Phase 4 staging endpoint. Request body:
{
  "party": "player",
  "source": "<lua code>"
}
curl -X POST http://localhost:8000/code/deploy \
  -H "Content-Type: application/json" \
  -d '{"party": "player", "source": "return 42"}'
On success:
{
  "ok": true,
  "party": "player",
  "bytes": 9,
  "lines": 1,
  "chars": 9
}
Deployed sources appear in GET /world under the deployed_lua key as { chars, lines } metadata (the source itself is not returned in the public world view).

POST /code/eval

Runs a Lua chunk and returns its return value. This is a local development tool — it is not the production sandbox and does not have access to the full Realm API.
Lua evaluation (POST /code/eval) is gated behind REALM_LUA_EVAL=1 and requires the lupa Python package (pip install -e ".[lua]"). It is intended for local development only. The sandbox is a minimal setfenv-based Lua 5.1 environment, not a security boundary for untrusted internet input.
Request body:
{
  "source": "<lua code>",
  "tick": 42,
  "purpose": "api"
}
The chunk runs with tick and purpose available as globals, plus a restricted math table (abs, floor, min, max, sqrt). No other globals are available.
REALM_LUA_EVAL=1 curl -X POST http://localhost:8000/code/eval \
  -H "Content-Type: application/json" \
  -d '{"source": "return tick * 2", "tick": 21, "purpose": "test"}'
On success:
{
  "ok": true,
  "result": 42
}
If REALM_LUA_EVAL is not set:
{
  "ok": false,
  "reason": "set REALM_LUA_EVAL=1 to enable (local dev only)"
}

Writing Lua services

The read API (free or low-cost)

world.time()               -- current game-tick
world.geography()          -- public map, plots, terrain
market.book(asset)         -- order book for an asset
market.history(asset, n)   -- last n price snapshots
me.inventory()             -- your own inventory
me.accounts()              -- your own account balances
me.contracts()             -- your active contracts
reputation.of(party_id)    -- public reputation summary
messages.inbox()           -- your inbox

The write API (consumes resources)

market.place_order(asset, side, qty, price, expiry)
market.cancel_order(order_id)
contracts.propose(template, params, counterparty)
contracts.accept(proposal_id)
messages.send(party_id, text)
services.call(service_id, args)   -- call another player's service (Phase 4)
me.transfer(account, amount)      -- move money between your own accounts

Example: auto-restock

The simplest useful service watches your inventory and places a buy order whenever stock of a material drops below a threshold.
-- Auto-restock: when iron drops below 50 units, place a buy order
local inv = realm.inventory("player", "iron")
if inv.qty < 50 then
  realm.market_bid("player", "iron", 50, 1500)
end
You can run this for yourself, or publish it as a template for other players to copy.

Example: logistics optimizer

A more advanced service accepts delivery requests and returns an optimized route plan. Other players call it and pay per use.
-- service: optimal_route_v1
function on_call(args)
  -- args = { deliveries = [{from, to, qty}, ...], vehicles = [...] }
  local plan = solve_vehicle_routing(args.deliveries, args.vehicles)
  return plan
end

service.define("optimal_route_v1", on_call, { per_call = 50 })
service.publish("optimal_route_v1", {
  category = "logistics",
  description = "Computes optimal delivery routes. Saves 10–30% on fuel.",
  price = "50 per call"
})
A shipping company calls this once a day to plan routes, pays 50 per call, and the optimizer’s owner collects ongoing revenue.

Example: market-making bot

A tick-scheduled service that maintains a spread around the mid-price for a material:
-- service: market_maker_iron
function tick()
  local mid = market.mid("iron_ore")
  local spread = me.config().spread_pct
  local size = me.config().order_size

  market.cancel_all()
  market.place_order("iron_ore", "buy",  size, mid * (1 - spread))
  market.place_order("iron_ore", "sell", size, mid * (1 + spread))
end

service.define("market_maker_iron", tick, { tick_every = 600 })  -- every 10 game-minutes

Services as a business

A published service is a deployed function with:
  • A name and description visible in the service marketplace
  • A pricing model (per-call, subscription, free, or custom)
  • An owner (the deploying party) who receives revenue
  • Reputation (uptime, response quality, customer reviews)
  • Versioning — services can be updated without breaking subscribers
The service marketplace (Phase 4.5) is a built-in directory where players browse by category (analytics, logistics, automation, finance), by reputation, by price, and by popularity.

Sandbox internals

The eval sandbox uses Lua 5.1 via lupa with loadstring + setfenv. The sandbox environment contains only tick, purpose, and a stripped math table. Forbidden patterns are checked with a regex before execution:
require, dofile, loadfile, loadstring, io., os., debug,
package, coroutine, getfenv, setfenv, string.dump,
collectgarbage, newproxy, module
The maximum eval chunk size is 8,192 bytes. The maximum deploy source size is 256,000 bytes. Both limits are enforced before any parsing or execution. In the full Phase 4 runtime, each deployed service runs in its own sandbox instance with:
  • A Lua interpreter with restricted standard library
  • A connection to the simulation API (read/write)
  • A per-service key-value store with quota
  • A hard CPU instruction budget per call
  • An execution history for debugging and audit
  • OS-level isolation if needed for additional security
For the full architecture context, see Technical Architecture. For the complete list of engine endpoints that scripts will call, see Engine API Reference.

Build docs developers (and LLMs) love