Seed and reset
Give your app known starting records, then return to them after a run. This example uses Stripe's real SDK with a local twin. Use Node 22.6 or newer and npm in an empty directory; installation needs registry access. No platform account or real Stripe key is needed.
Make the World
Save these files in the empty app directory:
{
"name": "seed-app",
"private": true,
"type": "module",
"dependencies": { "stripe": "17.7.0" },
"devDependencies": {
"@volter/world": "3.0.63",
"@volter/twin-stripe": "3.0.1"
}
}STRIPE_SECRET_KEY=npm install
npx volter world init --name seed-appReview Stripe and its exact package/version before keeping the selection. A twin may ship
starting data, but a published package need not include a seed script. Inspect the generated
files rather than assuming init created a story for every implementation.
Author the starting data
For this fresh example, save .volter/seed.ts. In an existing project, retain its seed entry and
add your story to it; do not replace a team's defaults.
import { World } from '@volter/world';
import Stripe from 'stripe';
World.open({ root: process.cwd() }).setClock('2026-01-15T12:00:00Z');
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY);
const existing = await stripe.customers.list({ email: '[email protected]' });
if (existing.data.length === 0) {
await stripe.customers.create({ email: '[email protected]', name: 'Grace' });
}Stored records go through the vendor API, so a rejected creation fails the seed. This seed looks before creating and sets the clock before its first vendor write. Running it twice therefore keeps one founder instead of creating a duplicate. Commit the seed with the World config, package manifest and lockfile; keep running state ignored.
Save check-story.mjs to assert the starting state:
import assert from 'node:assert/strict';
import Stripe from 'stripe';
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY);
const founders = await stripe.customers.list({ email: '[email protected]' });
assert.equal(founders.data.length, 1);
assert.equal(founders.data[0].name, 'Grace');
assert.equal(founders.data[0].created, Date.parse('2026-01-15T12:00:00Z') / 1000);
assert.equal((await stripe.customers.list({ email: '[email protected]' })).data.length, 0);
console.log('one founder, frozen timestamp and no app customer');Load and check
A fresh branch's first startup runs its seed. A later ordinary startup resumes stored state; it does not reload the seed just because you stopped yesterday.
npx volter world up
npx volter world run -- node check-story.mjsone founder, frozen timestamp and no app customerSeeded records are starting data, separate from the app's pending writes:
npx volter world log(no changes yet)Explicit seeding uses the same entry. Check the result instead of assuming the seed is idempotent:
npx volter world seed
npx volter world run -- node check-story.mjsone founder, frozen timestamp and no app customerLet the app write
Save signup.mjs:
import assert from 'node:assert/strict';
import Stripe from 'stripe';
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY);
const customer = await stripe.customers.create({ email: '[email protected]', name: 'Ada' });
assert.equal((await stripe.customers.retrieve(customer.id)).email, '[email protected]');
console.log('app customer created and read back');npx volter world run -- node signup.mjs
npx volter world logcustomer.createReset deliberately
Use this example's disposable branch. Reset discards its changes, boots again and loads the seed. Here the seed also restores the clock; a different project's reset alone need not freeze it.
npx volter world reset
npx volter world run -- node check-story.mjsone founder, frozen timestamp and no app customernpx volter world log(no changes yet)Grace is back and Ada is absent. A parent with dependent branches refuses reset: a child
references its history, rather than making an independent backup. Branch lifetime
explains the refusal. Ordinary down can still stop compute while retaining history.
npx volter world downResume with up to keep the state. Use resume a World for daily work.
Seeding creates records; scenarios script answers and faults.
A handler must never fake a successful stored mutation.
Make browser states in the seed
Vendor browser state is a separate input. The World's named-browser helper can add cookies,
local storage and context settings through requests to a twin's own sign-in flow. The project
must install @volter/world-core to import @volter/world-core/browser; a global CLI does not
supply that dependency to a project seed.
The browser state API owns names, limits and loading local or hosted profiles. Seeded vendor browser profiles are outside the log, diff, changesets and push. Branches inherit them; reset reloads the profiles created by the seed. Your application's own sessions, database and browser state still need their own setup: test an app in the browser shows a complete application example.
Load a named browser locally
Local exports live in the World state directory at .volter/browsers/<name>.json. The document
contains context and storageState. Load both, together with the World's proxy and CA settings,
when configuring your browser runner; run a full stack explains the
application setup. For a served World, read the named profile through the
browser state API. These are vendor sessions; they do not
automatically sign a person into your application.