Volter World

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:

package.json
{
  "name": "seed-app",
  "private": true,
  "type": "module",
  "dependencies": { "stripe": "17.7.0" },
  "devDependencies": {
    "@volter/world": "3.0.63",
    "@volter/twin-stripe": "3.0.1"
  }
}
.env.example
STRIPE_SECRET_KEY=
npm install
npx volter world init --name seed-app

Review 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.

.volter/seed.ts
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: 'founder@example.com' });
if (existing.data.length === 0) {
  await stripe.customers.create({ email: 'founder@example.com', 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:

check-story.mjs
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: 'founder@example.com' });
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: 'ada@example.com' })).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.mjs
one founder, frozen timestamp and no app customer

Seeded 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.mjs
one founder, frozen timestamp and no app customer

Let the app write

Save signup.mjs:

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: 'ada@example.com', name: 'Ada' });
assert.equal((await stripe.customers.retrieve(customer.id)).email, 'ada@example.com');
console.log('app customer created and read back');
npx volter world run -- node signup.mjs
npx volter world log
customer.create

Reset 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.mjs
one founder, frozen timestamp and no app customer
npx 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 down

Resume 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.

View Markdown source

On this page