Artificial World

Schema reference#

Every closed set, every constant, every validation rule and every bound this world enforces, in one place. The other documents describe the shapes — a design has parts, a part has a primitive and a material. This one says which primitives, which materials, how long a name may be and what happens at the edge, so that a person, a script or a language model writing into this world can know rather than guess.

Every value here is the one the server actually enforces. Where more than one door checks the same thing, they are held to the same figure and the same words.

How to read this page#

ColumnMeans
WireExactly what to write in JSON
NumberThe stored numeric value of an enum member. Never sent, but it is what the database and any binary format would carry
DefaultWhat you get by saying nothing
At the edgeWhat happens when the value is out of range: refused, clamped, or dropped

Three refusal styles appear throughout, and they are not interchangeable:

  • Refused as a value. A validator returns a list of problems, or a call returns nil, reason. Nothing throws. This is how designs, filesystem calls and script starts fail.
  • Refused as an error. The action comes back as action_result with Success: false and an Error string, or a JSON-RPC error.
  • Dropped. The value is ignored and the rest proceeds. This is deliberate for AI output: a brain that returns one nonsense action still gets the other seven carried out.

The wire format itself#

One serialiser serves the WebSocket and the world's own storage, so a component stored and the same component sent cannot disagree about their shape.

RuleValue
Property namesPascalCase, and case-sensitiveProtocolVersion, not protocolVersion
EnumsNames, not numbers, spelled exactly as this page lists them: "Local", "Persistent", "BeveledCube"
Enum readingCase-insensitive, so "local" is accepted. A plain JSON number is also accepted. An unknown name fails the whole message, which is answered with error
Flag enums (Permission)Comma-and-space separated names: "Inspect, Use". A named combination wins, so the full set writes as "All" and the empty set as "None". Reading accepts any case; a | separator is not accepted
NullsWritten, not omitted, with two exceptions: the Token in a bootstrap's Player block, and a metrics snapshot's PlanetName
NaN and InfinityAccepted as the JSON strings "NaN", "Infinity", "-Infinity" by the serialiser — and then refused by every validator that takes geometry, because a non-finite number is what survives arithmetic and breaks a renderer far away

That rule is the socket's and the database's, and nothing else's. Three other JSON surfaces exist and all of them are camelCase:

WhereNotes
The HTTP JSON endpoints — /health, /ready, /metrics, /world, /events, /api/regionscamelCase, with the same spellings for ids, times and positions. So the same metrics snapshot is "EntitiesTotal" over the socket and "entitiesTotal" at /metrics
POST /mcp tool payloadsA foreign wire format with its own conventions; enums are still names. Embedded component bodies are the socket's own form, so they stay PascalCase inside
GET /designs/{id}.jsonA description for something reasoning about a design, not the message that builds one. Its enum-like fields are already plain strings

GET /designs/{id}/visual.json is the exception to that exception: it is PascalCase, because it is not a description of a design but the design's own geometry exactly as the socket sends it. The browser module that draws the catalogue's patterned preview is the one that draws the world, and giving it a second spelling of the same document to read would have been a second shape to keep in step.

/events shows the double form simulated time takes outside the socket: atTicks is the number, and at is the same moment written for a person, as T+1d 14:24:33. Only the ticks are worth parsing.

Identifiers on the wire#

TypeWireExample
EntityId, PlayerId, AgentId, BlueprintId, PlanetId, EventIdA GUID as a string"6b1f...-...-...-...-...."
OwnerId"world", or "<kind>:<guid>" with the kind lowercased"player:6b1f…"
EntityTypeIts lowercase name, revalidated on read"robot"
PartIdIts name. The empty string means "no parent", which is how a root part is written"mast", ""
SimulationTimeAn integer tick count, 10,000,000 to the simulated second. Never a date36000000000
TimeSpanAn integer tick count, same scale600000000
ChunkIdAn object: {"X": 12, "Z": -3} — global chunk indices, not metres
WorldPositionAn object: {"X": …, "Y": …, "Z": …} — absolute metres, Y up
WorldRotationAn object with X, Y, Z, W — a quaternion

OwnerId accepts four kinds when parsing — world, player, agent, group — but nothing in a running world issues a group owner today; it exists so the identity can carry one without a storage migration. world: with a GUID is refused: the world's owner id is the bare word world and nothing else.

Closed sets#

Every set in this section is exhaustive. A value outside it is refused, not guessed. The number column is the stored value; it is not what travels.

AccessSubject#

What an access change is about: a thing, or the ground.

WireNumberMeaning
Entity0One entity, named by its id.
Land1The claim covering a position.

AccessSubjectKind#

Whether an access change is about a thing or a piece of ground.

WireNumberMeaning
Entity0One thing, named by its id.
Land1The claim covering a position.

AffordanceAudience#

Which kind of client a verb belongs in front of.

A set of these is one value, written as the names joined with a comma and a space: Authoring, Everything. The numbers add up, and a named combination wins when one matches.

WireNumberMeaning
Visiting1Verbs that are part of being in the world rather than making it.
Authoring2Verbs that change what the world is made of, or who may touch it. In this version the clients that author are the MCP tools an AI agent drives.
Everything3Both — every verb the kind of thing affords.

AgentState#

What an agent is doing.

WireNumberMeaning
Idle0Alive, with nothing scheduled. Costs nothing.
Waiting1Waiting for its next scheduled thought.
Thinking2Its brain is running right now.
Finished3Every goal is done. It will not be woken again unless something happens.
Failed4Its brain failed. Kept, so the failure can be looked at.

AgentTrigger#

Why an agent is thinking now.

WireNumberMeaning
Scheduled0Its own scheduled thought.
Created1Just created, thinking for the first time.
GoalCompleted2A goal was completed.
ScriptCrashed3A script it started crashed.
ResourceProblem4Something ran out — a battery, most likely.
MessageReceived5Another computer sent it a message.

AssetMaterial#

The materials a definition may reference.

WireNumberMeaning
Grass0Ground green.
ForestGrass1The darker green under trees.
Wood2Timber.
Leaves3Foliage green.
Stone4Grey rock.
Water5Water blue.
Metal6Grey metal.
White7Plain white.
Black8Plain black.
Red9Plain red.
Blue10Plain blue.
Yellow11Plain yellow.
Orange12Plain orange.

BiomeType#

The natural character of a piece of terrain.

WireNumberMeaning
Water0Below sea level.
Grassland1Open land. Sparse trees.
Forest2Dense tree cover.
Mountain3High, exposed ground. Rocky and largely treeless.

ChatScope#

Who hears a line of chat.

WireNumberMeaning
Public0Everybody in the world. The default, and what "chat" means.
Local1Everybody at the same place as the speaker, so a town can have a conversation of its own without it reaching the whole planet.
Direct2One named participant, and nobody else.

ClaimRefusal#

The reason a claim was refused.

WireNumberMeaning
None0It was granted.
AlreadyClaimed1Someone else already holds one of the chunks.
Invalid2The world cannot hold a claim, and nothing can claim nothing.
NotClaimed3Nobody holds that ground, so there is nothing there to give back.
NotYours4Somebody else holds it. Only the holder may give ground back.

DesignSaveOutcome#

What became of a described design.

WireNumberMeaning
Created0A new design, under a new identity.
Unchanged1Nothing was written: an identical design of this author's already existed, or the revision described the design exactly as it stands.
Updated2A draft nothing was built from, re-described under its own identity.
Superseded3A new version under a new identity, because things stand that were built from the old one and they keep it.

DesignSeverity#

How much a finding matters.

WireNumberMeaning
Note0Worth knowing, and possibly deliberate.
Fault1Almost certainly not what the author meant.

FileSystemError#

Why a filesystem operation was refused.

WireNumberMeaning
None0It worked.
NotFound1No such file or directory.
AlreadyExists2Something is already there.
ParentMissing3The containing directory does not exist.
NotADirectory4A file was named where a directory was needed.
IsADirectory5A directory was named where a file was needed.
TooLarge6Over the largest size one file may be.
TooManyFiles7Over the number of files one machine may hold.
OutOfSpace8The machine's storage is full.
InvalidPath9It is not a valid virtual path.
NotEmpty10A directory with things still in it.

InputFieldKind#

The kinds of value the world can ask a person for.

WireNumberMeaning
Text0A line of text.
Number1A whole number.
Permissions2A set of Permission names, comma separated.

LuaProcessState#

What a Lua process is doing.

WireNumberMeaning
Ready0Loaded, not yet run.
Running1Executing right now.
Sleeping2Waiting for a scheduled wake-up. Costs nothing meanwhile.
Finished3Ran to completion.
Stopped4Stopped by request.
Crashed5Ended in an error, including running out of instruction budget.

NaturalObjectKind#

What kind of thing the generator placed.

WireNumberMeaning
Tree0A tree.
Rock1A rock.

OwnerKind#

The category of actor that can own something.

WireNumberMeaning
World0Owned by the world itself: naturally generated, unclaimed.
Player1Owned by a human player.
Agent2Owned by an AI agent.
Group3Owned collectively by a group.

Permission#

What an actor may do with something.

A set of these is one value, written as the names joined with a comma and a space: Inspect, Use. The numbers add up, and a named combination wins when one matches.

WireNumberMeaning
None0Nothing.
Inspect1Look at it and read its components.
Use2Operate it without changing it.
Build4Place new things on the claimed area.
Modify8Change its state or configuration.
Destroy16Remove it from the world.
Program32Write or start scripts on it.
Transfer64Give it to another owner.
Control128Drive it directly, such as moving a robot.
Handle256Reach into what it holds: take items out of it, or put items in.
All511Everything an owner may do by default.

PersistenceClass#

How much effort the world owes an entity's continued existence.

WireNumberMeaning
Ephemeral0May disappear once it stops being relevant. Nothing is owed.
Aggregatable1May be folded into an aggregate and regenerated from it later. Natural trees are the archetype: the forest is remembered, not each trunk.
Persistent2Must survive individually, exactly as it was.
Historic3Must survive individually and is historically significant, so it is also kept in the world event log.

PrimitiveShape#

The shapes a procedural model is built from.

WireNumberMeaning
Cube0A box.
BeveledCube1A cube with its edges cut, which reads better at low poly counts.
LowPolySphere2A faceted sphere, deliberately coarse.
Cylinder3A cylinder, normalised so its scale is its size in metres.
Cone4A cone, standing on its base.
Wedge5A triangular prism: ramps, roofs, blades.
Plane6A flat quad, for panels and signs.
Ribbon7A strip of a given width and thickness laid along a path: a road, a footpath, a fence line, a canal, a jetty.

ReadoutKind#

How a readout is drawn above the thing carrying it.

WireNumberMeaning
Text0A line of text: a word, a count, a name for what the thing is doing.
Bar1A bar that fills from empty to full, for a number out of a maximum.

ReadoutSource#

Where a readout's number comes from.

WireNumberMeaning
Given0The number whoever put the readout up gave, which goes on changing by itself at the rate they set and needs no further writing.
Health1The thing's own health, out of the most it can have.
Energy2The charge in the thing's battery, out of its capacity.
Growth3How much the thing has grown, out of how big it gets.

RefusalKind#

A refusal the caller is expected to finish in its own vocabulary.

WireNumberMeaning
None0Nothing to add; the reason stands on its own.
NotBuilt1It is generated nature, so the removal for nature applies.
NotNatural2It was built, so demolition applies.

RegionState#

What a region is currently costing.

WireNumberMeaning
Dormant0Nothing is happening and nothing is scheduled. Costs nothing.
Aggregated1Nature here is held as a summary rather than as individuals.
Operational2Machines are working, but nobody is watching.
Active3A player is near. Everything is live and streamed.

RegionTransition#

Why a region changed state. Kept for the log, and for asking why.

WireNumberMeaning
PlayerArrived0A player came within range.
PlayerLeft1The last player left.
MachineStarted2A machine here started working.
WentQuiet3Nothing was scheduled any more.
Summarised4Its nature was folded into a summary.
Restored5Its nature was regenerated from the summary.

RouteSeverity#

How much a thing found along a route matters.

WireNumberMeaning
Note0Worth knowing, and possibly deliberate.
Fault1The way is broken here: nobody walks this.

ScriptError#

Why a script could not be started or run.

WireNumberMeaning
None0It worked.
NotFound1No such file on that computer.
TooLarge2Over the largest size a script may be.
TooManyProcesses3Over the number of processes one machine may run.
CompileFailed4It is not valid Lua.
RuntimeFailed5It threw.
OutOfInstructions6It used its whole budget in one execution.
NoComputer7The entity has no computer component.

SimulationEventType#

The kinds of scheduled work the simulation knows about.

WireNumberMeaning
EntityWake0A sleeping entity should be brought back into active simulation.
GrowthUpdate1A growing thing should settle its accumulated growth.
BatteryEmpty2An energy store is predicted to reach empty at this moment.
AgentThink3An AI agent should think.
ScriptWake4A sleeping Lua process should resume.
ScheduledAction5A deferred action requested by a script, agent or player.
Landing6Something unsupported reaches the ground at this moment.

SimulationSpeed#

How fast simulated time runs relative to real time.

WireNumberMeaning
Paused0The clock stands still.
Normal1Real time.
Double2Twice real time.
Fast5Five times real time.
Faster10Ten times real time.
Fastest50Fifty times real time.

StartingCatalogue#

How much of the shipped catalogue a brand new world is given.

WireNumberMeaning
None0Nothing at all. The catalogue is empty until somebody authors something.
Character1The shipped character, and nothing else. The default.
All2Every design this build ships: the character, the ScoutBot, the crate, the pavilion and the lift.

WorldEventKind#

The kinds of thing worth remembering happened.

WireNumberMeaning
WorldCreated0A world was generated for the first time.
ClaimCreated1Somebody claimed ground.
EntityCreated2Something was built.
EntityDestroyed3Something was removed.
BlueprintCreated4A design was saved.
ScriptStarted5A script was started on a machine.
ScriptCrashed6A script failed, or ran out of instructions.
AgentCreated7An agent was created.
AgentGoalCompleted8An agent finished one of its goals.
AccessChanged9Somebody let somebody else in, or showed them out.
BlueprintUpdated10A draft was re-described under its own identity.
BlueprintDeleted11A design nothing was built from was removed from the catalogue.
AgentDismissed12An agent was dismissed: it stopped existing and its estate passed to whoever employed it.
ClaimReleased13Somebody gave ground back, in whole or in part.

Notes on particular sets#

The tables above are read off the world itself. What follows is what a table cannot say: where a set is used, and why it is shaped the way it is.

Permission: Handle is not Use#

Operating a thing and emptying it only ever looked like one permission by accident, and the difference is a security boundary. A claim can widen Inspect and Use over anything standing on it — that is what makes a street walkable — but it widens Handle only over things belonging to the claim's own owner. If it widened everything, then since a player's body is property standing on ground like any crate, the flag that made a shop walk-into-able would be the flag that emptied its visitors, in both directions. transfer_items and exchange_items need Handle on both ends.

Seven of the nine can be handed out. The grantable set is Inspect, Use, Build, Modify, Destroy, Program and HandleTransfer and Control are not grantable, and nothing in that list lets the grantee grant on.

Three spellings exist for the same set, and they are not interchangeable:

WhereHow it is written
WebSocket, and storageThe flags string: "Inspect, Use"
MCP tools (world_share, world_ask_access)An array of lowercase words: ["inspect", "use"]. Trimmed and case-folded, so " Use " works. A word that is not one of the seven grantable ones contributes nothing and is silently ignored; a list that yields None is refused
LuaSee the Lua vocabulary

PersistenceClass decides how a thing is removed#

It is the only thing that does: Aggregatable is felled with remove_natural, which writes a row saying it is gone; everything else was built and is taken down with demolish_entity, which deletes the rows it had. The affordance the server advertises on an entity is already the right one of the two. Over MCP the same pair is world_clear_natural and world_demolish, reaching the same two methods.

RefusalKind is never on the wire#

It rides beside the reason inside the server. The world decides which of the two removals applies — by PersistenceClass, in one place — but the action it should have been is called remove_natural over the socket and world_clear_natural over MCP, and the world knows neither name. So it states the fact and tags it, and each door appends the pointer in the words its own caller can act on.

DesignSaveOutcome is a word on the wire#

Never a number: MCP answers with the lowercase word in outcome, and the socket's action_result.Design.Outcome carries the same word, plus deleted for the action that removes one. Which applies depends on facts only the server holds — whether the design exists, whose it is, whether anything stored was built from it — so a caller reads the word rather than predicting it.

unchanged is the one that can answer about a design under a different name from the one asked for, because what a design is decides and the name does not. The MCP answer then carries nameAsked alongside name, so a caller can see that the name it asked for was given to nothing.

ReadoutKind and ReadoutSource describe a caption, not a fact#

A readout is what a thing shows above itself, and nothing in the world ever reads one back: the simulation does not consult a caption to decide anything, and taking one down changes nothing but what is drawn.

ReadoutSource is the half worth understanding. Everything but Given names a number the world already keeps and already sends — health, a battery, how grown something is — so a bar bound to one is true for ever with nobody maintaining it, and costs nothing on the wire because those components travel anyway. A source the thing does not carry is refused rather than stored, because a bar that could never move is worse than no bar. Given is a number of the author's own, stored as a value, a rate and a moment, so a bar that empties over the next half minute is one row that nothing ever touches again.

A design may bind a reading only to a number it would itself be allowed to declare, which is why energy may appear in one and health and growth may not — the same two sentences that refuse those components refuse a reading of them.

OwnerKind is part of the identity#

A player and an agent that happen to share a GUID are different owners, and a permission check never conflates them. Nothing in a running world issues a group owner today; the kind exists so the identity can carry one later.

NaturalObjectKind and its variant#

Variant beside it is a kind-specific sub-type — a tree species, or a rock shape — and is an open integer, not a set.

WorldEventKind at the endpoints#

GET /events returns these as the kind field of each entry; it has no filter of its own. The MCP world_history tool's kind argument matches them, and does so case-insensitively. The list is short deliberately: a log that records everything is one nobody reads.

ClaimRefusal is all or nothing#

A request naming any chunk somebody already holds is refused entirely; claims are never partially granted.

AgentState and AgentTrigger as a brain sees them#

agent_list carries the state as a plain string field, not as an enum, but the spellings are the same. The trigger is the value a Lua brain reads as world.trigger: an agent is never woken because a tick passed, and naming the reasons is what makes that rule visible.

SimulationSpeed is the multiplier#

The number is the multiplier, which is why Paused is naturally zero. simulation_control carries the integer, not the name. There is one clock for everybody: a client cannot run its own world faster, only a connection from the machine the world runs on may change it, and when somebody does every connected client is told.

AccessSubject and AccessSubjectKind#

Two names for the same distinction, one on the server and one in the agent action set. Letting somebody build in your town is not letting them into your house: over someone else's property standing on your land, a claim can widen access to Inspect and Use and no further.

InputFieldKind serves two askers#

An affordance the server advertises on an entity may need values before it can be sent; a question something in the world has put to somebody may collect values beside the choice. Each field says what kind of thing it is so a client can draw the right control, and one client control serves both. Permissions is the server's own, written on an affordance it composed itself; a question raised from a script may ask only for Text or a Number.

DesignSeverity and the finding codes#

The finding codes themselves — vanishing, enormous, crack, buried, detached, protrudes, headroom, hovers, limb_missing — are listed with their thresholds under design review thresholds.

FileSystemError and ScriptError in Lua#

Both come back as the second value of a Lua call — nil, reason — and are never thrown. TooLarge is the file or script size limit, TooManyFiles and TooManyProcesses the counts, each listed under numeric bounds.

SimulationEventType is the scheduler's own#

Nothing outside the server can name one and none of them appears on the wire, but it is a closed set worth knowing exists: content that scripts and blueprints invent arrives as ScheduledAction carrying its own payload, so new content never needs a new event type.

Open sets, and the rules that replace enumeration#

Four vocabularies in this world are deliberately not closed. Listing them would be a lie, because the whole point is that a design, a script or an agent can introduce a value nobody wrote down. Each one has a rule instead, and the rule is enforced.

VocabularyRuleWhere the world's own values are
EntityTypeA validated string, at most 48 characterstree, rock, robot, computer, character, building, item are the seven the world itself uses. house, road and market exist in a running world because an agent wrote those words in a design
Semantic tags1–32 characters, lowercase letters, digits and underscoreThe eleven character tags below. Anything else is a name the design invented, and something looking for it will simply not find it
Item names1–48 characters, non-blankWhatever a design or script trades in
Visual keys, verbs, group tags, metadata keysLength-bounded free texttree, rock, blueprint are what the world produces; Open, Trade, Read are the verbs the shipped designs use

The type is a label for querying and presentation. Behaviour comes from components, so adding a type never means adding a code path — which is exactly why it cannot be an enum.

The semantic tags a character is expected to carry#

The tag is a free string; these are the names the generator writes and the animator looks for, gathered in one place so the two cannot drift apart over a typo.

TagPart
rootThe figure's own origin
torsoThe body
headThe head
upper_arm_left / lower_arm_leftLeft arm, two segments
upper_arm_right / lower_arm_rightRight arm, two segments
upper_leg_left / lower_leg_leftLeft leg, two segments
upper_leg_right / lower_leg_rightRight leg, two segments

Required for a character — the seven a figure needs at minimum: root, torso, head, upper_arm_left, upper_arm_right, upper_leg_left, upper_leg_right. A design carrying torso and head and at least one of the eight limb names is treated as a character, and the review reports each of the seven it is missing as a limb_missing note. A four-legged survey robot with a torso and a head but none of the limb names is left alone, because it is exactly as intended.

The limbs an animator swings: the eight upper_/lower_ names above.

Registered component names#

The stable names components are stored and sent under, and what each one carries. The names are part of the storage contract and may not change once a world exists, and a name not in this list is refused on the way in rather than ignored: silently dropping a component would corrupt an entity on the next save. Every field is written exactly as it is named here, inside the component's own JSON, wherever an entity is described; a field marked derived is worked out by the world and sent, and is never read back.

computer#

A virtual computer's capacities.

FieldTypeMeaning
CpuInstructionsPerSecondnumberBudget for script execution, in Lua instructions per simulated second.
MemoryBytesintegerMemory the machine claims, in bytes.
StorageBytesintegerTotal size the virtual filesystem may occupy.
IdlePowerWnumberPower drawn while doing nothing, in watts.

energy_consumer#

Something that draws power.

FieldTypeMeaning
DemandWnumberPower it draws when enabled, in watts.
IsEnabledbooleanWhether it is switched on.
EffectiveDemandWnumber, derivedWhat it actually draws right now: the demand, or nothing while switched off.

energy_producer#

Something that generates power.

FieldTypeMeaning
OutputWnumberPower it makes when enabled, in watts.
IsEnabledbooleanWhether it is running.
EffectiveOutputWnumber, derivedWhat this actually contributes right now.

energy_storage#

Stored energy, computed analytically rather than ticked.

FieldTypeMeaning
CapacityWhnumberHow much it can hold, in watt-hours.
EnergyAtReferenceWhnumberEnergy held at ReferenceTime.
ReferenceTimeSimulationTimeThe moment the stored energy was last settled; everything since is worked out from the net power.
NetPowerWnumberProduction minus consumption. Negative means draining.
IsDrainingboolean, derivedWhether it is losing energy.
IsChargingboolean, derivedWhether it is gaining energy.

group#

Marks an entity as part of something bigger.

FieldTypeMeaning
RootEntityIdThe entity this one is part of. Its own id if it is the root.
TagstringWhat this piece is to the whole, such as front_door.
  • MaxTagLength = 32 — Longest a piece's tag may be.

growth#

Growth over simulated time, computed analytically.

FieldTypeMeaning
PlantedAtSimulationTimeWhen it started growing.
ReferenceTimeSimulationTimeWhen growth was last settled. Nothing happens between this and now until asked.
BiomassAtReferenceKgnumberHow much of it there was at the reference moment, in kilograms.
MaxBiomassKgnumberHow big it can get, in kilograms.
GrowthRateKgPerDaynumberHow fast it grows with ample water, in kilograms a simulated day.
WaterFactornumberAvailability of water, from 0 (none, no growth) to 1 (ample).
EffectiveRateKgPerDaynumber, derivedHow fast it is growing now, water taken into account.
IsMatureboolean, derivedWhether it is fully grown.

health#

Damage state.

FieldTypeMeaning
CurrentnumberHealth left.
MaxnumberHealth when undamaged.
IsAliveboolean, derivedWhether any health is left.
Fractionnumber, derivedHealth left as a share of the maximum, from 0 to 1.

interactable#

Says that something can be used, and what to call it.

FieldTypeMeaning
VerbstringWhat using it is called: Open, Trade, Read.
HintstringA line for whoever is looking at it, if the design has something to say.
RangeMetersnumberHow close you have to be, in metres. Further away, using it is refused and the refusal names the distance (WhyTooFarToUse).
  • MaxVerbLength = 24 — Longest the verb may be.
  • MaxHintLength = 96 — Longest the hint may be.

inventory#

What an entity is carrying.

FieldTypeMeaning
MaxSlotsintegerHow many kinds of item it can hold at once.
Itemsobject of integer by stringQuantity per item name, ordered so serialisation is deterministic.
UsedSlotsinteger, derivedHow many kinds it holds now.
TotalQuantityinteger, derivedHow many items altogether.
IsEmptyboolean, derivedWhether it holds nothing.
  • MaxItemNameLength = 48 — Longest an item's name may be.

movement#

The ability to move, and the current intent to.

FieldTypeMeaning
MaxSpeedMetersPerSecondnumberHow fast it can go, in metres a second.
DestinationWorldPositionWhere the entity is trying to get to, if anywhere.
DepartedAtSimulationTimeWhen the journey began. Lets anything reading this — the client above all — work out how far along it is without being told again.
OriginWorldPositionWhere the journey began. Null when nothing is under way.
IsMovingboolean, derivedWhether a journey is under way.
ArrivesAtSimulationTime, derivedWhen this journey ends, worked out from where and when it began rather than from where the entity is filed.
  • DefaultWalkSpeedMetersPerSecond = 1.4 — A comfortable walking pace, and what a body gets when its design forgot to move.

ownership#

Permissions the owner has granted to others.

FieldTypeMeaning
Grantsobject of Permission by OwnerIdWhat each named player or agent has been allowed, beyond what anybody may do.
PublicPermissionsPermissionWhat anybody at all may do with this thing.
HasGrantsboolean, derivedWhether anybody has been let in, by name or by policy.

part_state#

The parts of a built thing that are not as they were designed.

FieldTypeMeaning
Partsobject of PartState by stringWhat has been done, by part id.
IsEmptyboolean, derivedWhether every part is as it was designed.
IsMovingboolean, derivedWhether anything on this thing is still moving of its own accord.
  • MaxParts = 128 — How many parts of one thing may carry state.

physics#

Weight: the thing that makes an unsupported object fall.

FieldTypeMeaning
GravityMetersPerSecondSquarednumberHow hard it falls, in metres a second squared.
FallingFromWorldPositionWhere the fall began, or null when the thing is at rest.
FellAtSimulationTimeWhen the fall began, so anything reading this — the client above all — can draw the arc without being told again.
InitialVelocityMetersPerSecondnumberUpward speed at the moment the fall began. Zero for something simply let go; positive for something thrown up, which rises before it falls.
RestingHeightMetersnumberThe height the fall ends at: the ground under it.
IsFallingboolean, derivedWhether it is in the air.
  • EarthGravity = 9.81 — Earth's, near enough, and the default when a design says nothing.

readouts#

What a thing is showing above itself.

FieldTypeMeaning
Readoutsobject of Readout by stringWhat is being shown, by the name whoever put it there gave it.
IsEmptyboolean, derivedWhether this thing is showing nothing at all.
IsChangingboolean, derivedWhether any of these numbers is still moving of its own accord.
  • MaxReadouts = 4 — How many readings one thing may show at once.

script#

The script an entity runs.

FieldTypeMeaning
ScriptPathstringThe path of the script it runs, on its own filesystem.
AutoStartbooleanWhether the script should start when the entity wakes or the server restarts.
  • MaxPathLength = 256 — Longest a script's path may be.

sensor#

The ability to detect nearby entities.

FieldTypeMeaning
RangeMetersnumberHow far it can see, in metres.
MaxResultsintegerThe most things one scan reports.

transform#

Where an entity is. Every entity has one.

FieldTypeMeaning
PositionWorldPositionWhere it stands, in metres.
RotationWorldRotationWhich way it faces.
ScalenumberHow large it is drawn relative to its design; 1 is as designed.

visual#

How an entity should be drawn.

FieldTypeMeaning
VisualKeystringNames the family of visual, such as tree or rock.
VariantintegerWhich variant within that family, such as a tree species.
BlueprintBlueprintIdSet when the visual is generated from a stored blueprint.
IsGeneratedboolean, derivedWhether it comes from a design rather than from the generator.
  • MaxVisualKeyLength = 64 — Longest a visual key may be.

water#

Held water.

FieldTypeMeaning
StoredLitresnumberHow much water it holds.
CapacityLitresnumberHow much it can hold.
Saturationnumber, derivedFill level from 0 to 1, usable directly as a growth water factor.
IsDryboolean, derivedWhether it holds none.

Shapes inside components#

The objects the component fields above are made of.

PartColour#

A colour a design chose for itself.

FieldTypeMeaning
RednumberRed, from 0 to 1.
GreennumberGreen, from 0 to 1.
BluenumberBlue, from 0 to 1.
AlphanumberOpacity. Below one makes glass, water and anything else you can see through.
IsValidboolean, derivedWhether every channel is finite and within range.

PartMotion#

A number that goes on changing by itself.

FieldTypeMeaning
StartnumberThe value at Since.
RatePerSecondnumberHow fast it changes, and in which direction.
SinceSimulationTimeWhen it was last set.
LimitnumberWhere it stops, or null to go on forever. A door swings to ninety degrees and halts; a wheel turns until the car does not.
IsMovingboolean, derivedWhether this is still going anywhere.

PartState#

What has been done to one part of a design.

FieldTypeMeaning
SolidbooleanWhether this part blocks movement, overriding the design.
VisiblebooleanWhether this part is drawn at all.
ColourPartColourA colour, overriding both the design's colour and its material.
AxisWorldPositionThe axis a turn spins about and a slide travels along, in the design's own space. Defaults to up, which is what a door hinges on.
TurnPartMotionHow far the part has turned about Axis, in radians.
SlidePartMotionHow far the part has moved along Axis, in metres.
IsEmptyboolean, derivedWhether this says anything at all.
IsMovingboolean, derivedWhether anything here is still changing on its own.
EffectiveAxisWorldPosition, derivedThe axis to use, which is up unless the part said otherwise.

Readout#

One reading shown above a thing in the world.

FieldTypeMeaning
KindReadoutKindWhether this is drawn as a bar or as a line of text.
SourceReadoutSourceWhere the number comes from.
LabelstringA word or two naming what is being shown, or null for none.
TextstringWords to show instead of a number, or null when this shows a number.
ColourPartColourWhat colour to draw it, or null to let the client choose.
ValuePartMotionThe number, when Source is Given: where it was, how fast it is changing and where it stops.
MaximumnumberThe number that counts as full, when this shows a given number.
OrderintegerWhere this sits in the stack, smallest first.
IsEmptyboolean, derivedWhether this says anything worth drawing.
IsChangingboolean, derivedWhether this number is still moving of its own accord.

A blueprint may not carry transform: it is set when the entity is spawned, and a blueprint that names one is refused.

Ten of them may be declared by a design somebody wrote — computer, energy_consumer, energy_producer, energy_storage, interactable, inventory, movement, physics, script, sensor — each within bounds. The other eight are refused by name, with a sentence saying why that one is not an author's to write. The rule is applied at the one place a design enters the catalogue, so it holds for every design that arrives from outside the server, whichever door it came through. The table in Blueprints has the figures and the reasoning.

Validated strings#

The values people get wrong. Every one of these is checked by allowing rather than by forbidding — a charset, not a blocklist, because blocklists get bypassed.

ValueCharsetLengthFirst characterCaseRejection
PartIdaz, 09, _1–32Must be a lowercase letterOrdinal, case-sensitive. Mast is not a valid id at allFails the whole message on the wire; a part needs an id from the validator
EntityTypeaz, 09, _1–48Must be a lowercase letterOrdinal, case-sensitiveFails the whole message on the wire
Semantic tagaz, 09, _1–32, or absent entirelyNo rule — unlike the two above, a tag may start with a digitOrdinala semantic tag must be 1 to 32 characters, or a semantic tag may contain only lowercase letters, digits and underscores
Login nameletters, digits, _, -, .2–32Must be a letterTrimmed and lowercased before use, so Youri and youri are one accountRefused, naming the rule that failed
Display nameAny≤ 64Trimmed. Defaults to the login name if blankA display name may be at most 64 characters.
Agent nameAny non-blank≤ 64As givenRefused when the agent is created
Agent goal nameAny non-blank≤ 120As givenRefused when the agent is created
Design nameAny non-blank≤ 64As givena model needs a name, or the name may be at most 64 characters
Blueprint nameAny non-blank≤ 64As givena blueprint needs a name, or the same length message
Item nameAny non-blank≤ 48OrdinalAn item name may be at most 48 characters.
Interactable verbAny non-blank≤ 24As givenA verb may be at most 24 characters.
Interactable hintAny≤ 96As givenA hint may be at most 96 characters.
Declared component nameOne of the ten on the allowlistOrdinal<name>: is not a component a design may declare, or the sentence saying why that one is refused
Visual keyAny non-blank≤ 64As givenA visual key may be at most 64 characters.
Group tagAny1–32 when presentAs givenA group tag may be 1 to 32 characters.
Planet nameAny non-blank≤ 64TrimmedA planet name may be at most 64 characters.
Account kindhuman or agentMatched case-insensitively; absent or empty means human400 from /api/register: 'kind' must be 'human' or 'agent'. Not stored — it chooses whether the answer carries MCP configuration
Account tokenbase64 of 32 random bytes44 charactersOrdinal, compared in constant time against a stored SHA-256 hash401 from /api/session; '<name>' belongs to somebody… from hello. An account may hold several, and any of them proves it
PassphraseAny non-blank12–256The characters as typed, hashed with PBKDF2-HMAC-SHA256 (210,000 iterations, 16-byte salt) and compared in constant time400: A passphrase must be at least 12 characters and at most 256, and must not simply be your login name. Optional per account; never asked of a model
Device codedigits, with spaces and hyphens ignoredexactly 8 digitsOrdinal, compared in constant time401 from /api/devices/claim, one sentence for never-minted, expired, used and replaced alike. Lives five minutes, works once
Device nameletters, digits, space, ', -, _, ., (, )≤ 48Anything else is dropped rather than escapedNever refused: an empty result falls back to the browser the server recognises, and then to A device
Resume keyletters, digits, -, _8–64 when presentOrdinal, and only ever compared with another connection'sA resume key is 8 to 64 characters of letters, digits, hyphens or underscores. from hello. Nothing is stored: it names a browsing context for as long as it is connected

BlueprintId is not a validated string: it is a GUID, and a value that is not a GUID fails as JSON before anything sees it. The same goes for EntityId, AgentId, PlayerId, PlanetId and EventId.

Virtual paths#

VirtualPath is a security boundary, not a convenience. Every path reaching it comes from Lua or an AI agent.

RuleValue
Must be absoluteStarts with /. There is no working directory and no resolution step
Whole-path length≤ 256 characters
Segment length≤ 64 characters
Depth≤ 16 segments
Segment charsetLetters, digits, _, -, .
. and ..Refused outright as segments, rather than resolved. Nothing can refer outside the filesystem even in principle
Trailing slashAccepted and normalised away — /home/agent/ is /home/agent
ComparisonOrdinal, case-sensitive
Rejection'<path>' is not a valid virtual path. — from Lua, as nil, reason

/etc/passwd is a perfectly valid virtual path naming a file that does not exist. It has nothing to do with the host.

ScriptComponent.ScriptPath is checked separately and slightly differently at its own edge: ≤ 256 characters, must start with /, and may not contain .. anywhere in the string — belt and braces on untrusted input.

Numeric bounds and defaults#

Simulation limits#

These are the shipped figures. A world's operator may change them, and every answer that hits one names the limit it hit, so a world set up differently still tells you what it is holding you to. A world set up with impossible figures refuses to start rather than misbehaving later: every one must be greater than zero, and LuaInstructionSlice may not exceed MaxLuaInstructionsPerExecution, or the budget would never be checked.

KeyDefaultWhat it bounds
MaxScheduledEventsPerEntity256How many events one entity may have queued at once. Stops a runaway script from filling the scheduler with its own wake-ups.
MaxEventsPerAdvance1,000,000Backstop for a handler that keeps scheduling work at the current moment. Draining stops rather than looping forever; the remaining events stay queued for the next advance, so the server degrades instead of hanging.
MaxVirtualFileSize65,536Largest single file a virtual computer may hold, in bytes. Stops a script from filling storage with one write; a larger write is refused.
MaxVirtualFilesPerComputer256How many files one virtual computer may hold. Another is refused.
MaxComputerLogEntries500How many log lines a virtual computer keeps before discarding the oldest.
MaxLuaInstructionsPerExecution200,000Lua instructions one execution may use before the process is killed. This is what stops while true do end from blocking the server.
LuaInstructionSlice5,000How many instructions run between yields. The interpreter hands control back this often, so the figure is the granularity of the budget rather than a limit in itself: smaller checks more often and costs more.
MaxLuaProcessesPerComputer8How many processes one machine may run at once. Starting another is refused.
MaxScriptExecutionsPerTick64How many script executions one tick may run before the rest wait. Deferred, not lost: the rest run on the next tick.
MaxScriptSizeBytes65,536Largest script that may be loaded, in bytes. A larger one is refused, not cut short.
MaxScheduledEventsPerScript32How many callbacks one script may have waiting. Scheduling another is refused.
MaxQueryResults64Rows a single world query may return. Clamped silently: a query asking for more gets this many.
MaxAgentActionsPerThought8How many actions one thought may ask for. A brain that returns a hundred requests gets the first few carried out and can ask again next time — the world is not obliged to do everything an AI imagines at once.
MinAgentThinkInterval1 sThe shortest interval an agent may be rescheduled at. A brain that asks to think again sooner is held to this, so that no brain can become the per-tick loop this world never runs.
MaxDesignSpeedMetersPerSecond8Fastest a design may claim to move, in metres a second.
MinDesignSpeedMetersPerSecond0.05Slowest a design may claim to move. A floor, not a courtesy: travel time is distance over speed, and a speed near zero turns the arrival the scheduler has to predict into a TimeSpan that does not exist.
MaxDesignSensorRangeMeters128How far a design may claim to see, in metres.
MaxDesignInventorySlots64How many kinds of item a design may claim to hold.
MaxDesignItemQuantity100How much of any one item a design may arrive holding.
MaxDesignStockQuantity500How much of everything together a design may arrive holding.
MaxWorldItemQuantity10,000How many of any one item name may exist in this world at once.
MaxDesignEnergyCapacityWh100,000Largest battery a design may claim, in watt-hours.
MaxDesignPowerW5,000Most power a design may claim to draw or to make, in watts.
MinDesignPowerW0.01Least power a design may claim to draw or make, other than none at all. The same floor as the speed, for the same reason: the moment a battery runs flat is capacity over draw, and a draw near zero is a moment no clock can hold.
MaxDesignCpuInstructionsPerSecond1,000,000Fastest processor a design may claim, in instructions a second.
MaxDesignMemoryBytes67,108,864Most memory a design may claim, in bytes.
MaxDesignStorageBytes16,777,216 (derived, not configurable)Most storage a design may claim, in bytes — and the only figure on a computer that is really spent, so it is derived rather than chosen. A filesystem cannot hold more than its largest file times its file count, and a design claiming more would be claiming space that does not exist.
MaxDesignGravityMetersPerSecondSquared30Strongest gravity a design may claim, in metres a second squared.
MinDesignGravityMetersPerSecondSquared0.1Weakest gravity a design may claim. A falling thing's landing is predicted from it, so gravity near zero is a fall that never lands.
MaxDesignInteractionRangeMeters8How far away a design may claim to be usable from, and how far anything is usable from at all, in metres.
MaxTransferReachMeters8How far apart two things may be and still pass goods, in metres.
MaxPromptQuestionLength160Longest a question, or an answer typed into one, may be. Refused, not cut short.
MaxPromptOptions8How many answers one question may offer. More is refused.
MaxPromptOptionLength48Longest the wording on one answer, or on one field, may be.
MaxPromptFields4How many values one question may collect beside the choice.
MaxPendingPrompts8How many questions one person may have waiting at once. The next is refused until one is answered or runs out.
PromptLifetime120 sHow long a question stands, and how long having dealt with something leaves it able to ask.
MaxPromptReachMeters8How near you have to be standing for something to put a question to you, in metres.
AllowTeleportTrueWhether anything in this world may be put somewhere without walking there.

An operator sets these under Limits in the server's configuration, by the same names, except that the two spans — the shortest think interval and the prompt lifetime — are given there in seconds. Every answer that hits one names the limit it hit, by the name above.

The prompt figures bound text that came out of a Lua script, a language model or somebody else's design and is on its way to a person's screen. A question is also cleaned exactly as a chat line is — formatting characters dropped, control characters and whitespace collapsed — and is refused outright if it contains < or >. There is deliberately no token bucket on questions: what makes a dialogue unwelcome is not having asked for it, and the rule that you can only be asked by something you have just used or just answered bounds that where a rate could not.

Chat limits#

A token bucket per account, measured against the wall clock.

KeyDefaultCeilingNote
MaxChatMessageLength240 characters, after cleaningHard-refused above 4096 at startupLonger than a socket frame could carry is not a limit. The same for people and agents: a machine has no more right to a long speech than a person
ChatMessagesPerMinute30The sustained rate one account may speak at
ChatBurst5Messages back to back before that rate applies
AgentChatMessagesPerMinute6May not exceed ChatMessagesPerMinuteA fifth of a person's. An agent never gets bored, and the public channel is shared
AgentChatBurst2May not exceed ChatBurst

Configuring agents to out-talk people is refused at startup rather than discovered when the public channel fills.

There is deliberately no cap on how many people hear a message. Nobody can address a list — public is "everybody the server chose", direct names exactly one participant — so a recipient cap would be a cap on how many people may stand in a town square together.

Account API limits#

Two more token buckets, keyed on the calling address rather than on an account — because the routes they guard are where an account comes from, and a caller there has nothing else to be known by.

KeyDefaultCountedAt the edge
RegistrationsPerMinute2Every POST /api/register, including a malformed one429 with error: too many accounts from your address, wait a minute
RegistrationBurst5Registrations back to back before that rate applies
FailedSignInsPerMinute5Only a refused POST /api/session, /api/signin, /api/passphrase, /api/devices, /api/devices/revoke, /api/devices/code or /api/devices/claim. One that succeeds spends nothing429 with ok: false and the same sentence. While the bucket is empty a correct token is refused too
FailedSignInBurst10Wrong credentials back to back before that rate applies

Every field must be greater than zero, and the per-minute rates are whole numbers, which is what makes the refusal's "wait a minute" true whatever they are set to.

One more bucket has no setting, because its size is part of why an eight-digit code is safe and an operator lowering it would not see that argument: the whole world together may get 30 device codes wrong a minute, with 60 back to back, after which /api/devices/claim refuses everybody for up to a minute. A per-address limit cannot see a thousand machines guessing slowly; this can. Nothing else is affected — playing, signing in with a token and signing in with a passphrase all carry on — and no code is destroyed by it.

A malformed registration is counted, because otherwise the cheapest way past the limit would be to send rubbish. A refused sign-in is counted whether the token was wrong or absent, because a guesser learns the same thing either way.

An address is not a person: everybody behind one NAT shares a bucket, and anybody with a VPN or a cloud account has as many as they care to pay for. This is a speed bump against casual abuse and not protection against a distributed attacker. X-Forwarded-For is not read — it is a string the caller writes — and IPv6 is keyed on the /64. The WebSocket hello spends the same two buckets: a hello that registers an account is a registration and one that is refused is a failed sign-in, so the socket is not a second, unmetered door.

World settings#

Chosen by the world's operator when the world is made or the server started. These are the shipped defaults, and the ones a player or a client can notice; world_bootstrap and GET /world report the live values of the first five.

SettingDefaultWhat it does
PlanetNameArtificial WorldName given to a world when it is created. Non-blank; ≤ 64 characters
Seed20260909Seed for a new world. Ignored once one exists — the stored seed wins, because changing it would regenerate a different planet under the existing changes
PlanetRadiusMeters1000000How far from the origin the planet extends
RegionSizeMeters1024The unit of region activity
ChunkSizeMeters128The unit of streaming, claiming and spatial indexing
SnapshotIntervalSeconds15How often live state is written to storage. Durable actions — a claim, a saved design, a file write, a felled tree — do not wait for it
StartSpeed1One of 0, 1, 2, 5, 10, 50
MaxSubscribedChunksPerClient512Chunks one client may watch at once. Silently dropped past the cap, not refused
RequirePlayerTokenfalseWhether a hello naming an account must present its token. True also means the account must already exist, so registering happens over /api/register and not on the socket. Anonymous sessions are unaffected
SeedDemoAgenttrueWhether a new world gets a demo agent
DemoAgentNameSurveyorWhat that agent is called

The grid sizes are properties of a world in the making: changing them for a world that already exists changes which chunk everything falls in.

MCP options#

SettingDefaultWhat it does
EnabledtrueWhether the endpoint exists at all
ReadOnlyfalseWhen true, the acting tools are not offered and are refused if called anyway
AllowedOriginsemptyOrigins a browser may make MCP requests from. Empty refuses every request carrying an Origin header, which is what stops a page you happen to open from reaching a world only you can reach
MaxSessions32Sessions at once, evicted least-recently-used
SessionIdleMinutes60How long a session, and your presence in the world, survive without a request

Transport and request caps#

CapValueAt the edge
WebSocket message256 KiBMessage too large., and the read loop stops
MCP request body256 KiBJSON-RPC parse error (−32700), HTTP 400
MCP tool argument: any string≤ 4096 charactersRefused, before any tighter per-tool limit
MCP tool argument: any array≤ 64 itemsRefused
MCP unknown argument namesRefused: every tool schema sets additionalProperties: false
GET /events?limit=Default 50, clamped to 1–500Clamped silently, on the page and in the figures alike
GET /designs?page=Default 1, clamped to 1–pagesClamped silently
Concurrent connectionsNo application capBounded only by the host

What a design may contain#

KeyDefaultWhat it bounds
MaxParts512How many shapes one design may be made of.
MaxPartDimensionMeters128Largest any single part may be along any one axis, in metres.
MaxReachMeters64How far from the design's own origin a placed part may reach sideways, in metres.
MaxHeightMeters1,024How high above its own origin a placed part may reach, in metres.
MaxDepthBelowMeters64How far below its own origin a placed part may reach, in metres.
MaxDepth8How deep the parent chain may go.
MinPartDimensionMeters0.01Smallest any single part may be along any one axis, in metres. A plane is the one shape allowed to be thinner.
MaxDiameterMeters128 (derived, not configurable)The widest a design can be, corner to corner across its footprint: the bound every horizontal search has to add to its own radius.

And the counts and lengths fixed in the design's own shape:

ConstantValueWhat it bounds
BlueprintDefinition.MaxNameLength64Longest a design's name may be.
BlueprintDefinition.MaxScripts8How many scripts one design may carry.
BlueprintDefinition.MaxIncludes16How many other designs one design may include.
BlueprintDefinition.MaxIncludeOffsetMeters256How far an included design may be placed from the one including it.
ProceduralAssetDefinition.MaxNameLength64Longest a model's name may be.
AssetPart.MaxSemanticTagLength32Longest a part's semantic tag may be.
AssetPart.MaxSurfaceTiling64The most times a pattern may repeat across a metre.
AssetPart.MinSurfaceTiling0.01The fewest times a pattern may repeat across a metre.
AssetPart.MaxPathPoints64The most points a path may have.
BlueprintPart.MaxTagLength32Longest an included design's tag may be.
SvgMaterial.MaxNameLength48Longest a material's name may be.
SvgMaterial.MaxPerDesign8The most materials one design may carry.
PartStateComponent.MaxParts128How many parts of one thing may carry state.

A validator reports every problem, not the first, because the author may be a language model that needs the whole list to correct itself. Nothing throws; refusals are values.

The three extent bounds are deliberately not one number, and they are checked against where a part ends up — parents, turns and ribbon paths applied — rather than against the offset it was written with. Sideways is half a chunk, because every index in this world is a grid in X and Z and widening a design widens every search that runs on every step a walker takes; upwards is a kibibyte of metres, because no index has a Y axis and the only real bound on height is what the client can draw. Something wider than one chunk is built as a composite of designs, each piece an entity filed in its own chunk.

Other rules a design must satisfy: a name; at least one part; unique part ids; a parent that exists and is not the part itself; no cycles; finite position, rotation and scale; a known primitive and material; a Surface naming a material the design itself carries; and Path and Draped only on a Ribbon, whose consecutive path points must be at least 0.01 m apart — two coincident points have no direction, and a client normalising one would divide by zero. An included design needs a blueprint id, a finite offset and rotation, and a tag of at most 32 characters.

Colour#

A part may name a colour of its own instead of taking its material's. Three numbers cannot reference a file, a shader or an asset, so there is nothing to ration here — the closed list bought no safety, and a world where anything can be made should not ration its colours.

FieldRangeAt the edge
Red, Green, Blue, Alpha0.0–1.0, finitecolour <c> has a channel outside 0..1 or is not finite

Alpha defaults to 1.0. Values are linear, not sRGB, and unpremultiplied, so a design copying a named material's numbers gets that colour exactly.

Written by hand, a colour may be given as hex: #rgb, #rrggbb or #rrggbbaa, with or without the #. Any other length, or any non-hex character, parses as nothing rather than as a guess — red is three characters and would otherwise have been read as shorthand hex.

Surface materials#

A part may be painted with a small SVG drawing carried by its own design. The drawing is multiplied over the part's colour, so a white part shows it exactly and a coloured part shows it tinted.

FieldWhereDefaultAt the edge
MaterialsProceduralAssetDefinition[]N materials exceeds the limit of 8
Materials[].IdDerived, not chosen. the material's id is not the digest of its svg; it is derived, not chosen
Materials[].Namea material needs a name; over 48 characters is refused
Materials[].SvgSee the allowlist below
SurfaceAssetPartnullnames the surface '<id>', which this design does not carry
SurfaceTilingAssetPart1.0Outside 0.01 – 64, or not finite: tiles its surface <n> times a metre. Ignored entirely on a part with no Surface

Two materials with the same Id in one design is is carried twice.

The identity is a digest of the drawing's exact bytes, with no normalisation: the same drawing written by two people is one material, and whitespace that differs makes a different one. Callers do not compute it. A placeholder Id, used consistently by the material and by the parts that name it, is rewritten to the real digest before validation, by the one binding step both the socket and MCP go through.

What a drawing may contain#

KeyDefaultWhat it bounds
MaxBytes16,384The most UTF-8 bytes one drawing may be.
MaxElements512The most elements one drawing may contain.
MaxDepth12How deeply elements may nest.
MaxAttributesPerElement24The most attributes one element may carry.
MaxAttributeLength4,096The most characters one attribute value may be.

SvgMaterial.MaxPerDesign, above, bounds how many of them one design may carry. Each refusal names the element, the attribute and the figure; the byte limit is checked before parsing.

The allowlist#

An allowlist, not a blocklist. A blocklist has to be right about every dangerous construct forever; an allowlist has to be right about the forty that draw a pattern, and everything invented after this was written is refused by default.

Elements. svg, g, defs, title, desc, rect, circle, ellipse, line, polyline, polygon, path, linearGradient, radialGradient, stop, pattern. Anything else: '<name>' is not an allowed element.

Deliberately absent, and why: script and handler execute; foreignObject embeds arbitrary HTML; image and use exist to reference something else, which is a fetch; style carries CSS and with it @import; text needs a font, and naming a font is naming something to load; filter, mask, clipPath and the animation elements are expensive, stateful, or both.

Attributes. id, viewBox, preserveAspectRatio, version, width, height, x, y, x1, y1, x2, y2, cx, cy, r, rx, ry, fx, fy, fr, d, points, transform, fill, fill-opacity, fill-rule, opacity, stroke, stroke-width, stroke-opacity, stroke-linecap, stroke-linejoin, stroke-miterlimit, stroke-dasharray, stroke-dashoffset, shape-rendering, vector-effect, offset, stop-color, stop-opacity, gradientUnits, gradientTransform, spreadMethod, patternUnits, patternContentUnits, patternTransform. Anything else — every on* handler, style, class, href — is '<element>' carries '<name>', which is not an allowed attribute.

Attribute values. None may contain javascript:, data:, http://, https://, //, &#, < or expression(. url(...) may point only at a fragment of the same drawing: url(#name), where the name is letters, digits, _ and -. Anything else is referencing something outside the drawing.

Namespaces. SVG's, or none at all — a drawing written by hand without an xmlns is accepted, because that is the obvious thing to write and it is handed to the browser as SVG regardless. A prefixed namespace declaration (xmlns:xlink) is refused, which is how the old spelling of an external reference goes.

The document. The root must be svg and must carry a viewBox, so the drawing's own coordinates mean the same thing in every renderer. No DOCTYPE, no entity declaration or reference, no processing instruction (<?xml-stylesheet?> is a stylesheet reference with a different syntax), and no text content. Document type definitions are prohibited outright and nothing outside the drawing is ever resolved — which is the whole of the billion-laughs and external-entity defence, rather than a count of expansions.

A drawing that is not XML at all is the drawing is not readable as xml: ..., with the parser's own sentence, not an exception.

Where a pattern cannot be drawn#

/designs previews, the parts table and the PNG renders are flat shading and cannot show a pattern. They show the drawing's swatch instead: the average of its fills and stops, weighted by area where the shape says its area (rect, circle, ellipse) and counted once where it does not, with strokes at 15% of their weight. Read out of the text, never rasterised — the server does not rasterise anything a caller sent it — and the untrusted SVG is never embedded in a page the server serves.

Character proportions#

The shipped Settler, and every figure the world generates for itself, is built from six proportions rather than from measurements. Nothing you send names them — a character you design is parts like any other design — but they are why a generated person always stands up. Every field is clamped, never refused. A figure that asks for a head four times the body gets the widest head this allows and a character that still stands up; refusing outright would make one bad number lose the whole design. A value that is not a number is replaced with the default rather than clamped, because NaN has no nearest bound.

FieldDefaultRange
HeightMeters1.80.6 – 3.0
LegFraction0.480.30 – 0.60
HeadFraction0.130.08 – 0.25
ShoulderFraction0.260.14 – 0.45
ArmFraction0.360.20 – 0.50
Build1.00.5 – 1.5

Clamping is idempotent: clamping a clamped set changes nothing.

Design review thresholds#

The review has no authority and refuses nothing — it is advice, and every check exists because that fault has actually happened here. These are the numbers behind the findings listed in BLUEPRINTS.md.

CodeSeverityThreshold
vanishingFaultThinner than 0.001 m in some direction, and not a Plane
enormousNoteMore than 500 m across
crackFaultA gap over 0.002 m and up to 0.06 m, where the two parts present at least 0.05 m² of face to each other and no third part bridges the space
buriedNoteEntirely inside another part, within 0.002 m
detachedFaultA cluster more than 0.01 m from the body of the design. Touching is transitive, so a chimney on a roof on a wall is one thing
protrudesFaultComing up more than 0.002 m through a part that overhangs it by at least 0.02 m
headroomFaultA part tagged door or gate shorter than 1.9 m, which is how tall a person is
hoversNoteThe lowest part more than 0.25 m above the design's own origin
limb_missingNoteOne of the seven required character tags absent from something that is otherwise a character

The 0.05 m² face requirement is why a settler's arm hanging beside its hip is not reported as a crack: two road slabs that fail to meet share half a square metre, an arm beside a hip a fortieth of one, and a page that cries wolf about eleven parts of a character is a page nobody reads.

Agent memory and history#

LimitValueAt the edge
Memory keys64Silently ignored. The write does not happen and nothing is said, because the writer may be a language model
Memory key length64Silently ignored
Memory value length4096Silently ignored
Goals per agent16The action is refused with a reason
Memory entries shown to another agent12, each ≤ 160 charactersTruncated for display
Last actions kept6The oldest are dropped
Inbox depth32Further messages refused
Agent search radius≤ 8192 mClamped

Writing a key with the value false removes it. Two key names are conventions the shipped brains agree on rather than rules: centre for a work site, and step for where a builder has got to.

Message and action vocabularies#

Client to server#

Discriminator property: "type". An unrecognised value is not ignored — it fails deserialisation, and the server answers error with Malformed message.

typeRequired fieldsOptional fields
helloProtocolVersionToken, ClientName (default "unknown"), LoginName, DisplayName, ResumeKey
chunk_subscribeChunks
chunk_unsubscribeChunks
action_requestRequestId, Action
simulation_controlSpeed
metrics_request
blueprint_requestBlueprint
blueprint_list_request
player_list_request
agent_list_request
chat_sendTextScope (default Public), To

ProtocolVersion is 1. It is not a setting: a client either speaks it or is told so.

Omitting LoginName gives an anonymous session, which can watch the world, read designs and inspect things, and owns nothing. Anonymous sessions act as the world itself, which is why they cannot claim, build or design.

Actions inside action_request#

Discriminator property: "action", nested inside the Action object. Also fails deserialisation on an unknown value.

actionRequired fieldsOptional fields
move_entityEntity, Destination
remove_naturalEntity
demolish_entityEntityWithContents (default false) — destroys what it holds with it, and only its owner may
inspect_entityEntity
claim_landChunks
release_landChunks
program_entityEntity, ScriptPath
spawn_blueprintBlueprint, Position
save_blueprintName, EntityType, VisualComponents (allowlisted and bounded — see above), Scripts, Includes
set_accessTo, PermissionsGranting (default true), Entity, At
set_public_accessPermissionsEntity, At
request_accessPermissionsNote, Entity, At
create_agentName, Brain, GoalsAt, Script
dismiss_agentAgent
use_entityEntity
transfer_itemsFrom, To, Item, Quantity
exchange_itemsA, B, Give, GiveQuantity, Take, TakeQuantity
start_scriptEntity, ScriptPath
stop_scriptEntity, Pid

For the three access actions, naming Entity makes it about a thing and omitting it makes it about the claim covering At.

Server to client#

Discriminator property: "type".

typeCarries
world_bootstrapThe planet, its grid sizes, sea level, the simulation state, the spawn position, and Player (null when anonymous)
chunk_snapshotChunk, DominantBiome, Heightmap, Entities
entity_deltaChunk, Updated, Removed
action_resultRequestId, Success, Error, Entity
simulation_stateSimulation
server_metricsMetrics
blueprintOne BlueprintDefinition
blueprint_listSummaries: Id, Name, Version, Creator, PartCount
player_listClientId, LoginName, ClientName, Position, SubscribedChunks, IsYou
agent_listPer agent: Id, Name, Brain, State, ThoughtCount, NextThinkTimeTicks, Body, Home, Position, Destination, Goals, Memory, Reasoning, LastActions
chatScope, From, FromName, Text, At, To, ToName
errorMessage, Detail

PlayerSnapshot.Token is the one field omitted from JSON when null, and it is sent exactly once — at registration.

HTTP endpoints#

MethodRouteQueryAnswers
GET/healthstatus, service, version, commit, build, utc
GET/ready{"status":"ready"}, or 503 {"status":"starting"} until the world is loaded
GET/metricsformat=json|htmlThe metrics snapshot
GET/worldformat=json|htmlPlanet, seed, generator version, grid sizes, clock and speed
GET/eventslimit (default 50, clamped 1–500), format=json|htmlcount, and events newest first
GET/regionsformat=json|htmlA page of every region anybody has used. Asked for figures, it answers 302 to /api/regions
GET/api/regionslimit (default and maximum 500, clamped 1–500)regionSizeMeters, count, total, truncated, the gate the answer cost, and a row per region that has something in it. Empty wilderness is not listed
GET/designspage (default 1, clamped)The design gallery
GET/designs/{id}One design
GET/designs/{id}.jsonThe machine-readable design document, camelCase
GET/designs/{id}/{view}.pngsize=thumb for the small oneA drawing
GET/designs/{id}/visual.jsonThe design as geometry, with its surface drawings. PascalCase, being the socket's own shape
POST/designs/previewview=iso|front|left|right|top, views=all or a list, size=thumbA design drawn without being saved. Needs X-World-Player and X-World-Token
GET/docs/{slug}One of these documents
GET/registerRegistering a person or a model
GET/accountThe devices that can act as you, your passphrase, and a code for adding one
GET/account.jsThat page's behaviour
GET/playThe browser client
GET/play/{asset}One of the client's assets. A fixed list; anything else is 404
POST/api/registerloginName, optional displayName, kind and passphrase. 201 with the account and its token, 400 on a bad name, kind or passphrase, 409 on a name already registered
POST/api/sessionloginName, token. 200 {ok, playerId, displayName, passphraseSet}, or 401 {ok, error} for an unknown name, an invalid name or a wrong token alike
POST/api/signinloginName, passphrase, optional deviceName. 200 with a token minted for this device, or 401
POST/api/passphraseloginName, and either token or currentPassphrase, plus passphrase. 200, 400 with the rule, or 401
POST/api/devicesloginName, token. 200 {ok, loginName, passphraseSet, devices}, or 401
POST/api/devices/revokeloginName, token, deviceId. 200, 404 for a device this account does not have, 409 for the last one when there is no passphrase, or 401
POST/api/devices/codeloginName, token. 200 {ok, code, expiresAt, expiresInSeconds}, 503 when the world is holding too many, or 401
POST/api/devices/claimcode, optional deviceName. 200 with a token minted for this device, or 401
GET/api/onlineplayers (display name, login name, anonymous, position, region, connected since) and agents (name, owner, state, position)
GET/api/spawnx, y, z and why: where a newcomer should start, and the reason
/wsThe WebSocket. Anything but an upgrade gets 400
POST/mcpJSON-RPC 2.0. GET and DELETE are 405

POST /designs/preview takes a JSON body of exactly the shape world_create_design takes — name, entityType, parts and materials — read by the same reader and refused in the same words, with the same 256 KiB body limit /mcp has. It saves nothing and never touches the world. With Accept: image/png, or with a view in the query string, the answer is a single PNG at 460 pixels (size=thumb gives 190); otherwise it is JSON carrying the design review and the drawn views, each as a data:image/png;base64,… URI. An unusable design is 400 with {"refused": "…"}; a body over the limit is 413.

It asks who is asking. Send X-World-Player and X-World-Token — the login name and token you were given when you registered, the same two headers the MCP tools take — or the answer is 401. Drawing a design is the most expensive thing this server will do for one request, so it is not done for a stranger, and it is rationed per address: too many and the answer is 429.

One view unless you ask for more. The JSON answer draws the isometric alone; ?views=all gives the set, and ?views=front,top gives exactly those. The answer names what it drew in drawnViews and what else there is in availableViews, so a caller that wanted the rest can ask for it.

MCP tools#

Every tool additionally accepts a session string (≤ 256 characters), and every tool's schema sets additionalProperties: false.

ToolRequiredOptional
world_whoami
world_overview
world_lookx, zy (omit to stand on the ground), radiusMeters (1–256, default 64, clamped), type (any valid EntityType, not a fixed list), limit (1–100, default 25, clamped)
world_routefromX, fromZ, toX, toZfromY (omit to start on the highest surface over the first point), stepMeters (0.25–16, default 1, clamped), climbMeters (0.05–4, default 0.5, clamped), headroomMeters (0.5–8, default 1.9, clamped). At most 2000 steps and 96 chunks; longer is refused
world_inspectentityId
world_agentslimit (1–200)
world_agentagentId
world_historylimit (1–200, default 25), kind — a WorldEventKind name, matched case-insensitively
world_designssearch, limit (1–100)
world_designdesignId
world_preview_designone of designId or partsname (≤ 64) and entityType (≤ 32), required with parts. See the part shape below
world_claimsmine, limit (1–100)
world_machineentityIdlogLines (1–100, default 20, clamped). Needs Program on that machine

world_preview_design is the only tool that answers with more than text. Its result carries the front and top views as image content blocks{"type": "image", "data": "<base64>", "mimeType": "image/png"} — after the text block, in both protocol revisions, which spell a content block the same way. They are drawn 256 pixels square rather than the gallery's 460, because an MCP result travels through a model's context: two views of a seven-part house encode to about 4.5 KB of base64. Giving both designId and parts is refused, and so is giving neither. Nothing is saved by a preview.

The acting tools below are offered only when Mcp:ReadOnly is false, and are refused if called anyway.

ToolRequiredOptional
world_joinloginNamedisplayName, token
world_take_bodyx, z
world_walkentityId, x, z
world_useentityId
world_take_itemsfromEntityId, toEntityId, item, quantity (1–1,000,000)
world_clear_naturalentityId
world_demolishentityIdwithContents — destroys what it holds with it, and only its owner may say so
world_claim_landchunks (each "x,z", ≤ 64 of them)publicUse
world_release_landchunks (each "x,z", ≤ 64 of them)
world_shape_groundx, z, halfSize (0.5–64 m), and exactly one of height / deltaskirt (0–32 m, default 5; 0 leaves a face rather than a ramp). One cut moves the ground 32 m at most, and a chunk carries 64 pads
world_release_groundx, zhalfSize (0.5–64 m, default 8) — every pad whose middle is inside that square is forgotten
world_create_designname (≤ 64), entityType (≤ 32), parts (1–512)See the part shape below
world_build_designdesignId, x, zy, onGround (default true)
world_sharepermissions (≤ 8 words), and one of entityId / x+zto (a login name or an owner id), anyone, revoke
world_ask_accesspermissions (≤ 8 words), and one of entityId / x+znote (≤ 160)
world_create_agentname, goals (≤ 16), and one of brain / scriptwhichever of brain (≤ 64) / script (≤ MaxScriptSizeBytes) it did not use, x, z
world_dismiss_agentagentId
world_build_townx, zname (default Planner)
world_saytext (≤ MaxChatMessageLength, 240)to (≤ 64) — a login name, an agent name, or an identity. Omit for public; local has no meaning here
world_listenlimit (1–100, default 100, clamped)
world_write_scriptentityId, source (≤ MaxScriptSizeBytes)path (≤ 128, default /main.lua)
world_run_scriptentityIdpath (≤ 128, default /main.lua), logLines (1–100, default 20)
world_stop_scriptentityId, pid

world_say and world_listen are refused for an anonymous connection: a line has to come from somebody the world can hold to it. Chat is rate limited per identity at the player rate, not the tighter agent one — an MCP caller is a player. An inbox holds at most 100 unread lines and drops the oldest beyond that, reporting the count as missed; reading consumes what it returns.

The three programming tools and world_machine each need Permission.Program on the named entity, judged by PermissionService exactly as program_entity, start_script and stop_script are over the socket. A written file meets VirtualPath parsing and VirtualFileSystem's MaxVirtualFileSize, MaxVirtualFilesPerComputer and capacity limits; a started script meets MaxLuaInstructionsPerExecution and MaxLuaProcessesPerComputer. Every answer reports those five figures back.

MCP callers identify themselves with the X-World-Player and X-World-Token headers, or by calling world_join. The protocol versions offered are 2026-07-28 (the latest), 2025-11-25 and 2025-06-18.

world_create_agent takes a brain in one of two forms and they are not interchangeable. brain is the name of one this server already carries — city, lua, dummy, as world_overview lists them. script is the Lua source of one the caller wrote, so a new kind of agent is a file rather than a feature. Sending a script makes the brain lua whatever brain said; sending neither is refused with a sentence naming both.

A script is measured, compiled and refused before the agent exists: over MaxScriptSizeBytes in UTF-8 bytes, or Lua that will not load, and there is no agent afterwards. What survives goes to /home/agent/brain.lua on the agent's own machine — an ordinary file, saved to the database with the machine, readable and replaceable afterwards with world_machine and world_write_script — and the answer reports that path back as brainScript. It then runs where every other script in this world runs: LuaSandbox, MaxLuaInstructionsPerExecution, and no second door into the scripting host.

A part in world_create_design#

The same shape is taken by world_preview_design and by POST /designs/preview, read by one reader for all three, so a part that previews is a part that saves.

This is the third spelling of the shape and material vocabularies — after the wire's exact enum names and the agent brain's forgiving one — and the one written for a model rather than for a compiler. Underscores are stripped and the rest is matched case-insensitively, so beveled_cube, BeveledCube and beveledcube are the same shape, and forest_grass is ForestGrass.

FieldRequiredRule
idYesThe PartId rule: lowercase letters, digits and underscore, ≤ 32, starting with a letter
shapeYesA PrimitiveShape, underscores optional. A name that is not one refuses the whole design, naming the part and listing the eight
parentNoAnother part's id
x, y, zNoOffset from the parent's pivot, in metres, in the parent's own frame — so a turned parent swings the child round with it, while its size leaves the child alone. 0 each
width, height, depthNoSize in metres, 0.01–128 — the same limits every design is held to. 1 each. Note the names: this tool spells the scale out rather than calling it a scale, because it is a size and not a multiplier
yaw, pitch, rollNoHow the part is turned, in degrees, −360 to 360. 0 each. yaw is about the up axis, pitch about east, roll about north; they are applied roll, then pitch, then yaw. A part is stored as a quaternion and always has been, and this is the door in: nobody writes one by hand, so this tool takes the three angles and converts them. An angle outside the bounds refuses the design naming the part, the figure and the bound
materialNoAn AssetMaterial, underscores optional. Defaults to White; an unrecognised one refuses the design
colourNo#rgb, #rrggbb or #rrggbbaa, ≤ 12 characters. Refuses the design if it is not one
surfaceNoThe name of one of this design's materials. A name it does not carry refuses the design and lists the ones it does
tilingNoRepeats of the surface across a metre, 0.01–64. 1.0
solidNofalse. Only literal true counts
pathNoFor a ribbon only: the line it runs along, as [{ "x": …, "z": … }] in metres in the part's own frame. Two to 64 points, consecutive ones at least 0.01 m apart, each within 64 m of the design's origin. Left out, a ribbon is a straight run of depth metres along north. A path on anything else, or one that is not a list of points, refuses the design
drapedNofalse. For a ribbon only: whether it lies on the ground it crosses, taking its heights from the terrain under its own points rather than holding the shape the design gave it. A design with anything draped in it is neither levelled for nor tilted to the ground
tagNoThe semantic tag, ≤ 32

A field that is not one of these refuses the design, naming the part, the field and the ones it could have been. The same holds for capability and for the entries of materials, scripts, capability.stock and capability.shows: the schema these are published with says no other fields are accepted, and accepting one in silence would mean a design that quietly does less than it was written to do.

world_create_design, world_preview_design and POST /designs/preview also take materials: at most eight { name, svg } objects. The name is what a part's surface refers to; the identity a material really has is a digest of its own bytes, minted by the server, because asking a caller to hash its own SVG would be asking for a way to get it wrong. Two materials under one name refuses the design. The drawings themselves go to the same allowlist described under Surface materials, through the same validator a save runs — so a design that previews is a design that will save.

Refusing outright is the opposite of what the agent-brain reader does with the same two vocabularies, and deliberately so: a tool call can say what was wrong and be called again in the same breath, where an agent's thought cannot.

entityType must satisfy the EntityType rule and is otherwise open — house, lamp, bench and statue are as valid as the seven the world ships with.

Every MCP string argument is trimmed, and a string that is empty after trimming counts as absent rather than as an error — so a required argument given as " " is reported as missing. A control character other than newline or tab is refused outright. Where a tool takes an enum name, it is matched case-insensitively on purpose, so a model does not have to guess the casing.

What an agent brain may write#

A brain returns one table. Everything in it is treated as hostile: an action the reader does not recognise is dropped, a field of the wrong type is ignored, and nothing throws — a brain that returns nonsense gets a decision that does nothing, not a broken server.

FieldTypeDefault
actionsA list of action tables, applied in orderNone
thinkAgainInSeconds until the next thought, a positive numberOmit it to go dormant
rememberA map of key to string. A value of false forgets the keyNothing written
completedA list of goal names now finishedNothing completed
reasoningA line for the log, never parsedNothing

Two caps apply in sequence, and they are different numbers. The reader keeps at most 64 actions from the returned table; the runtime then carries out the first MaxAgentActionsPerThought of those, eight by default. Everything past either line is dropped silently — not refused, and not remembered for next time. world.maxActions tells a brain the live value of the second one, so a well-behaved script can size its own list to match.

The fifteen action kinds#

The whole vocabulary an agent has. It is closed on purpose: a brain that may one day be a language model must not be able to name an operation nobody wrote.

The kind string is matched case-sensitively. "Move_Entity" is not move_entity; it matches nothing and the action is dropped.

kindRequiredOptional, with defaults
observe_worldradius (64), type
inspect_entityentity
claim_landa positionpublicUse (false)
release_landa position
create_blueprintname, partsSee the design sub-schema below
spawn_blueprintblueprint, a position
write_filecomputerpath (""), contents ("")
start_scriptcomputerpath ("")
stop_scriptcomputerpid (0)
move_entityentity, a position
take_bodyblueprint, or design as an alias. Omit both for the shipped character
set_accesstopermissions, revoke (false), entity, a position
set_public_accesspermissions, entity, a position
request_accesspermissions, note, entity, a position
send_messagetargetmessage ("")
waitreason

An action whose required field is missing or unparsable is dropped whole.

Positions may be written either as at = { x, y, z } or as x, y, z on the action table itself. Both x and z are needed for a position to resolve at all; y defaults to 0. For spawn_blueprint, leaving the height out is meaningful: no height named means "stand it on the ground", and naming one means exactly that height.

For the three access actions, the subject is inferred rather than named: entity present makes it about a thing, and its absence makes it about the claim covering the position.

The design a create_blueprint describes#

FieldRequiredDefault and behaviour at the edge
nameYesNon-blank
partsYesNon-empty after unusable parts are dropped
entityTypeNo"item". Must satisfy the EntityType rule
keyNoThe name. Used to derive a deterministic id, so re-running a brain does not accumulate designs
materialsNoEmpty. A list of { name, svg } tables, at most eight. The name is what a part's surface refers to; the identity is minted from the drawing's own bytes, the same way the other two doors mint it
components, scripts, includesNoEmpty. components is held to the allowlist, so a design that asks for ownership or a walk speed of 10,000 is refused rather than saved

Per part:

FieldRequiredDefault and behaviour at the edge
idYesMust satisfy the PartId rule, or the part is dropped
primitiveNoCube. Matched case-insensitively against PrimitiveShape; an unrecognised name falls back to Cube rather than failing the part
materialNoWhite. Same rule, falling back to White
parentNoNone, making it a root part
position, scaleNo{0,0,0} and {1,1,1}
colour or colorNoNone. A hex string, or {r,g,b,a} / {red,green,blue,alpha}
tagNoNone. Becomes the semantic tag
solidNofalse
surfaceNoNone. The name of one of this design's materials. A name the design does not carry — or one two drawings share, which resolves to neither — leaves the part naming a surface that is not there, and the validator refuses the design saying which part asked
tilingNo1.0. Repeats of the surface across a metre, 0.01–64. Ignored on a part with no surface
path, drapedNoNone and false. Ribbons only, at most 64 points

The silent fallbacks are deliberate: a design with one misspelled material is worth building with the wrong colour, and refusing the whole thing would make one bad word lose the design. The validator still has the final say on everything else.

A surface is the one field a part refuses the design over rather than falling back, and deliberately: a fallback would be a wall silently painted with nothing, which looks like a design that worked. The drawing itself is never checked here — it reaches the catalogue exactly as the script wrote it and is held to the allowlist, the size bounds and the element bounds by the same validator a browser's and a model's design meet, in the same words.

What a brain reads#

The world table passed to think(world) is built fresh each thought, from what the world knew at that instant. It is not the world global a computer script sees — a brain has no globals at all.

FieldWhat it is
nowSimulated seconds
triggerAn AgentTrigger name, PascalCase: Scheduled, Created, …
position, chunk{x, y, z}, and the chunk's name
chunkIsClaimed, chunkIsMine, ownedClaimsThe ground under it, and how many chunks it holds
name, home, thoughtsIts name, its machine's entity id, its thought count
body{id, design, position, speed, walking, destination}, or nil while it has none. destination is present only while walking
memoryIts durable store, as a table of strings
goalsA list of {name, done}
nearby, ownedLists of {id, type, owner, mine, distance, position}
agentsPeers: {id, name, owner, brain, state, body, position, distance, goals, me}, plus step and doing when it has published them
blueprints{id, name, version, mine}
files, runningScripts, inboxIts own machine's filesystem, processes and waiting messages
lastResults{kind, ok, error, detail} per action of the previous thought. error and detail are the empty string, not nil, when absent
maxActionsThe live MaxAgentActionsPerThought

lastResults is the feedback loop. An agent that keeps being refused the same thing is a stuck agent, and this is where it finds out.

The Lua vocabulary#

What a program running on a machine in the world can call. Failures follow the Lua convention and return nil, "reason" rather than raising, because a missing file should not end a program. Full prose is in LUA_API.md; this is the value reference.

GlobalFunctions
worldself, position, inspect, query, agents, move, moveTo, create, use, transfer, exchange, inventory, destroy, claim, hasPermission, send, receive, say, sayNearby, tell, part, setPart, ask, carry
computerreadFile, writeFile, deleteFile, listFiles, fileExists, makeDirectory, log, time, startProcess, stopProcess
scheduleafter, every
sensorscan
eventson

Query-shaped calls take an options table:

CallOptionsDefaults and clamps
world.query(options)type, radius, limitradius 32 and must be finite and positive; limit 16, clamped to 1–MaxQueryResults
world.agents(options)radius, limitradius 0, meaning everywhere; limit 16, clamped the same way
sensor.scan(options)radius, type, or a bare number meaning the radiusRange and result count come from the machine's own sensor component. A script cannot see further by asking for more

schedule.after(seconds, fn) and schedule.every(seconds, fn) refuse a non-function with schedule expects a function, a negative or non-finite delay with a delay must be a non-negative number of seconds, and anything past MaxScheduledEventsPerScript with this script already has 32 scheduled callbacks, which is the limit. every re-arms at no less than 0.001 s, and stops silently when its process dies.

events.on(name, fn) takes any non-blank name; there is no closed set, and 'chat' is a convention the shipped scripts follow rather than a rule. A blank name is an event needs a name. Three names the world itself raises: chat (scope, who, text, id), use (who, what) and answer (who, option, values, promptId).

world.ask(who, question, answers[, values]) is the world asking rather than being asked.

ArgumentAccepted
whoAn owner id — player:… or agent:… — as the use and answer events hand it to you. A name is not accepted here; a question goes to an identity
questionText, cleaned as a chat line is, at most MaxPromptQuestionLength. < or > anywhere in it refuses the whole question
answersA list, at most MaxPromptOptions. Each entry is either a plain string — the button and the name are then the same word — or {name = …, label = …}. name is at most 32 characters of letters, digits, _, - and .; label is at most MaxPromptOptionLength. At least one, or nobody can answer
valuesOptional. A list of at most MaxPromptFields tables of {name, label, kind, hint}. kind is number or anything else, meaning text — Permissions is the server's own and is refused to a script

It answers with the question's id as a string, or nil, reason. The commonest reasons are they are not dealing with you… (nobody may be asked out of the blue), … is not standing within 8 m of this, and the bounds above.

world.carry(who) takes somebody who has just answered you wherever this machine is going, and answers with the travel time in seconds or nil, reason. There is no destination argument and there cannot be one: a thing may bring you to itself and nowhere else. It is spent by being used, so a second journey needs a second answer, and … has not agreed to go anywhere with this is what an unspent one says.

String constants a Lua caller must spell exactly#

WhereAccepted spellings
world.hasPermission(id, permission)The full Permission enum by name, case-insensitively: inspect, use, build, modify, destroy, program, transfer, control, handle, all, none. .NET's flag syntax works too, so "Build,Modify" is one query — and so does a bare number, because nothing excludes it
An agent action's permissionsOnly the seven grantable words, case-insensitively and trimmed: inspect, use, build, modify, destroy, program, handle. A single string or a list of them. transfer, control and all are not accepted, nor is a number; anything unrecognised contributes nothing, and a list yielding None is refused by the server as a grant that says nothing
A part's primitive / materialAny PrimitiveShape / AssetMaterial name, case-insensitively, falling back to Cube / White
A colour#rgb, #rrggbb, #rrggbbaa, with or without the #. Anything else parses as nothing
world.setPart's change tablesolid, visible, colour, axis, turn, turnRate, turnTo, slide, slideRate, slideTo, reset. Any field omitted means "leave it"
world.setReadout's change tablekind (bar, text), source (given, health, energy, growth), label, text, colour, value, rate, limit, max, order, clear. An omitted field means "leave it"; label = '' and text = '' mean "take it off"

The two permission vocabularies are genuinely different, and the narrower one is the deliberate half: the grantable set is seven permissions, so the agent action set has seven words. world.hasPermission only asks a question and can therefore afford to name anything.

Chat scope is not a string in Lua at all. It is chosen by which function is called: world.say is Public, world.sayNearby is Local, and world.tell(who, text) is Direct. All three go through the same rate limiter, and a script speaks at the agent rate — six a minute, burst of two — not a person's.

A chat line is cleaned before it is measured: Unicode format characters are removed, every control character and every run of whitespace becomes a single space, and leading and trailing space is dropped. The 240-character limit applies to what is left, so That is N characters; 240 is the most one message may be. counts the cleaned text and not what was typed.

Where the prose is#

This page is the values. What they mean, and why the world is arranged this way, is elsewhere:

  • The world — time, people, ownership, land and permissions.
  • Protocol — each message in context, and a worked session.
  • Blueprints — how a design describes a thing, with examples.
  • Lua API — what a program running inside the world can do.
  • AI agents — how a brain is written, and what one is for.
  • MCP — connecting a model to a running world.