
Security News
Happy Birthday, Shai-Hulud
It has been one year since Shai-Hulud made its first appearance on npm.
worldfixture
Advanced tools
WorldFixture runs reproducible software worlds on a local computer. A world can contain people, messages, repositories, mail, files, and a live timeline. Applications, MCP servers, tests, scripts, people, and AI agents use normal provider-shaped APIs and protocols to work with that world.
[!WARNING] WorldFixture is pre-release software. See the release checklist for the external-use proof.
All included people, organizations, messages, and financial records are synthetic.
arm64 or amd64 computerBuild the local image:
PYTHONPATH=compiler python3 -m worldfixture_compiler build \
worlds/business.saas-company.v3/world.json \
--output dist/business.saas-company.v3
docker buildx build --load -t worldfixture:local .
Start the default business.saas-company:v3 world:
node runtime/bin/worldfixture.mjs up
The command starts one background container. It uses the preferred local ports when they are free and selects free ports when they are in use. It records and prints the actual addresses.
A full v3 start takes about two minutes, because the mail service creates a mailbox for every person and delivers all 3,069 seeded messages over LMTP. The Workbench URL is printed after a couple of seconds and is usable while the rest of the world is still loading.
Start only the parts you need and that wait mostly disappears:
node runtime/bin/worldfixture.mjs up --only slack,github
Measured on an arm64 machine: every part 117s, everything except mail 41s,
slack,github 1s. The parts are slack, github, site, mail, s3 and
providers; the default is all of them.
Use the world from another terminal:
node runtime/bin/worldfixture.mjs status
node runtime/bin/worldfixture.mjs people
node runtime/bin/worldfixture.mjs slack history --channel general
node runtime/bin/worldfixture.mjs mail inbox --as maya
node runtime/bin/worldfixture.mjs open
node runtime/bin/worldfixture.mjs events --follow
Run node runtime/bin/worldfixture.mjs env to print application bindings. Use
Ctrl-C to stop events --follow. The background world continues to run.
Restore or stop the instance:
node runtime/bin/worldfixture.mjs reset
node runtime/bin/worldfixture.mjs down
reset restores provider, mail, storage, runtime, clock, and timeline state to
the accepted start. It preserves PostgreSQL and MySQL application data. down
stops and removes the container but keeps the local diagnostic state in
.worldfixture/.
If startup or a service fails, run:
node runtime/bin/worldfixture.mjs doctor
doctor reports problems and recommended commands. It does not change state.
Start WorldFixture first. Then use one of these examples:
An application connector maps neutral world packs to an application's existing domain model. It can seed a starting state and accept live events. The connector is enabled only for local development or tests.
Run these commands from the application root:
npx worldfixture up
npx worldfixture connector prompt http://localhost:3000
npx worldfixture run -- npm run dev
Give the generated prompt to a coding agent. The portable
add-worldfixture-connector skill is in skills/add-worldfixture-connector.
After the agent adds the connector, check and seed it:
npx worldfixture connector check http://localhost:3000
npx worldfixture connector plan http://localhost:3000
npx worldfixture connector seed http://localhost:3000
The Workbench provides the same connect, prompt, preview, seed, and live event operations under Target. See the connector overview.
A full seed of the v3 world sends 13,385 records. A quick check does not need
them, so plan, seed and check take a slice:
npx worldfixture connector seed http://localhost:3000 --scale smoke
npx worldfixture connector seed http://localhost:3000 --limit people=25,messages=5
smoke keeps at most 25 of anything and sends about 570 records. sample keeps
at most 250. full is the default. --limit sets counts per collection and
overrides the preset for the collections it names; a nested list such as
messages is counted per parent, so messages=5 means five per channel.
A slice is always whole. It never contains a record that refers to a record it
does not contain, and it never empties a collection the world has records in — a
membership list such as member_ids is trimmed to the people who are present
rather than the record being dropped. The command prints what it actually sent,
including any collection that came out short because something it depends on was
left out.
Each slice is its own seeding operation, so seeding smoke and then full is
two operations rather than a repeat, and the second carries the records the first
did not.
Select optional services in .worldfixture/project.json:
{
"api_version": "worldfixture.project/v1",
"application_url": "http://localhost:3000",
"services": ["mysql"]
}
Use "postgres" instead for PostgreSQL. worldfixture run supplies the active
connection bindings. MySQL-compatible MariaDB 10.11 provides MYSQL_HOST,
MYSQL_PORT, MYSQL_USERNAME, MYSQL_PASSWORD, MYSQL_DATABASE, and
MYSQL_URL. PostgreSQL provides the equivalent POSTGRES_* bindings. Ports can
change when a preferred port is in use, so applications must use these values.
Normal worldfixture reset preserves database data. The connector always
declares database reset unavailable and never deletes application records. A
future destructive database rebuild must use a separate, explicit command.
worldfixture up [world] Start or reuse a local instance
--only <parts> Start only these parts of the world
--no-rebase Start the world at its authored anchor, not today
--world-path <dir> Start a world artifact you built yourself
worldfixture build <source> Compile a world source into an artifact
--output <dir> Where to write it (default dist/<id>.<version>)
worldfixture validate <source> Check a world source without building it
worldfixture open Open the recorded Workbench URL
worldfixture status Show live service health
worldfixture env Print application bindings
worldfixture doctor Diagnose the local setup
worldfixture reset Restore the accepted starting state
worldfixture down Stop and remove the instance
worldfixture people List people and provider identities
worldfixture slack ... Read or send Slack messages
worldfixture mail ... Read mail over IMAP
worldfixture events [--follow] Read the runtime event ledger
worldfixture connector ... Check, seed, or send events to an application
--scale <name> How much of the world to send: smoke, sample, full
--limit <list> Per-collection counts, as people=25,messages=5
worldfixture run -- <command> Run an application with active bindings
Run node runtime/bin/worldfixture.mjs --help for options.
The default v3 world is the active product world. The smaller v2 world remains the compatibility and byte-parity fixture.
consumer.retail-brand:v1 is a second world of a different shape: a
direct-to-consumer homeware brand with a catalog, an order book, a subscription
club, product reviews and a public journal. It is deliberately small — 41 people
and about 150 messages — so it starts in seconds with every part running. It is
in the product image alongside the other two.
node runtime/bin/worldfixture.mjs up consumer.retail-brand:v1
worldfixture build compiles a world source into an artifact. The compiler runs
inside the WorldFixture image, so this needs Docker and Node and nothing else —
no Python, and no checkout of this repository.
A world source is a directory holding a world.json: either a self-contained
worldfixture.world-source/v1 world, or a worldfixture.world-manifest/v1
manifest naming fragment files beside it. The worlds under worlds/ are worked
examples of the second shape at full size.
Start from examples/minimal-world, which is
the smallest world that runs: two people, one channel, two messages, one project
and one task. Copy it and edit it.
cp -r examples/minimal-world ./my-world
validate compiles the world and throws the result away, so anything it accepts
will build. The business.operations/v1 profile compiles seven domains and
indexes rather than defaults them, so a world declares communication,
finance, software, support, work, agentic and stories even when it
has no records for one; validate names any that are missing.
npx worldfixture validate ./my-world
npx worldfixture build ./my-world
npx worldfixture up ./dist/demo.my-world.v1
build writes to dist/<id>.<version> unless --output says otherwise, and
prints the up command for what it wrote. up takes the artifact directory as
its first argument or as --world-path, and the world source is mounted
read-only, so a build can only ever write to the output directory.
Because the image compiles the world, the artifact always matches the runtime
that will serve it. A world started this way starts at its authored anchor
rather than today: rebasing needs the world source, and up is given the
artifact.
Validate and compile v3 from a checkout, with Python:
PYTHONPATH=compiler python3 -m worldfixture_compiler validate \
worlds/business.saas-company.v3/world.json
PYTHONPATH=compiler python3 -m worldfixture_compiler build \
worlds/business.saas-company.v3/world.json \
--output dist/business.saas-company.v3
Create one deterministic bundle:
PYTHONPATH=compiler python3 -m worldfixture_compiler bundle \
worlds/business.saas-company.v3/world.json \
--output dist/business.saas-company.v3.tar
Bundle members have fixed ownership, permissions, timestamps, and order. The same source produces the same artifact bytes.
A world is authored at a fixed anchor, and every date in it is relative to that
anchor. up rebases the world onto the day it is started, so the world's own
"now" follows the session: history ends a day or two before today, scheduled
arrivals are still ahead, and a message you send lands at the top of the list
instead of a year below it. The first screen prints both dates.
This is the one thing about a running instance that is not a function of the
source alone. The compiler is unchanged: the same source at the same anchor
still produces the same bytes, and worldfixture up --no-rebase starts the
world exactly as it was built.
world definition -> deterministic artifact -> environment lock -> instance
-> reset
The runtime has three main concepts:
The default image uses tini as PID 1. One Node.js supervisor starts the
provider composer, HTTP targets, Cyrus IMAP, SMTP, SeaweedFS, SQLite runtime,
and Workbench. Applications do not read internal stores. They use the published
interfaces.
compiler/worldfixture_compiler/ deterministic world compiler
schemas/ public JSON Schema contracts
worlds/ reviewed synthetic world sources
runtime/ CLI, resolver, supervisor, state, and UI
emulators/ provider, HTTP, mail, and S3 services
examples/ normal app, MCP server, and protocol client
tests/ contracts, parity, and image tests
docs/ release checks
Each service in emulators/ has a README with its protocol, ports, test command,
and current limits.
Provider coverage is documented by provider. See the Notion support matrix and the provider implementation process.
Run the dependency-free compiler and contract tests:
PYTHONPATH=compiler python3 -m unittest discover -s tests -t .
Run the runtime tests:
cd runtime
npm test
Some runtime and protocol tests require Docker or a built
worldfixture:local image. See CONTRIBUTING.md for the full
test groups and change rules.
| Project | Role | Pinned version | License |
|---|---|---|---|
emulate | Provider-shaped APIs | 0.10.0 | Apache-2.0 |
| Cyrus IMAP | Mailbox storage and IMAP | 3.6.1 | BSD-3-Clause-CMU and component notices |
| SeaweedFS | S3-compatible object storage | 4.41 | Apache-2.0 |
| PostgreSQL | Optional PostgreSQL database | 15.19 | PostgreSQL License |
| MariaDB | Optional MySQL-compatible database | 10.11.18 | GPL-2.0 |
| SQLite | Runtime events, schedules, cursors, and state | System package | Public domain |
Dependency versions stay pinned.
Vendored interface descriptions. The Notion emulator's contract tests assert
against fixed copies of Notion's published OpenAPI documents and one recorded
hosted MCP tools/list response, so that a test failure means the emulator
drifted rather than that the network did. These are interface descriptions, not
Notion software; their source, retrieval date, digest, and redistribution basis
are recorded in
emulators/emulate/contracts/notion/THIRD_PARTY_NOTICES.md.
Notion is a trademark of Notion Labs, Inc.; this project is not affiliated with
or endorsed by Notion.
On MariaDB and the GPL. MariaDB and PostgreSQL are unmodified distribution packages installed into the container image and run as separate server processes. WorldFixture reaches them over their wire protocols; no WorldFixture code links against, statically or dynamically, or derives from either of them, and neither is redistributed as part of the WorldFixture source. The image is an aggregate of separately licensed works, each of which keeps its own licence: MariaDB stays GPL-2.0, PostgreSQL stays under the PostgreSQL License, and WorldFixture's own code stays Apache-2.0. Using WorldFixture in a commercial setting therefore does not put a GPL obligation on your application.
Third-party notices for the mail, S3, and Notion contract material are in their own directories.
The product scope is the local runtime, v3 world, compiler, Workbench, and examples that work today. The release proof is one outside developer who can build the image, start the world, use it, reset it, and stop it from this README. See the release checklist for that proof.
WorldFixture is available under the Apache License 2.0. Attribution and the third-party notice index are in NOTICE.
FAQs
Run a local software world and seed a connected application.
The npm package worldfixture receives a total of 47 weekly downloads. As such, worldfixture popularity was classified as not popular.
We found that worldfixture demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.

Security News
It has been one year since Shai-Hulud made its first appearance on npm.

Research
/Security News
Operators behind PolinRider used a compromised GitHub account to plant malware in four development versions of a Packagist package with 700,000+ downloads.

Security News
GitHub Actions now supports cache-mode, a least-privilege control on the Actions cache aimed at the cache poisoning technique behind recent compromises.