This is the release where Aether stopped being a renderer with a websocket and became something you can extend. Most of it is machinery, and the interesting part of machinery is the constraint it was built under, so this post is more technical than the ones after it.
What changed
Mods, and the sandbox they run in
- One binary, whatever we shipped in it
- Mods installed per world, with the capabilities they ask for listed before you agree
- Extending it would mean running someone's code
- Code runs in a sandbox, never in the server process
A mod declares what it contributes and what it needs. Installing is a per-world decision, so a mod on one table is not on another, and the capability list is shown before install rather than discovered afterwards. Two ship in the repo as working examples: a dice roller and fog of war.
Content packs and the marketplace
- Hand-built worlds only
- Declarative content packs that seed or merge into a world
- Nowhere to get anything
- A catalogue, with a publishing path open to people who are not us
A pack is data, not code: types, sheets, entries. It carries a lazy index, so installing one does not load everything it contains. Two starters exist, a D&D 5e SRD set and a world-building taxonomy.
The world-building layer
- Documents with fields
- Documents with pages, and pages that link to each other
- A link that breaks when the target is renamed
- A link stores an id, so renaming relabels every mention
A GM can define their own document types while the world is running, rather than waiting for us to add a faction or a ship. Backlinks come free: if a page mentions an entry, that entry knows.
Documentation, and knowing what is running
Two audiences on one site: a GM who wants to run a session, and a developer building against the SDK. Alongside it, the release discipline that made everything above safe to change. The running version is readable from the web app, the gateway and every world server; a tag cannot deploy from a commit CI has not passed; and after every rollout a world is created, woken, joined over a real socket and removed, so a deploy that breaks the table fails the deploy.
Why
The sandbox is the decision the rest of this rests on, and it was made in the most restrictive direction available.
Third-party code never runs in our process
A plugin system is usually a module loader: you import the third party and hope. That is fine for a desktop app and unacceptable for a hosted one, because one bad mod then reads another table's secrets, or blocks the event loop for everyone sharing the machine.
So mod code runs sandboxed with no ambient access, and reaches the world only through capabilities a GM granted by name. Declarative-first is the same rule from the other side: if a contribution can be data, it is data, and then there is no code to sandbox at all. Most contributions turn out to be data.
Content packs follow from that. A pack carrying executable code would need the review a mod needs; a pack that is only data can be merged into a world and read by the engine that already knows how to read documents. It is why the SRD starter is a file of data rather than a plugin.
The world-building layer stores an id and resolves the label when the page is read, for a related reason: anything cached is a second copy that can disagree with the first. Rename a character and every mention changes, because there was only ever one name.
What it does not do
None of this is finished. It is a floor, not a building. The marketplace has a catalogue and a publishing path with moderation after the fact, not a review queue in front of it. Packs seed and merge; they do not migrate a world that has since drifted from the pack it came from.
Pages were not yet a good product. The world-building layer arrived as two panels over the same content, Documents and the Wiki, and nothing on screen said which to open. The foundation was sound and the surface on top of it was not, which took until v0.3.0 to undo.
The changelog for this release shows one entry, which is correct rather than broken. A change reaches that page only when someone can write a sentence about what it means at a table, and that filter is what keeps refactors off it. Almost everything here is a sentence about what it means to a developer instead, which is why this post exists.
Where to find it
The Mods button inside a world, the marketplace on this site, and Docs for the SDK reference.


