
What WASM ruleset sandboxing protects against
The problem being solved
Some worlds need real mechanics: a custom combat system, a ship-to-ship duel, a skill check with unusual modifiers. Authors who want that write a ruleset, which is a small program the game runs to resolve an action. That raises an uncomfortable question for anyone evaluating a platform. If strangers can upload code, what stops that code from doing something harmful?
Fabulator's answer is not to trust the code. The design assumes a ruleset could be buggy or hostile, and it limits what the code is able to do no matter what it intends. This article explains those limits in plain terms, based on the platform's architecture documents.
What a ruleset is, and what it is not
Rulesets are compiled to WebAssembly (WASM) and run inside the Extism plugin runtime. WebAssembly is a format designed to run untrusted code in a contained space. A module can only use the capabilities its host explicitly hands to it.
A ruleset's job is narrow. It receives a structured description of the situation, such as a character's stats and a proposed action, and returns a structured description of the outcome, such as dice rolls and proposed changes to the game state. It is deterministic arithmetic and logic, not a program with a view of the wider system.
The specific limits
The sandbox design documents set out several boundaries:
- No network access. The sandbox is isolated so that it cannot call external services, send data out, or take part in attacks on other systems.
- No filesystem access. A ruleset has no access to the host's files.
- Capped resources. The documented limits include a 64 MB memory cap and a 100 millisecond execution timeout. A ruleset that loops forever or tries to hog memory is stopped instead of slowing the game down for everyone.
- Short lifetime. Each run starts from a clean environment, receives its input, produces its output, and is then destroyed. Nothing carries over from one execution to the next inside the sandbox.
- No secrets in reach. The sandbox runner must not inherit the environment variables that hold database credentials or provider API keys. Even a ruleset that tried to read them would find nothing.
How a ruleset gets its data without touching the database
A ruleset never queries the database. Before it runs, the platform's Go orchestrator gathers the character, inventory and location data that the action needs and hands it over as a single block of JSON. The ruleset works only on that block.
There are two narrow exceptions, and both are designed to be boring. A ruleset may ask the host for a memory lookup or a relationship check through audited, read-only callbacks. The host intercepts each request, limits it to the current campaign, and answers it. The code itself never touches the database or the network. Randomness works the same way: a ruleset does not generate its own dice, but asks the host to roll, and the host caps each request at 20 dice and a maximum die size of d1000.
What happens to the ruleset's answer
A ruleset does not get to change the game directly. Its output is a set of proposed changes, and the host validates them before committing them to the database in a single transaction. Hook plugins that adjust a roll or amend a change are handled the same way, with a shorter per-plugin timeout than the overall budget.
This follows a rule that runs through the whole platform: writes are proposals. The same principle applies to the AI narrator, which suggests changes but never writes the records itself. A ruleset is treated as one more untrusted party whose suggestions must pass a check.
Every roll made during resolution is recorded with its raw values and modifiers, so a player can see how an outcome was reached.
What this protects against
Put together, the design addresses a specific list of concerns:
- A malicious ruleset stealing credentials or player data, because it has no network, no filesystem and no secrets to read.
- A ruleset reading other players' campaigns, because it only sees the data passed in for one action, and its callbacks are scoped to one campaign.
- A runaway or deliberately slow ruleset degrading the game, because of the time and memory caps.
- A ruleset corrupting game state, because its output is validated before it is committed.
- A ruleset rigging its own dice, because rolls come from the host.
What it does not claim
Sandboxing reduces risk; it does not abolish it. The architecture documents themselves note that sandbox escapes are a known class of vulnerability, which is why the design adds a separate execution boundary and scrubs secrets as a second layer. Where the platform is deployed in production, the documents describe running ruleset code on a separate isolated executor rather than inside the main application process. A sandbox also cannot judge whether a ruleset is fun or fair, only whether it can cause damage.
Narrative-only worlds need none of this. A campaign can run without a ruleset at all, in a rules-free mode where the story resolves through narration alone.
Where to learn more
The sandboxing and deployment architecture, the pluggable rules engine specification and the Creator SDK specification in the project documentation describe these mechanisms in detail. Rulesets are also an optional feature, so you can try a campaign in rules-free mode first and add mechanics later if you want them.
