Volter World

Changelog

Unreleased

  • A World is a place a person steps into, locally and hosted alike (architecture, "Viewing a World"; HTTP reference, "Looking into a world"). Every served World mounts each twin's mirror at /<org>/<world>/<vendor>/mirror/ (the mount moved from the hosted supervisor into the World's doors) and answers map, timeline (the twins' logs merged newest first, narrowed by twin or by W3C trace id), diff, clock (set and advance, forward only) and history?at=. The read token opens a browser session and every mirror: a twin whose manifest names requestScopes: ["read"] (34 packs: slack, github, stripe, openai, resend, aws; those whose GETs could write for their caller, or took a virgin World's credentials; the OAuth authorize packs; xai; the metered packs; the poll-driven lifecycles and deliveries: architecture, "Viewing a World") gets every request of the read token marked x-volter-read-only, a request to a read-only twin as far as the pack knows, and the kernel refuses its writes (appends, blob writes, git ref moves) while the vendor's own catch-up still lands. A viewer changes nothing the app can see or do: a read-only poll answers the current state without advancing or delivering, a read consumes no one-time result and spends no quota, and an authorize leg is refused. At any other twin the read token's GETs pass as before and nothing else. Twins record an incoming traceparent on the entries a request causes and continue it on the webhooks those entries cause (stripe, clerk, github, jira, linear, mailgun, paypal, postmark, qstash, resend, sendblue, sentry, slack, vital). A host makes branches of a World as of an instant (POST …/branches { at, ttl }): cloned from the World's history cut there, its clock frozen at the instant, removed when its time runs out; world-host and the local view make them with LocalBranches, a hosted World's supervisor on its alarm. volter world view serves one World, its mirrors, its branches and the console, and opens the console, which now has an overview map, a timeline with live updates, a trace waterfall, a wall of mirrors, the changes, the World clock and "View as of…". Mirror clients build on Windows (filePathOf). Verified: world-runtime served-world and world-view tests (the doors against stub twins, and branches as of an instant, kept across a restart); world-core request-scope, trace-context, twin-fetch and file-path tests; slack and openai read-scope tests, stripe and linear traceparent tests; the console's fake-backed and real-host World journeys under Chromium; a volter world view run with the real slack and github twins (a read session reads Slack over POST, its chat.postMessage answered twin_read_only; the mirror wall renders both); the cloud bundle and its 28 mirror builds. Typecheck and biome on every touched package. Not run: the full catalog gate, the world-host suite (its fixture symlinks need a privilege this Windows machine lacks). Each World's browser session lives on an origin of its own (<world>--<org>.localhost under world-host and volter world view; hosted behind WORLD_ORIGIN_SUFFIX, off until the zone serves them: deploy/RUNBOOK.md "World origins"), and a session's writes need Sec-Fetch-Site: same-origin on every host. The hosted World was walked under local workerd (wrangler dev): provision, traced writes, read session, mirror, timeline, trace, map, clock, diff, branches as of an instant and a branch removed by its alarm.

  • An app's own production hostnames reach it inside a World: volter-world app-url <world> --host <name> records them, and the injector and the redirect proxy route them to the app with their Host and x-forwarded-proto.

  • In a World, a Stripe webhook endpoint is signed with the World's own secret (STRIPE_WEBHOOK_SECRET, and STRIPE_CONNECT_WEBHOOK_SECRET for a Connect endpoint): the app verifies its first delivery with the value its env already holds, where it used to be rejected until someone copied the endpoint's minted secret into it.

  • A World no longer sets a repository's empty .env.example keys: init records them as declared and leaves them unset, and up leaves an empty configured value unset, as world-machine does, so the app's own .env.local is read on the host as in the machine. A caller's own value for such a key now reaches run; runtime.environment.strip keeps it out.

  • The managed platform's people and orgs are the Volter identity service's (id.volter.ai, volter-ai/identity ADR-0002). A person signs in with their Volter account (the authorization code with PKCE) and the platform keeps its own session; orgs, members, roles and invitations go through the service's product door with the platform's own client_credentials token. The platform reads VOLTER_ISSUER, VOLTER_CLIENT_ID and VOLTER_CLIENT_SECRET; the operator registers it at the service (RUNBOOK). The account page links to the Volter account. The journey and the rehearsals boot the Volter identity twin and register the console there.

  • The managed platform provisions onto enrolled hosts only: the machine pool, POST /-/pools, the Fly deploy (deploy/fly/) and its rehearsal are gone (company decision 0017). POST /-/hosts { id, base, adminToken } enrolls a host once its admin door answers the token, and the hosted World on Cloudflare answers those doors with a world-host's inventory row (the admin token as x-volter-token or a bearer), so the platform provisions onto it. rehearse-managed.ts runs the managed journey against apps/cloud under workerd. packages/clerk-worker is removed, and its deployed Worker (no requests in seven days) deleted after a backup. Under Bun, removing one signal listener uninstalled the process's handler while others remained; the runtime now re-registers the rest, so a caller's own SIGINT teardown runs after a World boots.

  • Hosted Worlds on Cloudflare (apps/cloud, company decision 0017): one Worker, one Durable Object per World running the same kernel and packs as a local World in an isolate of its own, state in SQLite through SqlWorldStore, blobs in R2, rooted twins sealed under the Worker's key and refreshed on the object's alarm, checks in a network-less sandbox, mirrors, branches, and the admin, session and attach doors (HTTP reference, "Hosted on Cloudflare"). bun run deploy rehearses the unmodified wrangler deploy on the cloudflare twin first. Deployed as volter-world-cloud, it serves the engagements at twins.voltertest.xyz: the v1 Worker's state, its sealed credentials (moved server to server, never read out) and its forwarding links (now World links) moved over, every path checked against v1 before the route moved, and the v1 Worker, its bucket and source branch deleted (backed up in prod-ops custody). Measured there: PeakHealth's Jira World answers its board in 0.2-0.4 s warm, 0.71-0.75 s from a cold isolate; its rooted Jira pulls from the client's Jira. Shipped with it: a read folds only what changed, served Worlds' doors run through the World store, live URLs carry a World's mount path, webhook delivery reads endpoints from the tree (stripe, clerk, jira, linear), and a local id the vendor holds names the vendor's subject. The TCP twins are hosted too, over a stream: smtp and PlanetScale's MySQL wire answer on a WebSocket that volter-world attach and @volter/world-core/attach bridge to a loopback listener, so nodemailer, mysql2 and Python's smtplib connect unmodified (MySQL at PLANETSCALE_MYSQL_TWIN_URL); tunnel's relay answers its control and visitor WebSockets on the World's vendor API, driven by the real @volter/tunnel client. The injector carries the World's key on requests an SDK addresses to the World itself, and the manifest names each twin's endpoint variable. Verified by drives through the product; no automated suites were run.

  • World command launchers hide the injector routing banner by default; --verbose shows it without changing routing or warnings. Existing admitted quiet settings remain valid. Verified manually: default/verbose child inheritance, explicit environment override, command-side flag isolation, public run, disposable-run IPC and environment stripping, and GitHub hostname routing. Targeted runtime/SDK suites: 41 passed, one existing caller-cwd attachment failure also reproduced on unchanged main. Reference/architecture checks: 152 passed; both package typechecks and touched-file lint passed. Independent review found no blockers. Full catalog gate not run.

2.0.0 — Neon for every SaaS

The model is branching storage under every vendor's API, with the vendor as the root (company contract "The model"). What changed for a user:

  • Node. The volter command, the host and every twin run under Node 22.3+ as well as Bun: npm install -g @volter/world, npm install -D @volter/twin-<vendor>, and the same commands. The server seam behind every twin is Bun's server under Bun and node:http under Node; smtp and tunnel (raw TCP, WebSocket) stay on Bun and say so in their READMEs.
  • Installable packages. Every public package and every twin publishes built output (dist, types included) with its manifest pointing at it; the same tarballs the docs' pages install.
  • Host worlds for a team. A guide for @volter/world-host and the console: many worlds under one URL, provisioned through its HTTP API, opened in the console.
  • The platform (first slice). @volter/world-platform is the hosted Volter World service — and an app in a world, dogfooding World: its identity is Clerk, run through Clerk's unmodified SDKs (@clerk/backend, clerk-js) against the clerk twin in rehearsal and real Clerk through the twin's root in production, with no vendor secret held by the platform; an org is one document, a world is provisioned onto an enrolled host through its admin doors and listed with the host that serves it. It never serves a twin — a host does — and it serves the same console as the portal. Proven end to end by a journey that signs in through clerk-js against the clerk twin, makes an org, provisions a world on a real world-host, and reads it back — no real Clerk, no real cloud. A world now mints Clerk-shaped keys for an app that reads them (the publishable key names the host the boundary resolves to the twin).
  • The console. @volter/world-console is the one UI of a world: the host mounts it at /-/console/ when it is installed, and it reads nothing but the host's and the world's own endpoints with the token you type — the worlds a host serves, a world's twins with their protocol, root, sealed credential and last refresh, its log and tree, its changesets; and, with the admin token, provisioning, rotation and removal. A world answers GET …/twins for the console's first read. Its proof is a Playwright journey through a real host, gated like a twin's.
  • The platform packages carry the product's name. Volter is the company; World is the product; a twin is the unit. @volter/twin-core is @volter/world-core and @volter/twin-attach is @volter/world-attach; @volter/world (the volter command), world-runtime and world-host already fit. Twins stay @volter/twin-<vendor>. In the repo the kernel directory is flat (packages/world-*, packages/cli), and the local-infrastructure runner is volter-world-infra (was managed-infra, a name the hosted product now needs).
  • The host has the hosted product's endpoints. @volter/world-host mints an admin token once and keeps it beside its directory; under it, GET /-/worlds is the inventory (every world, its address, its tokens, its twins), POST /-/worlds provisions a bare world and mounts it without a restart, …/rotate mints both tokens anew, DELETE removes. The admin token opens nothing under a world. The v1 shell's volter remote provision|rotate, its wrangler config and deploy script are gone; the reference describes the host that exists.
  • One log, branches as pointers, checkpoints. A branch starts where the world stands and records its own changes; a read is the nearest checkpoint plus the changes since. volter world log shows the whole history; diff says what it measures from.
  • Which pack is at which protocol is visible. volter world status prints each twin's protocol; the catalog index header tallies the catalog by protocol; GET …/twins/<vendor>/status answers a twin's protocol, root, sealed-credential fingerprint, last refresh, position and last receipt. A pack at protocol 1 is deprecated: served under a warning, its vendor half (sync, push) throws until it moves; its own verification states that as out of date, not as a failure.
  • The slack-live-sync cookbook is retired. It taught the v1 flow (a local plan, a review, a push from the pack); the on-call-agent cookbook and the sharing and deploying guides are the v2 way.
  • Under live use, a refused or failed write does not stay in the tree. The head appends the entry, performs it, and reverts it when the vendor did not take it; the receipt stays on the original. Every answer of a rooted twin carries x-volter-observed-at, the instant of the last completed refresh.
  • A reference follows an adopted id. A twin declares which fields hold another subject's id; when the vendor mints the parent's id, a comment written against the local number is performed against the vendor's, and the world's tree reads the vendor's. An observation is atomic: a branch or a read at a position never falls inside one refresh.
  • Slack's state lives under slack. The twin's log and blobs moved from .volter/world/chat to .volter/world/slack, the same name as the pack, so a root, a credential and a reference declared for slack reach the twin. A world made before this rename keeps its history by renaming that one directory.
  • The GitHub twin needs no git binary. Its git plane (blobs, trees, commits, tags, refs, clone and push over smart HTTP, compare and merge-base) runs on the kernel's git library: one content-addressed object store per world, refs as world state carried by a snapshot. A fork copies refs, never objects. A world's .volter/world/github/git layout changes accordingly.
  • A remote is a world. volter world init --bare <org>/<world> --twins a,b makes a shared world; volter world serve puts it on a URL; volter remote add names it; clone, fetch and push move changes between worlds and never touch a vendor.
  • A World saves what it substitutes. Format-2 manifests group identity, discovery, runtime, serving, scenario and provenance explicitly. Init selects application use by default, while saved usage/vendor selectors opt dependency, build or deployment tools in. Legacy manifests remain readable and migrate explicitly with a byte-identical backup. Regeneration preserves authored notes, infrastructure stubs and seed entry points, and refuses new endpoint claims that collide with saved services before writing files.
  • A twin's root is the vendor. volter twin <vendor> root <url> [--scope] --deploy auto|gated|hold and volter twin <vendor> credential (sealed beside the world under a key in your config directory). volter world deploy performs landed changes; verify and approve are the gates. Every receipt lands on the change: log --receipts.
  • Checks are CI on deployment. .volter/checks/ files run before any change is performed; the shipped no-secrets check refuses credential-shaped strings. A refusal is a receipt.
  • Packs are protocol 2 plugins. github, jira, slack and openai declare it and pass the two kernel gates (scripts/protocol-2.test.ts, scripts/branch-round-trip.test.ts): their serve paths read only the tree, keep no process-level truth, tombstone with deleted: true and take history from the log. Every other pack is a protocol 1 plugin served under a deprecation warning until it moves. A jira watcher or vote is now one subject per account (<issue>::<account>); a github delete carries its tombstone; a scripted openai tool call's id is its position in the response.
  • The head is the write path. A twin whose root says deploy auto performs a write the moment it arrives, in the process that serves it: checks, the vendor, the receipt, and the app is answered with the id the vendor minted — or refused in the vendor's own error shape. Point an app at a shared world and it is pointed at the vendor with no key in the app. The push door and volter world deploy perform through the same path; the push ledger is gone from it.
  • Refresh is the kernel's fold. A pack observes resources; the kernel appends to the root's log only what changed. volter twin <v> refresh [--force] is throttled by the root's --at-most (else the pack's); a served world schedules the root's --refresh (else the pack's every). Two new guides walk live use and reading the vendor through a shared world, with rebase naming a conflict.
  • Proven against real vendors. A shared world with api.github.com as its github root performed an app's wire write at the head (GitHub minted the number, the log shows it under the vendor's id, the secret check answered in GitHub's words) and refreshed from the real account; a slack root at https://slack.com refreshed a real channel into the held copy. Found and fixed on the way: the slack client now form-encodes every call (Slack's info-style methods refuse JSON), waits out a rate limit as Retry-After asks, skips the history of a channel the bot is not in, and a root's --scope names the channels a refresh reads. The slack root is https://slack.com, not /api.
  • Just like Neon, and nothing else. One branch shape for local branches and clones (a pointer to the parent at a position; fetch caches, pull and rebase move the position); one position per twin; volter world branch <name> --at <instant|twin@n> branches from any point in the history; one solid, discoverable URL per served world (serve.json, a second serve refuses, tokens survive restarts); the host serves a directory of worlds under one URL (volter-host serve --dir). The v1 machinery left the kernel: the push ledger, shadow refs and bases, observed deltas as a row kind, egress intents, plans, leases, reconcile, remote refs, the queue lifecycle, the v1 status, validation and inspector, the poll runner, the v1 apply, the v1 operator CLI, the v1 host and its worker render. The names a protocol 1 pack still imports throw on first call.
  • Push is fast-forward only; rebase names the field. A push carries the clone's position on origin and is refused when origin moved past it, with what to do. A changeset records where the parent log was cut; rebase compares the parent's entries since the cut field by field and names each conflict by record and field. A served world keeps its token across restarts; a failed deploy lands a failed receipt and is retried on the next deploy.
  • Every pack peers on the workspace core. The seven packs that pinned @volter/world-core@0.1.0 (figma, github, jira, linear, notion, slack, stripe) now declare workspace:* like the rest, so an install against a 2.0.0 registry resolves instead of waiting on an upstream that never answers.
  • Retired: push to reality, backing, link, feed, pending, the confirm row, the separate push ledger, the observed log as a second row kind. @volter/world-remote is @volter/world-host, the hosting product.

Unreleased

Catalog and fidelity

  • A Prisma client constructed without a driver adapter gets the twinned database vendor's HTTP adapter from the injector, declared as prismaAdapter on the pack; PlanetScale declares @prisma/adapter-planetscale over PLANETSCALE_DATABASE_URL. Prisma's client-engine build, the one a browser tab runs, refuses to construct without one.
  • Moonshot and Together AI join the catalog as protocol-2 twins with vendor-censused surfaces, deterministic inference envelopes, scenario and budget handling, real-SDK integration coverage and completed T0/adversarial review evidence (moonshot fd070670, moonshot review close-out c8717b70, togetherai 8a577347, togetherai T0 5f3c479e).
  • Slack delivers Events API messages through native Socket Mode, with transport-local monotonic delivery timestamps and message authors retained as workspace members (ffd5d919, e5048471, eb2b34fe).
  • GitHub repository hooks are persisted twin state and drive signed native deliveries while preserving source identity; draft transitions work through the native GraphQL surface (f0fcb322, a8fe9055).
  • Jira supports individual comment create, edit and delete through the native path, retaining vendor comment IDs after deployed mutations and accepting mention-only edits (539b78af, f4734f71, 5d85d7e2).
  • New pack scaffolds start on protocol 2, pack hygiene checks their full package shape, and the capability mutation sweep mechanically proves declared verification seams, including bounded failure of hung probes (b26e50e0, 05419b81, 2995a878).
  • Hosted Worlds preserve twin routing for native HTTP clients instead of losing the injected destination at the redirect proxy (4a856084).

The app repo is the world

  • volter world init runs in the app and writes .volter/world.json beside the code, with the twins' default data and handlers, and a .gitignore for the running state. The config names each twin as a package (@volter/twin-<vendor>), resolved from node_modules at boot, and carries no path, key or timestamp; a service-account credential is $mint, minted per boot into the live env only. init is byte-deterministic with no exception.
  • volter, from @volter/world, is the user's command and acts on the world of the current directory: init, up, run, activate, shell, status, log, diff, seed, reset, down, branch, checkout, clone, fetch, origin, changeset -m, push. up resumes a branch with its state; reset is the way back to the default data.
  • World.open() takes no argument; World and Repo carry the same verbs as the command.
  • Tokens for a remote live in ~/.config/volter/credentials.json, written by clone --token.
  • A changeset carries the author's message beside its generated summary.
  • One tutorial, executed as a test; guides; a drift-checked reference; the user glossary and a sweep that holds the user pages to it.

Journeys, and what they found

  • volter takes a noun, then a verb: volter world …, volter twin <vendor> serve|mirror|conformance, volter remote serve|provision|rotate.
  • A tutorial page is its own journey: the tutorial and every guide are executed exactly as written by packages/cli/src/journeys/tutorials.test.ts (bash fences are the steps, text fences the expected output, file= fences the files), and nothing runs that a page does not show. scripts/docs-media.ts records each page with vhs and screenshots every command into docs/media/<page>/, which the page embeds and lists in its Playback gallery.
  • The remote deploys under the policy's meaning: auto deploys any changeset, gated needs a verified and approved one, hold refuses. Before, every policy required review.
  • POST …/remote/<vendor>/refresh refreshes now. The Bun remote resolves performers like its pulls and pushes, and takes --allow-insecure-loopback for a twin standing in as reality.
  • The anthropic twin honors scenario faults ({ "fault": { "kind": "status", "status": 529 } }) instead of failing with a 500. reset works on a world whose twins ship no default data.

The pages install from a registry

  • scripts/registry.ts publishes this checkout's packages to a Verdaccio on the machine. Every tutorial page installs from it, bun add -g @volter/world then bun add -d @volter/twin-<vendor>, and is recorded from that install. The private flag left every pack; the support tier lives in policy/MAINTAINERS.md. Packs ship their defaults/; core ships attach.cjs and its generated artifacts; the runtime and the remote find core's artifacts and each other by package name; the kernel packages declare their dependencies.
  • volter world clock show|set|advance and volter world replay <changeset> --into <branch>.
  • volter world clone takes the world's own token; --admin-token copies the whole tree.

The World runs where no child blocks

  • volter world run -- <command> and the seed await their child instead of blocking on it; stdio, signals and the exit code are the same. A runtime with no synchronous child, the browser engine, runs them.
  • scripts/publish/pack.mjs <directory> [package…] packs the World and any twin named, closed over their workspace siblings, into a directory of tarballs a registry can serve.

The PlanetScale twin

  • A derived table in FROM is read as a table: SELECT COUNT(*) FROM (SELECT id FROM t WHERE …) AS sub, the shape Prisma's count emits, answers the count, and a derived table with no alias is MySQL's errno 1248. Measured with Prisma's client through the real PlanetScale adapter and driver against the twin: create, find, update and count round-trip.

Renamed

was is
@volter/twin @volter/world
@volter/twin-world @volter/world-runtime
@volter/twins-host @volter/world-remote
volter-world init --repo <app> into <app>/../<name>-pilot/ volter world init into <app>/.volter/; --out still emits a pilot elsewhere
volter-world env, attach -- <cmd> volter world run -- <cmd>
volter-world changeset push volter world push
placeholder remote default data
sealed world sandbox (volter world up --sandbox)

Retiring after one release

The remote's link, feed and pending endpoints (now remote, mirror, log) and the keys remote.origin, pushPolicy, sync, syncState, pushState (now deployment.url, deployPolicy, refresh, refreshState, deployState); the volter-world verbs tail, fork and changeset apply (now log, branch, changeset push).