The world and its rules#
What this world is made of, who owns what, and what you are allowed to do. This is the document to read before writing a client, a script or an agent: the rules below are enforced by the server, and everything else has to live inside them.
What a world is#
One procedurally generated planet, created the first time a server starts against an empty database and kept in a PostgreSQL database from then on. A world has a name, a seed and a generator version, and those three fix everything natural about it — the same seed produces the same coastlines, the same forests and the same rocks on any machine.
That is not a nicety. It is what lets the server store almost nothing:
BASE WORLD a pure function of seed, generator version and coordinate
+ WORLD DELTA "this tree is gone" — one row per change
+ ENTITIES everything somebody made
= CURRENT WORLD
There is no table of trees. A chunk of untouched forest costs zero rows; felling one tree costs exactly one.
Terrain and biomes#
Height is metres above sea level, and sea level is 0. Terrain is a broad landmass shape with local relief on top; moisture decides where forest grows, and height decides where the ground turns to bare mountain.
| Biome | Where | What grows |
|---|---|---|
Water | At or below sea level | Nothing |
Grassland | Dry, drier air | Occasional trees, some rocks |
Forest | Dry, damper air | Dense trees, few rocks |
Mountain | Above 30 m | No trees, many rocks |
A newly arrived player is put down at a spawn position the generator works out from the seed: dry land, not a mountain, and with enough room around it to walk. It is the same place every time for a given world.
The generator decides what the land starts as, not what it stays. Ground can be shaped: a square of it moved to a height you name, easing back to the hillside over a skirt you choose, or cut with a face where you want a floor against a slope. Building already does a little of this on its own — anything with walls cuts itself a level plot first — and world_shape_ground makes it something you can ask for outright, with world_release_ground to take it back. It is judged the way building is, on every chunk the change reaches, and anything standing on the ground comes with it: a house on a terrace rises and falls with the terrace. What the ground has been changed to is world state like any other, so it survives a restart and everyone sees the same hill.
Regions, chunks and positions#
| Unit | Size | What it is for |
|---|---|---|
| Region | 1024 m | The unit of activity. A region nobody is in costs nothing |
| Chunk | 128 m | The unit of streaming, claiming and spatial indexing |
Positions are absolute doubles in metres, with Y up. Chunk bounds are half-open, so a point on a shared edge belongs to exactly one chunk. Everything that maps a position to a chunk goes through one type, so the flat chart this version uses could become a sphere without the simulation noticing.
Time#
The world has its own clock, measured in ticks — 10,000,000 to the simulated second. It is never the wall clock, and it is never a date: it is how long this world has been running.
Anyone connected can change the server's speed to 0, 1, 2, 5, 10 or 50. There is one clock for everybody, so a client cannot run its own world faster; when somebody changes it, every connected client is told.
Nothing is stepped. There is no loop calling Update() on every object: work is scheduled for the moment it is due, and anything that changes smoothly — a battery draining, a tree growing, a robot walking, a crate falling — is stored as a value at a reference moment plus a rate and calculated when somebody asks. A tree advanced a hundred simulated days costs nothing at all, because nothing happens in that window.
The consequence worth knowing as a client author: an entity in transit stays at its origin until it arrives. Its movement component carries the origin, the departure time and the speed, so a client can draw the journey itself.
People#
A player is an account, identified by a login name and proved by a token issued when the account is registered. See the protocol for exactly how that exchange goes.
An account is a person, not a character. What you own lives on the entities you own, so a server that one day hosts more than one planet would still know you.
A connection without a login name is anonymous: it can watch the world, read designs and inspect things, and it owns nothing. Anonymous sessions act as the world itself, which is why they cannot claim land, build or create an agent.
Owners#
Everything in the world has exactly one owner, and there are three kinds:
| Owner | Written | Means |
|---|---|---|
| The world | world | Nature, and anything nobody has taken |
| A player | player:<uuid> | Somebody's |
| An agent | agent:<uuid> | An AI's, and the AI is not a person's proxy |
An agent owns what it builds. That is the point: when a player asks for an agent, the agent acts as itself, so asking for one grants no authority the asker did not already have, and an agent cannot build on ground somebody else has claimed.
An agent is also visible. It is given a machine to work from and a character to walk about in, and that character is an ordinary entity that arrives in chunk snapshots like anything else — so an agent building a town is something you can watch walking between its plots rather than an invisible owner of a computer.
Land claims#
Land is claimed a chunk at a time. A claim is a set of chunks rather than a rectangle, so it is already an arbitrary shape.
- Unclaimed ground is open: anybody may build on it, and anybody may fell the trees on it.
- Claiming is first come. A request that names any chunk somebody already holds is refused entirely, and says who holds it.
- A claim carries its own permissions, so its owner can let a neighbour build, or let everyone pass through, without granting anything on each individual thing standing there.
- A claim made through
claim_landgrants the public nothing beyond the Inspect everybody always has. An agent can instead claim ground for public use, which grants everyone Inspect and Use — that is what makes a town something other people can walk into and open the doors of.
The world itself cannot hold a claim, which is what keeps "unclaimed" from being ambiguous — and is another reason an anonymous session cannot claim.
Permissions#
Eight of them, checked server-side on every action. A client may hide a button; that is decoration, and this is the rule.
| Permission | Allows |
|---|---|
Inspect | Look at it and read its components |
Use | Operate it without changing it |
Build | Place new things on the claimed area |
Modify | Change its state or configuration |
Destroy | Remove it from the world |
Program | Write or start scripts on it |
Transfer | Give it to another owner |
Control | Drive it directly, such as moving a robot |
What you may do to a particular thing is worked out from three sources together:
- Yours is yours. An owner has everything over their own things.
- Grants on the thing itself. Its owner can give another owner named permissions.
- The claim covering the ground it stands on. Unowned nature on claimed land is the claim's to decide — that is what makes a claim worth holding. Over someone else's property on your land, a claim can widen access to Inspect and Use and no further; owning the ground never means owning what is on it.
Everyone can always Inspect. A refusal always says why, naming either the claim or the owner that refused it.
Six of the eight can be handed out: Inspect, Use, Build, Modify, Destroy and Program. Transfer and Control are the owner's alone, and nothing a grantee receives lets them grant it on. The exact spellings each interface accepts are in the schema reference.
Which action needs what:
| Action | Needs |
|---|---|
| Inspect an entity | Inspect |
| Use something, or transfer items | Use — on both ends of a transfer |
| Move something | Control |
| Fell a tree or clear a rock | Destroy |
| Program, start or stop a script | Program |
| Build from a design | Build on the chunk it will stand on |
| Change a part of a thing from Lua | Modify |
Things#
An entity is an identity, a type and a bag of components. There is no Robot class: a robot is an entity carrying movement, energy, computer and sensor components, which is exactly why a design can describe a machine nobody wrote code for.
The type is a validated string of at most 48 characters, not a fixed set. tree, rock, robot, computer, building, character and item are the ones the world itself uses; house, road and market exist in a running world because an agent wrote those words in a design it authored.
Every entity also has a persistence class, which says what the world owes it:
| Class | Meaning |
|---|---|
Ephemeral | May disappear once it stops being relevant |
Aggregatable | May be folded into a summary and regenerated later — natural trees |
Persistent | Must survive individually, exactly as it was |
Historic | Persistent, and significant enough to be kept in the world log |
What a thing shows above itself#
Anything in the world can carry readings, which every client draws above it: a bar, or a line of text. A health bar, cargo 3 / 10, charging, a countdown, meet here — all the same thing with different words in it. They are put there by whoever may change the thing, from a browser, from a script, from a model, or by the design it was built from; nobody has to add a feature for a new one.
A reading can name a number the world already keeps — health, a battery, how grown something is — and is then true for ever with nobody maintaining it. A reading can also carry a number of its own, given once as a value, a rate and where it stops, so a bar that empties over the next half minute is set going in one go and then changes by itself.
A thing shows at most four at a time, they are shorter and quieter than a name and go before a name does as you move away, and everything that can see the thing sees them. Nothing private belongs on one.
Today the world deals no damage, so a health bar is a full bar over anything that has health, and a character has none at all — a body's battery is the number on it that really moves.
What survives a restart#
Live state is in memory. Ordinary state rides on a snapshot taken every fifteen seconds by default, because a position update is not worth a database write. Four things do not wait for it and are durable before the caller is told they worked: a claim, a saved design, a virtual file write, and the removal of a natural object.
| Comes back | How |
|---|---|
| The planet and the simulated clock | Stored with the world, and written by every snapshot |
| Persistent entities and their components | Snapshot, plus durable writes for things built through an action |
| Felled nature | World deltas, written before the removal, so a crash cannot resurrect a felled tree |
| Claims | Written before the caller is told the claim succeeded |
| Designs | Written on save |
| Virtual filesystems | Snapshot, plus an immediate write for a script an agent wrote |
| Agents, with their goals and memory | Written after every thought |
| Running scripts | Restarted from the top, not resumed mid-stack |
What does not come back is whatever a snapshot had not yet reached when the process died: a position from the last few seconds, an energy level. The world is consistent afterwards, just slightly older.
A Lua coroutine's stack cannot be stored, so what survives is the intent — this script, on this machine. A script must be written to cope with starting again.
World history#
Significant things are written to a log you can read without a client. Open /events in a browser and it is a page — newest first, what each thing was, when it happened in the world's own time, and what it happened to. Ask for it from a command line and it is the same history as figures:
curl 'http://127.0.0.1:5080/events?limit=20'
Beside it, /world is what the planet is, /metrics is what the server is doing, and /regions is where things are — every region anybody has used, as a map and a list. Each of those four is a page for a reader and figures for anything else; ?format=json on any of them gives the figures to a browser too.
| Kind | Raised when |
|---|---|
WorldCreated | A world is generated for the first time |
ClaimCreated | Somebody claims ground |
ClaimReleased | Somebody gives claimed ground back |
EntityCreated | Something is built |
EntityDestroyed | Something is removed |
BlueprintCreated | A design is saved |
ScriptStarted | A script is started on a machine |
ScriptCrashed | A script failed or ran out of instructions |
AgentCreated | An agent is created |
AgentGoalCompleted | An agent finishes one of its goals |
AgentDismissed | An agent is dismissed, and what it built passes to whoever employed it |
AccessChanged | Somebody grants or withdraws access to a thing or to ground |
BlueprintUpdated | A design nothing was built from is changed in place |
BlueprintDeleted | A design nothing was built from is deleted |
It is a debug and observation view rather than an API to build on: newest first, capped, read-only.
Where to go next#
- Protocol — every message and endpoint.
- Schema reference — every value the world will accept: the closed sets in full, the charset and length of every validated name, and every limit with its default.
- Blueprints — how to describe a thing so the world can build it.
- Lua API — what a program running inside the world can do.
- AI agents — how something decides what to do here.
- MCP — connecting a model to a running world.