Architecture — the rules a twin must obey
This is the concrete, checkable statement of the twins architecture, so drift from it can be named: each rule below is numbered, and tagged [auto] (a rule a check can enforce) or [review] (judgment, held at review).
What enforces it in this repository. Each [auto] rule names the check that holds it; the checks are this
repository's scripts/, run from the pack repository's root, and gates says when each runs (the fast ones
on every commit, by the pre-commit hook). A [review] rule is held when a slate is verified.
If you want to change the architecture, change the rule here first (and its guardrail), then the code — never let the code drift away from the rule silently.
Two kinds of thing
Layers. @volter/world-core owns the shared state kernel: one entry model, checkpoints,
parent-position branches, landing, receipts, and the state system at the head. Its storage can
hold inherited and branch entries in separate files; these are segments of one logical history.
@volter/world-runtime owns process lifetime and World operations; @volter/world-host mounts
Worlds under one origin. @volter/world exposes World and TwinLog and the volter CLI.
Vendor packages (@volter/twin-<vendor>, in the pack repositories: the catalog)
implement their vendor's API and state semantics. @volter/world-tooling provides development-only tooling.
The model owns the user-facing semantics. This document owns the contributor boundaries; Creating a pack is the procedure a pack is built by.
This repo holds a platform and a catalog of packages, and they are different kinds of
thing built by different processes. The platform — the world runtime plus the twins kernel — is
one thing: it owns the protocol a package implements (the descriptor, the fetch adapter, the
store door, the world-store and blob seams, the world clock), the host boundaries (the Bun
shell, workerd), the resolver (covers), the installer (init, up), injection, the registry
(pack-facts) and the gate. Its defining rule is the kernel's: don't break userspace —
installed packages keep working across platform releases, so the protocol is versioned and
changes go through deprecation. A twin pack is a package: one self-contained implementation
of that protocol aimed at ONE external upstream (the real vendor), with its own version, tests,
capabilities, gate, census and page — a Debian package, a Terraform provider, a kernel driver.
Packages reach the platform through one narrow SDK (@volter/world-core) and never through each
other. A world is an install: its config is the application's external package.json and
instance.json its lockfile, naming which packages are present and (intent) at which version.
The verification follows the kind (gates): a package's own checks, and for a platform change the
packages it can break. A pack's standing is its grade (the grade) and the pack repository's generated
STANDING.md; no hand-written list states it. STANDING.md counts a pack in the form when it and each lane meet the
grade's form section and each unit holds the files conformance reads (UNIT_FILES in
packages/twin-standard/src/conformance.ts: its decision table, its vendor's life), so a pack the final run's
conformance would fail for a missing file is never shown in the form.
covers resolves detected connections as well as vendor names. The pack descriptor's existing
transport and endpoint facts describe its interface; an HTTP twin cannot satisfy a native
MySQL connection merely because both belong to the same vendor. Conversely, a raw-TCP descriptor does not claim
REST URL or token variables merely through a shared vendor stem; that HTTP interface remains an unresolved signal.
Database URL schemes and
Prisma datasources supply connection evidence, including the app's endpoint variable. Coverage
requires wiring that variable to a declared World service; unresolved connection types or
bindings remain visible as unknown. Generated infrastructure publishes its declared connection
URLs through the existing external discovery mechanism, so an instance records the bindings
its lifecycle actually provided. Like a package requirements check, this is a static
availability and routing check, not proof that the app's workflows work or that multiple
interfaces share state. Those claims require an executed app scenario.
Coverage also attributes a managed native binding to the pack whose managedService.scheme
and raw transport declare that interface. A registered vendor-server wrapper supplies its
vendor identity only when its command matches the backing declaration and its endpoint binding
uses its allocated listener; replaced instance bindings remain uncovered. These are the same
declared mechanisms on config and instance paths, including application-owned binding aliases.
Neither a plain process name nor an unrelated HTTP pack proves native vendor coverage.
First need: Postiz's completed scheduling walk used World-managed Redis and Temporal, but
covers reported both vendors missing while already covering the injected redis:REDIS_URL.
Generated infrastructure has two private backings behind one declared service: a container
runtime runs the compose definition, and without one volter-world-infra serves the kinds it
can without a container — postgres with the machine's own PostgreSQL where it has one, else with
PGlite (the selection is below) — at the same loopback ports and URLs. MongoDB
always runs its own mongod process, without a container, only when declared. Each service's bytes
are under the World's data directory, so they survive down and a resuming up. A package that serves an
infrastructure kind is an ordinary twin package whose state is in the kernel; the backing runs
its CLI as a process and never imports it. Without a container runtime, a kind the containerless
backing cannot serve is refused by name.
The PGlite host serves the literal POSTGRES_USER, POSTGRES_PASSWORD and
POSTGRES_DB declared by its managed infrastructure definition. Its gateway
requires the declared username and password and answers a failed login with
PostgreSQL's FATAL 28P01. An undeclared database is FATAL 3D000, rather than
an alias. The backend opens the declared database itself, so current_database()
is PostgreSQL's own answer. Existing single-database state named postgres is
renamed through a maintenance connection before reopening under the declared
name; it is retained, never replaced by an empty database. Missing or nonliteral
credentials refuse startup. First need: the PostgreSQL catalog's wire reading
accepted an incorrect password and arbitrary database names.
PGlite's named gap is independent PostgreSQL sessions. Concurrent wire connections, including driver pools, share its one backend. A protocol cycle owns the backend until ReadyForQuery reports idle; an explicit transaction keeps that ownership through COMMIT or ROLLBACK (including failed transaction state), and an extended-protocol batch keeps it through Sync. Other connections wait in arrival order, including readers; none runs inside another connection's transaction. A disconnected holder is rolled back before the next turn. There is no gateway idle-transaction timeout that terminates a holder to admit a waiter. This gives committed-data visibility by serialization, not concurrent PostgreSQL sessions. A transaction awaiting another connection's query cannot finish until it ends its own transaction; concurrent transaction scheduling requires the native or container backing.
Session state still belongs to the one backend: session advisory locks held
across transactions do not exclude another connection (its own
pg_try_advisory_lock sees the same session and returns true); session SET values
and temporary tables are visible to other connections and outlive the connection
that created them. LISTEN/NOTIFY does not supply independent listener sessions,
and pg_backend_pid() names the same backend. The gateway does not emulate
locks, isolation, settings or temp objects. Existing protocol prepared-statement
name scoping is retained for pooled Prisma clients; SQL statement-name spellings
outside that scoping remain shared. Apps needing independent session state get it from
native PostgreSQL, selected wherever the machine has one, or the container backing. One PGlite per
connection supplies separate databases; opening the same files twice does not
supply a shared multi-session server. Native PostgreSQL is the faithful design
for those requirements. First need: the PostgreSQL catalog's real-driver reading
and Rallly's pooled Prisma reads; read-only borrowing let another connection see
an uncommitted row.
Explicitly selected containerless teardown checks the recorded lifecycle even when startup refused an unsupported kind. With automatic backing selection, a selected containerless down follows the recorded lifecycle; capability refusal applies to up and status. Product status uses the runtime’s per-service lifecycle state, including lingering owned groups. An external lifecycle marker is active only while that World is running; a stopped World does not report its infrastructure marker as running compute. A kind with no retained child or process receipt needs no stop; an unsupported kind with a retained child or process receipt is refused for migration with its owning runtime. First need: Postiz's rejected Redis infrastructure boot could not be purged because down repeated startup's capability refusal despite having started no infrastructure.
Managed-infrastructure failures retain the backend diagnostic beside the capacity classification, redacting known credential environment values and URL user information before bounding it to the last 40 lines and 8,192 string units. First need for preserving the existing redaction before truncation: Postiz’s bounded backend diagnostic must not print literal credentials. A classification alone cannot identify a failed destination or operation. First need: Postiz World teardown reported only local execution capacity unavailable after its initial boot failed, hiding the container backend's reason.
Without a container runtime, a declared PostgreSQL is the machine's own PostgreSQL
wherever it has one, as MongoDB is its own mongod. At every phase the backing is
selected in this order:
- Retained data keeps the backing that made it:
native-postgres-datastays native andpglite-postgres-datastays PGlite. - Otherwise native, with its tool directory from
VOLTER_WORLD_POSTGRES_BINor the directory of apostgresserver executable on the infrastructure service's PATH. That directory is resolved through its links and must holdinitdb,psqlandcreatedbbeside the server. Client tools alone (libpq'sinitdbandpsqlwithoutpostgres) select nothing. - Otherwise PGlite. Its boot line in the infrastructure service's log names its single-session gap and the cycle that gap causes: a read through the pool inside a transaction waits on itself.
The runtime never installs PostgreSQL. VOLTER_WORLD_INFRA_BACKING still forces a
backing. A definition declaring more than one PostgreSQL, or a kind beside it that the
containerless backing cannot serve, stays on PGlite.
Fidelity: native is the installed PostgreSQL, not PGlite's pinned build, so its version
and extension set are the installation's (PostgreSQL 17.9 on the measuring host against
PGlite's 18.3). Its database time is machine time, so a World whose database must follow
the World clock forces VOLTER_WORLD_INFRA_BACKING=pglite.
A World's PGlite cluster may be prepared before its first boot. volter-world-infra prepare,
run with VOLTER_WORLD_CONFIG naming the World, runs the PGlite host's own open path once
(pglite-host.mjs --prepare: initdb, the declared database and role, a clean close) into
<World dir>/.volter-prepared/pglite-postgres-data. Beside it is a stamp naming the
runtime's version, its exact PGlite pin and the declared user and database. Those are
everything the open path writes into the cluster: the role has no password (the gateway checks
the declared one per connection), and no extension is created in it. At up, an empty PGlite data
directory takes the prepared cluster by rename (copied across file systems), and PGlite resumes
it through its own existing-dataDir path. Because the stamp vouches for the declared database
and role, the host then opens that database directly (--prepared), with no template1
maintenance open. A cluster whose stamp differs is left in place
and named in the host's log, and the boot initializes as before. The preparation is the
same wasm the tab runs, so its bytes are the ones the tab would have written. Seeding is
unchanged and still goes through the vendor's API. First need: Rallly's docker boot in the
tab, where initdb and its file writes on every boot were most of the database's 2.2 s
(ADR 0018).
First need: Twenty at pin 1d561b8. Its workspace creation opens a transaction
(sign-in-up.service.ts:726) and, inside it, reads through its pool rather than the
transaction's connection (subdomain-manager.service.ts:126). On PGlite that read waited
for the transaction that awaited it, until the 10-second query timeout
(sessions-constraint-reading.json, thread-b-images/twenty-step3). Its pools are 10 core
connections plus a separate workspace pool, so no pool setting avoids the wait.
The managed service, not a recipe or an application, initializes a private cluster, runs postgres on
its declared loopback port with Unix sockets disabled, records its server PID,
checks readiness and stops that exact owned cluster on down. It retains data
across stopped boots and refuses a listening port or a PID belonging to another
cluster. A branch copies its stopped native cluster, then resumes it under the branch's own lifecycle; a live cluster is refused, never copied. Other supported
containerless kinds keep their existing backings.
This is real multi-session PostgreSQL; it is not another serialized PGlite
session and changes no application's transaction timeout. Like the container
backing, native database time is machine time, a stated gap in R9.
First need: Cal.diy 54343aa685ae's unchanged db-seed on PGlite after all migrations,
with no application server running. Its 50-member Promise.all batch calls nested
user upserts (scripts/seed.ts:175, seed-utils.ts:61). The captured pg-client
measurement (cookbook/calcom/measure-pg.cjs, World-attached db-seed) fails P2028:
BEGIN waits 2,094 ms for the backing's one transaction-affinity session. Alone,
the same nested upsert's transaction takes 13 ms, BEGIN 1.7 ms; the full helper
including password hashing takes 667 ms (measure-alone.cjs). This is transaction
admission serialization rather than a slow SQL statement. Native PostgreSQL gives
the clients independent sessions. The before/after captures state the backing,
source pin, command and remaining machine activity; shared-host timing is not a
claim of isolated performance.
Native PostgreSQL startup retains its child handle and a startup ownership receipt before publishing the ready owner. Publication, readiness, abort and early-crash failures retire that handle's process group and await confirmed exit with bounded escalation; a missing postmaster PID file cannot prevent startup rollback. Receipts remain while retirement is uncertain. Initialization and database probes share the existing managed readiness deadline, propagate cancellation and await their child groups. Cold-copy branching compares the source's actual storage format with the child's before replacing bytes, in both directions, for PostgreSQL and MongoDB. A service's unsupported branch policy is recorded at boot, so changing a config cannot make its retained state branchable. First need: the native PostgreSQL lifecycle check found cancellation before postmaster.pid, a stalled psql probe and a native-to-PGlite branch that reported an empty database as success.
Lifecycle command availability is read from executable files: an explicit path
is checked directly, and a bare name is searched on the executing process's
current PATH. This requires no subprocess or external which utility. A guest
that cannot synchronously launch that utility must still start and stop its
installed lifecycle commands; an absent executable remains a refusal.
Retained-state resume preserves the World's clock file alongside its data. A frozen instant or running-clock offset belongs to the branch, not the lifetime of its compute; fresh boot and reset discard it. The installed customer journey observes a declared frozen clock before stopping and after resuming, refusing a lost or changed instant before retained-state readback. First need: restarting the browser example retained records but dropped its frozen clock because the resume preservation list omitted it.
Database process receipts use a same-directory temporary file, file fsync and atomic rename. The child handle is retained before publishing anything, and its spawn identity is published separately before the mutable readiness receipt. An unreadable readiness receipt means ownership is uncertain; it never suppresses retirement through the retained handle or the independent spawn identity. A later up or down can retry that retirement without requiring a readiness PID file, and removes damaged receipts only after confirmed exit. Startup and cleanup errors are preserved together through the shared error renderer. First need: native PostgreSQL receipt publication can fail after spawning, and MongoDB registered its child for rollback only after writing its PID. Temporal's service recorder retains its child and the World publishes its ownership through the atomic World store. A retained database child is cleared on current exit evidence, not an earlier lifetime promise's rejection. A retry checks its close event and owned group, and checks the recorded spawn identity before signalling a group that remains. Confirmed exit allows receipt and handle removal even after an earlier unconfirmed retirement. First need: MongoDB's retained command lifetime rejects permanently, while its process may finish exiting before a later down in the same runtime process.
The infrastructure service's up runs in the World's own up process when it names
volter-world-infra (src/infra.ts, one phase per call); status and down run this runtime's
sibling infrastructure entry with its own executable. Built-in lifecycle names are pinned to the
executing runtime even when PATH contains another installation; an explicit executable path is
an operator override. Source TypeScript entries under Node use type transformation, while built
JavaScript entries and Bun need no extra loader. First need: a cookbook runner's node_modules/.bin
resolved an infrastructure entry from another checkout and Node's strip-only loader refused its
kernel's constructor parameter properties before startup or teardown. The phase gets what the tool's process got: the infrastructure service's environment,
which the hosts it starts are spawned with, and the boot's abort; its commands do not block up's event
loop. The reason is where a process's children live: in a browser tab's process layer a child ends when
the process that spawned it exits (measured 2026-09-28, a detached, unref'd listener), so hosts started
by a tool that exits once they serve ended with it, while children of up live as long as the World's
service recorders do.
A vendor whose product is a managed Postgres, or whose data plane is a server in front of one (Supabase's PostgREST,
Storage and Auth), is served over the World's own managed Postgres. This is the declared exception to "state is in the
kernel": the store is the application's database, which the application's migrations, its own direct queries and the
twin all read and write. The vendor's own servers are not World infrastructure (PostgREST and GoTrue are native
binaries a browser tab cannot run) but a twin's conformance oracle on a host (measured 2026-09-28: the company repo's
twin/log/2026-09-28-supabase-data-plane-study.md). Supabase's demand is the database alone (RH2 speaks the Postgres
wire to it, with no PostgREST, client library or anon session), so its pack hands the application the database's URL
through its credential door and serves its APIs as the gap. The exception carries these terms:
- The runtime binds the twin to the World's managed Postgres, a fact the pack declares as it declares
prismaAdapter(managedDatabase: { kind: 'postgres', arg }, compiled into pack-facts): at boot the runtime hands the twin the loopback URL of the postgres service the World'sworld.infrastructure.ymldeclares (the connectionvolter-world-infrapublishes) asserve <arg> <url>, or the colocated factory'sdatabaseoption (managedDatabaseForinworld-runtime/src/runtime.ts). The pack never reads a connection string from the environment, and the database session on its serve path lives in its declared engine module (the tree references none of its rows; the engine-slot gate counts thepgdriver'sClient/Poolconstructors in a module that importspg, and holds a pack that declaresengineto the slot at any protocol). A World whose Postgres is outside it (none declared, or a URL outside the declared service) binds nothing, and the twin refuses its data plane (503), not served. A handler reads the bound URL asctx.engine.url: what a credential door hands the application as the database's address (Supabase'sSUPABASE_DB_URL), the World's own loopback URL, since no route carries a Postgres connection to a vendor's host. - The data plane's writes are not log entries: they have no history, checkpoints, changesets, push or deploy, and no derived core. A branch carries them only as the managed database branches: its current state copied from a stopped base, never at an instant or position, and not at all under the container backing. What the twin keeps of its own (an account, a project) keeps the kernel's model.
- Each request is one batch that ends in one Sync (a simple Query, or an extended-protocol pipeline of
BEGIN,SET LOCAL ROLE …,set_config('request.jwt.claims', …, true), its statements andCOMMITwith a single Sync, values travelling as parameters; the role, which Postgres takes only as SQL text, is one the pack's rule allows (Supabase'sanon,authenticatedandservice_role), checked and quoted as an identifier, never a claim copied in), so no other connection runs inside it: the PGlite host serializes whole transactions, including readers, and native PostgreSQL supplies independent sessions. A batch that fails leaves the transaction aborted, and the twin ends it withROLLBACKbefore the session is released. Row-level security is then the database's own, where the application's tables are not owned by the role switched to and the service role bypasses it, as on Supabase. - The World's PGlite Postgres (world-runtime
pglite-host.mjs; native PostgreSQL keeps machine time) reads database timestamps from the World clock:now(),CURRENT_TIMESTAMP,clock_timestamp()and a transaction's start use the host'sDate.now(), updated from the World's clock file at each connection message. A branch's replacement host receives the branch's World environment, including that file: the branch loadsconfigPaththroughloadWorldConfigand usesexternalServiceEnv, the same environment builder as the infrastructure service's boot. The instance'sconfigis its id, never a config object. Nothing is laid in the database's schemas, sosupabase db diffandpg_dumpsee the application's own schema. First need: RH2'sclock_timestamp()stamps. Randomness remains PGlite's host randomness, not the kernel's_world_seed; RH2's walked migrations and readiness queries need none. A frozenDate.now()also preventspg_sleepfrom completing (PGlite 0.5.8, node host, frozen at 2026-01-05T09:00:00Z:SELECT pg_sleep(0.05), clock_timestamp()still pending after 702 ms; the boundedbun -emeasurement at twin-world commit 7a0e05f completed in 2,489 ms). Statement and lock timeouts under a frozen clock are unverified; this backing promises no elapsed-time behavior while frozen. Its gateway timers keep machine time. The container backing keeps the machine's time and randomness, a gap in R9. - The twin's own Postgres login in a World is the database's superuser (PGlite's declared
POSTGRES_USER): a request switches to its API role, and the vendor's own work runs as that superuser.
The World manifest format owns saved selection, configuration grouping, and migration. Selection grants neither network access nor credentials.
A vendor's native and HTTP interfaces share one state owner. A native protocol frontend may translate requests into the vendor's existing HTTP state door, retaining a separate vendor session per native connection. The World owns both declared services and their ports; the frontend owns neither a second database nor another lifecycle system. This avoids racing read/derive/write sequences across independent processes over the same kernel tree.
Failure classification uses each test's diagnostic block. Bun's final recap repeats those failures and is not additional evidence. A deprecated connector failure keeps its diagnostic; an independent assertion failure or an unexplained recap entry still blocks the gate.
World manifest format
Format 2 is the emitted format. The runtime also reads legacy unversioned manifests, normalizing both shapes before execution. The config reference owns user-facing fields.
Document ownership and layout
.volter/world.json is the portable, committed intent. No spec wrapper, command-specific
autoInit section, or generic policy bag. Each section has one owner:
| path | owns | previous fields |
|---|---|---|
schemaVersion | document format; integer 2, independent of package and protocol versions | absent means legacy format 1 |
metadata | { id, description? }; stable World identity | id, description |
discovery.selection | usage selectors shared by init and coverage | new; not runtime egress policy |
services | ordered service declarations; unique ids; process/external startup retains environment order | services |
runtime.isolation | process, colocated, or worker | isolation |
runtime.environment | { values?, strip? }; declared variables and excluded inherited names | env, stripEnv |
runtime.network | `{ egress: string[], writes?: string[], phase?: 'build' | 'runtime', passthrough?: string[], publicReads?: boolean, ownLoopback?: boolean }; exact HTTPS origins, exact paths or trailing-slash subtrees; egressreads,writes` writes, otherwise denied |
serving | { mode?, name?, share? }; mode app or bare, name <org>/<world> | bare.name becomes { mode: "bare", name }; share becomes serving.share |
remotes | name to World URL/path; no credentials | remotes |
scenario | { actors?, fixtures? }; opaque metadata, not twin mutations | actors, fixtures |
provenance.catalog | unchanged birth stamp { sha, protocol? }, preserved on regeneration | catalog |
Only schemaVersion, metadata.id and services are required. Empty services are valid.
Omitted runtime isolation means process; serving mode means app. Bare mode requires name;
app mode omits it and retains the existing app-serving identity. The existing share provider
and service-selection object moves unchanged. Environment precedence, $mint, stripping,
remotes, roots and scenario semantics do not change merely because their paths change.
Each path retains its existing resolution base: cwd/colocate paths use the World root;
preloads and process-relative paths use the effective service cwd. JSON nesting changes neither.
Unknown structural fields are errors. Document- and service-level //-prefixed keys are comments;
nested structural sections reject them. User-keyed maps and opaque scenario values retain their
existing contents. Deprecated resources has no format-2 field.
An issued URL credential may be requested as $issue:<service>@direct. The
runtime preserves its issued user information, path and query while replacing
only its scheme and authority with that declared twin's local endpoint. An
opaque or file credential cannot use this form. This is caller configuration,
not a vendor-shaped rewrite or a fabricated credential. A browser SDK cannot use
the Node preload or a process's proxy environment; its URL must reach the twin
directly. A build embeds that URL, so its service needs an explicit stable port
across build and runtime boots. First need: Cal.diy 54343aa685ae initializes
Sentry's browser SDK with NEXT_PUBLIC_SENTRY_DSN_CLIENT
(apps/web/instrumentation-client.ts:9); the ordinary issued DSN names Sentry's
public ingest authority and would leave normal Chrome outside the World.
An explicit network policy belongs to the World, never a program pack. Its single shape is
{ egress: string[], writes?: string[], phase?: 'build' | 'runtime', passthrough?: string[], publicReads?: boolean, ownLoopback?: boolean }, with runtime as
the default phase. A grant is a canonical exact HTTPS origin, an exact URL path, or a subtree
path ending in /. Port zero is refused because transports can substitute their default.
Scheme, host and port must match; /css2 never grants /css2anything
or /css2/other. Paths are decoded once, dot segments resolved, and encoded separators,
backslashes, malformed escapes and nested escapes refused. Queries and fragments do not
participate; grants themselves contain neither, nor userinfo. Parent/child policies intersect
by these same rules, keeping the narrower grant. The effective ceiling (packages/world-runtime/src/schema.ts:415, packages/world-runtime/src/runtime.ts:1862) is recorded at boot
and survives branch and checkout. Descendants intersect config permission with both the
base's recorded ceiling (packages/world-runtime/src/branch.ts:65) and the launching process's policy (packages/world-runtime/src/runtime.ts:590); neither a changed config nor
an open launching shell restores a denied grant. Every boot record states its ceiling, including
explicit unrestricted when no policy existed. The ceiling prevents a branch’s config,
an edited config or a retained-state re-up from widening the base’s boot permission, and a
receipt digest detects damaged or half-edited records. An actor consistently rewriting the
record and its unkeyed digest is outside this cooperative mechanism and requires an enforced
Machine boundary; the digest is consistency evidence, not authentication.
Ordinary record reading derives an absent ceiling from the World’s own stored configuration,
intersected with any bound policy its environment retains, and writes the complete record.
It also derives absent owner, inventory and service branch permissions from the stored lifecycle,
service records and that configuration; where a value cannot be derived, it refuses with a
generic error naming the record. A stopped receipt is checked before reading fields and updated
with the completed record under the instance lock. Fresh up does not require that record
shape: after verified teardown it boots and writes a complete record, retaining any readable
permission ceiling. First need: existing stored Worlds must branch, check out and resume without
manual state removal, while a restricted base must still refuse a wider config grant.
TLS passthrough requires the build phase and an explicit exact-origin egress grant;
a path grant never permits passthrough.
Reads are granted generously, writes narrowly. An egress grant admits only read methods (GET, HEAD,
OPTIONS); any other method needs the same destination in writes, a list with the grant grammar above. A build-phase
World also reads, without listing them, the public build-artifact origins every demanded build fetches from:
https://fonts.googleapis.com, https://fonts.gstatic.com (Next's next/font), https://binaries.prisma.sh (Prisma
engines), https://brand.volter.ai (Volter products read the brand at build, decision 0018), https://registry.npmjs.org, https://index.crates.io and https://static.crates.io (package managers).
publicReads: false turns that set off. The set is a read allowance in the build phase only, never a write, and it is
part of the permission a ceiling carries: a World whose ceiling is not a build World with public reads does not gain
it, so sealing still never widens. The method is enforced where it is visible, on inspected egress; a passthrough
origin is opaque, so it may not appear in writes. The public build-artifact origins are themselves tunnelled
unopened, not inspected: Next 16's next/font fetch runs in its native module, which verifies TLS with
rustls-platform-verifier and on macOS trusts only the system keychain (NODE_EXTRA_CA_CERTS and SSL_CERT_FILE are
not read), so an inspected origin presenting the World's authority fails the build. Their method is therefore not
enforced; these are credential-free public artifact hosts. Measured on macOS (Rallly's tab build, Next 16.3.3: curl
and Node reached the inspected font origin, the build's own fetch did not; the native module's verifier read from
next-swc.darwin-arm64.node); the Linux verifier's trust sources are not measured. Twin routing still takes precedence. First need: Rallly's tab
build fetched Inter through next/font and its fresh World listed nothing; Cal.com, Dub, Postiz and Rallly each
hand-listed the same font and Prisma origins.
A lifecycle caller may supply the unique instance token returned by up to down
(including --purge) as --if-instance. Boot mints one UUID from the platform’s random
source, persists it with the instance, and uses the same token for boot supervision;
createdAt remains display time. Before any side effect, under the same instance lock used
by boot, teardown compares the retained or running token and refuses a mismatch with the
generic ownership error. Its stopping claim excludes replacement throughout asynchronous
teardown and purge. Existing records without a token receive one once under the record lock
when ordinary reading completes them. First need: two boots under a frozen parent can have
identical timestamps; a recipe must still refuse to purge the replacement using the first
boot’s token. Callers retain the token returned by their own boot, never discover cleanup
ownership by looking up a name later.
The injector computes HTTP's effective URL after overload options, including port and path, and sends inspected HTTP and global Fetch through the scoped proxy's owned transport. An authority with any grant is inspected unless its exact origin has explicit build passthrough; this includes origin-wide grants without passthrough. It does not invoke caller agents, their port getters, dispatchers or connection callbacks on that path: explicit port/defaultPort overrides apply, otherwise the owned transport uses the protocol default. A mismatched Host, upgrade, or unmediated transport is refused. There is no request-context socket exemption: raw sockets and HTTP/2 require explicit build origin passthrough and cannot use path grants. Shared undici dispatchers refuse inspected external hops because their arbitrary connector cannot be mediated; independent agents receive no raw-socket exemption. The proxy's plain inspected connection is parsed as HTTP, forwards one authorized request, and closes after its response. A parser-dropped or second parsed request closes the socket with a refusal diagnostic; parser errors also close it. Pipelined second requests never reach the upstream. CONNECT for an inspected grant terminates TLS on a server pinned to the CONNECT authority (including port); every decrypted request is authorized again, its Host checked, and forwarded as one HTTP request. Guest inspected traffic is refused because this transport cannot preserve the guest's checked destination pin. World/proxy credentials and hop-by-hop headers are stripped, and redirect answers are returned without following them.
First need: Dub's own Prisma generation downloads a public engine beneath a pinned commit
path. Socket instances need the same outbound refusal as socket factories, and a branch
launched outside its base's parent must retain the base's effective egress ceiling. Cal.com's certificate-verifying build also needs explicit origin-wide
passthrough; Postiz needs the same inspected artifact path. Recipes delimit build by a separate
World's up/own build command/down --purge, declare phase: build there, and keep their
runtime World's egress empty. The phase is a declared lifecycle scope, not a timer or a claim
to detect a command's purpose.
The runtime
publishes it as VOLTER_WORLD_NETWORK_POLICY, bound to the instance name, for a
trusted execution boundary to consume. This value contains permission, not
credentials. Twin routing takes precedence except for a request explicitly granted in a build
World: that exact public artifact/API path is inspected and sent to its real origin, including
on a twinned host; all other paths retain twin routing. This permits the Twin product
recipe's pinned GitHub scanner release, OSV exact /v1/querybatch and /v1/vulns/
subtree, and manual release-asset redirect hops without an API-wide grant. Sandbox mode
still refuses untwinned traffic. The injector and scoped proxy apply this policy
to real external destinations in ordinary mode. As with their existing sandbox
refusal, this is cooperative enforcement, not a raw-socket boundary. Global Fetch follows redirects through checked requests: inspected hops use the owned
proxy and each new destination re-enters authorization, including twin destinations. One
20-hop counter spans every follower and fetch re-entry; exceeding it rejects the chain.
First need: an inspected self-redirect must terminate even when each hop re-enters Fetch.
Native fetch is called in manual mode under a policy, so its internal redirect follower
never opens an unchecked hop. Explicit manual and error modes retain their semantics;
cross-origin hops drop scoped credentials. A browser
container's trusted host applies the same policy outside its guest realms.
Omission retains the existing host-runtime behavior; an explicit empty list
refuses real external origins through mediated HTTP/Fetch and guarded Node connections
(transport coverage); native traffic requires an enforced boundary. This adds no inbound listener or ingress grant;
the existing declared-service/share lifecycle remains the ingress door.
Capability decisions
The World runtime answers every capability request a guest makes, on every substrate. It uses one decision, with one of three outcomes:
- PASS: let the request through.
- TWIN: route the request to what the World serves in the vendor's place: a twin, or the application's own hostnames.
- REFUSE: block the request, with a named reason.
The substrates enforce that decision and decide nothing themselves: the Node injector, the scoped proxy, the browser
tab's relay, native image runs under macOS Seatbelt (packages/images in volter-ai/browser-substrate), and any
later VM or Linux runner. A capability passes only when the guest declares it and the World grants it. Neither
side alone is enough. The decision is packages/world-core/capability-decision.cjs. It is pure: no I/O, no
environment reads and no import side effects, so the injector, world-runtime, the twins' own outbound calls and
external substrates all import the same code.
The vocabulary. These are the capabilities that are decided today:
| capability | request | declared by | granted by |
|---|---|---|---|
network.egress | an outbound destination: URL, method, transport (http, socket, datagram, http2, upgrade) | capabilities.network.egress: a list of origins, or '*' | runtime.network (egress, writes, phase, passthrough, publicReads); a sealed World grants nothing |
network.ownLoopback | a loopback connection or datagram (TCP or UDP), on any port, to a listener the run itself holds | capabilities.network.ownLoopback | runtime.network.ownLoopback (default true) |
network.services | a loopback connection to the World: a door (its proxy, a twin, the World's own origin) or a declared service | a door needs none (an attached guest reaches its World); a service by capabilities.network.services: names, or '*' | membership: the World granted what it declared and started |
network.host | any other address on the host's networks (loopback, private, link-local) | capabilities.network.host | a host-process run is on the host's network; a guest has no grant field yet |
files.read | a file the guest reads | its own tree, implicitly for every image (a container sees its own filesystem); a compose bind mount's host path; a host process declares the host's filesystem (files.host) | the World's own files (its provided files and its root) by membership; no grant field for a host path outside the World's root |
files.persist | a path the guest writes as its data (VOLUME) | capabilities.files.persist | none for a run-scoped path: it passes writable for the run and does not outlive it, as Docker gives an unmounted VOLUME an anonymous volume; a path kept beyond the run needs a grant field, defined at that need |
Twin routing is not a capability of its own. It is the TWIN outcome of network.egress, so a vendor host still
needs its declaration: an undeclared call is refused, never routed.
The declaration source per substrate. One shape: { network: { egress?, ownLoopback?, listen?, services?, host? }, files?: { persist? } }, plus derivedFrom: [{ capability, file, line }] so a refusal can cite the line that
declared or failed to declare the capability.
- An image records it on its runtime binding (
kind: "volter-image-runtime-binding", written at assembly). The images CLI derives it from the recipe:EXPOSEand composeportsgivelisten: inbound exposure, a separate question from own loopback.ownLoopbackis true for every container recipe, because a container reaches its own loopback on any port. Turbopack's and Next's worker IPC use ephemeral ports that no Dockerfile names.- compose
depends_ongivesservices. An image with no compose file declaresservices: '*': Docker restricts no service a plain container reaches. The World wiring a service into the app's environment is the grant, never the declaration. - only
network_mode: hostgiveshost. VOLUMEand composevolumesgivefiles.persist.- every image reads its own tree, implicitly (
files.tree: true, asownLoopback); a compose bind mount gives its host path infiles.mounts. egressis'*', because Docker gives every container open egress and a recipe names no destinations.
- A browser program declares its hosts (
programNetwork(program, declaredHosts)in browser-substrate's runtime policy):egressis that list. - A host process attached by the injector has no declaration format. It is an ordinary process on the host, so
its declaration is the open one:
egress: '*',services: '*',host: true. ItsownLoopbackis what it has observably opened: a listener the injector's listener registry recorded. Opening a listener declares it. Until that registry exists, nothing is recorded and a guest's own-loopback connection is refused for a missing declaration. A substrate whose guest format can declare must supply the declaration, and may not substitute the open one.
How a declaration reaches the injector. An image run's processes carry the declaration in
VOLTER_WORLD_DECLARATION. Its value is { version: 1, world, capabilities, derivedFrom }:
capabilitiesandderivedFromare the runtime binding's, verbatim.worldis the attached World's name (VOLTER_WORLD), binding the value to that World as its network policy is bound.
The images CLI's withImageUseRuntime sets it for the image's CMD tree from the binding. When it is present, the
injector decides with it in place of the open declaration, and decides the run as a guest: as in Docker, the host's
network is not its own. A value that is unreadable, or names another World, is an empty declaration, so every request refuses with
declaration missing.
- Tampering. The application's own processes can write the variable. Tampering can only narrow what passes, or reach what the World already grants, because a capability passes only when declared and granted. The images CLI renders Seatbelt from the binding, never from the variable.
- Images built before the field. Their capabilities are derived at assembly from the retained reading of their recipe.
Where the evidence stops: the image fields above follow browser-substrate's recorded enforcement surface (images
lane, 2026-10-06). Compose extra_hosts and networks are not yet mapped.
The World's grant is the World's own configuration, carried with its ceiling:
runtime.networkwith its existing fields. With the field omitted, egress is unrestricted, as before.ownLoopback(boolean, defaulttrue): whether this World lets a run reach its own listeners. Parent ceilings intersect it like every other grant:falseanywhere isfalse.- A sealed World (
VOLTER_TWIN_STRICT_EGRESS) grants no external egress, whatever the list says. - The World's services and twins: the World grants its guests what it declared and started.
controlPlane services are the World's own infrastructure, not guests. Nothing is decided for them.
The contract. The decision is called as decideCapability(request, { run, declaration, grant, facts }):
run.guestsays whether the run has the host's network as its own (a host process) or not (a local light guest, a World Machine, an image, a tab) or is a host process.grantis the parsed published policy,undefinedfor unrestricted, plusstrict.factsis what the substrate observed and the decision may not discover for itself:- the twin route the World's routing table (
resolveTwin) gives the destination; - the vendors whose twins share the host;
- whether a loopback destination is a World endpoint or one of the run's own listeners.
- the twin route the World's routing table (
The answer has this shape:
{ outcome: 'pass' | 'twin' | 'refuse',
via?: 'direct' | 'inspected' | 'passthrough' | 'guest-proxy', // how a PASS travels
twin?: { vendor, origin },
capability, target, reason,
missing?: 'declaration' | 'grant' | 'transport' | 'twin' }missing names which side refused: the guest did not declare it, the World did not grant it, the transport cannot
carry the grant (an inspected grant over a raw socket), or a twinned host has no twin for the path. Egress is decided
in this order:
- An invalid destination is refused.
- A World-internal address is a loopback question, not an egress one.
- An explicit build-phase public grant is a PASS that wins over a twin.
- A twin route is a TWIN.
- An unclaimed path on a twinned host is refused (
twin). - A sealed World refuses (
grant). - A policy that does not list the destination refuses (
grant). - An inspected grant is a PASS
via: 'inspected', refused for a raw transport. - A passthrough grant is a PASS
via: 'passthrough'. - Anything else is a PASS
direct.
The injector's request, fetch, HTTP/2, undici and socket seams, the scoped proxy and the twins' own outbound
(worldEgressRefusal) are all this one order.
decideCapabilitySet(declaration, grant, world) is the same rules evaluated up front, for an enforcer that renders a
profile instead of asking per connection. It returns:
{ network: { egress: 'none' | 'proxy',
loopback: 'none' | 'own',
listen: [ports],
endpoints: [{ host, port, kind: 'door' | 'service', name }] },
files: { persist: [paths] },
refusals: [answer] }Egress in the set is proxy whenever the guest declares any. The twin route and the per-request listing belong to
the World proxy, and a profile forces traffic through it.
How each substrate enforces. Each enforcer renders the decision at the finest granularity it has, and states its fidelity gap: what its rendering cannot reproduce of the decision. It never widens a grant it cannot see. A grant finer than the enforcer can see goes through an inspecting layer (the World proxy), or the request is refused.
None of this is containment. The injector and the World proxy refuse cooperatively. A native image run's Seatbelt profile is a fidelity instrument: it reproduces what Docker lets a container do, so an app that depends on the host (its files, its other listeners) fails locally as it would in Docker, where the owner sees it. A restriction Docker does not have does not belong in the profile. A native run executes only trusted sources, the vendors' pinned commits we chose; untrusted code runs in the browser tab, whose isolation is the browser's.
- The injector asks
decideCapabilityat every connect and request. - The scoped proxy asks it per CONNECT and per decrypted request.
- The browser tab's relay sees only
host:port. It may admit an origin only where the decision admits every method and path there. Anything narrower goes through the inspecting layer or is refused. - Seatbelt gets its profile from the decision set. Network-outbound can name only
localhost:<port>,localhost:*or*:<port>(a literal IP is refused at profile load), so:- endpoints render as
localhost:<port>; - external egress goes through the World proxy only;
loopback: 'own'renders aslocalhost:*. Its fidelity gap: Docker scopes a container's loopback to its own listeners, and macOS cannot, so the native run reaches every loopback listener on the host.(remote ip "localhost:*")admits loopback TCP and UDP alike;remote tcpandremote udprender one protocol each (measured on macOS with Darwin 25.4: under(remote tcp "localhost:*")a datagram to loopback answersEPERM; underremote udporremote ipit is sent).listenrenders asnetwork-bindports;files.persistrenders as writable subpaths.files.readrenders as(deny file-read*)plus allowed subpaths, as Docker shows a container only its own filesystem and its mounts: the run's tree, the World's provided files, the granted mounts, and the system paths a run needs. That list is measured on a real image run, never guessed: the decision takes it as a fact (facts.system), from the substrate that measured it.
- endpoints render as
- A VM or Linux runner renders the same set into its namespace and firewall, where own loopback is exact.
Reporting a refusal. Every refusal is one record:
{ kind: 'volter-world-refusal', version: 1, capability, target, missing, reason, grantField?, declaredBy? }grantFieldnames the World field that would grant the capability.declaredByis the declaration'sderivedFromline.
A Node error carries the record as error.worldRefusal, and its message ends with the missing side in words. Seatbelt
denies a connect with EPERM. It writes a kernel record for most programs, but none for Homebrew Node 24 (measured by
the images lane). So:
- the injector's socket guard reports a connect it let through that failed with
EPERMunder an image run, as this record; - the images CLI's log-stream reporter emits the same record for other programs.
Both reporters build the record with sandboxRefusal in capability-decision.cjs, so the reason text is one text.
The record says missing: 'transport': the decision passed, and the enforcer's rendering is narrower. The common case
is a client that dials directly while Seatbelt admits only the World proxy and loopback.
- The kernel masks a refused IP, so a non-Node record's target is
*:<port>, with the process id and executable beside it. - Node's denials are never kernel-logged, so the injector is Node's only reporter.
Reading files. A read is decided like a connection. decideCapability({ kind: 'read', path }) passes a path inside:
- the run's own tree (
facts.tree, when the declaration reads its tree); - one of the World's provided files (
facts.worldFiles: its Node redirect copy, the certificates its environment names); - the measured system paths (
facts.system); - a declared mount inside the World's root (
facts.worldRoot).
Anything else refuses. A declared mount outside the World's root refuses for the missing grant: no field grants a host
path the World does not own. Any other path refuses for the missing declaration. A host process declares the host's
filesystem (files.host) and passes, as it does for the host's network. The decision set lists the readable roots, each
with its kind, for a profile to render. Paths are compared as normalized absolute POSIX paths; the decision resolves no
symlink, since that is I/O, and an enforcer that follows links checks the target it opens.
Persistence beyond the run (design, not built). runtime.files.persist ('*' or a list of declared container
paths) would make a path that is declared and granted the World's state, per branch:
- a branch takes a copy-on-write copy, and checkout switches it;
- reset and purge remove it;
- it does not push or deploy: a volume is no vendor, and has nothing to perform against.
It is never a host path the World names, so a grant cannot open the owner's filesystem. Until a need asks for the field, a declared path passes run-scoped, as above.
Attribution. OS listener attribution (asking lsof or /proc who owns a port) is not part of the decision. A
guest that declares ownLoopback has every loopback destination that is not one of the World's doors decided as its
own loopback, on any port: there is no run-scoped listener fact, so the declaration decides it. What remains is each
enforcer's fidelity gap (Seatbelt's localhost:*), stated in the run's record. Attribution is at most a declaration source for a guest with
no declaration format that opens listeners natively (an N-API module in a light guest).
Outbound transport coverage
An explicit network policy mediates the transports listed below.
The shared Undici dispatcher and imported Undici request/fetch doors route each twinned vendor hop to its declared
World endpoint, preserving the original host and request headers, while retaining the caller's Request implementation
for unclaimed private/local hops. An imported Request
crossing into the global Fetch implementation is adapted through its URL, method, headers, body and request options;
passing an incompatible Request object must not break an application's internal control plane.
The routed Node HTTP request preserves its writable destination state: writable is true while accepting a body,
writableEnded becomes true on end(), and ending or destroying it makes it non-writable. Legacy streams such as
Slack's multipart FormData check that state before calling write; the injector must forward the bytes they supply
with their original framing headers. The request's existing finish, response and close events retain their ordering.
Routing prefixes the endpoint path once and never follows a redirect with World credentials;
the caller must dispatch the next hop through the same routing decision.
An exact origin grant permits that origin's encoded resource paths, including
registry package names containing %2f. Exact-path and subtree grants still
require an unambiguous normalized path: an encoded separator cannot expand them.
This distinction applies to every request transport and preserves the origin
ceiling while allowing package managers' ordinary scoped-package requests.
An inspected Node HTTPS request reaches the same World proxy through CONNECT and performs its TLS handshake
against the World's origin-bound certificate. Its response exposes the actual authorized TLS socket, preserving
clients that explicitly check it (@cypress/request, used by the tutorial registry, is the first need). The proxy
still inspects and authorizes the HTTP request inside that tunnel. A plain HTTP proxy socket must not be presented
as an authorized TLS socket, and client verification is not disabled to make the transport pass. Each request's
tunnel agent is disposed when its request closes; native abort, stream and timeout behavior remains the request's.
| Node or Bun transport | Enforcement and scope |
|---|---|
http(s).request/get, global Fetch and Bun global Fetch | packages/world-core/inject.cjs:1146, packages/world-core/inject.cjs:648 and packages/world-runtime/src/redirect-proxy.ts:351: effective URL mediated per request by inject.cjs request/fetch wrappers and the scoped proxy; each followed redirect re-enters authorization. |
net.connect/createConnection, new net.Socket().connect, tls.connect | packages/world-core/inject.cjs:1070 and packages/world-core/inject.cjs:1082: factory and instance guards call the same socket refusal; external path grants cannot lend a raw connection. TLS origin passthrough requires its explicit build permission. |
tls.TLSSocket over an existing socket | packages/world-core/inject.cjs:1059 / packages/world-core/inject.cjs:1082: wrapping opens no new connection. A subsequent instance connect is guarded; an already connected descriptor must have entered through a guarded seam or an outside native layer. Proxy TLS wraps a verified loopback peer. |
Unix net.connect({path}) and an adopted fd | packages/world-core/inject.cjs:894: Unix paths stay local; adopting an existing fd opens no connection and cannot establish its provenance. A local relay or fd supplied by native code is outside this policy's proof. |
dgram UDP send/connect | packages/world-core/inject.cjs:1098: external datagrams refused under a policy; HTTPS grants convey no UDP permission. Internal destinations remain allowed. |
| Host-process redirects to loopback/private addresses | Allowed: the World’s twins and declared services live there; guest proxy isolation remains distinct. |
| Local light-guest HTTP(S) | Product commands receive VOLTER_WORLD_GUEST_PROXY as HTTP(S)_PROXY and VOLTER_WORLD_PROXY, with no NO_PROXY bypass. The Node/Bun global Fetch and Node HTTP wrappers mediate vendor and untwinned destinations through that guest listener; explicit twin endpoint doors remain routable. A World service the instance records (its URL, a side's, an external service's discovered address) is reached directly, as the capability decision admits a declared service; other private and non-granted destinations refuse. The agent's control transport retains host routing. |
| Local light-guest subprocesses/native transports | Proxy-aware programs encounter the guest listener. A process, native connector, addon or FFI that ignores the proxy/preload is not mediated; this ordinary host process has no kernel network boundary. |
| DNS lookup and native resolver UDP/TCP | packages/world-core/inject.cjs:986: DNS lookup routes twin names; external name resolution remains the OS resolver. Native resolver traffic does not carry an HTTP path and is outside preload enforcement. This host run has no enforced DNS boundary. |
http2.connect | packages/world-core/inject.cjs:1235: twins routed to their door; inspected external authorities refused. Explicit passthrough may lend origin transport. |
| Direct imported undici Fetch/request, custom dispatchers | packages/world-core/inject.cjs:520: shared dispatcher and imported request/fetch doors rewrite twinned hops; inspected external hops are refused and independent Node agents encounter the socket guards. Native connectors not using those seams are outside the preload. |
Native Bun.connect and native Fetch aliases bypassing the global | No preload guard over these native primitives; proxy-aware calls encounter proxy checks, otherwise this host process has no enforced boundary. A separately enforced Machine/executor boundary is required. |
Child processes (curl, engines), addons, FFI, retained native primitives | packages/world-runtime/src/redirect-proxy.ts:821: proxy environment names; not intercepted by the preload. Node children honoring the preload use the same guards. Other proxy-aware programs use World proxy environment variables and encounter proxy checks; programs ignoring them have no boundary in a host process World. Only a separately enforced Machine/executor network boundary can cover them. |
Cooperative sandbox refusal is not a hermetic network boundary. The proxy variables are
routing hints, not enforcement over subprocesses. Machine forwarding is covered only
when its host actually installs the network boundary (packages/world-machine/src/host.sh:51
tries the forwarding DROP rule, but does not fail closed if it cannot install it). This
recipe uses host processes and does not presume that boundary.
The scoped proxy's local pass-through is for the World's own host. The proxy has a second
listener for its guests, published as VOLTER_WORLD_GUEST_PROXY, and a World Machine's
forwarder targets it. Every connection there is a guest's, whatever a VM's NAT makes its
source look like. So is any connection to the first listener from anything but the host's
loopback (127.0.0.1, ::1); a machine's forwarder connects from 127.0.0.2 where it
shares the proxy's host. For a guest, every destination that is not a twin, or the
application the proxy routes to, is refused when it is, or resolves to, one of the host's
networks. That means loopback, private, link-local, shared, reserved or multicast IPv4;
unspecified, loopback, unique-local, link-local, multicast, NAT64 or 6to4 IPv6; an IPv4
address mapped into IPv6 in any spelling; or an address of the host's own interfaces. The
guest is then connected to the address that was checked. A machine's host is not the
machine's network: its loopback holds the World's control doors and the twins' own ports.
Minimal generated example:
{
"schemaVersion": 2,
"metadata": { "id": "acme-web" },
"discovery": {
"selection": {
"include": [{ "usage": "application" }],
"exclude": []
}
},
"runtime": {
"isolation": "colocated",
"environment": { "values": { "GITHUB_TOKEN": "twin-fake-github-token" } }
},
"services": [
{
"id": "github",
"type": "twin",
"source": { "package": "@volter/twin-github", "version": "2.0.0" },
"execution": { "colocate": { "export": "createGithubTwinServer" } },
"endpoint": { "port": "auto" },
"bindings": { "injectEnv": "GITHUB_TWIN_URL" }
}
]
}Selection
selection is { include: Selector[], exclude: Selector[] }. A selector is a nonempty
object with only usage? and vendor?; fields match exactly and conjunctively. Vendor names
are canonical discovery ids, including ids for unmapped services. No globs, expressions,
ordered rules or inheritance. Within each list selectors are alternatives; exclusion wins.
An empty include list selects nothing. Omitted selection uses the example's application-only
default; init materializes it so the decision is saved, not repeatedly inferred.
Usage is one of application, dependencies, build, deployment. It describes a use, not
a permanent vendor category: dependencies means package acquisition; build and deployment mean
the supporting tools. Application calls made during a build still count as application use.
Pack descriptors classify vendor-facing tool packages (for example Wrangler as Cloudflare
deployment); registry destinations are dependency use and SDK/fetch evidence is application use.
Discovery records { vendor, usage, evidence } for each use. Unclassified evidence stays
visible as unresolved; it must not be silently excluded by failing to match a usage.
An exact package in the discovery registry's notExternal map may be local state
plumbing even when its npm scope also contains vendor adapters. Its ruling cites
the package's implementation and does not exclude other adapters in that scope.
First need: Harness's @chat-adapter/state-memory 4.39.0 stores subscriptions,
locks, queues and cache in JavaScript Set and Map objects; it opens no vendor
connection (the package).
Apply selectors to uses, then select a vendor if any use survives. Consequently an exclusion
of dependency-tooling npm use cannot erase an application's npm use. { "vendor": "npm-registry" }
deliberately addresses every use of that vendor. Selecting a twin still routes its configured
destinations throughout that execution environment; this is not per-request or per-process
usage routing. Different simultaneous routing requirements need separate execution environments.
Selection governs automatic proposals and coverage obligations, not whether an explicitly
declared service starts. Init preserves saved selection and never silently removes or rewrites
manual services; a conflicting proposal requires an explicit config edit.
Endpoint claims from saved services participate in regeneration conflict checks before any write.
Regeneration preserves document comments and merges infrastructure stubs by id, with saved stubs
winning. An existing seed entry point is operator-owned: preserve its bytes and report the imports
and calls for newly added default seeds rather than rewriting its order. covers reports
selected-but-missing, excluded, unresolved and declared-but-unreachable separately. Every declared
twin is checked for routing even if not detected. Excluded is not covered, and never means
real-service access or inherited credentials are authorized. Ordinary tooling exclusions need
neither questions nor individual acknowledgments. Existing explicit coverage acknowledgments
remain distinct from selection and must not conceal a fixable declared routing failure.
For a colocated instance, the host command lists every twin's module in --spec arguments.
Coverage takes a service's pack identity only from the spec bearing that service's id; another
service's spec is no evidence for it. Each declared twin remains a separate coverage obligation.
Coverage resolves a declared package from the World's root and reads its generated vendor facts.
The selected artifact's identity takes precedence over package, directory and executable naming
conventions, which remain fallbacks for older source checkouts. It never imports candidate code
to identify a vendor, and an independent package name creates no additional vendor obligation.
First need: Tabnode's colocated GitHub and npm-registry host lists both packs; coverage must
resolve each service from its own --spec entry.
Service layout
Each service requires id and type (twin, process, external). Keep the ordered array:
switching to a name-keyed map would obscure the existing startup-order contract. Service sections
are optional unless the selected type requires them; their leaf semantics are unchanged:
| path | previous fields / contract |
|---|---|
description | former // rationale; comments may also remain comments |
source | { package?, version? }; twin origin/constraint; version also applies to checkout-command twins without a package field, and is not the resolved version |
execution.process | { command?, args?, rootArg?, portArg? }; launch a twin or process; package twins may omit command and append args to the package's serve command |
execution.colocate | previous colocate, including module/export/scenarioPath; twin factory alternative to process launch |
execution.lifecycle | previous external up, status, down, readyWhen; external services only |
execution.cwd | previous cwd |
execution.environment.values | previous service env |
execution.preload, execution.controlPlane | previous preload, controlPlane; external lifecycle services may be control-plane services too |
endpoint | previous port, portReason, ready; World-owned listeners only |
bindings | previous injectEnv, injectEnvTemplates, cliRedirect |
bindings.discover | previous external.discover; external lifecycle output only |
root | unchanged vendor root, scope, deploy and refresh policy; twins only |
A twin has either source.package or execution.process.command, not both. source.version
may constrain either form; command twins resolve it from the same entry/module package as today.
A package or
process-backed twin may additionally declare execution.colocate; isolation chooses the
execution mode using existing eligibility rules. A process requires execution.process.command
and forbids source, colocate, lifecycle and root. An external service requires lifecycle up/down;
it forbids source, process, colocate, endpoint, preload and root, and uses discover
rather than injectEnv/templates/cliRedirect for outputs. Cwd and declared environment remain
available to its lifecycle commands. Invalid combinations fail validation before any process
starts. A fixed port still requires its reason; probing and external teardown retain current behavior.
Runtime records and migration
Actual ports, URLs, pids, timestamps, minted credentials and resolved package versions remain in the ignored instance record, not the manifest. No new lockfile. Birth provenance is different from resolution: preserve the catalog birth stamp, and record what actually ran separately. Coverage against a manifest uses its selection and declarations. Coverage against an instance uses its boot-time selection and actual inventory and identifies that source in the report; editing the manifest cannot retroactively change the instance's contract. An absent selection on a legacy instance means legacy coverage behavior, never the new application-only default.
The reader accepts legacy format 1 and format 2 and normalizes both to one internal model. Unknown versions and mixed old/new layouts fail loudly. Ordinary reads never rewrite files. Migration is explicit, backed up and atomic, and reports the field moves. Preserve service order, ids, roots, remotes, handlers, seeds, comments, scenario data, credential custody and birth stamp. Legacy default twin types become explicit. Preserve legacy selection by materializing includes for all four usages; switching to application-only is a separate visible decision. Deprecated resources is reported and retained in the backup, not reinterpreted as enforced limits. Unknown legacy fields or unsupported service combinations block conversion rather than being discarded. Do not rewrite running instances or ownership records; their pinned lifecycle performs teardown.
Round-trip and migration tests must cover package twins, version-pinned checkout-command twins, app processes, external databases, bare serving, sharing, roots/remotes, environment inheritance and scenario metadata, including custom cwd with relative preloads and process paths. Selection tests must cover all usages, mixed uses of one vendor, exclusions, new discoveries, saved-policy regeneration, unknowns and pre-boot versus running coverage. The format number is independent of the platform protocol; old runtimes are not format-2 readers.
The state kernel
The kernel reads a twin's tree through twinResources and writes through applyTwinWrite; a pack's handlers reach
both through their context. The head selects a simulated or real state system. The kernel owns log
ordering, checkpoints, branch positions, landing and receipts; the pack owns the vendor's wire,
resource semantics and executor adaptation. Serve paths do not reconstruct state by replaying
history or maintain a second mutable truth store.
Observations compare against the upstream view: the pinned inherited history, including its
projection layout, followed by this root's observed and landed entries. This root's local writes
are excluded from that comparison and remain overlaid when serving reads. Repeated upstream
state appends nothing even after local edits; an upstream change that matches a local edit still
lands upstream. A complete listing tombstones missing upstream subjects, never local-only work.
Adapters that validate vendor identity use readParentTreeMap for that upstream view alongside
the projected tree, so a local edit cannot hide an identity collision during refresh.
An adapter refusal identifies the vendor, resource and operation template beside the vendor's status and message,
so diagnosis can locate the failing read without exposing sealed credentials or guessing which operation ran.
A refresh scope collects both single-resource and batch observations, including empty complete listings. Nothing is appended until its adapter succeeds. The final fold owns the report and applies each service's completion declarations; a failed adapter cannot leave partial observed state behind. Collected roots remain distinct unless the caller supplies the destination root. A parentless list read through a scoped root contributes observations without declaring the account complete. Only a resource with a declared parent can contribute parent-bounded completion. REST and GraphQL refresh keep the same distinction, so a scoped read cannot tombstone retained records outside that scope.
A durable branch position identifies an immutable history view plus an offset within it.
Views retain verified prefixes of shared log segments and their projection layout; they never
resolve an ancestor through its mutable current branch pointer. Local forks copy no log payload.
View dependencies retain every referenced storage generation until the owning World is removed. A read's fold is memoized per store and per
process or isolate, keyed by the parent and branch logs' size and version and branch.json; a hit
hands out a clone and takes no coordinator, a branch that only grew by appends extends the memoized
tree with its new entries, and anything else folds in full. Each extension records the subjects it
touched, so a projection kept beside the tree (the derived core's index by type, a pack's own image such
as PlanetScale's SQL tables) follows a write by those subjects (treeChangesSince; a pack reading one type
uses that index, twinResourcesOfType, not a filter over twinResources) and refolds only when
the kernel cannot say which changed; a projection rebuilt from the whole tree after every write makes each
write's cost, and its garbage, grow with the World. A log's reader parses only what was appended since it
last read, and on a store with readRange reads only those bytes, checking that the last entry it parsed
still stands at its offset; a store without it reads the whole file. A checkpoint records the same facts, so a
cold read trusts it without re-hashing the logs; its digests remain the check when the facts differ.
Capture, observation batches, log appends and folds of one state directory share that directory's history lock
(history.lock in it); no captured view exposes a partial batch. A fold reads its inherited segments without their
directories' locks: they are immutable and pinned. The ancestry coordinator (one per VOLTER_HOME) is held only for
ancestry bookkeeping (a directory's generation and pins, branch pointers, removal receipts) and for the check that a
directory is not being removed, never across an append, a fold or a caller's critical section, so one World's work
never waits on another's. A state lock is taken before the coordinator, never inside it, and a branch's history lock
before its ancestors'. A removal records its receipt under the coordinator, then waits on each lock inside the removed
tree in turn, holding none while it waits on another, before deleting it: a holder that checked before the receipt
finishes first, and one after it is refused. A wait that fails deletes nothing and releases the receipt.
Checkpoints bind the exact inherited view, not only row counts.
Remote pages name one immutable view; fetch verifies and publishes a complete cache head
atomically, keeping payload rows shared with previous cached views. Push compares view identity
as well as position. A local fork baseline and its tracked remote origin are distinct references.
Captured views retain the origin cursor and its layout layer, so a historical or concurrent fork
cannot borrow a newer cursor from its mutable base. Fetch records a candidate origin; rebase or
push acknowledgment replaces that origin layer while retaining inherited local layers and their
storage ownership. Fetch updates the candidate under the same coordinator as selected pointers;
a fetch cannot restore a pointer that a concurrent rebase advanced. An existing local fork must
inherit an origin from its base before fetching one; attaching an unrelated first origin is
refused without changing its history. A historical cut that excludes part of the origin requires pull/rebase before
push. Rebase selects a newer view while existing descendants keep their old view. Push lands entries in the parent; deploy performs
landed entries at a real-system root under its policy. See
log.ts,
head.ts and
state-system.ts.
State persists through one synchronous store seam, WorldStore: whole-file read and write,
line append, list, stat and a lock. The same kernel runs on every backend. FsWorldStore serves
local Worlds. SqlWorldStore serves a hosted World from a Durable Object's synchronous SQLite: a
request touches only the rows it reads, and no load-then-flush boundary caps a World's size.
Versions reported by stat never repeat for a path, including across restarts, because
storage caches validate on them. The active store is scoped per async context
(withWorldStore over AsyncLocalStorage where the runtime provides it), so concurrent requests
for different Worlds in one isolate never see each other's store across an await. The
module-global swap remains only for runtimes without AsyncLocalStorage, and there store-scoped
work must be serialized. A served World's doors, its changesets, roots, sealed credentials and
refresh markers go through the same store, and the doors run against a host that supplies the
World's layout and each twin's wire: a local host forwards to the twin's own port, a hosted World
calls the pack in-process. What wraps sealed credentials comes from the host: a key
(setSealingKeySource, the user's key file when none is set), or a vault that holds the key and
wraps for it (setSealer with OpenBao's transit engine, company decision 0034: a credential a person
sets in a hosted product is held by a proven vault, never under a key the product keeps). The hosted
product gives each served org slug a transit key of its own (<VAULT_TRANSIT_KEY>-org-<slug>), so one org's
key never opens another's records and an org's key, deleted, takes exactly its credentials with it; the
fingerprint key stays one for the deployment, outside those names, so a real credential's ledger is one
across Worlds. Each sealed
record names what wrapped it (v: 1 a key, v: 2 a transit key). A v: 1 record opened on a
host with a vault is sealed again by the vault, keeping its placedAt and fingerprint, so the key a
deployment held opens each record once. The fingerprint of a v: 2 record is the vault's hmac with a
second transit key that is never rotated, so rotating the wrapping key and raising its
min_encryption_version leave it, and every ledger keyed by it, as it was. A record's DEK is rewrapped by an
operator's sweep after a rotation (POST …/credentials/rewrap, the vault's transit/rewrap); an old version of a
wrapping key is trimmed only after every World the key serves answers nothing left to rewrap. A root's credential enters custody
through volter twin <vendor> credential (company decision 0033: a vault is never required). A record
is bound to its destination (bound, origin=<the root's or link's origin>, in the AEAD's associated
data), set when it is sealed, which needs a root or a link: it opens for that origin and no other, so
whoever may change a root or a link cannot send the credential elsewhere. Once open, the executor sends it
to that origin and to the hosts the vendor's pack declares (a token endpoint, an upload host), which are
code, never a World's configuration. A record from before bindings refuses until it is bound, which its
World does as it wakes on code that binds, before it serves or refreshes, to where it went then. What is
piped in, else, when the person is signed in to a vault (BAO_ADDR or VAULT_ADDR, and that CLI's
token as bao login leaves it), the vault's kv/vendors/<vendor> record, else the one credential the
repo's .env files hold for that vendor. An app in a World keeps its fake credentials: admittedEnv
drops BAO_* and VAULT_*, so a person's vault sign-in never reaches a World's services (a token file
such as ~/.vault-token stays readable by any process of the same user: a World does not isolate files). A World's twins never share a module
instance with another World's: local Worlds run in their own processes, and a hosted World runs in
an isolate of its own (the Worker Loader, keyed by World and code version), so a pack's module state
(a delivery registry, a cache) belongs to one World by construction.
Credential input is read through EOF before deciding whether the pipe is empty. A delayed producer cannot cause
fallback to a vault or env file, and a read error is reported rather than treated as an empty credential. The CLI
keeps its stdin read alive on both Node and Bun until the producer closes it; no input bytes are printed.
The World's root refresh override uses atMost alongside every and webhook, matching RootConfig and the
existing --at-most command. This is distinct from a pack descriptor's refresh.onDemand.atMost default.
A vendor with multiple services remains one package with service areas and shared vendor-specific authentication, errors and client code. Common storage mechanics belong to the kernel; the kernel does not impose one vendor's resource schema or query semantics on another.
A local write is an occurrence. Two calls are two entries even when their content and frozen timestamp match:
kernel write ids include an occurrence ordinal, so a pack never defends itself against a write "returning" a subject
to an earlier value. At-most-once behavior is opt-in through idempotencyKey, scoped to service, operation, subject
and key; a vendor that supports idempotent creates resolves that key before minting a new subject id.
Observation and local writes share one tree. A write carries only the fields the operation owns; an explicit
null is a real update. Refresh uses observeResource, with complete vendor reads and vendor-specific handling of
missing resources.
Viewing a World
A World is a place a person can step into: look at it through each vendor's own screens, act in
it, move it through time and set one branch beside another. Viewing is the same on every host. The
parts a World answers belong to the served World's doors (WorldDoors) and the console; the parts
that make or remove a World (a branch) are the host's, and every host answers them alike, as
world-host and the hosted World already answer the same admin doors. A host supplies only what it
already supplies, the layout and the wires. A viewing capability built into one host's front and absent from the
others is drift.
A site a World's twins answer (a Workers Custom Domain, an R2 public domain: the hostnames a twin's hostsClaimed
door lists) is read in a browser at <hostname>.<world>--<org>.localhost, an origin of its own beside the World's
(siteOfHost, WorldDoors.site). The request goes to the claiming twin with the hostname as its original host, as the
injector routes it inside the World. It is reads only (GET and HEAD), on loopback origins, where the World's own page
needs no token either. A vendor's own screen links a site where the vendor would (Cloudflare's Workers & Pages lists
a Worker's domains): the World sends every request it forwards to a twin the address it shows sites at
(x-volter-world-sites), and the page links the hostname through twinSiteUrl, which falls back to the hostname itself
where the World shows none (its proxy routes it). The local view and world-host route these names. A host says where with a
sites template (world-core siteUrlOf): {host} nests the hostname under the World's origin on this machine, {site}
puts it as one label beside it on a hosted World (<hostname as a label>--<world>--<org>.volterdev.com, its dots as
hyphens, so one wildcard certificate covers it). A hosted site is shown to the World's people only: the board opens it
through the World's own origin (GET /-/<world>/sites/go?host=&land=), which hands the site's address a one-time read
pass, and the site's address opens a session bound to that one site from it, as the content plane's does.
A World is seen on two planes. Its doors, its console and the browser session that may approve, deploy, make keys and
change rules live at its own origin (<world>--<org>, the control plane). Its twins' pages, which carry other apps'
content and every pack's markup, are shown at an origin apart (<world>--<org>--pages, the content plane;
DoorHost.pagesOrigin), as a vendor keeps its users' content off the origin of its accounts (github.com and
githubusercontent.com). There a page holds a session of its own, opened from a one-time pass the control plane gives its
session or token (WorldDoors.pagesPass, a minute long): one per session that asked, shared by every frame of a board,
kept in the World's store for twelve hours from its last opening and ended with the session that asked (signed out,
or its shared link revoked). A branch's, opened through its parent's branch door, cannot know when the parent's session
ends, so it lasts an hour; it is kept per asker all the same. It uses the twins at its scope and opens no door, and it never forwards to a vendor itself (a link spends the
World's sealed credential, so a link is used only from the control plane). Its cookie is Partitioned and
SameSite=None, and the twins' own sign-in cookies are rewritten so, because the console's frames may be another site
(each *.localhost name is a site of its own in Chrome, measured 2026-10-02; a hosted suffix under one registrable
domain is one site). A twin's page is framed only by the console whose session asked, and the opening page only by the World's own
console or its parent's (its frame-ancestors); what
keeps another page from acting with a content session is that its writes must come from the content plane's own pages
(Sec-Fetch-Site: same-origin), as on the control plane. A browser's navigation to a twin's page on the control plane
is answered with the content plane's address and a pass, and anything else its session sends a twin there is refused.
A branch has its own two planes, so any branch, a pull request's preview included, is framed beside its World with
nothing of the World's session reachable from it. Every host gives a World both: the local view and world-host at
<label>--pages.localhost (or under --world-origins), the hosted World under WORLD_ORIGIN_SUFFIX. A World behind
a proxy, on a shared origin, or whose label is too long for the suffix (63 characters with --pages) has no content
plane and shows its pages where it always has. Sites stay at their own hostnames' origins, as they already were.
The view shows and moves the World; it does not play in it. Actors, the application under test and a scripted run (a launch rehearsed across vendors) are the caller's, reaching the World through its doors while a person watches, and their reusable forms are cookbook recipes. Nothing here schedules, generates or drives writes.
The RH2 review recipe declares its database, media, core Worker, Market Worker and ingress as World services. Its
one origin routes /market and its descendants to Market and all other paths, including /api/v3 and the console,
to core. The Market's requests back to RH2 therefore use that same origin. The recipe takes a prepared RH2_ROOT
checkout and an owner address; initialization and seeding use that checkout's migration and API doors. Its optional
Machine attachment declares a local Teams server and a task-owned machine consumer, trusting only this World's RH2
issuer and using credentials issued locally. It neither borrows an enrolled machine's credentials nor fabricates
Machine operations. ADR 0007 records the recipe's boundary.
The recipe's disposable PostgreSQL listens on loopback TCP only; it disables Unix sockets so a caller's task
directory length cannot exceed the operating system's socket path limit, and supplies the C locale when caller
environment filtering removed it. The core-only RH2 World recipe uses the
same service helpers, builds through RH2's package scripts and reaches its console and release through the product's doors.
Its AI binding retains the shipped declaration; explicitly declaring remote: false is rejected at startup.
Wrangler warns that AI calls require remote resources: those calls still require World coverage before execution.
The application Worker remains an injected consumer, including its Wrangler children, so outbound vendor calls
cross the World's routing seam. Its Hyperdrive connection URL carries a disposable password even for a trust-auth
database, as Miniflare validates the URL before connecting.
-
The board is the World's pages, chosen for the situation. Two jobs, two owners. A twin says what there is to see; the board chooses what is shown. Each pack lists its vendor's pages from its state (
semantics/board.ts, served atGET /twin/board): every page there is a record's own (an account's profile and each of its posts, a subreddit and each thread, a company page and its posts, a site and each page it serves), each with its kind and title, the page it sits under (parent: a post under its author's profile), the vendor's container it is grouped under (section, with the ids it was known by before,aka: an account a deploy adopted), the URL the vendor shows it at, the records it shows (covers), and whether it is the World's own (ours: an account the World's applications or people act as). Every field is a fact of the World's state; a pack never says which pages a person should look at. The kernel marks a pagedrawnwhen a built screen of the vendor (any of its lanes) takes its URL; a pack without the file lists its vendor's home workspace alone, answered before its clock or API runs. The World's board door (GET /-/<world>/board) gathers every twin's pages and says where each opens here: a hostname the twin claims as a site at the origin its host routes sites under (its sites template: nested on this machine's loopback, one label beside the World on a hosted World and opened through it, none behind a proxy), a drawn vendor page at the twin's place, or nowhere, saying why (unseen: a screen the pack owes, or sites not routed here). It counts each page's changes not yet cut by the requests that made them (an action'scorrelationId), giving each change's writes and when the first was made (since). Each page also carries the latest log position of a write to its covered records, including cut changes and landed receipts, excluding internal_bookkeeping. Each board read indexes those positions in one pass per vendor, then looks up the records each page covers. A frame refetches when that position moves (its change count and first-change time when no position is available); unrelated writes leave it loaded. A frame in use holds its document until the person leaves it, then applies the pending revision. Entering the board creates fresh iframe documents at the listed addresses. Reloads remount the iframe without changing the page URL; cutting a changeset alone leaves a frame with a position loaded. Frames load lazily so offscreen pages do not ask for page passes together. A page has one copy per browser whose site address can be made, including a signed-out browser. If any browser address cannot be made, the page also has one plain copy at its claimed-site address or vendor screen path, or with the reason it cannot open here. A browser label means that browser's state loads there. Plain frames precede named browser frames. Saved browser-less frame/section keys alias only the first browser by name, with a plain fallback frame taking its own key. Other copies are new frames. The server and console share one frame-key function: browser keys have a prefix no vendor name can start with. Saved positions found through an alias count as placed when determining a section's bounds. A board action edits only the saved key the person acted on. The console groups and labels copies by browser name, including hidden entries; it chooses no browser state. Browser frames in a branch view report that branch sites are unavailable; they do not open the parent's browser-site door. A board read gathers claimed hosts once to determine its page addresses. Ordinary site addresses name hosts actually claimed. A home frame's URL is its workspace path; its screen destination starts with a slash. A claimed host without a routed address reports sites-not-routed, even if the pack draws its page.Vendor marks use the console's Simple Icons where present, otherwise Google's favicon for a concrete hostname the twin declares in its workspace manifest or vendor page URLs, excluding pages marked
ours. A customer's site never supplies the vendor's favicon. The Worlds list shows the initial for a vendor with no bundled logo. The initial also appears when no hostname is declared or the image fails to load; the console keeps no vendor-to-domain table.The board shows the list three ways, over whichever state is looked at (the World as it is, or a branch: a stage beside the World it would change):
- Changes: the pages whose covered records changed (on the World, its changes not yet cut; on a branch, what it changed from the World), at any depth, with the pages it removed shown as they were. Posting makes the post's page and its author's profile appear; nobody arranges anything first.
- Everything: each twin's pages with no
parent, by section, the pages under each opened from it. - A board people keep: the pages they pinned, and whole sections with their pages to come.
A page is compared from the board: before is a read-only branch of the World as of the moment before that first change, made for the comparison and removed when it closes, its site read at the branch's own address, a vendor's page on the branch's own content plane (above), framed through this World's branch door (
GET /-/<world>/branches/<org>/<branch>/pages?land=<path>, a navigation answered with a one-time read pass to the branch's plane). Any branch of the World may stand in for "before", as a compare view sets one branch beside another (a pull request's preview beside the World, chosen in the comparison's header), and any page opens a comparison, changed or not. The boards as the World's people arranged them are the World's own file (.volter/board.json,PUT /-/<world>/board, at most 256 KiB), shared by everyone who opens it and never a vendor write: what each board pins, hides and includes, where its pages sit and its sections (a pack's group laid out as a section until it is moved, resized, renamed or removed, and sections people drew), a page being in the section that holds its middle, as on a canvas. A page pinned by an address no twin lists is framed with no changes counted, and is the twin's list to complete. A save names the version it was made from, and one made from a version another save has replaced is refused with the boards as they stand (409), never written over them; only a session that writes arranges them. Nothing is configured first: the board opens on Everything, with changed pages marked. Changes is a filter a person chooses; a board explicitly named in the URL takes precedence.The board's Viewing choice names the World or one of its branches in the URL. A branch's board is read through
GET /-/<world>/branches/<org>/<branch>/board, with read access, through the host to the branch's own World. Its changes count from the start of its own action ledger (world-boot), including changes already cut there; inherited history is in its parent log. It reuses the World's arrangement read-only. Its vendor pages open through the parent's branch pages door; a hosted branch's sites say they can only be shown on this machine.Built in x, linkedin, cloudflare, reddit and hackernews (twin-packs-p3
*/semantics/board.ts: every page from state,parentandours, a parent covering its deleted children). Not yet so (the work this rule orders): linkedin lists no member's profile (no screen draws one); every other pack keeps its home workspace; the board door drops a page its state no longer holds; a board pins no page by address. Where the evidence stops: the split is drawn from the owner's dialogue on the launch's board (2026-10-03) and the launch's five twins; no other pack's pages were listed against it. -
A World's changes count from its latest mark.
GET /-/<world>/markslists the marks andPOSTtakes one ({ note? }) while no twin writes;volter-world mark <world>takes a mark on this machine. The diff, the board's Changes and a new changeset start from the latest. The endpoint returnspassed, the number of uncut changes made part of that starting point; a changeset cut afterwards no longer holds them. A World seeded on this machine lands its seed as data already there; a World set up through its endpoints (a hosted one: accounts made through a twin's doors, a site deployed into it) is marked once it is set up, so what it starts with is not counted as its changes. -
The vendors' screens are the packs' own. A twin's discovery door (
GET /twin) lists its pack's built workspace screens (screens: [{ id, path }], Screens); the served World reports them on the map and a twin's status, and a person is shown one at the twin's place (/<served>/<vendor><path>), which a credential opens and where the twin answers it as it answers the screen at the vendor's host. The page reads and writes the World's wire through a browser session; acting in it is a write through the vendor's API, logged like any other. -
The read token can look, and the twin enforces it. A read-token holder opens a browser session whose scope is read (the session door is answered before the read scope's refusal of non-
GETdoors). A read-scope request reaches the twin marked read-only for that request, and the twin refuses anything that writes by the same check a read-only twin applies (D3), so the refusal is the vendor's and the pack's, never a vendor switch in the doors (A2). A derived pack already decides it by what an operation does, not the verb it came by (its classretrieve,listorcomputed, or its id in the manifest'sreads;crossCuttinginderived-core.ts), so a Slack Web API read sent asPOSTpasses and a write is refused. The marker isx-volter-read-only: 1, set on every request of the read token at a twin that enforces it (a GET that would write for its caller, as Upstash runs a command from a GET's path or Mixpanel ingests from/track?data=, is refused there too); at any other twin the read token'sGETpasses as it always has. It only restricts, so a twin trusts it without a token. To the pack, a marked request is a request to a read-only twin (the handler seam passesreadOnly), so it refuses in the vendor's own shape and takes no first-use credentials from it. Beneath every pack the kernel holds the line: a marked request runs in a read-only scope (request-scope.ts), and every append, blob write and git ref move refuses in it before anything is written, so a write a pack's own check missed (a GraphQL mutation, a git push) is refused all the same, once, never by asking the pack twice. Read-only refuses the caller's writes, not the vendor's own: a pack's catch-up (moves no API call makes) runs underrunAsVendorMoveand still walks the moves due by the World clock, as a real vendor renews a subscription whoever is looking. A handler that catches a move up itself (a read that renews what fell due) runs it inctx.asVendor(fn), the same scope, so it is recorded as the vendor's, never the caller's. A pack opts in by wrapping its fetch inwithRequestScopes(derived packs throughderivedRequestScopes) once nothing it does on a request escapes those seams, and says so in its manifest (requestScopes: ['read']); only such a twin is handed the read token's other requests, and any other getsGETandHEADonly. The rule for a viewer is that observing changes nothing the application can see or do (vendor fidelity is for the application's own tokens): a read-only poll answers the current state without advancing a job or delivering its webhook, a read-only read consumes no one-time result and spends no quota, credits or rate window, and an authorize leg is refused (a viewer mints no code). Every pack enforces it:createPackFetchwraps the pack's fetch in the kernel's request scopes and advertisesrequestScopes: ['read']; what a read-only request may not do at a vendor (a GET that writes, a first-use credential, an authorize leg, a deferred result consumed, metering, a poll that advances a lifecycle) is the pack's ruling where its handler acts. -
A World's browser session lives on its own origin. Worlds on one origin would share it: a page one World's twin serves could ride another World's session, and a same-origin page can even script into another World's screens in a frame, so nothing short of separate origins separates them. A host that gives each World an origin (
browserOriginon the host;<world>--<org>.localhoston world-host andvolter world view,<world>--<org><WORLD_ORIGIN_SUFFIX>hosted) serves that World and the console there and nothing else of its own; the World honours its session cookie only there (host-only,__Host-over https), and answers a session asked for elsewhere with 409 and its origin, which the console follows, carrying its token in the URL's fragment. A host's admin acts on its own origin, never a World's. On every host, with or without origins, a session-authenticated request that is not a read must carrySec-Fetch-Site: same-origin. Path addressing stays for callers that present a token, whose credential is never ambient. -
A local World asks for no token. A World this machine serves on loopback is the person's own: its pages never ask for, show or take a pasted token, and a token is asked for only by a World served for others (a hosted World, a team host, a non-loopback bind). What stands in for the token is proof that a request is the World's own page, as Vite checks its dev server's Host: any website can send requests to 127.0.0.1, so the local host's session door (
localTruston the host) grants a write session to a request that presents no credential only when the World has an origin of its own, the request's Host is a loopback name (a rebound DNS name is not), its Origin is exactly that origin (another site's page cannot claim it) and it saysSec-Fetch-Site: same-origin(one that does not fails closed). With--no-originsevery World's pages share one origin and none is handed a session: the console takes the token in the URL's fragment, as before. The console asks for the session first, on every open, so a restart, a cleared cookie or a rotated token is picked up without the person; the token stays in the World's files for apps and scripts. A session is an opaque id the World keeps, never a token, and rotating the tokens ends every session. The claim is not a secret. Anything that can reach the World's loopback port can make it: this machine's other accounts, a container or a WSL or VM guest that reaches the host, a port forward, a sandboxed agent on this machine. A local World trusts whatever reaches its port, as Vite's dev server does (Jupyter, by contrast, keeps a token by default), so its port is never forwarded or exposed; a World served for others binds a non-loopback--hostand asks for its token. On loopback read-only is a courtesy, not a boundary: any page of the World can open a write session. A browser's session deploys to the real vendors only what the request names (confirm), so no page, link or path deploys by being opened. A launch nonce the CLI opens, exchanged once for the session, would narrow the claim to the browser the CLI opened, at the cost of asking again on a fresh open; a call not yet made. On loopback,volter world serveandvolter world vieware the same front;viewalso opens the browser. -
The clock is a door. A World's clock lives in its store on every host and the doors answer
clock(show; set and advance with the write token), so a hosted World runs on scripted time as a local one does. A local World hands each twin process the clock's path; a hosted World's isolate, which is that World's alone, sets the same variable for itself. Advancing stays an explicit act, never a drift, and the clock moves only forward: never before its set instant nor, unset, before the World's newest entry. A pack's catch-up stamps each move at its due time, so a clock set back would put new entries before old ones. A World is taken back in time by branching, never by its clock. Advancing is instant and realizes nothing itself; each twin's catch-up walks what fell due on its next request, so the first request after a long jump pays for it. -
The past is a branch. A World as of an instant is a branch made there: the World's history door cuts each twin at the instant, and the host's branches door makes a World cloned from those cuts, its clock frozen at the instant so no twin's catch-up walks it on, and viewed like any World. A console's "as of" view is such a branch, short-lived: removed on request or when its time runs out, and until then kept (a local host mounts it again when it restarts). Local hosts make them with
LocalBranchesbeside the Worlds they serve (world-host under its directory,volter world viewunder.volter/branches/); a hosted World's supervisor makes its own and removes it on its alarm. The vendor wire answers only the branch's current state; no pack answers an as-of read. An instant is only as fine as the clock: under a frozen clock, entries stamped at one instant are all before it or all after it. -
Branches are compared, and changes move by changesets. The doors answer a branch's difference from its base (
diffWorld). A change moves from one branch to another as it does today: cut into a changeset, pushed and landed. There is no cross-World replay: a hosted World cannot reach a sibling World. -
The timeline is the World's logs, merged. Each twin keeps its own log; a door answers the entries of all of them ordered by each entry's
occurredAt, then by twin and position, paged, which the console renders beside the vendors' screens. The World keeps no order across twins finer than its clock (under a frozen clock many entries share an instant), and the timeline claims none. It reads the logs and folds nothing. -
A cause is followed by the tracing standard. A World links writes across vendors by W3C Trace Context, the way distributed tracing links services, never by an id of its own. A twin records an incoming
traceparenton the entries a request causes, and a delivery a twin makes (a webhook) carries thetraceparentof the entry that caused it, so the application's handler continues the same trace. An application instrumented with OpenTelemetry already sends it on its outgoing calls; the vendor's wire is unchanged, since real vendors ignore the header. Without it the World knows only order, and a view says so rather than guessing a cause. -
The console is the view, and it has several. It reads nothing but a World's doors (it holds no data path a
curlcould not call), and each view is another reading of the same doors: the map (every twin and what it holds, counted from its tree by resource type, each linked to its vendor's screens), the timeline, a trace (one cause's entries across vendors, from their sharedtraceparent) and the vendors' screens side by side under one clock. A view never gets a private door: what it needs is added toWorldDoors, where every host answers it and every other view can use it. The console also shows the clock and the branches. A host that mounts it (world-host at/-/console/) shows every World it serves; a local World that is not hosted serves it for itself by its own command beside the World, never by a step added to the runtime'sup.
The dashboard reviews what a World would do and manages its branches. What a World changed is cut into changesets (its changesets door: list, verify, approve, deploy). The dashboard shows them as a reviewer reads a pull request: what each changes at each vendor, its checks, its approvals and its receipts, with Verify, Approve (signed as the person the session names) and, for a shared World with a real root, Deploy, each refused where the session's scope or the World's rules refuse it. Branches are made, reset and compared there as a database's are: a branch as of now or an instant, reset to its parent, and the compare view between a branch and its parent. Every action is the World's own door; the dashboard adds no rule of its own. A branch made with a named key answers its maker with a write key of the branch named for that key, never the branch's own token: what a key may not do on the World (open a seal, change a check or a rule, make keys, open a write session) it may not do on its branch either. A session is answered the branch's read token.
The hosted product
Hosted tutorial fixtures hand the reader a signed-in person, an org and a token;
the page itself creates the World it promises to create. A host owns shutdown of
its mounted Worlds. Fixture cleanup calls that owner once, retains the fixture
when shutdown refuses, and reports the refusal as a failed walk. It never repeats
down against metadata the host has already retired. First need: the hosted
product tutorial's failed identity setup was followed by a second teardown of
the host's seed World, hiding the original failure with missing metadata.
The host resolves its existing World directory to its physical path before discovering or making Worlds. A CLI started in that directory sees the physical working directory too, so checkout-relative pack paths have the same base at initialization and mounting. First need: Twin's hosted bootstrap World uses checkout-relative pack paths through a symlinked output directory.
Volter World is one open product that runs on a laptop, on its owner's server and on Volter's cloud, as PostHog and Cal.com are: people, orgs and sign-in are part of the open product, and billing is the one part that is Volter's alone. Four layers, each usable without the one above it:
- The World (
world-core,world-runtime) knows nothing about people. Its doors decide one thing about a request: the grant it carries, which World,readorwrite, and who. - A host (
world-host, andapps/cloudon Cloudflare) serves many Worlds at one URL. It knows Worlds, their keys and the owner org recorded when each was made, never people or membership. Its admin doors (inventory, make, remove, keys, import) are one contract, written once over a storage interface, with a Node adapter (files) and a Cloudflare adapter (Durable Objects); a door or rule on one adapter and absent from the other is drift, as it is for viewing. - The platform (
apps/platform, open, self-hostable) is where people and orgs are: sign-in through an access provider, orgs, members, invitations, tokens for its own API, the hosts it has enrolled, and opening a World for a person. It reaches a World only through the host's doors, never its storage, and holds each host's admin credential in a secret store. It runs with no biller: then nothing is limited and nothing is priced. - Billing (
apps/billing, Volter's) is plans, checkout, usage, quotas and reminders over Polar. The platform asks it what an org may do and passes its doors through; the platform names no plan, price or meter itself.
A hosted vendor site serves its vendor's other declared or claimed hosts on its own origin at
/_host/<hostname>/<path>. The World routes that reserved prefix to the twin answering the target
host, only when it is the same vendor that answers the page's site. Unknown hosts and another
vendor's hosts answer 404. The page's site session, browser and access rules apply to the request;
no session at a sibling site is needed.
Product surfaces and documentation.
- The dashboard (
apps/console) is a World's pages: its vendors' screens, overview, branches, changes, activity, keys, the clock. It runs everywhere a World does and is the same page in every mode. It holds nothing about orgs, members, plans or money, and shows nothing that does not work on a laptop: no greyed-out feature, no upgrade prompt, no pricing link. Opened from a platform, its one link out is back to that platform (the issuer of the pass it verified). - The platform's pages (
apps/platform/client) are sign-in, orgs, an org's Worlds, members, settings and tokens, and a Billing page when a biller is attached. - The front door (
apps/www) is the static product and catalog site: landing, pricing, exact twin releases and legal pages. It is the only surface that carries analytics or marketing tags; the platform and the dashboard load no third-party script. Catalog browsing presents compact vendor cards with package descriptions, publishers and selected versions from the released index. Package pages lead with installation and workflow documentation; version history, compatibility, assessment measurements and provenance remain available separately. Publisher identity never implies measured coverage. Browsing does not import or assess packs. Static pages name their stylesheet and scripts by content hash, so cached assets cannot outlive the HTML version that selected them. - Documentation (
apps/docs) uses Fumadocs to render this repository’s canonical Markdown and explicitly exported cookbook examples. Fumadocs owns the docs layout, navigation, search and code presentation. A static export includes browser-local search and can run on ordinary static hosting, independently of the product site and World runtime. The build prepares content and brand assets into ignored directories; authors edit the original pages, never a generated copy. The desktop docs frame fills the viewport: navigation starts at the left edge, the article fills the column between navigation and the table of contents, and framework padding supplies its gutters. The documentation map owns navigation order. The product site links to the docs origin and redirects its former documentation addresses when that origin is configured. No sign-in, billing, analytics or World state is embedded in documentation. Both renderers use the configured public repository policy: a private source has no GitHub links, unexported case studies stay out of the public cookbook index, and the source link offers the served Markdown. Only declared example assets and documentation media are exported; a source link never exports a checkout. Preview serves the same static export used for hosting; publishing is a separate, explicit operation. The product/catalog and documentation exports use separate Cloudflare Pages projects,volter-world-wwwandvolter-world-docs. A production upload names the project's production branch explicitly, even when the operator builds from an isolated feature branch. Activate the documentation hostname before configuring the product site's documentation redirects. - The kit (
@volter/world-console/kit) is the brand's tokens, the header and the shared components. The platform's pages build on it; nothing in it knows an org or a price.
The dashboard's package depends on neither the platform nor billing, so neither can reach its bundle; the platform reaches billing only through the questions it asks.
The platform's state is SQLite. People and orgs (where the platform keeps the directory), the
Worlds each org holds, tokens, sessions, the audit log, webhooks and their deliveries, and the
biller's records are tables in one SQLite database, written through one schema (Drizzle) whose
migrations are plain SQL checked in beside it. The same schema and queries run on three drivers: a
file under the platform's state directory with Bun's bun:sqlite or Node's built-in node:sqlite
(the published platform needs Node 22.13 or later), and on Cloudflare the SQLite of one Durable
Object, which serializes writes and gives transactions. A write that must hold together (the last
admin, a key and its record) is one statement or one transaction, never a read then a write the
platform hopes is still true. A World's own state is not the platform's: it stays behind
WorldStore.
Access providers are optional. A World on this machine needs none: its page is trusted by loopback. A World on a host opens by key. People sign in only where a platform runs, through the provider its operator configures:
- Volter (
volter): Volter Identity (id.volter.ai). Sign-in, and the directory: people, orgs, memberships, roles and invitations are the identity service's, reached through its product door with the platform's own client credential. Volter's cloud runs this provider. - OpenID Connect (
oidc): any OpenID Connect issuer (Google, Microsoft Entra, Okta, Keycloak). Sign-in only; the platform keeps the directory itself, in its state. - GitHub (
github): a GitHub OAuth app, or one on GitHub Enterprise Server. GitHub is not an OpenID Connect issuer: the person is GitHub's own record of them, read once when they sign in, with their primary verified address, and keyed by their GitHub id, which a rename does not change. Sign-in only; the platform keeps the directory itself.
A platform has one provider, which brings its directory (Volter Identity's is the identity service; an OpenID Connect or GitHub provider's is the platform's own). The dashboard never talks to a provider: it trusts the passes of the platforms its host enrolled with, whichever provider signed the person in.
Access is decided in one place. Every door of a World asks one module (world-access) for the
request's grant, and a grant comes three ways, which a host is configured to accept:
- This machine: a loopback World trusts its own page (the local-trust rule under Viewing a World).
- A key: a World's key, named when made and revoked alone, for an app or a script. A World's existing token is its first key; keys are what an app holds, never what a person signs in with.
- A pass: a short-lived grant the platform signs for a person, naming the World, its own
origin (the one place the pass opens), the scope (
writefor a member acting as themselves,readfor a support session or a token that may only read) and the person's subject, checked against the public keys the platform publishes. A host trusts only the platforms it was enrolled with, over https (or http on loopback). A pass opens a World session only at the World's own origin, and only for a World that has one; a World sharing an origin opens by key. The platform never hands a person's browser a World's key; it issues passes. The command's key comes only to a token (volter login), the write key only to one that may write Worlds.
Who acted is recorded. Each grant says who acts through it, a person or a service: a pass names its person; a
key acts as itself, a service (an app, a CI job, an agent), for the person it was made for when it names one (whom the
platform made it for, recorded by their name beside the subject that revokes it, else the person whose session made
it); a World's own token is the World's service identity, as a deploy key or a CI token is a repository's; this
machine's page is the person volter login signed in on this machine (the command hands its person to a World it serves
here), else this machine. A write records its caller from the grant (key:<name>, key:<name> for:<person>,
person:<who>, machine, person for a session that names no one, token); a key's name says what holds it and never
holds for:. A changeset records who cut it (cutBy, the same form; the command on this machine records its signed-in
person) and its owner, the person accountable for it when the cutting grant names one. A served World takes a push from
any grant that may write it, as git takes an automation's. As a git commit's author is the sender's account and the push
is what the remote checks, a changeset arrives with the owner and cutter the World it was cut in recorded, and the
receiving World vouches only for what it records itself: who pushed it (pushedBy), and the pusher's person as owner of
a changeset that arrived with none. A changeset landed once keeps its first record. A changeset with no person behind it
is a service's, and says so. Who cut, owns or pushed a changeset is outside its hash, as its approvals are: recording it
binds nothing an approval bound to. A World's changeset rules (changesets in its config: how many different people approve a changeset before it is deployed, and whether a cut's summary is required) are a person's or the World's token's to change, never a key's: a key's holder does not relax the review its own changes go through. A key approves as itself, whatever name it gives, and counts for the person it was made for, so a person and their agent are one approver.
A World may be kept to some of its org. By default every member of an org reaches its Worlds. An org admin can restrict a World to named members (admins always reach it), as a repository or a database is restricted elsewhere. The platform decides it where it already decides membership: it signs no pass, hands no key and lists no World to a member the World is kept from, and taking a member off a World revokes the World keys held for them there, as leaving the org does. A host needs no new rule: it trusts the platform's passes, and the platform no longer signs them.
Every World has an owner from its making. A World is made through the platform, which records it in an org and asks an enrolled host to make it with that owner; the host keeps the owner with the World and answers it in its inventory. A World made directly on a host (self-hosted, or before the platform ran) is claimed into an org once, by the platform's operator, and the host records the owner then; a host that already records another org refuses the claim. Its existing token stays its first key, so its apps notice nothing.
People arrive at the provider. Signing up and signing in are the provider's (a new person is
sent there to make an account and comes back signed in); the platform keeps its own session and
nothing of the credential. volter login signs the command in through the browser: the platform
shows the command a code (the device authorization grant's shape), and the person, signed in as
above, approves it on the platform, which hands the command a token of its own for the platform's
doors: named for the machine, scoped to what the command does, expiring, revoked alone or by
volter logout. A World the command reaches (volter remote add <org>/<world>) is reached with a
key made in that World for it, shown once and revoked alone in the World's settings. The command
holds keys, as gh, the Vercel CLI and neonctl hold theirs; passes are for browsers, since a pass
names a World's own origin, which the command never uses.
The modes are the same code.
| Laptop | Self-hosted | Volter's cloud | |
|---|---|---|---|
| Dashboard | yes | yes | yes |
| Platform | no | optional, with the operator's provider | yes, with Volter |
| Billing | no | no | yes |
A local volter world view is a World with this machine's grant and no host or platform. A
self-hosted host takes its admin token and keys, and passes if its operator enrolls it with a
platform (Volter's, or one they run). Volter's cloud is the Cloudflare host enrolled with Volter's
platform, which runs the Volter provider and the biller. Nothing in the World, the host or the
dashboard branches on which.
The way in is the app's folder, for a person or their agent. The standard setup starts where the
code is, not in the dashboard: volter world init detects the app's vendors, volter login signs the
command in, and volter remote add origin <org>/<world> links the app's World to one on the platform,
making it there from world.json's vendors when it does not exist yet (asked, or --create for a
script or an agent). The dashboard's Create World stays for a person without a terminal. volter open opens a World's dashboard, local or hosted. The CLI installs from npm or a one-line script,
says when a newer version exists, and completes its own commands in the common shells.
Agents are first-class clients of the same verbs. volter mcp is an MCP server over stdio whose
tools are the CLI's verbs (status, up, down, view, log, branch, reset, link, push) called through
the SDK, never a second implementation: locally with the World's own token, on the platform with the
person's token from volter login. volter agents install puts the skill (skills/volter-world,
shipped in the package; it covers running Worlds on this machine and linking them to a platform) and
the MCP server into the coding agents it finds (Claude Code, Cursor, VS Code, Codex), and volter world init offers it. The front door publishes llms.txt, llms-full.txt and each page as
Markdown beside its HTML. Where a person starts (the landing page, the platform's Get started, the
dashboard's Connect) they can copy a prompt for their coding agent that does the setup above.
CI runs in a World, and a pull request gets its own. A GitHub Action in this repo
(actions/setup-world) installs the CLI, brings up the app's World from world.json and runs the
job's command inside it. Given an org token, it makes a preview World for a pull request as a branch
of a shared World on the platform, comments the preview's dashboard link, and removes it when the
pull request closes. A preview is an ordinary branch (its lifetime, keys and owner are a branch's);
the Action with an org token is the integration, as Neon's branch actions are, and no GitHub App is
needed. Other CI systems get a documented template.
A new org sees a World working. Creating an org offers a sample World: the platform makes
<org>/sample with a few common vendors and seeds it through the vendors' own APIs, from a story,
so its screens show an app's work before the person has written any. On first need, the sample's
caller uses the World's write token to obtain credentials at each pack's declared credential door,
then carries those issued credentials on the same World's vendor wire for every seed API call.
A World's dashboard can hand
out a read-only link (a read key, revocable, in the link), and follows a theme the person picks as
well as the system's.
Where this stands. Built: the four layers and three surfaces above (the dashboard's bundle
carries no org or billing code; the platform serves its own pages on the kit; apps/billing
attaches through the biller contract; the front door is a static site); the three providers and
their directories; every door's grant decided by world-access; passes, signed by the platform and
spent at a World's session door on world-host and the hosted World; owners recorded on both hosts
from a World's making or its claim; signing up at the provider; volter login, whoami and
logout; named keys; per-World origins on a host reached at its own URL (--world-origins); the
platform published and run under Node. The public product/catalog site is at world.volter.ai,
and the separate documentation export is at world-docs.volter.ai. The public site offers local
use with VOLTER_HOSTED=off; hosting these static exports does not activate a hosted platform.
Analytics identifiers are unset.
The catalog: where twins come from
The platform, pack repositories and moderated distribution have separate ownership. twin-world holds the shared platform and public authoring tools; publishers own their packs in independently operated repositories; the catalog admits immutable releases. This table names Volter's repositories, not the set of permitted publishers:
| repository | holds | changes |
|---|---|---|
volter-ai/twin-world (this repository, formerly volter-ai/twin) | everything that is not an individual pack: the kernel (@volter/world-core), the runtime, the host, the tooling, the CLI, the apps, the cookbook, and every document and tool a pack is created with (this architecture and its procedure, derive-pack, create-pack, check-sources, demand-scan, score-pack, the grader, the pack-facts compiler) | worked on |
volter-ai/twin-packs-p3 | Volter's Protocol 3 packs: one npm package per vendor, @volter/twin-<vendor>; no shared platform tooling | Volter's pack work happens here; independent publishers use their own repositories |
volter-ai/twin-catalog-open | source registrations, immutable submissions, pinned assessment policy, moderator admission records and the distributed index | catalog operation has its own release cycle |
twin-world holds no pack. volter-ai/twin-packs-open is the public trusted publisher root;
volter-ai/twin-packs-p3 remains the pack development repository. Private catalog history remains in
volter-ai/twin-catalog; public admission and distribution use the fresh twin-catalog-open root.
Protocol 3 only. The platform, the pack repository and the catalog index hold nothing of an older protocol: the
kernel's protocol is 3 and it refuses a pack that declares another, a catalog lists only
derived packs, and registered sources declare Protocol 3; independent publishers register their own repositories. scripts/p3-only.ts reads all three
repositories for any trace of an older protocol and --check fails on one. A pack that replaces a name npm already
holds publishes the next major (Creating a pack), and starts from the vendor's spec, never from an
earlier implementation.
A pack is built with the public standard against its own directory. Independent publishers use released
twin-standard commands with their installed SDK, without a platform checkout. A twin-packs-p3 checkout develops against a
checkout of twin-world beside it (its node_modules/@volter/* link to twin-world's packages), and is released against
published platform versions; the tools take a pack's directory in any repository, or a vendor or lane of the packs
repository found by scripts/pack-dir.ts: bun <twin-world>/scripts/derive-pack.ts <vendor>.
Repository tools and the pre-commit hook use scripts/siblings.ts to look for twin-packs-p3 and twin-catalog
beside this checkout, then beside the main checkout located through Git's common directory. A worktree can live
elsewhere without copying or linking those siblings; when neither location exists, the caller reports the missing
repository as before. The pre-commit hook reads each sibling at a stated revision, its origin/main
(VOLTER_SIBLINGS_AT), from a detached worktree kept under .volter/siblings/, never a shared checkout's working
state: another actor's uncommitted pack edits, or a checkout behind what is published, cannot refuse or shape a
commit here. A pack repository's worktree links its node_modules into this checkout's install.
An unreleased pack installs from the release's own artifact. scripts/pack-release.ts --pack-to <dir> --only <vendor,…> stages and builds the named packs exactly as a release does (facts compiled in, platform peers pinned,
dist/ built and the manifest pointed at it) and writes each as the tarball npm publish would upload, without bumping
a version, publishing or announcing. A World that must run a pack before its release (a browser-tab image, whose Node
refuses TypeScript under node_modules) installs that tarball, never the checkout's source entry.
A World's catalog is a list of sources, each a checkout of a pack repository or packages installed under a
node_modules (world-runtime/src/catalog.ts, mergedCatalog). A source lists only derived packs (a pack that carries a generated surface); installations of the same package in more than one source are served
by the higher major; different implementations require an explicit selection; init names the source and version of each twin it chose (the plan's source column), and
boots each twin the way its own source serves it. A pack repository holds vendors directly under its root;
checkoutCatalog takes that root. In a twin-world checkout, the pack source is twin-packs-p3 beside it; in an
application, sources are Protocol 3 pack packages installed or linked above its World root; their generated facts name the vendor independently of the npm scope and name. Discovery
never treats the platform's package tree as a catalog. Checkout services name the selected pack's actual CLI and
module paths relative to the World root, while installed services name their package. The kernel's host-side directory resolver also binds
pack assets to these derived sources, lazily, with no filesystem or process work at import. Registration refuses a
missing or incompatible descriptor before arming its hooks; hosts check the loaded descriptor before invoking a factory.
Compiled facts provide preflight evidence only: they cannot admit an incompatible loaded descriptor. The public website reads an exact installed catalog distribution through its released ./browse projection,
including checksum-bound assessment evidence. It never scans publisher checkouts, reads local grades or imports a
vendor. The landing page uses that same snapshot. Vendor pages show every implementation and recorded release,
its publisher, default selection, exact installation pin, admission mode and measured scope; absent measurements
say not measured. Immutable implementation/version pages preserve rejected and revoked history without offering
those versions as installation choices. Public contribution links lead to the distribution's own process and
registration surface. Website updates pin an index version and rebuild independently of catalog publication;
there is no polling or additional moderation service.
The user documentation connects discovery to execution: choose a release for the application's operations,
install its exact package/version, inspect the generated World selection, run the application's unchanged SDK,
and verify its result. The first tutorial teaches local simulated execution, reads after writes, inspection and
repeatable starting state. Task guides own publisher comparisons, explicit updates, scenarios, browser work,
CI and shared Worlds. The docs index routes readers by their task; catalog installation actions link to the
user walkthrough before contributor instructions. Admission and assessment measurements keep their recorded
scope and do not imply complete vendor fidelity, application test coverage or enforced network isolation.
The assessment policy also pins its invoker runtime, including Bun, and independent publishers qualify with that
runtime. A runtime change preserves pack assertions and is measured against the failing behavior before its pin
moves. First need: Bun 1.3.11 retained a native WebSocket count and stop promise after server-initiated closure;
Bun 1.4.2 retired the same policy close, normal close and abrupt termination, then completed the selected OpenAI
3.0.2 served customer life unchanged. Conditions: macOS, source fc4426f, one source kernel/evaluator, task-owned
World commands python3 run-command.py bun-1-4-2-close-mode-complete . <bun-1.4.2> bun-open-close-diagnosis.ts
and python3 run-command.py openai-served-life-bun-1-4-2 . <bun-1.4.2> openai-served-life-diagnosis.ts; retained
in company task evidence catalog-twelve-launch-20261006/bun-open-close-diagnosis.json and
openai-served-life-diagnosis-receipt.json. This is runtime/shutdown evidence, not full pack admission.
Publish builds require installer integrity and the production
catalog identity; explicitly selected rehearsal distributions are labeled in local previews. Cloud source builds carry packages by their manifest entry and current create<Name>Fetch
factory (including earlier create<Name>TwinFetch names), using file dependencies on checkout packages; index builds
use the selected published versions. ADR 0005 records this resolution.
Each pack carries its own facts (its hosts, adoption and World wiring: the descriptor),
compiled into its package (generated/pack-facts.json, written by pack-release), and every reader of facts goes through
one loader, world-core/pack-facts.cjs: the facts the kernel was built with (world-core/generated/pack-facts.json,
which scripts/pack-facts.ts compiles from the packs repository beside this or the main checkout and the explicitly declared
real-server backings in world-runtime/src/backings.ts), overlaid vendor by vendor by the facts of each pack the World
resolved from installed packages. The runtime lists those files in the World's instance file (packFacts), which every
World process reaches through VOLTER_WORLD_INSTANCE; the injector and vendor-hosts.cjs read through the loader.
The compiler never preserves entries merely because they were in a previous artifact. A backing's facts name its
server and adoption, with no pack protocol or factory claim; an absent pack's demand identity remains in the demand
scan's input until its own descriptor replaces it.
The catalog is an independently operated distribution. The platform owns the kernel, runtime, pack protocol and
versioned standard; independent publishers own their pack repositories; volter-ai/twin-catalog-open owns submission,
evaluation, moderator admission and publication. Its process contract
owns the submission schema, state transitions, trust boundaries and acceptance cases. It consumes released tools and
immutable npm artifacts, never a platform checkout. Website and hosted-deployment integration must consume the approved
index; neither is a prerequisite for publishing it. Catalog operation does not rebuild or release the platform.
Catalog preparation installs the released standard's development clients for its official SDK cases. A candidate's development dependency cannot override those evaluator-owned client versions; candidate-only development clients remain available for its examples. The retained preparation report records both requested ranges and the evaluator's chosen range when names overlap, alongside the resolved installation lock. This measures the standard's declared client case, never every SDK range in a candidate manifest. First need: OpenAI's candidate declares an older SDK without the Realtime module that the standard's documented cases use. A publisher's packing step confirms the selected artifact identity, inclusion of generated P3 facts and compiled entrypoints against npm's packed file inventory before upload. A passing source-directory journey cannot establish that omitted files were shipped. First need: Supabase's file list excluded generated pack facts despite a successful build and source qualification.
App initialization reads a selected pack's managedDatabase fact as a required World backing, even when the application's only connection variable is issued by that pack's credential door. It reuses an already declared managed backing; a fresh backing uses the existing managed-infrastructure recipe and a World-owned connection name, preserving $issue:<vendor> for the application's credential. Existing saved infrastructure is retained, never replaced from a descriptor. First need: selecting Supabase with only SUPABASE_DB_URL previously emitted its twin without the Postgres that must bind it, so its credential door correctly refused with 503.
The vendor being simulated and the package implementing it are separate identities. A vendor can have several implementations, from different scopes and repositories. The index names a recommended package explicitly when there is a choice. A World records the selected package and version; unrelated publishers' version numbers are not ordered against one another. Installed discovery reads the pack's generated facts (with conventional names retained for older checkouts), without importing executable package code. An ambiguous choice refuses and asks for a package selection. Existing World pins survive catalog changes. Publisher status never substitutes for artifact verification or an explicit admission trust rule.
User journeys connect discovery to execution. The website renders the canonical task guides and the cookbook index plus explicitly exported starter READMEs from this repository; other case studies link to publicly configured source and are not implicitly exported. A public cookbook entry identifies prerequisites and limitations, links its canonical example files and uses the public GitHub source when configured, and links the task guide; the same example is not copied into another source tree. Catalog vendor pages compare the available release measurements before installation. Installed first-use results show their actual SDK and CLI/runtime pins, retention and teardown separately from HTTP coverage and browser replay; historical reports without that result say it was not measured. A release page uses its measured CLI pin when available and a known released CLI otherwise. Its setup installs the CLI globally and the selected twin in the app, so the product command does not depend on a flattened project bin that another dependency can claim. Release setup preserves vendor/package/exact version context and names those installed choices in the World config; recommendations never change an existing pin. A served twin reports its booted package/version separately from its configuration pin, its execution/root policy and its declared data origin. The console consumes this projection, links an exact booted release to the catalog where available, and explains missing identity without inferring one. First-use Changes offers stored records, screens and Connect an app before changeset sharing; every control retains its existing owner. Init wires a generative twin’s project-owned handler file even when its published package contains no journey starter, preserving authored contents and using an empty handlers document for a new file. Tutorials cover manual adoption, recovery, browser/session/webhook work, repeatable scenarios, branches, CI worker isolation and deliberate candidate trials with released dependencies. No tutorial promise establishes unmeasured vendor fidelity or hermetic routing.
Worked-example links on catalog release pages are curated for an exact package and version, not inferred from vendor or publisher identity. Each points to the canonical tutorial or explicitly exported cookbook page whose released dependencies were walked. A different release does not inherit that example's execution claim.
Runnable tutorials declare their app files and commands for the shared fence executor (packages/cli/src/journeys/tutorial.ts). Task guides may instead lead into a canonical worked example, or describe conditional and interactive actions; they do not inherit a test claim from their directory. The static documentation check requires the executor reference and runnable commands on file-declaring tutorials, and preserves the CLI, SDK, HTTP-reference and complete navigation-map checks. Actual execution and its scope remain evidence recorded separately; the static check never runs a tutorial or measures its behavior.
The tutorial registry publishes the requested closure of this checkout's packages. Dependency versions named by
the page remain literal: a pinned historical release or a dependency maintained outside this checkout resolves
from the public npm registry when the local registry does not hold it, including packages in the @volter scope.
For a prepared workspace package, the registry's latest tag stays on those local bytes when upstream metadata is
merged. Other tags and explicit versions remain available; a newer public release cannot replace the source under
an unversioned tutorial install. The registry records and confirms each prepared version's artifact integrity after
enabling fallback. Its supported metadata filter owns this tag choice; no page command or version pin is rewritten.
Registry readiness requires a successful JSON ping while its own child is still running. Startup or publisher
registration failures retain the HTTP status and response type; an unrelated HTML response cannot be accepted as
a registry credential. The fixture stops only the child it started.
Each tutorial fixture sets Bun's global package and binary directories explicitly and gives that package directory
its own manifest. A global install must stay within the fixture instead of finding an ancestor's package or CLI.
The walkthrough retains which versions actually installed; a successful local package build cannot establish
that the page's dependency installation succeeded. The fence executor stops at its first failed step and retains
that result, then performs its existing shell and World cleanup. It does not execute later steps against missing
prerequisites. A documented refusal whose expected output matched remains a successful step under the existing
fence rules.
Failed walkthroughs retain their workspace and World diagnostics after normal cleanup. The executor selects
file-declaring guides and explicitly marked journeys (including self-hosting); conditional task guides are not run as empty apps.
An SDK walkthrough declares every credential its app reads and seeds required records through the vendor API before
the first dependent act. A World access token opens the served World; it is not a vendor credential. Rehearsals use
the current pack's declared account doors and vendor sign-in screen, never retired fixture endpoints or assumed
credentials. GitHub examples use the issued world account and explicitly create its repository; the platform org
in a served World's name does not create a GitHub organization.
Credential issuance during boot is account setup, recorded through the existing placeholder observation window
under that twin's control root. It creates usable stored identity without adding a pending application mutation
to the first changeset. The window closes on success or refusal and preserves any enclosing seed window.
Release pages distinguish a passing customer journey from measured fresh app installation. Historical customer reports without app-local dependency evidence retain their journey result and say fresh installation was not measured. A later standard or publisher badge cannot supply that missing evidence.
The first-use path begins with a concrete outcome and a choice between trying a small local example, bringing an existing app and joining a team's World. Documentation and catalog pages provide a next task and recovery route without requiring contributor knowledge or a platform account for local work. Returning users inspect the saved World and resume it; they do not initialize or reset it again as a routine start. SDK routing claims distinguish Node injection from explicit endpoint/proxy setup in other clients. Catalog empty searches lead to choosing or reporting a missing implementation. The documentation adapter labels file-declaring fences with their canonical path and offers copy controls for those files and runnable commands. Output and conditional console transcripts have no copy control, preserving the distinction between commands and observations. Suggested next tasks omit the current page.
Ordinary app initialization honors --twins before installation and planning, recording that explicit
vendor selection in discovery. --source vendor=package selects an installed implementation before
discovery can refuse ambiguity, and init records its installed version. The SDK exposes the same
choices as vendors and sources. A requested source must declare the requested vendor; a conflicting
saved World pin refuses instead of being silently replaced. Existing pins retain precedence over
catalog recommendations. The kernel's package discovery accepts these initialization choices as data;
it never imports a candidate to make the selection. Excluded vendors remain excluded in the coverage
report rather than being described as covered.
Config syntax accepts independent npm package names; publisher scope and naming conventions do not
establish vendor identity. The installed descriptor and Protocol 3 validation retain that authority.
Admission uses machine-prepared evidence and explicit account trust. A pull request supplies data only. The
catalog's trusted base evaluates the exact artifact with pinned released tooling in a credential-free execution
boundary, records integrity, provenance, dependency resolution and deterministic results, and reports readiness on
the submitted head. A changed submission, policy, source registration or tool version requires a new assessment.
An author explicitly trusted by numeric GitHub account ID, exact login and account type needs no separate
moderator approval, including on a fork PR. A named authorized maintainer merges after current-head readiness;
the evidence records trusted-account admission. Other authors need current-head moderator approval. Initially, trust decisions and assessment of outside
publishers belong to people; explicit trusted publishers, including Volter, need no separate review. Exact artifact
pins and the existing readiness process remain. Additional assessor hardening is deferred work, not a prerequisite
for trusted publication or human consideration of an outside submission. Maintainers
may merge their own changes without a separate review. The separate internal path requires an explicitly trusted
repository, a same-catalog PR, an authorized maintainer merge and the same readiness and provenance. Fork ownership,
organization membership, publisher badges and bot authors alone grant no account trust. Catalog workflows never
approve or merge a pack.
An internal publisher App is recognized only by its explicitly configured GitHub bot user ID and login, together
with the trusted source, same-catalog PR origin and authorized maintainer merge. Its key belongs only to trusted
publisher workflows. A repository-scoped token grants catalog contents and pull-request write to propose data;
publisher automation never approves, merges or changes repository administration. After an accepted npm upload,
submission preparation confirms exact-version metadata before proposing data, never repeating the upload.
Publication verifies protected-branch policy and the merger's current permission with repository-scoped,
read-only GitHub App access. The credentialed publication job mints a short-lived installation token for its own
catalog repository; no candidate evaluation receives the App key or token. The catalog's process contract owns
the installation and secret configuration. Missing policy access refuses publication rather than weakening admission.
The released standard owns fixed pack-file creation and indexing, the pack-facts compiler and assessment API. The compiler accepts explicit pack
locations and imports their descriptors only in the publisher's build; it requires no platform checkout.
Pack indexing reads vendor identity from the manifest; published assessment reads it from generated facts. Neither
infers the vendor from an independent publisher's package name. The published standard locates its manifest and
license at its package root in both source and compiled layouts. Client SDK fixtures ship their own manifests and
locks, declared by the standard; catalog preparation installs them before the offline phase and retains their locks
in the assessment receipt. Companion lookup keeps source siblings rooted at the pack repository, while installed
dependencies resolve from the selected pack's directory, including its local node_modules before ancestor
installations. Facts, fetch adapters, answer views, streams and read-only pack assets share that origin and the
kernel's existing package identity and selection rules. Each walked World binds the existing local asset-store seam
to those selected directories and restores its previous binding on teardown; evaluation cannot read assets from a
different checkout through host discovery. Clerk's published bundle is the first asset need. Its published Google
OAuth companion is the first workspace-local dependency need; the
standard does not require a publisher to hoist it or copy another pack's source. A kernel
dispatch observer measures operations actually exercised against the declared surface, retaining gaps in the
denominator. HTTP customer journeys replay through Chromium from each request's own origin, with fresh state,
a frozen World clock and pinned World draws. These journeys are authored HTTP transcripts, not scripts running
in one application origin: they may cross API, console and checkout hosts, capture redirect Location and Set-Cookie,
and name different people's Cookie headers. The transport observes the actual fulfilled HTTP response, including
redirects, and sends the authored HTTP headers to the pack. It does not substitute the browser's current session for
an explicitly named person's session. Reports label this per-origin HTTP replay and leave application-origin CORS,
native cookie-jar and DOM coverage unknown. A Chromium request still executes each step exactly once; no second
server request supplies an answer. Explicit absolute URLs stay explicit, and relative requests use HTTPS.
The separate strict browser-fetch transport retains one application origin, complete browser request headers, the native cookie jar and the step's explicit credentials mode, defaulting to same-origin. It returns only what JavaScript Fetch exposes; opaque redirects cannot stand for HTTP redirect captures. Missing CORS permission remains a denial, rather than letting the interception library add permission. Anonymous cross-origin reads accept wildcard permission; credentialed reads require matching origin and credential permission. The versioned transport fixture verifies those refusals separately from HTTP journey replay, including redirects and different people's authored cookies in the latter. Owners can run that fixture in Actions before publication; catalog verification consumes the same fixture from the released package.
An owner-dispatched served-transport diagnostic runs the exact published artifact and pinned assessment tools inside a disposable World on Linux. It records loopback client start/response/error and server handler start/response/error with monotonic elapsed times, retaining the dependency lock and journey results. Its served-only comparison repeats the same packaged customer journey with pooled Bun fetch, fresh-connection Bun fetch and the standard's existing Node HTTP client. It does not rerun Chromium or SDK suites. It changes no deadline, answer or admission criterion and has no automatic trigger. This separates a handler that does not return from a transport that does not deliver its returned response; it grants no catalog admission.
Pinning recognizes kernel draws in source and compiled layouts; pack-authored randomness remains observable as replay differences. Both transports use Linux libfaketime only in their owned Chromium process tree: native wall time is frozen at the World instant, updated before each request, with one-second precision; page JavaScript retains millisecond precision. Monotonic timers retain elapsed machine time. A missing native clock backend refuses. The strict fixture verifies Expires and Max-Age before and after a World clock advance without rewriting expiry. Owners can also assess a named unpublished pack tarball with released tools in a separate consumer World, retaining artifact identity, dependency lock and the report. It uploads no npm release and creates no catalog admission receipt. Provenance and moderator admission remain independent catalog checks. Quick journey replay and full admission conformance are distinct scopes. Reports retain the full declared API denominator, answered journey steps, recorded failures, replay equality, and unknown measurements; no aggregate score turns a missing measurement into a pass. Browser pages consume these reports; their redesign is independent work.
Catalog release pages also project package description, license, Node and kernel requirements, repository directory,
and README/issue links from the exact artifact's preparation report. Source documentation links use the commit
verified by npm provenance, not a moving default branch. Publisher maintenance and admission readiness remain
separate from supported workflows and measured coverage. Historical reports without package metadata show it as
unavailable; the website never fills those fields from a sibling checkout or current npm metadata. Independent
publisher builds select one named pack explicitly, retain its source and all lanes, and use released tool versions;
each selected release receives its own immutable artifact and submission through the existing manual process.
Published conformance runs the standard's compiled JavaScript SDK cases and Node runner beside dist/src; a source
evaluator uses the corresponding TypeScript files beside src. Node's type stripping is never used for files under
node_modules. A compiled evaluator must neither report existing cases as absent nor grant a client-case exemption
because it searched the wrong directory. SDK runners bind the
artifact's declared host keys to the assessed server through the existing injector; untwinned destinations keep the
enclosing World's refusal policy. Transport deadlines use monotonic machine time so a frozen World clock cannot
prevent a timeout.
The catalog publishes @volter/twin-catalog: source registrations, approved vendor/package versions, explicit
recommendations, revocations and evidence references. The CLI delegates volter twin submit to that package's
catalog-owned command, which prepares data for a PR without publishing or sending it. Installed discovery and
init consume the index; a missing recommendation among competing publishers is an actionable refusal. Pack release
tooling emits submission files instead of writing live catalog versions. Public-source provenance and protected
admission rules are activation prerequisites, not claims inferred from a successful npm upload.
Worlds within a World
A World's processes can start Worlds of their own: a hosted product's host provisions its customers' Worlds, and a release job builds in a World of its own. The inner World sits inside the outer one, since the World that covers npm cannot install anything itself:
- Routing chains. A service's environment is its launcher's admitted environment plus its World's own, so the outer
World's twin addresses (
<VENDOR>_TWIN_URL, the registry the npm twin answers as) reach the inner World's processes, and the injector routes a vendor the inner World does not twin to the outer World's twin of it. A vendor both twin is the inner World's. A process carries one injector, the inner World's: itsNODE_OPTIONSreplaces an outer World's--require …/world-core/inject.cjsrather than appending a second (worldNodeOptionsinworld-runtime/src/runtime.ts). - Sealing never widens. A World started by a process under a published network policy (the outer World's)
publishes at most that policy: its egress is its own list intersected with the outer one's, or the outer one's when
it declares none (
networkOfinworld-runtime/src/runtime.ts). A World started inside a sandbox inherits its cooperative HTTP/Fetch and guarded Node connection policy (transport coverage). - Remote attachment keeps the ceiling. A served manifest carries its network grants as
network, separately from suggested credentials. CLI attachment and the Node preload use the kernel's same projection: the target's grants intersect the caller's published ceiling, and the resulting policy is bound to the attachment's actualVOLTER_WORLDreference. An omitted target policy inherits the caller's ceiling; an unreadable caller policy closes external egress. A strict source or caller remains strict. Changing the reference never leaves another World's policy attached to it, drops that policy to make the request pass, or grants an untwinned destination. - The outermost World installs nothing from itself. Its runtime, twins and apps are on disk before it starts; its
npm twin (seeded from a local cache,
cookbook/catalog-release/seed-npm.ts) is what the Worlds inside it install from.
The World's Actions runner
GitHub runs a workflow on its own hosted runners: compute GitHub provides. In a World that compute is the World's, so the
runner is part of the runtime (world-runtime), not of the github pack (company RFC 0012, decision C: act needs a
container runtime and pulls runner images over the network, which the cooperative sandbox refuses only through mediated transports; native Docker traffic requires an enforced boundary (transport coverage)). It is GitHub's hosted
runner as a World service:
A job whose workflow grants id-token: write is given ACTIONS_ID_TOKEN_REQUEST_URL and
ACTIONS_ID_TOKEN_REQUEST_TOKEN, as GitHub's runner gives them: the GitHub twin's runner door hands them out, and the
twin answers the request as GitHub's OIDC provider (token.actions.githubusercontent.com).
- It takes queued runs from the World's GitHub twin: the Actions API lists them, and the twin's runner door starts and
completes them (
POST /_twin/repos/:o/:r/actions/runs/:id/start,…/complete { conclusion }). The twin keeps the run's state; the runner never writes it any other way. - Each job runs in its own workspace, the repository checked out at the run's commit over the twin's git plane. Its
steps run on the World's machine, with the environment GitHub gives a job (
GITHUB_*,RUNNER_*, the job'senv, the secrets it names) and the World's own: every call a step makes to a vendor goes to the World's twin of it, and nothing else leaves the World. run:steps run in the shell the step names (bash by default). The actions a workflowuses:are GitHub's own (actions/checkout,actions/setup-node,oven-sh/setup-bun,actions/upload-artifact,actions/cache), which the runner performs itself with what the World has; an action it cannot perform fails its step by name, never silently.- A secret reaches a job as GitHub's runners receive it: decrypted. The twin holds the repository's secrets key (the
public key
gh secret setencrypts to) and decrypts a secret for its runner; its API still answers no value.
Where the evidence stops: not built. RFC 0012's walks are its test.
The plugin contract
A pack is a plugin of the kernel in exactly seven things, and nothing else: the
descriptor, the wire, the tree contract, the engine slot, the real-system adapters, behaviour and
evidence. The kernel owns the log, checkpoints, branch positions, landing, receipts and the head;
the pack owns the vendor's wire, its resource semantics and its adapters onto the vendor. Standing is
pack.protocol read against PROTOCOL_VERSION (protocolStanding in
packages/world-core/src/packRegistry.ts): the kernel's own major is current, and any other, or
none, is refused. This section is the contract every pack implements; Protocol 3
is the form a pack is made in, which supplies most of it from the vendor's spec.
What the descriptor adds
pack: TwinPack, exported from the pack's index.ts; the descriptor under Protocol 3
defines every field, and is the field-level law. Beside identity, transport, archetype, resources,
hosts/hostsNone, endpointEnv/endpointEnvNone, adoption and rateBudget, a
descriptor carries:
protocol: '3'.refresh: { every?, webhook?, onDemand?: { atMost } }— how a root is kept current.everyschedules a pull in a served world (the root's ownrefresh.everyoverrides it);webhooksays the vendor pushes to the ingest door;onDemand.atMostis the least time betweenvolter twin <vendor> refreshruns unless forced. No other key is allowed.pullPostureremains the scheduling posture the v1 pull tooling guards (assertContinuousPullAllowed);refreshis what the served world reads.stateSystem: { perform, refresh?, ingest? }— the pack's half of the real state system, named on the descriptor, withregisterPack(pack)called on load ofindex.ts, so the head resolvesstateSystemFor(vendor)in the process that serves the twin. A pack bound to a root whose descriptor names nostateSystem.performis refused (adaptersForinworld-runtime/src/root.ts). A pack'sindex.tscallsregisterPack(.roundTrip— one minimal write on the vendor's wire, or a short sequence whose last write creates something new every time. It is what the branch round-trip sends; for a Protocol 3 pack the round trip derives it from the pack's life (below).references— which fields of which subject types hold another subject's id (referenceField(type, field, to), or{ type, to, key, adopt }), withreferenceTrip: a write that references theroundTripwrite's subject,{{field}}standing for the parent's field.shapeParity: 'held'once the write handler and the refresh adapter store the same shape, andparityOriginwhen the refresh adapter reads its scope from the origin.auth— how the vendor reads its credential when it is not a replaced header (D4).engine: { module }when the pack's state has a second half beside the tree.
Checked by scripts/pack-facts.ts (an undeclared archetype and a refused protocol are errors) and by the grade's
form section (twin-standard grade: the manifest, the pack contract, the handler imports).
The wire
One fetch factory, create<Name>Fetch({ root, readOnly, … }) in src/fetch.ts, returns a
(Request) => Promise<Response>: the kernel's createPackFetch (packages/world-core/src/pack-fetch.ts) over the
pack's parts, from the request and the tree to a response. Exactly one export matching create<Name>Fetch per index:
the catalog's walkers (packages/twin-standard/src/walk/vendor-fetch.ts), the branch round-trip and shape parity find the pack by it. The local server is that closure served through the seam.
The serve path is the import graph from that factory's module: every module it reaches by relative import, transitively. A file-name convention is not the rule. On the serve path:
- Reads go through the kernel's tree readers (
twinResources,readTree), history throughsubjectHistory, writes throughapplyTwinWrite, observations throughobserveResource. No serve module reads or appends a log row (listEvents,listActions,pendingActions,appendEvent,recordObservedDelta,syncPull). A pack's refresh, ingest and connector code reachesobserveResourceandapplyTwinWriteand nothing that reads or writes a row either. - No request-time egress; no clock but
worldNow()and no randomness in served content (R9). An application process a World serves reads the same time: the injector gives it the World clock, frozen or running, asDate—world-clock.cjsis the one home of the clock file's two forms; a World serving an application moves time withvolter-world clock shift, since a frozen instant stops the application's time. So the clock rule also covers what a twin signs, stamps or checks outside a served read — a delivery's signature timestamp, a JWT'siat/exp, a verifier's tolerance window: the machine's clock is used in no serve or delivery module of a pack, and each remaining use (the real-vendor roles, a browser-side helper) names its reason beside it. The one exception to the egress rule is a call that stays inside the World because the vendor's own topology makes it: a queue delivering to the application's endpoint or a proxy forwarding to its target. Each such module names its reason beside the call, and applies the World's egress rule itself throughworldEgressRefusal(@volter/world-core/network-policy), because the twin host runs without the injector: internal addresses pass, strict egress and the published network policy refuse the rest, exactly as the injector decides for applications. - A served value that must agree with the application's own env (a signing secret the application verifies with)
is read through
worldEnvValue, which answers only the variables the World sets itself (VOLTER_WORLD_ENV_NAMES, written by the runtime); what the caller's shell passes through never becomes World state. Two Worlds with the same configuration serve the same value. - The kernel's
RefusedWriteErrorandVendorWriteErrorreach the wire as the vendor's own error body and status throughanswerVendorErrors(fetch, shape); aHeadErroris a 500. A create's answer is built from the resourceapplyTwinWritereturns: under live use its id is the one the vendor minted, and the local id rides as an alias.
Checked by scripts/invariants.ts: the log-row (LOG), egress (EGRESS) and determinism (R9) rows; the clock rule at review.
The tree contract
- Every subject type the pack writes is declared in
resources; every write names type, id and fields. Bookkeeping the vendor never serves lives under_-prefixed types or fields, which shape parity leaves out by definition. - A module-level mutable map, set or
leton the serve path is refused unless its line says// cache:or// counter:with the reason. The tree holds state; a module holds nothing. - A resource's own
id,typeandupdatedAtcome back throughownFields(resource). The kernel keeps the subject's address on the row and the vendor's same-named fields beside it on a non-enumerable property, so a spread,Object.entriesor JSON of the row serves the address and never the vendor's field. Explicitly stored fields remain vendor-owned even when their values equal the subject address or logical timestamp; ownFields restores them. An absent vendor field is never invented from metadata. Jira comments and components first need this: the REST v3 spec names their own string id, naturally equal to the stored subject id (spec Comment.id and ProjectComponent.id). - A delete is the tombstone the write carries (
deleted: true). It holds until the subject is written again; a create under the same address resurrects it, and the write need not say so. - A fixed-width decimal id uses
{digits:N}: decimal digits derived from the same resource mint seed as hexadecimal ids, with a nonzero first digit and collision checks against held subjects. The declared width is preserved; the id is never the mint count. First need: Sentry's project creation example shows6758470122493650, a 16-digit id, also the account's numeric id form. - A sequence (an issue's open↔closed transitions, a changelog) is
subjectHistory— the entries that touched the subject, in order — never an accumulator a fold keeps. A set (watchers, votes) is one subject per member (<issue>::<account>), never a list one subject overwrites. - An id is minted from the tree: the next in its prefix over the ids state already holds, with
subjectHistorycounting the ids once minted and since deleted; never a module-level counter or a row count. A branch and its base minting the same id from the same tree is the expected outcome of determinism, and a receipt whose vendor id differs rebinds the subject.
Gate: the branch round-trip (the tree through a
checkpoint and a branch), shape parity (_-prefixed fields aside).
The engine slot
The side-effect-free multipart reader also exposes raw headers, part bytes, delimiter lines, preamble and epilogue
for consumers that compare wire framing. It recognizes MIME transport padding after a boundary or its closing
--, unfolds header continuations for field interpretation, and retains the original bytes for comparison.
The request reader and published-example comparison use this one parser; a malformed tail remains incomplete.
First need: a padded delimiter that an independent example parser mistook for a quoted upload value.
A pack whose state has a second half beside the tree that is not a projection — a git plane, S3
bytes, a SQL engine, a file's content — declares engine: { module }: the one module that owns
every write outside the world store. The tree references engine objects by hash. A file or
database write (writeFileSync, appendFileSync, Bun.write, renameSync, mkdirSync,
new Database(, PGlite(, or the pg driver's Client/Pool in a module importing pg) on the
serve path outside that module is not allowed, at any protocol, for a pack that declares engine. Bytes go through
the blob store (getActiveBlobStore); a protocol-3 handler reaches its pack's resource blobs as ctx.blobs (put,
get, remove by key, on the World's branch, a read falling back to an ancestor's; an upload's parts are ctx.parts(),
read from the body's bytes by world-core multipart.ts, an empty field name kept), and the World's managed Postgres
as ctx.engine (world-core managed-database.ts), with no engine module of its own; the kernel's module is the one
that holds a database session. The database's own clock is the World's under the containerless backing (managed
infrastructure, Two kinds of thing), so a row a default or a trigger stamps is stamped when the World says it was.
The log keeps a large request once. A write records the request that made it (input: the operation, its
parameters and body), so its history says what was asked. A string in it longer than 64 KiB (an attached file's
base64: an npm publish's tarball) is kept once, as a content-addressed blob under the service's state, and the entry
names it as { $blob, bytes }; recordedInput reads the request back whole. Measured at twin-world catalog-sources,
2026-09-29: seeding the npm twin with twin-world's 1,515 locked package versions left a 1.97 GB log for 703 MB of
tarballs, each publish's body recorded by each of its writes (bun cookbook/catalog-release/seed-npm.ts, a standalone
npm twin, a walk running beside it).
Git journey pushes accept a file's ordinary string content (mode 100644), or an explicit { mode, content } for an executable or symlink, and { mode: '160000', sha } for a gitlink. These encode native git tree entries and traverse the same smart-HTTP wire; a gitlink references a commit without inventing blob bytes. First need: the GitHub contents reference publishes symlink and submodule answers, which require those actual tree modes as their replay preconditions. The git library reads .gitmodules metadata as git configuration.
The git library renders complete unified diffs and email patches from its object store, including file modes, blob hashes, hunks, commit authors and dates. Pack handlers select their documented media type and hand the library the actual commit or comparison base; they do not rebuild git serialization. First need: GitHub's commit and comparison media types answer diff and patch bytes, alongside JSON and commit SHA options, over the same stored history.
A git-hosting pack mounts the kernel's git library (packages/world-core/src/git/: loose objects,
packfiles, refs as world state, smart HTTP as serveSmartHttp, protocol v0 with shallow fetches by deepen <n>) over
the blob seam rather than a git binary; a Protocol 3 pack reaches a repository as ctx.git(name) and serves it
from a content screen at the vendor's git paths (github's screens/git.ts, the Hugging Face Hub's).
Each named Git repository owns its object namespace as well as its refs. A hash is an address,
not a read grant: resolving a known commit through another repository or asking its upload-pack
for that hash must not reveal those objects. First need: the Hub's private repositories are
visible only to their owner or organization;
a shared service-wide object pool let a public repository serve a private repository's commit.
ctx.git(name) therefore mounts objects under that repository's prefix. Shared physical blob
deduplication can retain storage efficiency without sharing access.
Git LFS is the git library's too (git/lfs.ts): the batch API git-lfs speaks
(https://github.com/git-lfs/git-lfs/blob/main/docs/api/batch.md, basic transfer), a pointer file read, and an
upload accepted only when its bytes hash to the object's oid and its size is the one declared. The objects are the
pack's resource blobs (ctx.blobs, keyed by oid); the pack says where its objects are uploaded to and downloaded
from (the vendor's own hosts) and who may do either. Built at its first need, the Hugging Face Hub, whose repositories
are git with LFS weights and whose pinned revisions are real commits, so a World is seeded by pushing the real
repository and its objects.
The real-system adapters
This section is the specification of the vendor-backed half, which the kernel derives (world-core/src/derived-real.ts).
Its acceptance is scripts/vendor-backed.ts <vendor>, pack-done's step (g): a World whose twin's root is a second
World's twin of the same pack, on a loopback origin, deploys the life, refreshes from the second and takes its signed
events. A pack registered without its surface derives none: binding a root to it is refused (adaptersFor,
world-runtime/src/root.ts), and STANDING.md lists what each pack's manifest still lacks for it. Its check of a known
read asks it as the refresh does: the operation's own method, the scope's query filled from the subject, and the
answer read under answers.key (Telegram's getChat?chat_id=, asked by POST, answers { ok, result }). A vendor that
reads its key from the path ({ in: 'path', pattern }) has the key the life's writes carried there sealed as the root's
secret, and the acceptance's own requests carry it in that place, as the executor's do. The key is the one most of
the life's writes carry (a second bot's is another account). A subject of a known-GET type that both Worlds hold from the
account's own setup (a chat a person made on the vendor's site, a door's write on each World) is one the vendor knows,
though no root copy holds it before the refresh.
The acceptance and shape-parity walks carry the credential header the manifest declares, rather than assuming Authorization. Socket conversations are replayed on the second World after API setup has been deployed, using adopted ids and signatures issued by that second World; they are world-only wire activity rather than HTTP deploy calls. The score distinguishes a socket's normal close code from an HTTP refusal. First need: ElevenLabs' xi-api-key and signed conversations.
Vendor-backed acceptance reads every derived unit the standard discovers, including a vendor's own API beside its lanes. A front with no surface has no operations to perform; adding a content-download lane does not remove its parent's refresh scopes. First need: OpenAI's media lane exposed acceptance's assumption that any lanes replaced the root API.
Vendor-backed acceptance plants out-of-band resources through the vendor API. If a multipart or action-style create cannot be reconstructed from public resource fields, it replays a successful creating HTTP input from the life and verifies a new subject of a fully listed type. This preserves the original uploaded bytes and required body instead of inventing a filename or omitting creation.
A World runs a twin one of two ways (the model). Simulated: a write lands in the twin's
state and is answered from it; nothing reaches the vendor. Vendor-backed: the twin has a root (the vendor's real
account); a landed write is performed against the vendor, the vendor's state is refreshed into the root, and
its signed webhooks are ingested. Reads are answered from the World either way. The three adapters
(StateSystemAdapters, world-core/src/state-system.ts) are derived by the kernel from the pack's surface and
manifest: no pack writes adapter code, beyond a hook for a protocol one request cannot express ("Protocols a request cannot express"). They run over the kernel executor (RemoteExecute, remote-execute.ts), which
applies the sealed credential by the pack's strategy; the credential never enters pack code (D4).
Where it lives
world-core/src/derived-real.tsexportsderiveStateSystem(manifest, surface)and, for a vendor of lanes,deriveVendorStateSystem(vendorManifest, lanes)(lanes: lane name →{ manifest, surface }), each returning{ perform, refresh, ingest }.packOf(manifest, surface?)(packRegistry.ts, and the root export inworld-core/src/index.ts) attaches the result asstateSystemwhen a surface is given; it also registers the pack's references (below) and its rate budget.packOfthrows on a descriptor that declaresstateSystem,referencesorrateBudgetby hand. create-pack'sindex.tstemplate becomesregisterPack(packOf(manifest, surface)).adaptersFor's error names the manifest'srefreshscopes, not a handstateSystem.
What a pack declares
Vendor facts, cited like any other, on DerivedManifest (derived-core.ts) unless said:
-
The id field: the existing
ResourceDecl.idAs(the answer's id field when notid) andResourceDecl.key(a composite stored id): perform and refresh read ids through them. -
Cascade targets: a deletion cascade's
fieldmay name a nested vendor field by dot path, using the same field lookup as parent matching; an exact field name takes precedence. First need: Clerk's Backend API membership identifies its user inpublic_user_data.user_id. The pack keeps that vendor shape after local writes and refresh instead of inventing a second top-level user id for cascade matching. -
Counted children: a declared count's
bymay name a nested vendor field by dot path, with exact field names taking precedence, using the same lookup as parent matching and deletion cascades. First need: Linear'sUser.createdIssueCountfollows the issue's vendor-shapedcreator.id; the kernel keeps it through its existing counted write path rather than a pack maintaining a second counter or flattening that reference. A count may require a vendor field to be absent withabsent; null and missing both satisfy it. First need: Linear's defaultTeam.issueCountexcludes issues whosearchivedAtis set. The count remains a recorded parent write when an issue is created, changed or archived. -
ResourceDecl.refresh:{ list: '<operationId>' }(the list that enumerates the type; under the resource's existingparentdeclaration it runs once per parent),{ get: '<operationId>' }(a singleton), or{ none: '<reason>' }(the vendor offers no read-back). The operation a refresh reads by is in a pack's scope, as the life's are: the vendor-backed acceptance reads the twin as the vendor, so the twin serves it. A read that answers a live credential's secret (an API token's value) is not refreshed: the account's secrets stay in the head's credential custody, never in a World's tree.complete?: falsewhen the list is not the whole type (open items only);items: '<JSON path>'where the answer holds the list when the spec's reading does not class the operation a list (a nullable array, a union of list envelopes, a list nested in its envelope), any read then taken ($bodythe answer itself). A spec read from a vendor's own client (tinybird-cli's requests,spec-ir-client) classes every request an action and names no resource, so a resource the spec does not name is read by the GET the scope names, a list's items whereitemssays and a get's the answer itself; one the spec classes as a write is refused. First need: Tinybird'sGET /v0/workspace, the one Workspace the root's token belongs to, a singleton read by a request the client calls an action. A list asked by POST (an RPC list: AWS JSON'sx-amz-target) takes its page in its body, andnext(withcursorwhen the parameter is named otherwise) names a vendor's own cursor (AWS'sNextToken). Every call of an operation carries its query discriminator (S3's?uploads) and fixed headers, in a perform and a refresh alike. The operation a scope reads is one the pack serves (create-pack --indexrefuses one that answers the gap): a World rooted at a second World's twin reads it there. Every stored non-bookkeeping resource declares one. A list that returns summaries declaresdetail: '<operationId>': refresh fetches each listed id's detail before observing it. Detail path parameters are filled from the list row's fields and itsidAs(for the resource id), with the parent parameters retained; a detail read that names its subject in its body or query (AWS JSON'sDescribeSecret, asked by POST with{ SecretId }) declaresdetailWith: { <name>: '<template>' }, each{field}filled from the list row and{id}the subject's id, sent in the body of a detail asked by POST (as the operation encodes its body) and in the query of one asked by GET. The detail answer, not a blend of summary fields, is the stored shape. Every detail call carries the same credential and refuses a failed response before anything folds. First need: ElevenLabs lists agent and conversation summaries, while their detail operations carry conversation_config and metadata/transcript (agent list, conversation list). When a list's response schema is a page wrapper, its explicit items path and a declared detail read of the resource establish the stored type. Derivation verifies that detail is a read of that type instead of comparing the wrapper's schema name to the item resource. Jira's PageBeanProject.values with getProject is the first need; it stores Project, never the page wrapper (spec searchProjects: "A page of projects."). -
ingest(onDerivedManifest, and onVendorManifestfor a vendor of lanes, where it is declared once):{ scheme: EventScheme | EventScheme[]; type: { header?: string; body?: string; action?: string }; object: '<JSON path>'; types?: Record<'<event type>', { resource: string; deleted?: true }>; handshake?: { when: Record<string, string>; answer: string } }— the signature (events.tskinds); where the event names its type (a header such asX-GitHub-Event, a body path, and a sub-action path joined with.); where it carries its object; the type → resource map whereevents.typesread in reverse and the<resource>.created|updated|deletedpattern do not say it, each type with its own object path where it carries it elsewhere, the path of its parent's value where the event names the parent beside the object (GitHub'sissueunderrepository.full_name), where it names the subject's id when not as the resource does (id: Resend'semail_id), and the fields the type itself says (fields: Resend'semail.deliveredislast_event: 'delivered'); and a handshake answered instead of folded (Slack'surl_verification:when: { type: 'url_verification' },answer: '$body.challenge'). -
rateBudgetonDerivedManifest(the descriptor type omits it): the vendor's documented limits, charged by the executor on every live call (D8). -
headersonDerivedManifest: request headers that carry meaning to the vendor and are recorded with a write (a version header, a tenant header such asStripe-Account); the manifest's existingversion.headeris included. -
The credential strategy:
descriptor.auth(TwinAuthStrategy,executor.ts), absent meaning header replacement of the pack'sauth.header. An OAuth vendor declares anexchangeover a sealed refresh token (the board'soauth-rootscard, which covers every OAuth pack a product of ours calls, googlecalendar and googleoauth among them). A vendor that reads its key from the request's path declares{ in: 'path', pattern }: a regular expression over the path whose first group is the key's place, which the executor fills with the sealed secret after the origin is validated (a secret holding/,?,#or%is refused, so the path cannot be moved). First need: Telegram's Bot API, whose every request ishttps://api.telegram.org/bot<token>/METHOD_NAME(Making requests), and its fileshttps://api.telegram.org/file/bot<token>/<file_path>.
OAuth 1.0a roots require a new HMAC-SHA1 signature for each request. The executor's
signature strategy oauth1-hmac-sha1 reads consumerKey, consumerSecret, token and
tokenSecret from custody, collects query parameters and form body parameters (JSON and multipart
bytes are excluded), and signs the normalized URL after origin validation, with a fresh nonce and
timestamp. X's signature guide
requires these inputs; a precomputed Authorization header cannot authenticate a later deploy or refresh.
An ordered choice strategy selects by a nonempty sealed field, permitting OAuth 1.0a credentials or an
OAuth 2.0 refresh-token exchange for the same vendor; no applicable choice refuses before egress.
Acceptance and parity walks retain the credential material a signed life actually acquired, rather
than copying an already signed header onto a different request. Packs never read root secrets.
A REST refresh scope may declare fixed query parameters on its list, get and detail requests.
X's fields guide returns only default fields unless
explicitly requested. Refresh must request the stored vendor fields, and retain that projection
while following its cursor. A list asked by POST sends these parameters in its encoded body,
with numeric and boolean values typed by the operation's body schema (AWS's
ListSecrets needs
IncludePlannedDeletion: true to enumerate scheduled secrets too). These parameters are manifest data;
they do not change a caller's reads.
What the derived core records with a write
writeDetailed (derived-core.ts) records input = { operationId, params, body } today. It changes to record the
request as it came, so perform can send it again:
operationIdonly for an operation of the pack's (or lane's) surface; a door or screen recordsinput.door = '<id>', a gap writeinput.gap = true, and the clockinput.clock = trueinstead;lanefor a lane;path(the path parameters as sent),query(the declared query names as sent, a repeated name as its values) andbody(the body alone, before the core's parameter merge and alias adoption, with only the fields the operation declares when it declares any;$blobvalues stay references, seeserve.ts), andheaders(the manifest'sheadersas sent). A credential the caller put in the query or the body is not recorded: a perform carries the root's. The caller'sx-twins-request-id, when it sent one, is recorded asrequestId;- a GraphQL mutation:
input.graphql = { query, variables, operationName }in place ofoperationId; - a multipart request: the file parts in the World's blob store and
input.files = [{ field, name, type, blob }]; - an operation whose body is its payload's bytes (one
blobfield: a SmithyhttpPayloadblob, S3's PutObject and UploadPart): the body as it came, whatever it parses as, and never refused as malformed JSON for its label. A PutObject of a JSON file (the platform's nightly backup,content-type: application/json) was recorded as its parse and re-encoded by a perform, so the vendor stored other bytes under another ETag.
Perform
What is offered. The head's deployableEntries (head.ts) offers only entries that record a surface
operationId or graphql on a non-bookkeeping subject. Door, screen, gap, clock, seed and scenario writes are the
World's own: never offered, never refused, and a deploy report counts them as world-only. So is an entry whose
subject a refresh has deleted, or whose subject is only ever world-only; a deploy never stops on them.
An operation of the vendor's own API can be account setup rather than use: the manifest's accountSetup names each,
with who performs it and where (an operator registering an OAuth client on the vendor's admin routes, as a person
makes settings on its site). Its write is recorded as a door's, so it is the World's own: never deployed, and held by
the vendor-backed acceptance's second World as what the account already has. First need: Volter Identity's
adminCreateClient and adminLinkClientResource, which an operator performs as the signed-in person; a deploy that
sent them would register real OAuth clients and need that person's session, which no root's credential carries.
One request, one call. Entries share the request that made them. The pack fetch (pack-fetch.ts) mints the
request id itself, always (a caller's x-twins-request-id is kept as provenance, never as the group); it becomes each
entry's correlationId. The head performs per request, not per entry: performEntries groups the offered entries
by correlationId, in order, and calls perform once per group. Under auto (serve.ts, which performs at append
today), the pack fetch performs the request's group once, after the handler returns and before the answer leaves; the
answer's local ids are then rewritten to the vendor's through the aliases the group adopted.
perform(execute, group, ctx) (PerformAction takes the group; PushOutcome gains also?: Array<{ actionId, externalId }>):
- The request is rebuilt from the group's first entry's
input: the surface operation's method; its path frominput.path, each parameter resolved by kind: an id parameter byctx.resolve(<stored type>, value)(resolvetakes the stored type,storedAs); anumberparameter (ResourceDecl.number) or aparent.whereparameter by reading that field from the adopted landed copy; akeytemplate by splitting on its separator and resolving each part; its query frominput.query; its body frominput.bodywith$blobreferences rehydrated, encoded bybodyEncoding(json,form), or frominput.filesfor multipart;input.headerssent as recorded. Ids inside the body are resolved by the pack's references, whichpackOfderives from the manifest'sembedsand eachparent.field. When the manifest declaresidempotency, its header carriessha256(<root credential fingerprint> + ':' + <group's first entry id>), so a retried deploy makes nothing twice and two Worlds deploying into one account never collide. - A 2xx answer is read through
successand the operation'sanswers.key. Its id (byidAs, orkey) goes to the group's entry whose subject type is the operation'sanswers.resource(a cascade writes children first, so never simply the first entry);datais the answer. Each other entry takes its id from an answer path the resource declares (ResourceDecl.companions: { '<resource>': '<JSON path in the parent's answer>' }) and is reported inalso; one with no path settles deployed with no id and is adopted by refresh (below). The answered resource is the manifest's resource the schema name is, else the one its envelope key names (Slack'schannel,message); when no entry of the group is of that type (Slack'sconversations.joinanswers the channel, its group is the membership and thechannel_joinmessage), no entry takes the answer's id or fields. An answer with no id where one was expected settles deployed with no id; a non-string id is never adopted. A delete (op: 'set',deleted: true) answers its subject's own vendor id. An XML answer is read through its document element, as a JSON answer is read under its envelope key (S3's CreateMultipartUpload answersInitiateMultipartUploadResult, the upload's id itsUploadId). An answer whose schema names no resource of the manifest (an operation's own output structure: Secrets Manager'sCreateSecretResponse, S3'sCreateMultipartUploadOutput) is the group's subject's, its id read by that subject's resource (idAs,key). First need: AWS, whose UploadPart names the upload by the id its creation answered, so a perform that adopted none sent the World's id to the vendor. - A refusal: a 4xx other than 408 and 429 (409 included: a conflict does not clear on retry) is
RefusedWriteError('vendor', '<status>: <the vendor's message>', <first entry id>); the whole group settles refused and the deploy stops, as today. A group whose request names a subject that settled refused or world-only is itself settled world-only, never sent. - A failure: 408, 429 (with the vendor's retry-after), 5xx and a network error throw a plain error; the group settles failed and stays offered for the next deploy.
GraphQL (input.graphql) posts the document to graphql.paths[0]; the new id is read from the mutation's answer at
data.<mutation field>.<the returned type's id field> (the schema names the returned type). A gRPC unit, a socket, a
line protocol (smtp) and a managed database have no derived perform: a pack of one declares
vendorBacked: { none: '<reason>' } on its manifest, and binding a root to it is refused with that reason, never
swallowed. A pack whose own surface declares it beside lanes that are vendor-backed (PlanetScale's SQL wire beside its
API lane) keeps its lanes' adapters, and a deploy settles its own surface's writes as the World's, with that reason
(StateSystemAdapters.unsent), never sending them.
An operation the vendor takes as multipart (bodyEncoding: 'multipart') is sent as multipart whether or not the
recorded request held a file part: a Workers upload of static assets alone carries only its metadata field.
Protocols a request cannot express
Some writes are the last call of a protocol whose earlier calls the vendor answers with a credential the later ones
carry. Cloudflare's static-asset upload opens a session (assets-upload-session) that answers a JWT and the buckets
of files it lacks, takes the files with that JWT, answers a completion token, and only the script or version upload
that names the completion token is the write. The World's twin ran the same protocol, so the recorded upload names
the twin's token, which the vendor refuses; the asset files are the World's blobs, not entries. Sending the recorded
request again cannot work.
The manifest's performs names such a write's operation and a hook, a function of the pack's shared semantics, that
performs it in place of the derived request: performs: { '<operationId>': hook } (PerformHook, derived-core.ts).
The hook receives the root's executor, the group and its first entry's recorded input, the perform's context, send
(the derived perform of an input: the recorded one, or one the hook changed), resolve (the vendor's id of a World id
of a stored type), the surface's base path and the twin's blobs. localGroup retains the request's recorded effects
before root reference translation. A hook uses that local provenance for blob ownership, while resolve names the
root's account. Blob ownership captured with an upload remains local bookkeeping across adoption and push; a hook
never searches unrelated accounts for matching bytes. It runs the protocol's earlier calls through the
executor and ends with send, so the write itself is still the derived request and settles as one. A credential the
vendor issued during the hook goes on the request as issued (RemoteExecuteRequest): it is sent as the request's
Authorization, on the root's own origin, and the sealed credential is not. The hook never sees the sealed credential.
This is a new mechanism, so it has to justify itself. Before writing a hook, show that the derived request cannot be
made to work, with the vendor's protocol cited. A protocol that ids can express (an S3 multipart upload's UploadId
is a resource id that resolve maps) stays derived. Cloudflare's is the first hook (cloudflare/api,
uploadWithAssets). Measured on 2026-10-02: a toy site deployed into a World with wrangler deploy, pushed, approved
and deployed reached a second World standing in for Cloudflare. A new version of it took the versions path, and the
same changeset deployed to Cloudflare itself answered at its Custom Domain.
The account a root is
A vendor whose paths name the account (Cloudflare's /accounts/{account_id}/…) has a World account its own doors
made, which no deploy settles, so adoption never aliases it. The manifest's account names that resource. Its
refresh scope lists it. Before a deploy performs, and in a refresh, when the root's credential reaches exactly one
account, the World's unadopted account of that type adopts it. Every path naming the World's account is then sent
with the vendor's. The deploy reads that one list itself, because a refresh that another resource's refused list stops
folds nothing. A credential that reaches several accounts adopts none, and the requests name the World's id.
A vendor whose lanes authenticate apart (Cloudflare's API by bearer, its R2 by SigV4) declares auth: { in: 'lanes', lanes: { <lane>: <strategy> } }. A perform or refresh names its unit's lane on the request (RemoteExecuteRequest.lane),
and a lane not named takes the sealed headers.
Refresh
A refresh continuation reads the vendor's response field named by refresh.next; a string names one cursor, and a parameter-to-field map sends several. The next request uses the declared operation, carrying those markers. Missing markers end the list; repeated markers fail before observation. First need: AWS S3 and Cloudflare R2 multipart-upload lists carry both NextKeyMarker and NextUploadIdMarker (S3 ListMultipartUploads).
A list whose continuation is an HTTP Link header declares refresh.nextLink: { header, rel, query, when? }: select the relation, require its declared link parameters (for example
when: { results: "true" }), and read the named query marker from its URL. The next request
uses the declared operation and marker; it never follows that URL. Missing markers, malformed
links and repeated cursors fail before anything folds. A missing relation ends the list. First
need: Sentry pagination, whose next link is present even
when results="false". Its twin lists declare list.cursor with encoding: 'offset-template',
template: '0:{offset}:{reverse}' and link: { previous: 'previous', next: 'next', results, cursor };
the core reads the declared position and direction and emits the two links under the relation
names the vendor uses, each with its availability. The template, the relation names and the
link parameters are vendor data, not a kernel special case.
A parent selector's where keys may name nested vendor fields, such as project.id.
The core's parent check, perform's address adoption and refresh's parent expansion read the
same field path; a literal vendor field of that exact name takes precedence. First need:
Sentry's monitor check-ins, whose monitor embeds the project in its serialized answer.
A list's detail read may declare refresh.detailMerge: 'result' when the vendor returns only a
configuration fragment at that answer path ($body for an unwrapped object). Its fields merge
over the list item's vendor fields before observation;
the default remains a full detail replacement. The fragment is read through the root executor,
and any refused read aborts observation. First need: Cloudflare's
Cron Trigger read
returns a Worker's schedules but omits the script identity and upload metadata returned by the
Workers list. Both belong to the same script subject, not a second schedule resource.
A refresh identity may be a field path or a template in refresh.idAs, joining the vendor
answer's fields when its subject has a compound address. First need: Sentry's events, whose
projectID and eventID form the stored key while the route names those parameters differently.
A known-subject read may declare refresh.params mapping path parameter names to templates over
the stored subject fields. This preserves a compound address without guessing which id each
parameter wants. A known child retains its declared private parent field and parent.keep
fields when its vendor answer omits the routing association. First need: Sentry's event read,
addressed by organization, project and event id, while its stored answer names projectID and eventID.
A GraphQL resource's refresh keeps its list or get operation id and adds graphql:
{ query, variables?, items, pageInfo?, cursor? }. The query is the vendor's read document,
items its JSON path to the object or nodes, pageInfo its Relay page information, and
cursor the variable receiving endCursor (default after). Parent path parameters substitute
whole {parameter} variable values. The adapter POSTs the document to graphql.paths[0],
keeps paging while hasNextPage is true, refuses GraphQL errors even with HTTP 200, and
folds nothing until every scope succeeds. A missing or repeated continuation is a failure,
never a complete list. First need: Linear's GraphQL-only resources and its
pagination, which uses first/after and
pageInfo.hasNextPage/endCursor.
A request with several mutation root fields adopts each returned resource onto its matching entry in execution order, including each landed vendor shape. Selected aliases (including the id field) are decoded to schema field names before adoption. Inline string ids, like variable ids, cross through the alias resolver. HTTP transient failures remain retryable even when their body contains GraphQL errors.
The generated pack index carries the vendored GraphQL SDL beside its REST surface. Perform
uses that schema and the recorded document to identify the selected mutation payload's
resource and response aliases, including named fragments. It adopts the returned resource's
id rather than the payload wrapper; GraphQL errors settle the request refused. No vendor
names or payload-key guesses choose the subject. Linear's
issue mutation examples return issueCreate.issue,
not an id on issueCreate itself.
A refresh scope may declare blobs: [{ path, key }]: a dot path in the observed vendor object,
with * traversing object values or array items, to download URLs. The kernel reads each URL as exact bytes
through the root executor, checks its host against the descriptor, and retains it in resource blobs.
The key template may use {pathname} (the decoded URL path). It finishes every download before folding
any observation; a refused or failed download leaves the previous observations intact. npm's
metadata reference
returns version tarball links, and its attestation links return signed JSON bytes: refreshing only the
package document would expose links whose payload a fresh World does not hold.
A list that names no download link has its subjects' bytes read by an operation: a blobs entry
{ operation, field, headers?, headersField?, keepHeaders? } names a GET of the unit's surface, its path filled from the
observed subject's fields and its parent's parameters, sent with the entry's headers; the bytes go to the World's
resource blobs, their key to the private field, and the answer's headers keepHeaders names to the private
headersField (only those: a date or a length would change every read). Every read finishes before anything folds. A
list that pages by several markers names them as a map (next: { '<parameter>': '<answer path>' }), a vendor's flag
that a page follows as more (its absence or false ends the list), and a page naming the cursor it was asked with is
refused, never read again. A list may carry fixed headers ({name} its parent's parameter) and variants, header
sets each listed in turn with the type whole across them; a child keeps a parent's parameter it is addressed by again
as a private field (parent.keep: { _jurisdiction: '{jurisdiction}' }). First need: Cloudflare R2, whose
ListObjectsV2 answers keys, sizes and ETags and no bytes (GetObject
answers them, with the object's Content-Type and x-amz-meta-*); whose ListMultipartUploads pages by key-marker and
upload-id-marker with IsTruncated; and whose buckets are listed per jurisdiction by the cf-r2-jurisdiction header
(data location), a bucket's objects and uploads then
addressed on that jurisdiction's host. A part has no download operation: an observed part names no bytes.
Linked payloads may redirect within the descriptor's declared hosts. Refresh follows at most five
redirects, validating every destination before the guarded executor reads it. A blob key may use
{header:<name>} from the final response, and resource with fields can observe a declared
bookkeeping resource beside the bytes ({key} and {header:<name>} substitutions). No vendor
resource is synthesized. This keeps response metadata in the tree without storing bytes there.
ambientCG's public catalog returns downloadable files;
its /get link redirects to a public B2 object, whose download reference
supplies the file name, content type, SHA1, file id and upload timestamp. Both the redirect and these
headers are needed to serve the file after catalog refresh. A loopback root standing in for the vendor
receives declared absolute vendor URLs at its anchored root path with their original Host; live roots
retain the declared destination. Neither a redirect nor this substitution admits an undeclared host.
The vendor-backed acceptance exercises known GET scopes as well as whole lists. For a known scope it changes a known vendor subject through the acceptance fixture's vendor-state seam, verifies that refresh observes the change, then deletes a known subject and verifies its 404 removes only that subject. It never requires a known read to discover a new unrelated subject. The same fixture seam already simulates vendor deletions for list scopes. This matches the completeness of known reads above.
refresh(execute, { root, scope }) runs each resource with a refresh scope, parents before children, and runs
before a deploy performs (root.ts refreshes, then performs, today; this keeps that order):
list: paged by the manifest'slistmodel (limitat its max; the next page byafterwith the last id, bycursorfrom the envelope, bypage+ 1, or byoffset; per-operationenvelopes/limitswhere given), until a page comes back short or empty, or 10,000 items (the type is then not complete). Under aparent, once per parent observed: aparent.paramis the parent's id; a parent the path names by several parameters (parent.params, GitHub's{owner}/{repo}) fills each from the parent's observed fields through itsparent.wheretemplates (full_name: '{owner}/{repo}'), and a parent whose fields fill not every one is skipped; a template that is one parameter whole takes the field whole, its slashes included (an S3 upload'sKey: '{Key}', where object keys name folders with/, so ListParts is asked ofuploads/user/recording.webmtoo). Each item is observed{ type: <storedAs>, id: <the resource's stored id: its key template filled from the item and its parent, else item[idAs ?? 'id']>, fields: { ...item, [parent.field]: <the parent's id, or parent.value filled from its parameters> } }, the value the derived core stores in that field.- A list the vendor documents as one unpaginated response declares
refresh.unpaginated: true. The adapter sends no page-size or cursor parameter and treats that response as the whole scope, including a nonempty response. This is explicit rather than inferred from a missing page size: Fly's volumes list returns all volumes, and its apps list likewise has no paging parameters. A vendor's optional paging (Fly Machines enables it only withlimit) may use the unpaginated form too. - A list returned as an object keyed by resource id declares
refresh.keyed: trueand itsitemspath ($bodyfor the whole answer). Each map key is the observation's subject id; the value is stored unchanged, without injecting an id field the vendor does not return. - A list whose items are not each one subject's object declares
refresh.shape, which turns each answer item into the subject's fields before they are observed (the subject's id then comes from the resource'skeytemplate, filled from those fields and the parent's):scalar: '<field>', each item a bare value that is that field (Slack's conversations.members answers user ids);value: '<field>'withkeyed, each map value a scalar that is that field, the map key the id or, withkeyAs: '<field>', the field it is (emoji.list maps a name to its image URL);spread: { path, as?, carry? }, each item expanding into the array atpath, each element an object or a scalar namedas, with the item's fieldscarrynames ({ <field>: <path in the item> }) beside it (reactions.get groups its users under each emoji). A scope'sidAsis a path (message.ts). First need: Slack's memberships, emoji, reactions, pins and group channels. A parent named by several parameters (parent.params) sends each its read's path does not take in the query where the operation declares it there, as a singleparent.paramis (reactions.get reads a message by itschannelandtimestamp). Malformed maps refuse the refresh before folding any scope. First need: Poly Haven's catalog specification, whose assets schema maps asset slugs to asset metadata. An unpaginated keyed scope is complete even when nonempty; deleting a key drops the corresponding observed subject. - An XML answer (S3's lists) is read as JSON, an element's children its fields, a repeated child a list, a leaf its
text, so a scope names its items alike (
items: 'ListBucketResult.Contents'); a lone child is a list of one. get: one call, one observation (under aparent, once per parent id observed); a 404 is no observation.getwithknown: true: the vendor lists no whole type, so each subject of it the vendor is known to hold (the parent tree: landed or observed, never a World-only subject) is read again by its vendor id, its other path parameters filled from its own fields. A 404 is a subject the vendor no longer has; completeness is the subjects read, never the type. What those reads observe anchors its children's lists, which scope by observed parents as any child does. An answer that names no id of its own is the subject it was read for (Cloudflare's workers.dev subdomain, one per account, answers only{ subdomain }). First need: Volter Identity lists no people or organizations of a product; a person's organizations and an organization's members and invitations are each read under a known parent. A read that names its subject in the query rather than the path (an RPC method: Telegram'sgetChat, whosechat_idthe Bot API takes in the URL's query string as in its body, Making requests) declares the scope'squerywith{name}placeholders, filled as the path parameters are: each the subject's own field of that name,{id}its vendor id. Such a read asked by POST is a get when the manifest'sreadsnames it (an RPC read, as a list asked by POST is a list), whatever schema the spec says it answers: an RPC wire's reads answer a fuller form of the subject than its writes do (Telegram'sgetChatanswers a ChatFullInfo where a message names its Chat), read under the operation'sanswers.key(result).- A scope whose answer names its subject by another field than the resource's id declares
idAs(the field's name): the Hub's create answers a repository's ObjectId asid, while its read answers that ObjectId as_idand itsnamespace/nameasid, so its read scope saysidAs: '_id'and its other path parameters are the repository's own stored fields. First need: the Hub (repository API). - One
observeResourcesbatch (observe.ts);completenames each type listed in full whose scope does not saycomplete: false, restricted to the root'sscope(RootConfig.scope,state-system.ts) when it has one, and a type under a parent is complete only for the parents observed. - Adoption. An observation whose vendor id no local subject has adopted, and which matches a subject a deploy
settled with no id (same type, same parent, and equal on the resource's
adoptByfields, else itsalternateKeys, else created by the same group), adopts it (the kernel's alias) instead of folding a duplicate and tombstoning the local one. - A refused credential is never folded as an empty account: a 401 throws, naming the request; a 403 (a token scoped to
part of the account) leaves that type as the World holds it, never complete, and the answer names it (
unread) while the rest folds; a 429 throws with retry-after. - An answer holding every fixed leaf of the manifest's error template is the vendor's refusal whatever its status, as a
perform reads it: a refresh throws on it and folds nothing. A vendor that answers its refusals with 200
(Slack:
ok: falsewith itserror) would otherwise have a refused page read as an empty list, and a type listed whole (Slack's users, by users.list) folded as an account that holds nothing, every subject of it tombstoned; a token missing the list's scope (missing_scope) is such a refusal. - A vendor-backed World does not seed: the placeholder's default data is not written to a twin with a root.
Ingest
The door POST /-/<org>/<world>/twins/<vendor>/ingest (served-world.ts) is keyless: the signature is the
credential. ingest(request, { root, secret }):
verifyEvent(scheme, secret, body, headers, { now, tolerance: 300 })(events.ts, as it is, exported from the root) for each declared scheme;nowis always passed. Missing or invalid: 401, nothing folded.- A
handshakematch is answered as declared and folds nothing. - The type is read from the header, body path and action path the manifest names; the object at
object. The type maps to a resource bytypes, elseevents.typesin reverse, else the created/updated/deleted pattern. An unknown type is acknowledged (200) and folds nothing. - The object (at its type's own path, its parent's value filled from the event where the type names one) is observed
{ type, id, fields, deleted }as refresh observes it, and only when it is newer than the subject the root holds (by the resource'supdated_at-like field the manifest names asResourceDecl.version, else always); an older event folds nothing. - The answer is 200
{"received":true}.
The signing secret is one per root, sealed with the credential (root.ts). Registering the vendor's webhook to point
at the World's public ingest URL is the operator's step, written in the root runbook (volter twin <vendor> root
prints the URL and the secret to enter at the vendor).
Lanes
A vendor of lanes has one root and one state system. The executor's origin is the root's; a lane whose API is on
another host names it (LanesDecl route host), and perform and refresh send that lane's calls to it through the
same executor (RemoteExecute takes an absolute URL on a declared vendor host, as a presigned call does).
A lane whose host carries the call's own parameters declares it as remoteHost: { template, values? } on its
manifest: {name} is filled from the call's parameters (the path's, and its host's through hostParams, as the write
recorded them; under refresh, the parent's parameters and the subject's fields), and values maps a parameter to the
literal it puts in the host (an empty parameter to nothing). Perform and refresh then send that lane's calls to
https://<the filled host><path>, the request naming its lane (RemoteExecuteRequest.lane); the executor admits it
only on a host the descriptor declares (host, suffix or hostPattern, an exclude winning), and a loopback root
standing in for the vendor takes it at its own origin with the vendor's host kept (host and
x-volter-twin-original-host), signed over the vendor's URL. Where a lane authenticates otherwise than the vendor's
API, the descriptor's strategy is { in: 'lanes', lanes: { <lane>: <strategy> } }: a lane named takes its own, every
other call the sealed headers; one root credential holds both (its headers and, for a signature, keyId and secret).
A SigV4 scope may be data ({ region, service }) where it is fixed, so a manifest stays data. The executor signs at
its now (the wall clock; a fixture World's clock where an acceptance stands a twin in for the vendor). A write
records, beside the manifest's named headers, every header of a declared prefix (x-amz-meta-*). First need:
Cloudflare R2, whose S3 API answers each account at
<ACCOUNT_ID>.r2.cloudflarestorage.com (a jurisdiction's at
<ACCOUNT_ID>.<jurisdiction>.r2.cloudflarestorage.com, data location)
and takes SigV4 with region auto, while Cloudflare's API beside it takes a bearer token: the API lane's root
credential sent to R2's paths at the API's origin reached no bucket.
The branch round-trip and shape parity below are how this is held once it is built.
Behaviour
The scenario adapter (scenarioStatus on the fetch adaptation, the pack's scenario engine over
the kernel's) and the emitter (TwinPack.emitter). A generative pack's turn is a scripted scenario's when the World
loads one (handlers/<vendor>.json), else a labeled deterministic stub, never a model; faults are the kernel's grammar
(world-core/src/scenario.ts), and a pack on the OpenAI or Anthropic wire takes its scenario from the kernel's
(openaiWire.scenario). A Responses stream emits its scripted or labelled reasoning summary as
response.reasoning_summary_text.delta events before completing its reasoning output item; the same
summary is in the completed answer. Summary-part added/done events bracket those deltas so SDKs
can open and close the reasoning part. This is a wire event, never an inferred model thought.
Evidence
Signed-event acceptance constructs the declared type's object path, using the subject's vendor fields through ownFields, and supplies a separately declared parent at its event path. A composite subject needs that parent to resolve its stored key. Vercel's project domain events carry payload.domain beside payload.project.id; testing only a fabricated flat event does not exercise that contract.
An acceptance that spans credential rotation uses a setup account credential when the life provides one through its declared credential door. Otherwise it uses the last credential carried by a successful selected vendor operation, rather than the most frequently used historical key. Vercel's token page permits token deletion; a life that rotates and deletes its first token must not deploy or refresh with that revoked token. The selection is independent of vendor identity and ignores steps expecting credential refusals and credentials a partial setup has not yet captured.
Vendor-backed acceptance plants an out-of-band resource through its actual API, including a create under a parent. The request's path parameters and body may be taken from a successful create already recorded on the second World's log: that preserves the life's valid parent, credential and nested configuration instead of guessing them from response fields. A name is changed to make a new subject. The acceptance still requires a successful create, a new vendor subject observed on refresh, and removal of a vendor-deleted subject on a later refresh.
The vendor account setup walk excludes refused HTTP uses of an operation that recorded a
vendor write on the probe, as well as its successful uses. Otherwise an expected duplicate
creation can create the repository on the second World before the first deploy runs; that
mistakes a usage refusal for account setup and prevents testing successful adoption.
Account-door bookkeeping is excluded from the deployable log as all _ subjects are; the
acceptance counts only non-bookkeeping door writes when checking World-only settlement.
A pack's evidence is its report and its grade (the report, the grade), plus two steps the kernel runs against every pack with no pack code:
- The branch round-trip (
scripts/branch-round-trip.ts <vendor> …) sends the round trip at a fresh root. A Protocol 3 pack's is derived from its life: the life up to its first write on the vendor's wire (not a World door) after which a new subject stands in the tree and which, sent again with what the life captured, logs another entry; the setup is sent once and that write again on the branch and on the base. (This derivation is the 2026-09-30 carrying of the check, not an owner ruling: the descriptor'sroundTripis supplied by the derived core and no pack declares one.) A life that creates nothing has no round trip; a pack whose every creating write is refused when sent again (a content-addressed store, a write that needs its open session) is reported as having none, with that reason, apart from a failure. The tree holds what it made; a checkpoint is cut and a read through it equals a read without one; a branch is taken and starts where the base stands; the write goes again on the branch, and the branch's entries are its own, unpushed and absent from the base; the base moves again and the branch's base position is behind the base's log — a claim about positions, never about tree bytes, because a base and a branch minting the same id from the same tree are legitimately identical; a rebase then moves the branch with no conflicts and drops none of its entries. Withreferences, the reference trip is sent too, derived from the life as the round trip is: the first parent a declared reference points to and the first later write whose subject names it; the life is walked to just before that write, the parent is landed with a vendor id the way a deploy lands it (its alias adopted), and the write, naming the parent by its local id as the life does, is accepted and the child's reference reads the adopted id. - Shape parity (
scripts/shape-parity.ts) sends the round trip's writes on one root, runs the refresh adapter over that same wire into a fresh root, and compares every observed type's subjects field by field (_-prefixed fields andupdatedAtaside). Advisory until the descriptor saysshapeParity: 'held'; asserted after. A pack with no refresh adapter has one mapping and nothing to compare.
Gates owns when each runs.
Indexes, never a smarter fold
The fold is last-write-wins per field (foldEntries in log.ts), the same for the simulated and
the real state system, so the two never diverge in it. A derived lookup — by type, by a declared
field, a count the wire serves — is an index over the checkpoint or a field stored on the write
that changes it, never logic in the fold.
Action responses may name a newly created companion without returning the primary subject's id. The adapter retains an already adopted primary id on thin updates and reads companion selectors from the manifest declaration of its stored type as well as its response schema. A publish can therefore keep the draft's adopted id while adopting the carrier post named in its response.
When a successful grouped request updates an already adopted subject but its response names only the newly created subject, settlement retains the existing adoption for the unnamed entry. Explicit response ids take precedence. The pre-perform alias map supplies the retained id; an unadopted subject gains none. Otherwise landing a reply counter under its old local parent id falsely makes that id vendor-held and erases the parent's alias before a later reply or repost.
Alias-aware lookup at the request boundary
A failed deploy receipt is evidence of an attempt, not a vendor-held subject. Alias collision checks count only deployed receipts, observations and incoming batches; a later successful retry must resolve subsequent requests through its adopted id. Otherwise a simulated 500 before a create prevents the successful retry's reply and publication calls from resolving their parent.
After an adoption a caller on a branch may still address a subject by its local id. The serve
path resolves a local id through resolveSubjectId(service, type, id, root) — the alias map the
landed copies carry as aliasOf — once, where the path or the params are parsed, never per
lookup. Adoption is that alias and nothing more: a declared reference is resolved through the map
in the tree, and rewritten through it before the pack's perform sees the entry, so there are no
explicit adopted rows. The round-trip gate sends the referencing write by the old id after
adoption and expects it accepted.
Shape parity
A World may disclose an additive local connection field the application deliberately consumes
(Substrate reads twin_local_ports to reach its Fly guest on loopback). This is declared separately
from vendor facts in manifest.worldFields: resource → field → { type, why }. Names must begin
twin_, must not shadow a spec field, and have an explicit JSON type. SHAPE checks those declarations
and values while continuing to reject every other added field. They never patch the vendor's spec;
refresh observes the vendor shape without them, and the pack stores connection bookkeeping privately.
A pack has two shape mappings: what a local write stores and what an observation of the vendor
stores. They are equal when a parent's derived fields (a comment count, a last comment, a thread's
reply summary) are stored on the write that changes them, never derived at read, and when a local
create stores the vendor's full default shape, not a compact one. Observation cursors use vendor
fields distinct from kernel metadata (observedUpdatedAt for GitHub), so ownFields stays the
vendor's. The parity gate refreshes every twin from its own wire, with the credential its round-trip
writes presented. A refresh refuses by throwing, never by returning an empty observation (a returned
result marks the vendor observed); one that cannot be exercised for effect from the twin itself (an
identity pull the twin's wire refuses without a consent-minted token, a pulled id inside the twin's
reserved local namespace) declares the refusal it answers with (refreshRefusal), and the gate
passes it on that refusal alone. Subjects only one side holds are reported apart from field
differences.
The serve seam
Write callbacks use the intact request copy already retained by the kernel for a
handler's core continuation. Each hook and each event context gets its own copy,
with the original scope and write time. A handler may consume its request body
before writing; callback construction must not clone that consumed stream. First
need: GitHub's installed release qualification reached finishWrite after body
consumption and event rendering failed with Body is disturbed or locked
(publisher run 37647018031, before upload).
Scenario decisions cross the internal request seam as ASCII JSON: non-ASCII code
units are JSON-escaped and the existing JSON reader restores their exact values.
Model replies and match text keep their Unicode content. First need: the on-call
recipe's em dash reached Headers as an unescaped decision value, and Node's
ByteString conversion refused the request before the OpenAI handler ran.
A request front that reads its context and then rewrites headers receives a request rebuilt from the incoming
body bytes before the kernel opens that context. Its continuation and the dispatch each read their own buffered body,
and the incoming abort signal is kept through body decoding and vendor-path rewriting, so cancellation still reaches
the front, scenario and handler. The shared seam owns this preparation, not a vendor handler. First need: the
Claude Messages front adds request and workspace headers before forwarding
(Messages), and with the incoming stream read through the
front and again by the scenario, an authenticated POST served over HTTP from a chunked Node client never answered
(packages/world-core/src/pack-fetch-front.test.ts holds the case). Where the evidence stops: the Bun stream behaviour
behind the hang has not been characterized; buffering before the front is the fix measured, its mechanism unexplained.
A pack serves through the kernel's HTTP seam: serveHttp(options) from @volter/world-core
(packages/world-core/src/serve-http.ts), in Bun.serve's option shape (hostname, port,
fetch, idleTimeout, tls, error), returning { hostname, port, url, stop } asynchronously,
because binding a port is asynchronous on Node; url is a URL whose string form ends in /. It listens on loopback unless the caller names a hostname: a World reaches its twins over loopback, and a front meant to be reached (a served world, a host, reflect) names its own.
Bun-backed under Bun, node:http-backed otherwise, with node:http reached through
process.getBuiltinModule and never a static import, because the module rides into browser
bundles. Outbound WebSocket connections carrying headers use connectWebSocket in the same
serve seam, on the shared ws adapter. A World TLS relay forwards vendor
authentication, original host and offered subprotocols to its resolved twin,
while regenerating handshake keys and removing transport and browser-session
headers. Relay close during connection startup closes the eventual upstream too.
WebSocket upgrade is part of the seam: the portable upgrade: { accepts, open, message, close } callbacks serve on both runtimes, on Node through ws, the one place the kernel loads it
(B1). The request journal decorates the handler through the seam. A successful
WebSocket upgrade is recorded as HTTP 101; a thrown handler remains HTTP 500. A pack's serve factory
(create<Name>TwinServer; serveExport on the descriptor when a pack exports several) is
therefore async, its bin says #!/usr/bin/env node, and a Bun.serve( call in a pack that
never uses serveHttp fails hygiene. A pack's other runtime-specific needs go through the
kernel's helpers: fileResponse(path) for a file as a Response, bytesResponse(request, bytes, type) for stored bytes
(every satisfiable Range: bytes= a 206 with its Content-Range, bytes=0- included, as a media player such as
Safari's needs to seek; an unsatisfiable one a 416; a range that does not parse ignored for a 200), bundleClient(entry) for a
page's client bundle (the console's; built at serve time under Bun, served prebuilt from dist/client/ under
Node), nodeBuiltin(name) for a Node builtin. Volter's own pages (the console, the UI kit's shell) serve the
Volter brand's tokens and faces with brandTokensResponse(dir, rest), from a gitignored directory
scripts/brand-tokens.ts fills at install, pack and image build; nothing fetches the brand while a
world runs. A raw-TCP twin listens through the seam's byte-stream half, serveStream({ port, stream }), which serves
its create<Name>TwinStream on a TCP port. That protocol is one connection over
any byte transport (the kernel's TwinStream: greet through a sink, bytes in, settled once they
are answered): its listener hands it a socket's bytes, and a hosted World, which takes no inbound
TCP, a WebSocket's, which the attacher bridges to a loopback listener
(@volter/world-core/stream-bridge). A twin that answers WebSocket upgrades on its vendor API keeps
its relay host-neutral, so a hosted World answers them with a WebSocketPair.
One cause is followed across a World's twins by the tracing standard, W3C Trace Context, never an id
of the twin's own (packages/world-core/src/trace-context.ts). The kernel's request adaptations
(createTwinFetchFromHandler, createPackFetch) run the handler inside a valid incoming
traceparent (parsed strictly: a malformed value, version ff or an all-zero id is none); a pack
with its own fetch wraps it in runWithRequestTrace. Every entry appended there records it as
traceparent, authoring metadata like correlationId: kept in the log, excluded from replay
identity. A delivery a write causes carries deliveryTraceHeaders(cause?), a child of the cause
(same trace-id and flags, a new parent-id): the cause is the request being handled, or, for a
delivery made later (a queue's drain), the entry that caused it. No cause, no header. Nothing else
on the vendor wire changes: an answer gains no header and a request without one is served as before.
The publishing pipeline
A package is published from its built form: a platform package carries a tsconfig.build.json beside its own
(tests, uitests and generated/ outside the root set), build/prepack/postpack scripts pointing at
scripts/publish/, and dist in files; a twin pack carries none, and the same scripts build it (its src and
each lane's) and prepare its manifest, its type errors left to its grade's typecheck. scripts/publish/build.mjs emits
dist/src (.js and .d.ts, relative .ts specifiers rewritten to .js), carries every non-TS
file under src and every files asset beside it, and bundles each top-level client/*.tsx into
dist/client/<name>.bundle.js; prepare-publish.mjs turns exports and bin from src/*.ts to
dist/src/*.js and workspace: ranges into versions inside npm pack, and restores the live
manifest after. In-repo consumption stays from source. The runtime reaches an installed pack by
its manifest — exports['.'] for the module, bin for the cli (packEntry, packCli in
world-runtime/src/catalog.ts) — and runs it with its own process.execPath, so one resolution
serves the checkout and the published form. The tutorial registry installs every pack built, so a
page red on a built pack is the pack's build being wrong, never the page's.
Every push to main is released: the publish workflow moves each public package changed since
its last version (and every package that pins it through workspace:) to its next patch, then
runs scripts/publish/publish.mjs, which packs every version npm does not have yet (siblings
first, through pack.mjs) and publishes those tarballs, and commits the versions back to main.
Protocol 3: the derived pack
Protocol 3 is how every twin is created (creating a pack). A pack is derived when it has src/manifest.ts and
src/generated/. Four exemplar packs defined it: openai, github, stripe and slack, chosen by
demand and so that together they exercise every capability the platform has (streaming, WebSocket,
GraphQL, an RPC wire, the engine slot, webhooks, UI, real roots). Two packs it was not designed on
tested it: aws, through its Secrets Manager API (a Smithy model, operations named in a header, SigV4),
and smtp (a line protocol over a byte stream, with an RFC for a spec). What they
forced is in the rules below (other wires). The rest
of this document governs every pack, and where a rule below replaces one above, it says which. A
derived pack implements the plugin contract above, most of it supplied by the derived core
from the vendor's spec; no other form of pack exists (creating a pack).
A derived pack hand-writes only how its vendor behaves. Everything the vendor's spec states is generated: a derived core from the spec and the manifest, a hand-written semantics layer over it, the engine slot when the vendor has state beside the tree, and the vendor's UI surfaces. The kernel's log, checkpoints, branches, landing, receipts and head are unchanged.
Protocol 3 is how a twin is developed: from the vendor's spec, with only the vendor's behaviour hand-written and every hand-written line proven by a judged life, because that is what keeps a twin accurate and maintainable. A pack's standing is its Protocol 3 grade (the grade), how far it follows the method, read as a coverage figure is read: shown, never required at 100%. Its proof is the report (the report): its one customer life is judged plausible, no check on any step of the life fails, every example the vendor published for an operation it serves is replayed and judged, the life and the examples reach every hand-written line and every declared move and refusal or the pack records why no World can, and no operation of its spec is served by anything but a semantics handler, the derived core, or the vendor's own answer for an operation the twin does not model. The grade adds the form those rest on and the pack's own soundness.
The report measures the source customer life and published examples; its successful result says that source
journey meets its requirements. It does not declare the pack complete or its installed workflow ready.
pack-done --installed-assessment <report.json> owns completion, including the exact installed artifact's
customer workflow. A generated form or source-journey result never substitutes for that evidence.
Layers
A request passes the wire (the facade); a write becomes an entry and is decided by the twin's
state system, simulated or real, as the model defines them and
head.ts chooses them: a real head under deploy: auto performs the entry before the app is
answered; any other head lets it wait. Reads are served from the tree in both.
The successful response of an automatically performed request is the vendor's response, including its body, status and response headers. It is carried separately from the resource fields folded onto the log: an envelope or a numeric field such as GitHub's issue number cannot be reconstructed by replacing quoted local ids in a simulated answer. Transport encoding and length headers are removed when the executor has decoded the body. Numeric vendor ids use the same stored-id conversion as refresh, while their wire representation stays numeric.
- Wire: derived. Routes, dispatch, request validation, response shapes, status codes and the error envelope, from the spec. It is the same over both state systems.
- Write semantics (actions and state transitions) decide a write's effect. They run whenever
the head does not perform the entry now: with no root, and under a root whose policy is
gatedorhold. A real head underautoruns none: perform resends the entry's request to the vendor, with local ids translated through the alias map and declaredreferences, and the vendor's answer is the effect. The landed copy stores that answer on the subject the entry names (head.ts); what the write did to other subjects (a merge moving a branch) arrives by refresh or ingest, never by local computation. - Read semantics (
computedvalues and vendor query languages such as GitHub's search syntax) derive answers from the tree. They run in both state systems, over simulated state or observed state. A computed value the vendor returns as a field of a resource is stored on that resource by the write or the observation that changes it (shape parity), never derived at read. - Non-resource operations. Stateless compute is performed like any entry under a real head
with
auto, whose model promise is that the app is pointed at the vendor: the entry's subject is the call itself, and the vendor's answer is its effect. Under any other head it is answered through behaviour (a deterministic stub or a scripted scenario, as D4 rules). State beside the tree (git objects, blobs) is answered by the engine slot under every head.
Under a real head with gated or hold, the simulated semantics answer the app, and deploy
performs the entry later with the outcome the model gives deploy: a receipt lands on the entry,
and a refusal or failure stops the deploy. Only auto reverts.
A real head is a root with a sealed credential. A library that holds the person's credential in
its own process (ztrack's GitHub token) deploys through the same transaction without sealing it:
performEntries given the caller's execute performs against the twin's root.json with no
credential in it (its url, scope and policy, usually hold), with the same checks, reference
resolution, receipts and adopted ids. Such a root is not a real head: without a caller's executor
nothing performs against it, and a wire write under it is simulated as under no root.
The pack contract: the kernel's root
A Protocol 3 pack imports the kernel through one door: @volter/world-core, whose root exports the pack contract and
nothing else. What a pack's files may name is what this document gives them: the manifest's types (DerivedManifest,
its resources, states, transitions, screens, events and seed), the descriptor (TwinPack, packOf, registerPack),
the handler contract (Handler, HandlerContext, WriteHookContext), the derived fetch and core its fixed fetch.ts
builds (createPackFetch: the kernel serves the discovery door, the clock, the doors, the screens and the API from
the pack's declarations, each over a context it opens), the serve factory's pieces (serveHttp, a pack's assets), the kernel's deterministic
helpers (signing and digests, S3's and git's wires, the scenario grammar), and the World's read-only marker. Its
cli.ts also takes @volter/world-core/args and @volter/world-core/lifecycle.
Everything else the kernel is made of (the log and the tree read and written directly, checkpoints, branches, landing,
the head, the stores, the runtime's scopes and clocks) and the plugin contract's wide surfaces (the wide
SemanticsContext, a pack's own state system, rate budgets, the request-handler adapter) are
@volter/world-core/runtime: the runtime's, the CLI's, the host's and the tooling's. A pack never imports it. So a
pack cannot read or write the tree but through a context the kernel opens for it: where a pack needs something only the
runtime has, the kernel is missing a mechanism, and the mechanism is built into the contract.
The entry
Entries already carry the vendor operation (operation) and the raw request (input, not
projected) beside the effect (fields, projection). A derived pack keys operation by the
spec's operationId and records the path and query parameters in input, so the entry is enough
to resend the request. Replay folds recorded effects and never recomputes them: a change to a
pack's semantics never changes what an existing log folds to.
State
The shared JWT signer accepts an explicit protected-header typ, defaulting to JWT. A pack selects the vendor token type while the kernel signs it with the held key; it never rewrites or signs a JWT itself. First need: Clerk OAuth access tokens use at+jwt (RFC 9068), so the worker's unmodified Backend SDK recognizes them as OAuth tokens; session and OpenID Connect ID tokens keep JWT. The journey checks the issued token type and walks the worker's real token-exchange request.
The own-field view preserves an explicitly stored vendor id, type or updatedAt even
when its value equals the kernel's subject metadata. Equality does not erase provenance:
Linear's GraphQL Team.id is non-null, and its returned id normally equals its subject id.
A reserved field never stored by the vendor remains absent from the own view. GraphQL root
operations are carried by the generated fetch as data from graphql.gen.json; ownership
includes each root field, with unresolved fields reported as gaps, so decisions, demand,
refresh and published-example checks see the GraphQL surface as they see REST.
A transition that has a refusal and no target is a refusal of its own: it is considered when its operation and the
current state match, even when the caller asked for another destination, and it is observed and measured as a refusal,
never as a move (life coverage lists it as a refusal item). A move another transition allows is taken first. Needs:
OpenRouter's single-use PKCE code, and OpenAI's run
cancellation, which applies only to a run in progress (spec cancelRun: "Cancels a run that is in_progress.").
Bookkeeping totals can use ctx.accumulate(resource, id, deltas, operation): declared _ resources only, numeric
fields only, adding each delta to the current row within the kernel atomic write seam. This retains every concurrent
charge without exposing the wide context or allowing a handler to hand-roll a lock. Merge needs it to draw each call
from its organization credit (pricing, "Vendor cost plus a 5% Merge fee").
The narrow method refuses a missing row, nonnumeric fields and nonfinite totals.
A resource with declared unique keys can use ctx.createUnique(resource, fields, operation).
It mints, compares the selected fields with live rows, and creates under the same action lock.
It returns { created, row }, the existing own-field view on a collision; the pack renders its
own documented conflict or idempotent answer. No new write or timestamp is made on a collision.
First need: concurrent Hub repository creation
requests must not create the same name twice, and account-door requests must not overwrite
another account while generating its secret. Field factories are pure, as on ctx.create.
A subject is stored in the vendor's own response shape, as the spec's schemas define it, which is
the shape parity rule. The state schema is therefore derived, and an observation
needs no mapping. Bookkeeping stays under _-prefixed types and fields, and never reaches the wire:
nothing a pack keeps for itself is answered, under any name.
An operation answers the schema its spec names. Where that is a view of a resource (GitHub answers a
list's users as simple-user, a list's pull requests as pull-request-simple, a search hit as
issue-search-result-item), the answer is the view: the fields the spec gives it, taken from the
resource, never the whole resource. A schema composed over a resource with allOf answers the
resource's fields and its own (ruleset-version-with-state adds state).
The derived core
A handler can ask ctx.validate() to check the top-level body fields derived from its operation: required fields,
scalar/array/object types and numeric bounds. The manifest's body.validation chooses operations and supplies
missing, type and range response templates and status; unknown fields remain available to vendor semantics.
Merge's native Responses API requires an input array and returns a 422 detail array on schema failures
(its errors, "Schema validation failures on native POST /v1/responses
return 422 with a detail array instead of error"). Validation occurs where the handler asks, after its credential
and budget checks, before it issues or charges a call. Nested semantic validity remains the handler's.
Generated from spec/ and manifest.ts into src/generated/ by scripts/derive-pack.ts, through
the spec IR (packages/twin-standard/src/spec-ir.ts), committed so that a spec update is a
reviewable diff, never hand-edited. The spec is an OpenAPI document (2 or 3), a Smithy model, or, for
a line protocol, the RFC's own text (other wires). The
surface is data (surface.gen.json), not code, so it adds nothing to a typecheck. The IR reads what a
vendor declares about its own surface where it declares it: Stripe's x-resourceId names its
resources and x-expandableFields its embeds. Contents:
- The operation table, dispatch and the wire. An operation's id is the spec's operationId, or
the
method_pathslug the spec IR mints when the spec has none (slug). An answer that is a union of resources (customerordeleted_customer;card,bank_accountorsource) names the first as the operation's resource and keeps the rest as alternatives. Dispatch (packages/world-core/src/derived.ts) matches the most literal route first (and the headers an operation is named by, where the spec names it in one) and hands an operation to its semantics handler, or to the derived core, or answers the vendor's own refusal for an operation the twin does not model (the pack'sgap); nothing else answers a spec operation, and the kernel's dispatch takes no other (doors). The dispatch reports what owns each operation. - A request's parameters (
readParams): its query's, and its body's as its type says. A body labelled JSON is JSON, and a manifest withbody.json: 'always'reads any body as JSON first (GitHub reads curl -d's); a multipart body's fields and files are read from its bytes; a body is read as a form only when it is labelled one (application/x-www-form-urlencoded). Any other body (an image, a video or an octet-stream PUT to an upload URL, an NDJSON batch, a text/plain statement) is no form: its parameters are the query's, and the handler reads its text or bytes itself. Every field is set and read as the object's own (ownField), never through what it inherits, in a form, a query and a multipart body alike:__proto__,constructororprototypeat any level is a field of that name (Stripe's metadata keys are the caller's), and no request reaches every object of the process. A bracket key never writes under a key that already holds text (a=1&a[b]=2keepsa=1), and a named key under an array (a[]=1&a[length]=-1) has nowhere to go either: the first shape stands. First need: LinkedIn's video part PUT, whose bytes were read as a form and threw. - Resource schemas, and candidate state fields: every enum-typed field of a resource schema
with more than one value (a single-value enum such as Stripe's
objectis a discriminator).semantics/states.tsrules each candidate a state field or not; an unruled candidate fails the build, so leaving a state field out is a visible decision, never a silent drop. The cost, a line per candidate, is intended. - Operation classes.
crudis a create, retrieve, list, update or delete of the resource at its path whose answer is that resource.actionis a write whose effect is not storing its body (POST .../merge,/confirm, an RPC method such aschat.postMessage).computedis a read of values derived from other state, including a search.non-resourceis stateless compute, a protocol or a blob. An operation the generator cannot place isaction, nevercrud. - Generic CRUD for
crudoperations. A create sets each state field to the spec's default, or to the initial statesemantics/states.tsdeclares. An update applies its non-state fields generically; a change it requests to a state field is applied as the declared transition for that operation, from the current value to the requested one, with that transition's guard and effects. A requested change with no declared transition is unmodeled: it answers the vendor's gap for the operation (D2). - Perform, refresh and ingest, the vendor-backed half (the real-system adapters: specified, not built).
- The owners of each operation, for the report's breadth (the report), by unit (
ownersByUnit(): the vendor's own API under'', each lane under its name): an operation id two of a vendor's APIs share is two operations, each served or the gap on its own, and the flatowners()writes a lane's as<lane>:<id>. First need: Clerk's Backend and Frontend APIs both nameListOrganizationMemberships(and three more), the Backend's served and the Frontend's the gap, which one map keyed by id alone hid.
The core never answers an operation with data that is merely schema-valid. Shape is derived; data and behaviour are not.
Routing a spanning parameter
A spanning parameter (the surface's own, a Smithy greedy label, or one the manifest's spanning adds) keeps its
slashes as the caller sent them. The kernel's routing (packages/world-core/src/pack-fetch.ts) first rewrites the
path as the vendor routes it (the manifest's path prefix taken off, the spec's base in a base alias's place, a host's
path label put in front) and matches that path against the surface; when the match binds one of the matched
operation's own spanning parameters (keyed by the operation and the parameter, as its route compiles them: a whole
segment's label whose name is declared spanning) the path is kept as sent, and only otherwise are duplicate slashes
collapsed. A name another operation declares spanning, or a label holding only part of a segment, keeps no slashes. First need: QStash's
Publish a Message, whose destination URL
is its path's last parameter: https:// stays that scheme in delivery and read-back. The rule holds for every pack's
spanning parameter (a file path, a git ref).
Correcting the spec
A vendor's spec lags the vendor. Slack's conversation schema has no updated, and OpenAI's
fine-tuning job has no paused though its own pause example answers it. A pack corrects its spec as
data, in spec/patches.json: RFC 6902 operations applied before the IR, so the vendored file stays
the vendor's bytes and a spec update is still a clean diff. Each patch says why, citing what shows
the vendor differs from its spec: its documentation, the spec's own example, a recording, the SDK's
types. derive-pack.ts refuses a patch with no why, and one whose evidence does not hold (see
Evidence). A patch may cite source: { recording, quote } when the evidence is a recorded vendor answer under spec/recordings/. The derivation checks its safe filename and the quoted bytes offline, exactly as it checks a handler recording citation. First need: AI Gateway's captured GET /v1/models includes the integer released field omitted from its reference-derived schema (the pack's spec/recordings/2026-09-30-models.json.gz; model listing).
A patch corrects the spec to the vendor, never to the twin: a field the twin answers and the vendor does not send is the twin's defect, fixed in the twin.
Evidence that holds
A transition's source and a spec patch's evidence are checked, never trusted: a citation written
from memory named the wrong page for two of three Slack patches, and eight transition sources named
pages that do not exist. A citation is one of:
- a page:
scripts/check-sources.ts <vendor>fetches every page a pack cites and records its answer inspec/sources.json; a patch'ssource: { url, quote }also records whether the page holds the quote (markup and whitespace aside); - a vendor-published archive:
archive:<url>#sha256=<hex>!/<member path> "quote".check-sourcesfetches each archive URL once per run, checks its SHA256 before reading a ZIP or tar member, and records the digest and whether that member holds the quote inspec/sources.json. A different digest, missing member or quote refuses the evidence; derivation and conformance read that record offline.read-pageaccepts the same member locator so the author reads exactly what is checked. Pinned archive citations in handwritten sources, including pure engines, are collected; a published example can name the member assourcewith its literalquote. First need: tinybird-cli's template rules and SQL examples in its pinned 6.5.5 wheel (tinybird/sql_template.py), published on PyPI, have no working raw repository URL; - the vendored spec's own words:
spec:<operationId or /json/pointer> "quote"(a patch writessource: { spec, quote }), read from the spec before any patch, so a patch never vouches for itself; an RFC is cited by section,spec:4.1.4 "quote". Where a vendor's documentation refuses to be fetched (OpenAI's answers 403), this is the form; - an answer the vendor gave, recorded:
recording:<file> "quote", words ofspec/recordings/<file>(the file says what was asked, when and how), for what the vendor does and documents nowhere (Tripo's front answering an unknown path).
Page-citation discovery retains the semantics and manifest scope used by existing evidence records.
Pinned archive citations are also collected from other handwritten sources and archive examples;
that extension does not add page-quote obligations to unrelated packs. A library computation that
no served vendor path returns is an unserved published example, rather than a diagnostic door
introduced to count it as a replay. Such a record has wire: 'client', its actual computation as
operation, and no HTTP request; the example runner counts it as unserved. Tinybird's local
rendered-SQL doctests are the first such case. Published encoded form inputs and actual form requests
are compared through the same form decoder, preserving repeated fields; first need: Tinybird's
Data Sources examples publish their
URL-encoded request body as a string.
Archive reads verify the published digest before reading a member; no arbitrary archive byte limit
is imposed without a repeatable measurement supporting it.
derive-pack.ts reads the record offline and refuses a page that did not answer 200 or was never
checked, a quote the page or the spec does not hold, and a patch that cites nothing. A page that
exists shows a method exists (most of Slack's patches); a rule about a state needs the words that
state it, as a quote. Where the evidence stops: that is not yet enforced for transitions: a transition
citing a page is checked only for the page answering, and one quoting its spec for the quote.
A versioned vendor (Stripe's dated API versions) has one spec per version, and renders one account
of an object per version: a request pinning a version gets that version's shape, one pinning none
the account's. The Stripe exemplar does the same (src/engine/version.ts): it keeps its objects in
the shape its rules were written against, with the request parameters those rules read, and renders
every answer and webhook payload in the vendored spec's version, or as kept for a request that pins
a version before the first change it models. This is where a derived pack's objects are not stored
in the vendor's response shape: the stored shape is the pack's, and the shape rule holds at the
wire, for the served version. SHAPE does not judge an answer to a request pinned before the spec's
version, which the spec does not describe. Where the evidence stops: this is one exemplar's answer;
that every versioned vendor is served this way, and that one rendered boundary (Stripe's basil) is
enough, is extrapolation.
An OpenAPI response with a top-level data payload plus errors and includes is read through data. Problem arrays and expansion sidecars are metadata, not the primary resource. A referenced, id-bearing data object on a create is the response view, even when no GET returns that thin view. X's create reference returns a thin data object beside partial errors; misclassifying errors as the answer prevents adoption and refresh. The IR preserves the data key and schema name so a manifest can map the view to its stored type.
The semantics layer
Hand-written, under semantics/, grouped by resource family.
Each state field is a state machine declared as data: its values, its transitions (which
operation moves which value to which), each transition's guard with the vendor's refusal for it,
and the effects it writes (closed_at, closed_by). A resource with several state fields
(GitHub's pull request: state, draft, merged) has one machine per field; a guard may read
any field. A resource with no state field has two values, absent and present. Every transition
cites its source, and the citation is checked (see Evidence).
A declared transition is a rule the twin executes, never a description of the vendor. The core, a
handler (ctx.legal) or an engine that is not HTTP-shaped (transitionFor, which smtp's session
asks) consults the machine for every move it makes, and a move the machine does not declare throws.
A move the vendor makes and the twin does not (OpenAI failing a batch whose input does not validate,
or stopping a run at requires_action for its tools' outputs) is not declared: semantics/states.ts
names it in a comment beside the machine as the vendor's, so the gap is visible where the
rule would be. An operation the manifest lists as unmodeled declares no transition. Where the
evidence stops: this rule is the orchestrator's, minted while bringing openai to the coverage standard
below, where the machines declared moves no code made and their refusals no request could reach.
A handler is registered under an operationId and receives its context (HandlerContext): the request as
ctx.call (its path parameters in ctx.call.params), the query and the body merged in ctx.params, the body as
ctx.body, and the tree through the contract's reads.
Parameter extraction reads JSON, multipart and URL-encoded forms; an explicitly typed binary or other opaque
wire body stays on ctx.call.request and contributes only query parameters. A manifest's body.json: 'always'
does not turn a Git pack or an LFS upload into form fields after JSON parsing fails. First need:
localrouter's Hugging Face Git history seed and model LFS upload in cookbook/localrouter.
It writes through its context (ctx.write, each write the kernel's) and answers the response, or, for a read, the
answer. It replaces the core for its operation and asks the declared transitions for every move; it never runs
the core and patches the result. Handlers carry what data cannot: actions, computed values, one
resource's several views (GitHub's pull request and issue), and effects on other resources. A
handler or transition naming an operationId the spec no longer has fails the build.
The handler context's body
An OpenAPI request body declared as a whole string with format: binary is an opaque blob parameter in the
derived surface, rather than an empty field list. Its bytes stay in the handler's request regardless of the caller's
Content-Type; neither parameter extraction nor the shared JSON syntax guard parses an opaque payload. This uses the same blob parameter
contract as Smithy's payload, without a second handler or routing mechanism. First need: vgauth's same-site Clerk
proxy clones and forwards the incoming request
(lines 279–284), so even malformed JSON must reach the upstream vendor's own validation.
For an ordinary JSON operation, an around context opened before the syntax guard retains query and path parameters
when the body is malformed; the guard then answers the vendor's declared malformed-body refusal. Opening that
context must not throw before the vendor's credential and syntax gates can run. The same proxy's upstream request
is the first need for retaining that refusal boundary through a companion's around context.
The handler context holds both the parsed body (ctx.body) and the body's original text (ctx.text). A dispatch
guard that already parsed the JSON hands that parse to the context, but never replaces ctx.text with an empty string
or a reserialization: both read the dispatch's cached body text, without consuming the handler's request. The first
need was QStash, which signs the request body and delivers the
published payload's bytes, JSON whitespace included. The rule holds for every vendor that treats a JSON-labelled
payload as text.
Doors, screens and the gap
A derived pack's fetch has three parts in front of one another, and nothing behind them. The twin's
own doors (discovery at GET /twin, a store door, upload targets, the /_twin/ doors that stand
in for an act the vendor's API does not have) answer only their own paths. The vendor's screens
answer their hosts and paths. Everything else is the derived dispatch: an operation of the spec
reaches its handler or the core, and one the twin does not model answers the vendor's own refusal
for an unknown request (Stripe's Unrecognized request URL, Slack's unknown_method), which is the
pack's gap. A path the spec does not have answers the same, and a path the spec serves under other
methods answers the manifest's wrongMethod when the vendor distinguishes it (Tinybird's 405). A vendor
that checks the key before it routes (auth.beforeRouting: Supabase's Management API answers 401 to an
unknown path) refuses the credential before the gap. A gateway with several API skins can declare ordered
auth.wires path matches: each supplies its error template and optional missing/invalid error facts.
The kernel selects that wire at the credential gate, before scenario matching; an unauthenticated request
never consumes a scripted turn. OpenRouter's Messages skin needs its Anthropic envelope and canonical
error.error_type, while Chat Completions and Responses retain the gateway envelope. This selection
is manifest data, never a pack router or an authentication check deferred behind a scripted fault.
A vendor's CORS (cors) is answered on
every path, a preflight included.
What the vendor's front does to every request's bytes is declared, not written: a path prefix its hosts
may carry (pathPrefix, turbopuffer's region segment) is taken off before routing, its named groups (Jira's
/ex/jira/(?<cloudId>…)) and a host's (hostParams: E2B's envd at 49983-(?<sandboxID>…).e2b.app) joining the
operation's parameters, a prefix the vendor serves as its own base path (basePathAlias: Discord answers /api and
/api/v9 as the spec's /api/v10) is put in the spec's base's place, duplicate slashes collapse, and the content codings it reads and answers in (encodings: Tinybird's gzip and zstd event
bodies, its gzip answers on Accept-Encoding) are decoded before routing and encoded after the API
answers, by the kernel. First need: Codex's unauthenticated Zstd request decoding must
cost no more than a plain body of that decoded size could. Gzip, deflate, Brotli and Zstd request
codings use asynchronous decoding with a maximum output size: the descriptor's maxRequestBodySize,
or 128 MiB (defined and cited in world-core/src/request-body.ts). On Bun that is its existing
plain-body default; on Node this is a compressed-request output bound only, chosen as the largest
plain body Bun accepts by default so both runtimes decode the same. Plain HTTP bodies retain the
runtime's existing behavior, and Git inflation and generic gzip readers retain their existing
behavior. Overflow receives HTTP 413, corrupt streams the generic HTTP 400 refusal, and multiple
content codings HTTP 415. The decoded request streams the decoder's output buffer without another
full-size copy. Walk-tool response/fixture decoding stays off the service path.
A derived pack has no hand-written router, and an endpoint the vendor does not publish is not twin surface. A screen may use only the vendor's published API. Where a vendor's page is the only way to an act, the pack builds the page
(screens); until it does, a /_twin/ door stands in, and says which page it stands in for.
The vendor's discovery includes each lane's discovery, and GET /twin/scenario includes each lane's scenario status under lanes, beside the root's status when it has one. Root discovery cannot hide a scripted lane, whether the root has an API of its own or is only the lanes' front (Jira's auth and platform lanes). Discovery describes held credentials as issued credentials, and preserves the manifest's behavior description even when the root itself has no scenario adapter. First need: fal's Platform pricing is scripted beside its direct Model API, and both authenticate the shared issued key.
A vendor may address a compute service at a shared hostname and name its resource in a header. headerParams maps those declared request headers to operation parameters alongside path-prefix and host parameters. E2B's SDK 2.45 envd default is sandbox.e2b.app with E2b-Sandbox-Id and E2b-Sandbox-Port; its interpreter and RH2 also use per-sandbox hosts. Lane routes already match headers, so only parameter extraction needs this kernel mechanism.
Other wires: lanes, headers, line protocols, sockets and machines
A vendor with several APIs, each with its own spec, builds each as a lane: a derived unit under
the pack (aws/secretsmanager/, with its own spec/, src/manifest.ts,
src/generated/, journeys/ and fetch) that the pack's router sends that API's traffic to. The tools
take the lane by its path (bun scripts/score-pack.ts aws/secretsmanager). A lane is scored and graded as a pack
is.
A vendor whose own API holds the state its lanes read (Supabase: the Management API keeps the projects and
keys that PostgREST, Storage and Auth are reached through) is a pack at the vendor's root with lanes beside
it, not a lane among lanes: the vendor's src/ is the one place a lane takes shared state, helpers and
engine from. Its manifest's lanes names the path the vendor's gateway sends to each lane (/rest/v1 to
rest), any host that keeps that path for itself, and the gateway's CORS; the kernel dispatches them, and the
vendor's fetch.ts is the generated one that hands them over. No vendor writes a router.
A root unit's declared World door belongs to the pack by method and path and is reachable
through every hostname routed to that pack, including a hostname assigned to a lane. Its
/_twin/ paths are the World's doors on the pack, not routes published by those vendor hosts. Root door
selection precedes host and credential-based lane selection; lane host claims route vendor API
traffic, not the pack's root World doors. A door declared only by a lane still goes to that lane.
First need: Clerk's root credential door at its Frontend API hostname must issue the first
Backend API key without an authorization header, before that host or the absent-key fallback
selects the Frontend API lane.
A vendor whose every API is a lane (Cloudflare's API v4 and R2, AWS's services, Sigstore's Fulcio and Rekor) has no
spec of its own, and its root holds only its front as data: src/manifest.ts, a VendorManifest (its discovery
facts, its descriptor, and its lanes: routes by path prefix, host pattern or header prefix, first match first, and
the default lane for the rest), served by the kernel's createVendorFetch from the generated fetch.ts, which hands
each lane over with its manifest so a /_twin/ door goes to the lane that declares it. A rewrite a vendor's wire makes
before routing (S3's virtual-hosted addressing) is its lane's around. The gate refuses a root whose fetch is not
createVendorFetch or whose other files reach a lane.
One API published as several documents is not several APIs. Upstash publishes QStash's and Workflow's
OpenAPI documents with the one server qstash-{region}.upstash.io and one key set, and they share
endpoints (/v2/flowControl, /v2/keys, message retry): that is one pack, whose spec/openapi/ holds
each document as published, read as their union (packages/twin-standard/src/spec-documents.ts). A patch addresses a
document as /documents/<name>/.... A path and method, or a component, that two documents both give must
say the same; where they contradict each other neither page is evidence against the other, and derive-pack
refuses the union until a patch rules which holds, citing what shows it (the vendor's own client, a
recording). Where the evidence stops: read for Upstash's two documents (52 paths; 2 contradictions:
FlowControlKey and the message-retry answers); no pack is derived from them yet.
A GraphQL API beside a vendor's REST one (GitHub's) is the same state over a second wire, not a lane: its schema is
vendored beside the REST spec (spec/schema.graphql.gz) and derive-pack writes it to src/generated/graphql-sdl.gen.json;
the manifest's graphql names the paths it is POSTed to and the vendor's answer to a query selecting a field nothing
models; semantics/graphql.ts exports graphql, the resolvers by Type.field over the handler context, and the aliases
and stored fields the default resolver reads. The kernel's GraphQL wire (world-core graphql-wire.ts) parses and
validates a query against the vendor's schema, answers lists as Relay connections, refuses a mutation the World may not
make, and carries a resolver's GraphqlError type; it loads the graphql protocol library the first time a query
comes, installed by the pack's package.json as node-postgres is for the managed database, so the kernel's dependencies
stay B1's.
An operation named in a header rather than a path (AWS JSON's X-Amz-Target: secretsmanager.CreateSecret,
every operation a POST /) is routed by that header: the IR carries it as the operation's
headers, and dispatch and SHAPE match on it. A Smithy model is read into the same IR
(fromSmithy): the service's protocol trait names how an operation is addressed, and its output
structures are the resources.
A line protocol (SMTP; POP3, IMAP and FTP have the same form) has no request and response: a
client sends lines on a connection, the server answers each with a coded reply, and some replies
(354, 334) mean the lines that follow belong to the command. Its spec is the RFC, vendored as its
text (spec/rfc5321.txt), and its surface is the RFC's own table of commands and the replies each may
draw (twin-standard/src/spec-ir-lines.ts reads RFC 5321 §4.3.2), corrected by patches as any spec is
(the extensions the server offers, a code the relay it stands in for answers). The session's order of
commands is a declared machine the engine asks for every move, as a handler asks. The pack mounts as a
byte stream (create<Name>TwinStream, what a hosted World hands a socket's or a WebSocket's bytes), and
a journey speaks on it in line steps: connect opens a connection with the options the operator
started the server with, send sends lines, and the answer is the replies they drew. The fixed entry is
src/stream.ts, exported by the pack index and mounted by its server. A stream that reaches semantics through an
internal HTTP request uses the kernel's privateTransportKey(namespace): a lazy process-local capability, never a
credential, stored row or wire answer. RFC 5321 section 4.5.2 requires framing
and dot transparency; SMTP is the first need. The derived fetch reports command-table ownership from its front
and literal unmodeled set, so the decision checker can read line commands as it reads HTTP operations. SHAPE for
a line protocol is each reply's code being one the spec gives the command it answers.
The instance's read-only option enters the kernel's request scope before its front runs, so a private wire front
cannot bypass the write seam by answering before derived operation dispatch.
A request-reply stream (Redis's RESP; the MySQL and Postgres wire protocols have the same form) is a
byte stream too, but not a line protocol: a client sends typed frames (RESP: an array of bulk strings per
command, or an inline line), pipelines them, and the server answers each with one typed reply, in order.
What a connection is (its protocol version, database, name, transaction, watches, subscriptions, a blocked
command) lives on the connection, never in the tree; what the commands store is the tree's. The pack mounts
as create<Name>TwinStream, as a line protocol does, and its HTTP fetch serves only GET /twin.
Redis is served so, by the redis twin (@volter/twin-redis, transport raw-tcp), and its command
semantics are a kernel library, @volter/world-core/redis (packages/world-core/src/redis/: the command
core over the tree and the Lua interpreter), as git's are (the engine slot, above). The placement is forced
by the rules, not chosen for convenience:
- The command core existed in the upstash pack, and a second pack may not import it (A3) nor copy it (A0b). A lane of the upstash pack would be one of Upstash's APIs (its own RESP endpoint answers as Upstash, one database, its refusals), while a World's Redis is stock Redis; and a lane is a derived unit.
- Every vendor that serves Redis serves Redis's semantics: Upstash's compatibility page lists Redis's
commands with Redis's meaning, and a Redis Cloud or ElastiCache twin would too. So the library is shared
meaning that is the same meaning, as the git plane is for every git host, and not the "similar" meaning
A4 forbids. What differs between wires is a dialect (
RedisDialect), which each pack passes and the kernel never names: its refusal for an unknown command, its queue-time check, the commands it adds, whether a failing script keeps its writes, how a Lua number becomes a command argument, its Lua environment (the name of the command API, Redis's runtime libraries:cjson,cmsgpack,bit, Lua patterns) and its tree layout. Lua pattern matching is installed by the selected runtime libraries through the dialect’s environment extension; the base interpreter does not add string.match to other dialects. What a wire answers (its refusals, script rollback, number arguments) has no kernel default; the layout defaults to one subject per key and the queue-time check to each command's arity. - The stream's EXEC reuses connection dispatch in queue order, so SELECT changes the database of following queued commands; blocking commands execute without waiting. First need: managed Redis's SELECT-in-MULTI command contract. Canonical integer parsing accepts Redis's whole signed 64-bit domain; counters retain exact bytes and the RESP bridge carries integer tags across its private JSON request. First need: Redis's RPOP COUNT and INCR/HINCRBY argument domain.
- Expiry deadlines persist as decimal strings and are compared and subtracted as exact integers; JSON reply envelopes serialize an exact integer's decimal digits directly as a numeric token. First need: managed Redis's PEXPIRE GT comparison above 2^53 and Upstash's REST INCR reply above 2^53. Collection positions and counts convert only after integer-domain validation and are bounded by actual arrays; Lua numbers retain the vendor runtime's double semantics.
- The layout is the pack's resource semantics. The upstash pack keeps one subject per key; the redis pack keeps one per key and one per collection member (hash field, set member, sorted-set member, stream entry), as the tree contract says a set is kept, because a queue's events stream of 10,000 entries rewritten on every XADD is quadratic in the log.
- A vendor that carries Redis over HTTP (Upstash's REST API: a command in the path or the body, several at
/pipelineand/multi-exec, each answered{result}or{error}) is a derived pack whose surface is the command table. Its front (itsaround) reads the vendor's forms and envelope and runs a request's commands withctx.redis(commands, { dialect, database }), which the kernel runs over the call's own tree and clock (a handler never holds the tree's location); the queue-time check (redis.commandShapeError) is the front's, before any runs. Its generated surface is that table (commands, and nooperations), whichpackOftakes as the unit's own surface, beside any lanes it has, never as a map of lanes (Upstash's REST unit has the console and QStash beside it: read as lanes, its table's fields were taken for lane manifests and the pack did not load). The derived adapters perform and refresh HTTP operations, and a command table names none, so such a unit declaresvendorBacked.none(a command replayed alone, an INCR or a script, does not repeat what it did) and its lanes' adapters are derived alone, its own writes settled as the World's. - A reply as RESP2 bytes is the library's too (
redis.resp2(reply): a status, an error, an integer, a bulk string, the null bulk string, an array; a number that is not an integer as its bulk string, RESP2 having no double; a status's text with each CR and LF mapped to a space, an error's with those at its ends trimmed and each inside mapped to a space, as Redis writes them (sdsmapcharsin script_lua.c and networking.c), since a simple string never holds them), for any wire that answers Redis's own protocol. First need: Upstash's REST API, which answersUpstash-Response-Format: resp2"as binary similar to a TCP-based Redis client".
The MySQL wire is a request-reply stream of the same kind, and its framing is a kernel library too:
createMysqlStream in @volter/world-core/mysql (packages/world-core/src/mysql/). The kernel holds MySQL's
client/server protocol, which every MySQL-speaking vendor shares. The pack supplies what the statements mean: it
executes them in its own engine over the tree, and authenticates a user against the passwords it stores. The pack
need not hold a password: it keeps MySQL's own stored form of it (nativePasswordHash, SHA1(SHA1(password))) beside
whatever else it keeps, and the kernel checks the client's mysql_native_password answer against that, as a MySQL
server does. A parameterised statement reaches the pack with its parameters bound into the text as MySQL literals,
as PlanetScale's own driver formats them, so an engine that executes text serves both wires.
The PlanetScale twin serves the wire beside psdb, over the same engine and tree, so one database answers both, as PlanetScale's own product does. Its engine stays the pack's while it is the only one that executes MySQL. A second pack that executes MySQL moves the engine here, as Redis's command core moved.
The command subset is what the clients send, by citation. It was read from Prisma 6.19.1's engines (prisma-engines
c2990dca591cba766e3b7ef5d9e8a84796e47ab7, quaint's MySQL connector and the schema engine's, both over Prisma's
fork of mysql_async):
- Handshake. Protocol v10, with
mysql_native_password, then one text query reading@@socket, @@max_allowed_packet, @@wait_timeout. There are no init commands. - Parameterised statements.
COM_STMT_PREPARE,COM_STMT_EXECUTEwith binary rows and parameters, andCOM_STMT_CLOSEwhen the client's statement cache evicts one. - Text queries.
COM_QUERY, with multiple results for a multi-statement script: the schema engine sends a migration whole. This coversSET TRANSACTION ISOLATION LEVEL …beforeBEGIN. - Close.
COM_QUIT. - Replies. OK, ERR (MySQL's errno and sqlstate) and EOF.
The handshake's server version is the one the pack's engine reports through psdb. Commands outside the subset
answer ERR 1047 (ER_UNKNOWN_COM_ERROR, sqlstate 08S01), as MySQL answers an unknown command. A connection's state
(its user, database, session, prepared statements) lives on the connection; what it stores is the tree's.
First need: Dub's PrismaClient over DATABASE_URL (the native wire, imported by 918 files under apps/web at
7e0101363b2ab30afc537c1893f159d1dcba513a) and its prisma db push.
Where the evidence stops:
- Which SQL texts Prisma sends is measured through psdb with Prisma's PlanetScale adapter, which uses the same query builder. The wire itself has not yet carried Dub.
The Redis library also supplies RESP frame parsing and encoding, ordered per-connection dispatch,
transactions, blocking wakeups and pub/sub delivery. A pack supplies its command allowlist, refusal text,
server version and optional password; execution opens the pack's own context through a private transport
capability. Connection state and subscribers remain outside the tree. The library's memberStorage
layout writes collection members as individual subjects, diffing only members changed by a command;
key metadata retains kind, expiry and stream last-id. Its Lua runtime libraries implement JSON and
MessagePack values without vendor state. First need: Twenty's pinned BullMQ 5.78.0 scripts, blocking
worker marker and graphql-redis-subscriptions, and Postiz's ioredis upload sessions.
A journey's resp step sends command arrays on a named connection through the pack's byte-stream
adapter, preserving binary command arguments, pipelines and connection state. It checks parsed replies
(replies, last, error, frameTypes) with the ordinary capture and expectation rules. connect opens the stream;
read drains pushes or blocked replies without sending a command. The same frame reader is used by
the tooling, while the served walk sends the identical bytes to the owned TCP listener. First need:
the managed Redis pack's customer life and Redis's published command examples. Frame tags remain in
frameTypes, so an array (*) and a push (>) can never be indistinguishable in an assertion. The
managed Redis demand uses RESP2: its pinned clients send no HELLO protocol negotiation.
A journey's MySQL step speaks the pack's MySQL wire as an application's driver does, through the same byte-stream
adapter, with the kernel's client (@volter/world-core/mysql, the server's other side).
signInopens a namedlinkwith a user and password overmysql_native_password.mysqlsends a statement on it: a text query, or, withvalues, a prepared statement whose parameters and rows travel in the binary protocol, as Prisma sends one.- Its answer is the rows by column name in MySQL's text format, a count, or MySQL's refusal
{ errno, sqlstate, message }, which fails the step unlessrefusednames its errno.
First need: the PlanetScale twin's native wire, which Dub's PrismaClient connects over.
S3's wire is a kernel library too, world-core's s3 namespace (packages/world-core/src/s3/wire.ts; a handler takes
it as import { s3 } from '@volter/world-core', since a semantics file imports only the kernel's root): the XML S3
answers and reads, a bucket's CORS and lifecycle documents, a delete batch, an object's ETag, and a request's body kept
as its bytes. Every vendor that speaks S3's API speaks that wire (AWS S3, Cloudflare R2, and the S3-compatible stores
that follow), so it is shared meaning that is the same meaning, by the rule above. It had been written twice (the aws
pack's S3 code, then the R2 lane's port of it) because a pack may import neither. What differs is each pack's own:
where a request's account and bucket are (R2's account endpoint, S3's path and virtual hosts), what it refuses, and
which operations it implements.
S3 upload checksums are the same wire mechanic for every compatible vendor: s3.checksum(algorithm, bytes) returns
the base64 network-order CRC32, CRC32C or CRC64NVME digest, or MD5/SHA1/SHA256, and s3.concatBytes assembles
parts without interpreting their bytes. The pack chooses the algorithms/features its vendor supports, checks
provided digests before storing a mutation, and owns the vendor's refusal. The library does not authenticate chunk
framing or validate a streaming trailer. Intent: ADR 0009, first needed by
Cloudflare's ordinary object and multipart uploads.
ClickHouse's SQL is a kernel library too, the root's clickhouse (packages/world-core/src/clickhouse/: the
parser, the analyzer's checks, the evaluator and its function library over relations a pack supplies as a catalog, and
ClickHouse's values and types). Tinybird's pipes are ClickHouse SQL and PostHog's HogQL is ClickHouse SQL, so it is
shared meaning that is the same meaning, by the rule above; it was the tinybird pack's engine, which a second pack may
neither import nor copy. What differs is each pack's front: Tinybird's templates (a node's % marker, {{ }} and
{% %}) rendered before parsing, and HogQL's tables (events, persons, their distinct ids) and property access, each in
its pack. A query outside the grammar or the function library fails as ClickHouse fails, never with a guessed answer.
The shared date-conversion library includes the parseDateTime64BestEffort family, with explicit precision
and timezone and its OrNull/OrZero malformed-date outcomes. PostHog's person-property date filters
compile through parseDateTime64BestEffortOrNull; the
ClickHouse function contract
owns the return type and conversion semantics, shared by every SQL pack.
The shared string-function library includes ClickHouse's regex extract: the first capture group (or the whole match with no group), an empty string on no match, and nullable propagation. Tinybird's SQL query engine imports the kernel's ClickHouse engine (tinybird/src/engine/sql.ts) and serves these expressions through its query API. The kernel owns the shared ClickHouse function; the pack owns its query surface.
The OpenAI-compatible wire is a kernel library too, world-core's openaiWire namespace
(packages/world-core/src/openai-wire.ts): a chat completion and a Responses object answered from a turn, their
Server-Sent Events, and the labeled stub a generative pack answers when the World scripts no turn. Many vendors serve
OpenAI's chat completions or Responses as OpenAI defines them (OpenRouter, xAI, and the other OpenAI-compatible
providers), so it is shared meaning that is the same meaning, by the rule above. What differs is each pack's own: its
ids, its models, a field it adds (OpenRouter's provider and cost, xAI's reasoning_content), a keep-alive comment,
its refusals. The openai pack keeps its fuller implementation (stored completions, the Assistants and their threads);
moving it onto the wire is a later step.
The chat wire's current callers are AI Gateway, Merge, OpenRouter and xAI. Their request token limits use
maxTokens; Merge, OpenRouter and xAI supply answer and usage extras. Merge's
streaming contract requires interimExtra because
"Interim frames report service_tier: null", while the terminal frame carries its served tier and cost.
OpenRouter's streaming contract needs the keep-alive
comment, native finish reason and separate usage chunk; xAI's
chat contract uses
reasoning_content. OpenRouter's Responses defaults and OpenRouter/xAI response extras remain vendor
facts supplied by their packs. The shared wire renders one chat choice; it declares no choice count or stop-sequence option.
The wire preserves a scripted turn's reasoningDetails JSON-object array as Chat Completions message.reasoning_details and streamed delta.reasoning_details, beside its optional reasoning text. The scenario validates the array and its objects; the wire does not interpret or create provider signatures or ciphertext. Opaque values are labeled synthetic fixtures when no model runs. First need: AI Gateway's unified reasoning format, whose published examples carry provider-native thought signatures, summaries and encrypted data.
The wire also speaks the AI SDK's own language-model protocol (@ai-sdk/provider's LanguageModelV4), which Vercel's
AI Gateway serves at /language-model for @ai-sdk/gateway: a call's options (prompt, tools, toolChoice,
responseFormat, maxOutputTokens, the model in the ai-language-model-id header) read as the chat request they mean
(aiSdkChatBody), so the same stub and the same scenario decide the turn, and the turn is answered as the protocol's
generate result (languageModelResult) or its stream parts as Server-Sent Events (languageModelSSE). A scenario reads
a V4 call as it reads a chat completion.
Anthropic's Messages wire is a kernel library beside it, world-core's anthropicWire namespace
(packages/world-core/src/anthropic-wire.ts): a Messages request read as the wire request a scenario reads
(messagesWireRequest: its system and messages, the tools it offers, the tool results it ends with), the labeled stub
for one (messagesStubTurn), and a turn answered as a message (message: its thinking, text and tool_use blocks, its
stop reason, its usage) or as the stream's events (messageSSE). A gateway that serves Anthropic's surface beside
OpenAI's (Merge's Gateway) takes one turn, in the OpenAI wire's vocabulary, and answers it on whichever surface was
called: openaiWire.scenario reads an operation named in its readers with that reader, so a World scripts a gateway's
turns once. The anthropic pack keeps its fuller implementation (its token accounting, images, the prompt cache, its
refusals); moving it onto the wire is a later step.
A placeholder image is a kernel library too, world-core's placeholderPng (packages/world-core/src/placeholder-image.ts):
a real PNG of the size asked for in one flat colour drawn from a seed, labeled in a tEXt chunk as the twin's, the image
a generative pack answers when no model runs (OpenAI's images, fal's image models). Its image data is
fixed-Huffman deflate of Sub-filtered rows, so a flat image of any size is a few kilobytes.
A World's managed redis (volter-world init classifies REDIS_URL as infrastructure) is this pack:
the managed infrastructure (its up in the World's own process, as the generated-infrastructure section
says) serves every declared redis service with the redis twin on the port the definition
declares, containerless, its tree under the World's data (world-runtime/src/redis-backing.ts); the
container serves it only when the operator forces VOLTER_WORLD_INFRA_BACKING=docker or the twin is not
installed (the runtime does not depend on it), and a port that already answers is refused. The other kinds keep
their backings (docker, or without a container runtime the machine's PostgreSQL, else PGlite, for postgres); MongoDB uses its own server below.
A vendor's own server as a backing. Where what an application needs from a vendor is a media plane no pack can
hold as data, and the vendor publishes its server as open source, the World runs that server as a service instead of a
twin: init emits it for the detected vendor when the catalog has no pack for it (VENDOR_SERVER_BACKINGS in
world-runtime/src/init.ts), the vendor's own binary on an auto port, loopback only, and the application's key and
secret the ones the server starts with. LiveKit is the one: livekit-server (Apache-2.0, on PATH), LIVEKIT_URL its
ws:// address, LIVEKIT_API_KEY/LIVEKIT_API_SECRET its key, so RoomService, tokens, webhooks and WebRTC joins are
the vendor's own. Its rooms live in the server's memory until down: not World state, so they do not branch or check
out. Egress is a second LiveKit service (a browser and GStreamer, with Redis between them) that this does not run;
EgressService calls answer the server's own error. Its webhooks go where the application names them, as a LiveKit
project's settings name them (--config-body with webhook.urls), since the route an application takes them on is its
own code. The loopback backing also runs LiveKit's embedded TURN relay on UDP at the service's allocated port,
with loopback as its allowed restricted peer range. This gives normal browsers a media path when they hide local
ICE candidates; signaling and token issuance alone do not establish a media connection. The wrapper owns the
vendor binary's lifetime under the World's service recorder, without an additional fixed port or external relay.
Its adoption facts include LiveKit's React Native clients and only its vendor environment stem.
log/tail --logs also reads the declared services' existing process logs, labeled as process output and
preserving the server's own words. This opt-in observation makes native backing joins and media activity
visible without manufacturing kernel actions or HTTP statuses. It reads complete appended lines only;
vendor output has the vendor's content, unlike the credential-shape-only request journal.
An application's stems belong to its service declaration: serverBackingService receives envStems
and emits the matching endpoint and credential bindings. Coverage and init attribute those declared
binding names to the backing only in that World; discovery aliases rename only the injected binding,
never the producer's JSON path; they never add application stems to global adoption. Coverage
can resolve an empty example connection value from the protocol of an explicitly owned service binding; this same
resolved connection satisfies its native driver signal. A nonempty unknown value retains its unknown verdict.
The recipe's PostgreSQL initialization recognizes a database retained in its task directory across a World restart;
it records successful authority declaration and does not repeat that one-time migration act.
MongoDB is the database case of that same exception: a declared mongodb in
world.infrastructure.yml starts one loopback-only mongod, its --dbpath under the World's
mongod-mongodb-data, its pid and log beside it. No container, VM or look-alike runs, and a World
without MongoDB starts none. Like the native PostgreSQL example and LiveKit, the binary is found
on PATH; the runtime never downloads it. Readers install in a durable directory of their choice.
The pinned macOS ARM64 official archive
is MongoDB Community 7.0.16, SHA256
e01c5ce1ef8efbef4196797130564b8f121f6e29f20ccf70e025feba07e15739.
The MongoDB example
names a pinned vendor download and the commands that put it on PATH. A missing or unusable binary
is refused during boot, with any hosts that boot started stopped on failure. The existing
infrastructure lifecycle owns readiness, connection URL discovery, stop and purge. Readiness failures retain and report the probe error; lifecycle catches distinguish an absent process from a permission or I/O failure. Operator error rendering is shared across infrastructure phases, plain Node hosts and the CLI's owner processes; an AggregateError reports its message and every member recursively, so startup and cleanup failures both remain visible. A cleanup failure is collected alongside the startup error rather than replacing it. The renderer is plain JavaScript, usable by native Node hosts and re-exported by its typed entrypoint. Its declared infrastructure service can be passed to the same
serverBackingService with the World's envStems: MongoDB defaults to MONGODB;
envStems: ["MONGO"] declares MONGO_URI. Endpoint discovery bindings, including their
connection protocol, are remapped within that World; these aliases add no global adoption stems.
Readiness requires the child's own bound-listener
record and a driver ping; a declared replica set must also have a writable primary.
The default is one standalone node. A definition's explicit
command: ["mongod", "--replSet", "world"] requests one replica-set member, initialized by the
runtime through MongoDB's own driver. First need: LibreChat at f10b1d91f1ee, whose
packages/data-schemas/src/methods/mcpAuthority.ts starts snapshot transactions and whose
packages/data-schemas/src/utils/transactions.ts probes transaction support; no change-stream
call was found in its application source. Its configuration therefore needs the replica-set
command. Shared runtime code names no application. CRUD, indexes, aggregations and, when enabled,
transactions and change streams are MongoDB's own behavior. There is no multi-node replication,
failover, sharding, Atlas control plane, authentication or TLS configuration in this backing.
It uses machine time and randomness, not the World clock or kernel's ids.
Database writes have no kernel log, checkpoint, changeset, push or deploy. As for managed Postgres,
a branch copies a stopped base's current database bytes, never an instant or log position; branches
sharing a definition's port run one at a time. Only the backings whose native data directories
are being copied stop and restart for the copy; other declared infrastructure keeps its own lifecycle.
A Docker-backed PostgreSQL beside native MongoDB is not restarted through PGlite. Checkout resumes
those bytes, reset starts fresh state, and down --purge removes them only after verified teardown. Reset selects a fresh live
environment output (--env-out may name it) and validates it before purge: the removed instance
can no longer prove ownership of its old output file. The one-member configuration
is copied with the database and its member address must match the declared port; a changed name,
port or membership is refused rather than silently reconfigured.
Temporal is another own-server backing, first needed by Postiz's unconditional backend and orchestrator
connections. The World runs temporal server start-dev from PATH only when declared, one native development
server, loopback only, without Docker or a VM; its UI is disabled. The pinned official macOS arm64 archive is
CLI 1.9.1,
SHA-256 41e0425378fcb4fb5766340b97435e20fe47bbff2d7bf644ec2d51f7662b7c56; acquisition is an operator step,
never a boot download. serverBackingService binds TEMPORAL_ADDRESS to the allocated loopback gRPC endpoint,
TEMPORAL_NAMESPACE to default, TEMPORAL_TLS to false, and TEMPORAL_API_KEY to empty; alternate application
stems remain a World declaration. The launcher puts SQLite under the service's World data directory and requests
SQLite synchronous=FULL: completed transactions use SQLite's filesystem durability, subject to the OS and storage
honouring its syncs, not the kernel's event-log guarantee. Ordinary shutdown retains that database; reset performs
verified shutdown, purge and fresh boot, and explicit purge removes it after shutdown. There is no World history,
checkpoint, push or deploy for these workflow records. Services can declare execution.branchState: "unsupported";
branch refuses before starting child compute rather than silently dropping their persistence. Temporal declares it:
no World branching or historical checkout is modelled, while checkout of its own stopped World retains its database.
The server executes the vendor's workflows, activities, queues, retries and timers; no cluster, Cloud features,
authentication, TLS or mTLS is modelled. Timers and server timestamps use wall time: World clock control does not
reach this native server. The executed recipe owns the SDK and reset evidence.
Where the evidence stops: the lane is built for one API of one vendor (AWS Secrets Manager) and the line protocol for one protocol (SMTP); that the same forms serve AWS's other APIs, a restJson1 or query protocol, and the other line protocols is extrapolation. The request-reply stream is built for one protocol (RESP), derived from Redis's command table and walked through its byte-stream adapter; and the invariant matrix's resource-level R2 replays HTTP writes, which a raw-tcp pack has none of.
The light guest runner (world-runtime/src/guest-runner.ts) adapts the pinned published Substrate
packages @volter/browser-host-node@0.1.84 and @volter/browser-sandboxes@0.1.84 as the shared backing for
vendors running customer containers: Fly Machines, E2B sandboxes and Cloudflare Containers. First needs:
Substrate's Fly Node host and Workbench's E2B exec/files. Enroll { kind: 'light', images: { '<image>': { context, dockerfile?, runtime?, command?, cwd?, env?, prepared? } } } at the existing machine-pool door.
Image labels resolve only to these explicit local contexts, never a registry pull. The runtime supplies the
provider module to its twin processes; the kernel loads it lazily only when the enrolled pool asks to act.
This bootstrap selects no vendor or backend. The selected pool and image mappings stay in the vendor tree.
One guest is one Substrate host-node agent process with a directory below the World’s data directory.
The admitted program list in this interim adapter is exactly node, matching an image declared as Node.
Its argv must name an ordinary JavaScript entry file inside that guest folder; runtime flags, inline eval,
other runtimes, shells, shell-form Dockerfile CMD/ENTRYPOINT and PTYs refuse before execution. Remaining
arguments are literal application arguments. The agent and every product start/exec use an absolute trusted
Node executable: a Node runtime uses its own process.execPath; Bun development resolves Node once from the
runtime's operator environment before applying image/deployment environment. The agent uses its own
process.execPath for product children. Caller PATH never selects these programs. Caller NODE_* variables
(including NODE_OPTIONS, NODE_PATH and alternate REPL modules), LD_, DYLD_, OPENSSL_CONF and OPENSSL_MODULES
are stripped at deployment, exec and the final spawn seam; only the World's trusted Node preload and CA
variables are restored. The working directory is resolved and guarded inside the guest.
Substrate’s registered-program door carries an opaque launch id; its parser never receives guest command text.
The pinned host-node lane retains children and discovers ports. A synchronous Node spawn adapter replaces
only that lane’s /bin/sh -c launcher with the admitted argv before any OS spawn; no shell is launched and
no shell fallback exists. The adapter retains the exact ChildProcess and arc commandLifetime first, captures
its birth only while that handle remains live, and publishes the birth before acknowledging admission. It needs no VM, daemon, image layers or OS installation. Runtime overhead is small metadata and
directories; application code, dependencies and the application's process memory are additional, unbounded by
this backing. Shared dependencies may be linked from the prepared context.
The idle published agent's RSS is sampled through the SDK in the World by
cookbook/light-guests/measure.mjs: five fresh idle guests, one ps -o pid=,rss=,command= -p "$PPID"
sample per guest before its kill, with Node/macOS versions, source revisions and complete output kept in that
recipe. Small guest disk overhead is not a few-MB resident-memory guarantee; this pinned Node host does not
meet a hard few-MB RSS target.
Files are copied according to local
COPY instructions; WORKDIR, ENV, EXPOSE and CMD/ENTRYPOINT supply execution intent as defined in the
Dockerfile reference. This is an approximation of intent,
not an implementation of Docker's build engine. Multi-stage builds, remote ADD, OS-package installation and
unknown instructions refuse. prepared: true explicitly attests language-package build steps have already run
in the context; it does not waive an OS-package step. A declared command can instead run a prepared context.
Command admission is a twin capability boundary. The neutral pool's optional admitRun prepares and checks
an image command before the pack creates any machine state, including a create with launch deferred.
Unsupported command/runtime/entry-file forms raise WORLD_MACHINE_CAPABILITY_GAP; packs answer their
manifest's declared gap through ctx.gap(), never a vendor launch failure. Exec uses the same admission
before spawning. Fly and E2B disclose that SDK command execution beyond the admitted Node entry is unserved
in their decisions and manifests. Missing dependencies, failed startup and failed declared HTTP/TCP checks
of an admitted command remain execution failures; packs map those to their vendor's failure state/error.
Applications listen on their declared internal ports, including literal listen(8080); the runner does not
rewrite PORT. Substrate discovers those sockets by owning process group and forwards them through its
published RemoteRuntime.portUrl(internal)/request(internal) on each guest's allocated agent endpoint.
The neutral run result includes that URL; the pool request seam and vendor hosts use it. The host's port
namespace remains shared: a conflicting literal listener fails as a guest launch, never routes to another
guest's socket. The deploy's environment is combined with the current instance's injected environment (read at launch,
after proxy startup); deploy values cannot override World routing. Node uses the World preload. Other runtime commands currently refuse. These clients' existing cooperative routing limitations remain.
RemoteRuntime drives exec, files, archives and discovered listening ports through Substrate's HTTP agent.
The pool exposes neutral status, files, ports, exec and request operations; vendor ports, RPC paths
and response shapes belong entirely to the pack. The adapter builds no other command lane, filesystem or
port discovery. Status reports started, paused, stopped, exited with a code, or failed with a reason, including
successful command exits. Each compute pack catches that state up before its API/data-plane reads:
Fly maps exits to stopped ("Exited, either on its own or explicitly stopped"); E2B removes ended sandboxes.
Vendor wait endpoints use the kernel's ctx.awaitCondition(resources, timeoutMs, pollMs, condition) held-read
seam: the caller's documented timeout bounds repeated state observation, waking on resource writes and the
chosen observation cadence. The kernel owns elapsed time; a handler never reads the wall clock.
Compute packs pass a cited startup budget through the neutral MachineSpec.startTimeoutMs. The runner applies
that budget to readiness, control negotiation and declared-port/health discovery; cancellation alone is not a deadline.
Guest stop and World down use the existing bounded TERM→KILL retirement and group confirmation.
Declared HTTP health is checked once a declared socket is discovered; a failing check refuses launch.
The published host-node README
and sandbox README
own these contracts. Standard agent images receive the same Node argv/folder admission as application images; there is no healthy-agent shortcut.
Image command's absolute entry-file argument and declared path environment values map beneath the guest root; arbitrary application
code still sees the host filesystem. There is no isolation beyond the World's and no OS-image fidelity:
Linux /proc, cgroups, uid maps, OS packages, native ABI and enforced memory limits are not emulated.
The agent owns a private process group and the pool retains its ChildProcess/lifetime before publishing its
atomic spawn receipt. Admitted Node launches retain their Substrate and ChildProcess handles and publish receipts with birth
identities in the World's guest-processes directory. A durable launch intent precedes spawning; unresolved
intent or identity keeps ownership and refuses cleanup. Substrate commands are retired through their handles
and recorded groups use the same World TERM→grace→KILL→confirmation path. World down, restart, status and
prune include these receipts, even after the twin/agent has crashed. Only confirmed absence releases a receipt;
successful down/purge requires every recorded group gone. Pause/resume verifies published birth identities again before each command-group signal; profiler PIDs alone
never authorize a signal. A capture returned after its original held child exits is invalid, including missing
birth tokens. Startup and exec require the launch’s positive admission acknowledgement after atomic publication.
A settled command’s receipt remains until group absence is confirmed. Control IPC (pause/resume/stat/admission)
is cancelled at the existing COMMAND_GRACE_MS retirement budget, 5000 ms, or earlier caller cancellation;
all listeners are removed and the failure names the budget. This uses the runtime’s teardown contract, not a
new vendor timer. Disconnected-parent shutdown uses each command’s existing bounded commandLifetime, then
bounds agent/runtime drain by that same grace; errors retain receipts and report unconfirmed retirement.
The adapter uses the arc's owned-path helper for name-derived guest and volume destinations and inherits
the completed World runtime.network policy and ceiling, without a second egress permission mechanism.
Incoming HTTP uses the kernel's bounded decoding. Stop closes the agent and Substrate runtime; a lost IPC parent
also closes them, so World teardown reaches the command groups through their owning Substrate agent. Guest files are retained by stop and discarded by remove, unless the pack asks resetOnStop
(Fly rootfs). Declared mounts name directories beside the guest roots, retained across that reset; the pack
still owns the vendor volume state. This backing supplies no snapshot of live memory across a World restart.
The intended execution door is Browser Substrate’s local substrate (local-substrate branch,
packages/browser-substrate/bin/local.mjs lifecycle and bin/local-agent.mjs exec/files), a folder environment
with known operations and explicit unsupported-line refusals. It is not imported or vendored in this round.
Adoption needs a pinned release, translation of enrolled contexts and neutral argv into its prepare/start/exec
contracts, World-owned folder/environment placement, and integration of its retained lifecycle handle with the
World’s receipt, identity, retirement, network ceiling and port-request contracts. Vendor wire translation stays
in the packs. This interim Node admission is predictability by construction, not kernel isolation: application
code can still access host paths and shared prepared dependencies can live outside the guest folder. The local
substrate must own code/dependencies/HOME/TMPDIR/cache/image/log placement and its fuller known-operation
list. Historical captures using shell commands describe the earlier adapter and are not current execution claims.
A compute pack can declare machineRoutes: a host pattern with named captures, resource and where fields,
the resource's guest-name field and a literal/captured/stored port. The kernel routes that data plane through
ctx.machines.request; the pack's own resource state selects the guest and the guest's own application owns its
wire authentication. This adds no pack router. Fly's <app>.fly.dev is the first need; E2B envd retains its
existing lane authentication and file/exec handlers.
A machine (Fly's Machines: a vendor that runs its customers' images) is run by the World's machine pool, a
platform provider enrolled through a door, never read from the environment (world-core/src/machines.ts). A pack
that runs images declares machines in its manifest; discovery lists the kernel machine-pool door alongside the pack's own doors, and the kernel then serves the door POST /_twin/machine-pool
({ kind: 'docker' }, { kind: 'light', images: { … } }, or { kind: 'none' }), which records the pool in the pack's own tree, so a branch of the World
carries it. A handler asks ctx.machines: run({ name, image, env, ports, command }) answers the loopback port each
internal port is published on, exec(name, { args, env, cwd }) starts a process in a running machine and answers its
pid and its output as a stream of events (stdout, stderr, its exit), and stop(name) and remove(name) end it; each is the World's move after the vendor's state has
moved, and a machine's state is the pack's subject, ruled by semantics/states.ts as any. With no pool enrolled (or
none), a machine's state moves as the vendor's and no image runs; the pack's answer says so where the vendor has a
field for it, and a caller that needs the process (Substrate's sandbox on port 8787) finds nothing listening, as it
would on a machine that never booted. There exec rejects (no machine runs), and a pack answers it as the vendor answers a
process its machine cannot start, never with the pool's words (E2B's envd: the sandbox unreachable, 502 unavailable).
The docker pool's exec rejects too when the machine is not running, with docker's words, and a process a signal
ended exits 128 + its number, as a shell reports it. Capability verification uses a fake pool (machinePool('fake')), which records
what it was asked. Where the evidence stops: the docker pool is local; a hosted World runs no images.
A vendor front catches up every lane's declared clock before routing a request to any sibling lane. Time belongs to the shared vendor state, not the HTTP route: E2B's API owns sandbox expiry, and a delayed envd request must see that expiry even if no API call intervened. Each derived fetch exposes its catchup only to the kernel front; packs still declare semantics/clock.ts. A handler in a sibling service reads the lifecycle through the existing ctx.over contract, with the API manifest re-exported by that lane's shared module; the lifecycle machine is declared once.
Machine pools also expose request(name, port, request) for HTTP services in a customer image, and pause(name) / resume(name) to retain its processes and filesystem across an idle interval. The docker pool resolves only a declared, published port of its own container and forwards the request bytes and streaming response without redirect following; the none pool refuses. The fake pool accepts explicit HTTP reply fixtures at enrollment (httpReplies, each with port, method, path, optional request text, status, headers and body); an unmatched request refuses, and fixtures never substitute for the vendor's stored resources. First need: E2B's filesystem uploads and downloads, and the code interpreter /execute stream, called by Twenty. Its session sandboxes auto-pause and connect resumes them (the sandbox spec); stopping an image loses the live interpreter process. The pack owns the sandbox state and port selection; the kernel owns reaching that machine. Compute side effects remain in the machine, not the vendor's resource tree.
The tree preserves a vendor's explicitly stored id, type and updatedAt even when their values equal the kernel's address or timestamp. Equality never makes an explicit vendor field bookkeeping: E2B's template creation stores updatedAt at the World's time and its list must still answer that required field.
A socket (Discord's Gateway: a bot's WebSocket, on which Discord sends each guild, message, member and
interaction as it happens) is declared in the manifest's sockets (its id, path and, when the vendor serves it on
a host of its own, its host) and spoken by the pack's semantics/sockets.ts (a fixed file, under the handler contract: a session reads the World as a handler does), one export per socket id
(world-core/src/sockets.ts): what a session is sent when it opens, what it answers each frame, and which frames a
write of the World sends it. The kernel serves the upgrade through the serve seam (serveHttp's upgrade, on
Bun and Node alike; serveHttp takes the upgrade a pack's fetch carries before anything wraps the fetch (a World's boot probe), so a server.ts that passes only the fetch still upgrades, in a World or not, and scripts/serve-check.ts (pack-done's (d)) serves the pack as a World runs it, its boot id set, and opens each declared socket), holds the open sessions, and offers every
write to each, queued behind what that session is doing, after the write's webhooks: so a message a door writes
(a person posting in the client) reaches a bot's session exactly as a webhook reaches an endpoint, from the one
write path. The engine reads the World through the handler contract's context of the request that opened the
session, and writes through it as any handler does; a session's own counters (a sequence number, a heartbeat) are
the connection's, never World state. A session's id is numbered within its World (its service and root), so a life
replayed on a fresh World numbers its sessions, and any value built from them, as it did the first time. A client finds the socket where the vendor's API says it is (Discord's
GET /gateway/bot answers the twin's own ws:// base, from ctx.publicBase), so no host is intercepted for it; a client
that dials the vendor's published socket host itself, never asking the API (discord.py's fixed wss://gateway.discord.gg/, the
host Discord's Gateway page gives as its example), reaches the same socket
through that host in the descriptor's hosts, which the World claims as it claims an API host: a socket declared without a
host answers its path on every host the pack serves.
A vendor front also carries its lanes’ socket upgrades. The front selects the first accepting upgrade (its own, then its declared lanes), and remembers that choice per peer for messages and close. No pack forwards upgrades itself. First need: OpenAI Codex’s native Responses transport, whose provider enables WebSockets, while OpenAI Realtime remains the root API’s socket. This applies equally to a vendor made solely of lanes and to one with a root API beside them.
A life speaks on a socket in socket steps (socket names the connection; open a path, or an absolute ws(s):// url opened on its own host as a client that dials the vendor's socket host does, with the opening request's headers where the vendor reads a key at the upgrade, send frames, close):
the answer is every frame the server sent since the connection's last step, and its status the close code (101 while
open), so a bot's identify, a message a door posts and the dispatch it draws are one story.
A model's turn spoken over a session (a voice agent's reply on OpenAI's Realtime socket): the engine asks the World's
scenario for the turn as a handler's request would be asked (session.decide(asked), the turn in the form the pack's
semantics/scenario.ts adapter reads; undefined when the World carries no scenario, a fault the engine answers in its
own frames), else answers the labeled stub. A handler whose answer the request does not admit is passed over for the next
that matches, else the stub (the adapter's admits: OpenAI's and Anthropic's tool_choice: none admits no tool call,
over HTTP and a socket alike). The scenario is read from the World's store at every turn, a socket's as an HTTP
request's, so a World that rewrites its scenario file has the next turn decided by it, with no reboot; while the file is
unchanged the engine, and its handlers' counts, is kept. A session's end is the engine's close(session, ctx), run
after what the session is still doing over its own context, so what the end settles is written.
A socket's subprotocol: a socket may declare protocols, the subprotocols its server selects from those a client
offers (OpenAI Realtime's realtime, beside a browser client's openai-insecure-api-key.<key>); on Node the seam
answers the first offered that the socket declares, and for a socket that declares none the first offered (as the ws
library does); Bun answers the first offered by itself (measured, Bun 1.2.20: a header set beside it is a second one,
which a client refuses).
gRPC (Google's APIs as their client libraries call them: google-gax on grpc-js, HTTP/2 with TLS) is the kernel's
too, first needed by Google Web Risk: a proto unit's schema (src/generated/proto.gen.json, fields and
enums by number, rpcs by path, which the derive writes), the codec between a message's proto3 JSON form and its bytes
(world-core/src/protobuf.ts, exported as protobuf), an HTTP/2 server over a plain socket (h2.ts), the gRPC framing
over it (grpc-wire.ts: length-prefixed messages, the status trailers). A pack whose manifest says grpc: true is
served on its one port by grpc.serveGrpc: HTTP/2 and HTTP/1 on the same socket, and each rpc answered by the pack's
own fetch, as the derive serves a proto unit's rpc (POST /<package>.<Service>/<Method>, the request's JSON form the
body; an error's HTTP status, or Google's error.status, is its gRPC code). The injector sends an HTTP/2 session to a
claimed host (http2.connect, which grpc-js opens with TLS) to that twin's origin in plain text. A handler reads and
answers the JSON form, as a REST handler does.
A request answered by a session (the tunnel's relay: a public request sent down the client's control socket, and
answered by the frames the client sends back): a handler finds the vendor's open sessions of a socket (ctx.sockets(id),
this World's only), sends a frame on one, and awaits session.expect(key, ms), which settles when the engine, reading
the session's frames, calls session.fulfil(key, value), and rejects when the time runs out or the session closes. The
correlation key is the protocol's own (a request id); the value is the engine's (a Response, its body a stream the
engine keeps feeding). Built at its first need, the tunnel.
A read held until the World writes (HTTP long polling: Telegram's getUpdates, which a bot calls "to receive
incoming updates using long polling", its timeout the "Timeout in seconds for long polling",
Bot API): a handler that finds nothing to answer awaits
ctx.awaitWrite(resources, ms), which settles true when a write of one of the named resources lands in this World
(through any handler, door or the core), and false when ms pass or the client hangs up; the handler then reads the
tree again and answers what it holds. The wait is the connection's, never World state: the answer is read from the tree
when it is made, so a walk that makes its write before it asks, or asks with no timeout, is answered at once and alike in
every World. A read-only World holds no read (nothing can be written while it waits). Built at its first need, Telegram's
Bot API, whose clients ask again at once on an empty answer (the Chat SDK's polling loop): answered at once, a waiting
bot spins. Where the evidence stops: a waiter hears the writes made in its own process (a local World, where a twin
serves in one process); a hosted World's multiplexing front is not read for it.
A socket's subprotocol: a socket may declare protocols, the subprotocols its server selects from those a client
offers (OpenAI Realtime's realtime, beside a browser client's openai-insecure-api-key.<key>); on Node the seam
answers the first offered that the socket declares, and for a socket that declares none the first offered (as the ws
library does); Bun answers the first offered by itself (measured, Bun 1.2.20: a
header set beside it is a second one, which a client refuses).
A model's turn spoken over a session (a voice agent's reply on ElevenLabs' conversation socket): the engine asks the
World's scenario for the turn as a handler's request would be asked (session.decide(asked), the turn in the form the
pack's semantics/scenario.ts adapter reads; undefined when the World carries no scenario, a fault the engine answers in
its own frames), else answers the labeled stub. A handler whose answer the request does not admit is passed over for the next that
matches, else the stub (the adapter's admits: OpenAI's and Anthropic's tool_choice: none admits no tool call, over
HTTP and a socket alike). The scenario is read from the World's store at every turn, a socket's
as an HTTP request's, so a World that rewrites its scenario file has the next turn decided by it, with no reboot; while
the file is unchanged the engine, and its handlers' counts, is kept. A session's end is the engine's close(session, ctx), run after
what the session is still doing over its own context, so what the end settles is written (a conversation's final status
and duration). Both built at their first need, ElevenLabs Agents.
Where the evidence stops: a hosted World's multiplexing front (world-host) upgrades nothing yet; a local World,
where each twin serves on its own port, carries sockets.
Machine pool operations share the read-only refusal seam before calling a backend: run, exec, pause, resume, stop and remove mutate. A forwarded request is safe in a read scope only when the pack declares the operation as retrieve/list or in reads; RPC reads may POST, and computed guest execution may mutate. Secure sandbox authentication precedes traffic-driven resume and TTL renewal.
The Node socket guard permits TLS over an already connected socket only when that socket's actual peer is the configured loopback World HTTPS proxy, with the exact port. The SNI still names the vendor: a client using its own ProxyAgent tunnels through the enforced proxy. Merely supplying a socket, a local hostname, or a vendor SNI is insufficient; other direct raw sockets retain their refusal. E2B's official SDK with its native undici dispatcher is the first demanded use.
The descriptor
Every pack exports one descriptor, pack: TwinPack, from its index.ts (a lane has none: its vendor's describes it),
and registers it on load (registerPack(pack)). This section is the descriptor's law: each field is defined here
once, and the type in world-core/src/packRegistry.ts points here instead of restating it. A field this section does
not define is not a descriptor field, and adding one starts with its row here. A derived pack declares it in its manifest (manifest.descriptor, every field but
vendor, which is the manifest's), and its index.ts registers packOf(manifest): a fixed file (create-pack --init
writes both). A pack of lanes, and a pack written before, declares it in its index.ts, as data.
scripts/pack-facts.ts compiles the fields the injector and the runtime read (hosts, adoption, endpoints, the World
wiring) into world-core/generated/pack-facts.json; after any change to them it is run again, and a stale file fails
its gate.
Identity and package.
| field | declares | read by |
|---|---|---|
vendor | the vendor's id ([a-z0-9-]+), the kernel service its state is kept under | everything |
maxRequestBodySize | an explicit HTTP transport cap in bytes, with its byte measurement beside it; absent, the serve seam keeps its default | the pack fetch, serve seam and World fronts |
transport | how the vendor is reached: rest, graphql, web-api (an RPC API over HTTP), or raw-tcp (the pack's own socket: a line protocol, a request-reply stream, gRPC) | the injector, the runtime, the invariant matrix |
archetype | the serve family the invariant matrix keys its strictness off (PACK_ARCHETYPES) | the matrix; required |
protocol | the platform plugin contract the package implements ('3'); the kernel refuses another or missing declaration | registration, hosted mounting and pack-facts |
resources | the stored subject types the twin serves | the invariant matrix; required |
resourcesUnreachable | stored types the twin serves that no replay can create, each with why | the resource-level R2 check |
bin | the world-<vendor> operator command | the runtime's spawn path |
serveExport | the serve factory's name, when the package exports several | the colocated host |
assets | files under the package the serve path reads (a vendor's bundled script) | the publishing pipeline |
specSource | where the surface came from: the vendored spec and its upstream | review, the report |
description | one line, for people | the catalog |
Interception and adoption. How a World takes an application's traffic to the vendor.
| field | declares | read by |
|---|---|---|
hosts | the host rules the injector sends to the twin (host, suffix or hostPattern, with pathPattern and exclude where hosts are shared; optional demandPathPattern narrows only demand detection, a regex source or false to omit the rule from detection) | pack-facts → vendor-hosts.cjs, the injector, ctx.vendorFetch, demand-scan |
hostsNone | why the pack has no host rule (it is wired by an endpoint variable alone) | pack-facts; exactly one of hosts and hostsNone |
hostsClaimed | a door that claims hosts at run time (an application's own domains), and how | the runtime |
browserRouting | how the vendor's browser SDK addresses its API (apiPathPrefix, loaderHost) | the dev proxy |
adoption | what in an application names the vendor: sdks (npm clients), pypi, scopes, envStems (credential variables' stems: the name with its credential suffix (_API_KEY, _TOKEN, _SECRET, …) and any framework prefix (NEXT_PUBLIC_) removed, so TRIPO for TRIPO_API_KEY; world-runtime's covers.ts), configFiles, worldIds, tools (build and deployment packages, not the application's calls) | init, covers, the coverage index |
clientCasesNone | why the vendor's own client drives no client case though adoption.sdks names npm clients: the vendor maintains no official SDK (Discord: "Discord does not maintain official SDKs", Community Resources; the libraries sdks names are third parties'), or the demand's applications use none; its page cited on a // source: line above it | conform's client check |
World wiring. What volter-world init and the runtime give an application and the twin.
| field | declares | read by |
|---|---|---|
endpointEnv | the variable the vendor's own SDK documents for its base URL, which init points at the twin (name, templates for a value that is not a URL) | init |
endpointEnvNone | why no such variable is injected (inventing one the application never reads would report coverage while traffic reaches the vendor) | init; exactly one of endpointEnv and endpointEnvNone |
credentialDoor | the path and body of the door that issues this World's app credentials, and the env names filled from its answer (file:<field> writes an issued credential file beside the instance; directory:<field> writes a relative-file map there and fills its directory path; side:<side>:<field> fills a URL the twin issues for one of its sidePorts, its authority the side's listener as the boot placed it) | init, up |
credentialIssuer | another pack of the same vendor that issues this API's credentials (Web Risk's first need: Google OAuth); detection shows it and its reason alongside the API, for the operator's selection | init, covers |
Credential directories are first needed by OpenAI Codex: the pinned client reads $CODEX_HOME/auth.json. A directory:<field> credential-door fill takes a nonempty object whose keys are safe relative file paths and whose values are strings or JSON values. The runtime creates the directory beside the World instance, mode 0700, writes each file mode 0600, and fills the env name with the directory path. Configuration environment names must match [A-Za-z_][A-Za-z0-9_]*. Before writing either a single credential file or a directory, the runtime resolves every destination within its World custody directory and refuses traversal, child symlink components and linked files; validation precedes directory creation. Other name-derived runtime and init destinations likewise resolve within the real path of their owning directory; an operator's configured base may itself be a symlink to another volume. The runtime records the opened credential file's inode before checking its real parent again, verifies the real file after writing, removes an escaped opened file when detected and fails issuance; because Node offers no openat, a same-user process racing inside the World's own directory remains possible in principle. These are issued custody artifacts, owned by that World, never a vendor or application checkout. init recognizes directory fills as paths, as it recognizes file fills. No pack chooses a filesystem destination.
| prismaAdapter | the driver adapter a Prisma client reaches this database twin through (adapter, export, urlEnv) | the injector |
| managedService | the pack is the World's containerless backing for a managed-infrastructure kind (below) | pack-facts, the managed infrastructure |
| managedDatabase | the pack's data plane runs over the World's managed kind (postgres): the runtime hands the twin that service's URL as serve <arg> <url> or the factory's database option (managed infrastructure, the declared exception) | the runtime (managedDatabaseFor) |
| sidePorts | listeners beside the main port (the SMTP twin's mailbox page): each allocated before boot and handed in as <flag> <port> or <option>, recorded as sides.<name> | the runtime |
The supported SDK base-URL variable remains declared even when another client uses host routing: a transport that does not read the World's CA environment can use the twin's loopback HTTP URL through endpointEnv, with TLS verification retained for HTTPS. First need: Workbench's Python E2B client. A shared host's pathPattern also includes the caller's documented URL forms, such as GitHub smart HTTP with or without .git; the proxy must reach the screen that already accepts both.
Declared endpoint templates remain authoritative after credential issuance: a door supplies
the genuine key/token, while the endpoint template supplies the selected twin's transport
address. A URL returned with a credential cannot replace an explicitly declared local SDK
endpoint. First need: Dub's Next.js fetch transport bypassed the fetch injector for Upstash's
Redis driver; its REST URL must reach the twin directly alongside its issued database token.
managedService: a protocol twin as the World's own infrastructure. An application's database, cache or queue
(REDIS_URL, MONGO_URI: init classifies a connection variable as a managed-infrastructure kind by its name and its
URL's scheme) is World infrastructure, declared in world.infrastructure.yml. Where a twin speaks that kind's protocol,
the pack declares it, and the runtime serves the kind with the pack, naming no vendor:
managedService: { kind: 'redis', scheme: 'redis', note: 'stock Redis over RESP2' }- For each declared infrastructure service of
kind, the managed infrastructure (itsup, in the World's own process) starts the one installed pack that declares the kind, by itsbin:serve --port <the definition's port> --root <the service's tree under the World's data>. It is ready when the port accepts a connection, published as<scheme>://127.0.0.1:<port>(with the definition's database name as the path where the kind has one), and stopped bydown. Its tree branches with the World (world-runtime/src/branch.ts). - A container serves the kind only when the operator forces it (
VOLTER_WORLD_INFRA_BACKING=docker) or no installed pack declares the kind; without a container runtime the kind is then refused by name. A kind no twin serves keeps its own backing (the machine's PostgreSQL, else PGlite, for postgres; the vendor's own server for MongoDB). A port that already answers is refused, never shared. - Two packs declaring one kind is refused by pack-facts. The pack is a twin like any other (its state is the tree, its
time the World's), built to the derived form from its protocol's own spec: Redis's command table (
spec/commands/, whichderive-packreads), MongoDB's command reference (a command-table reader, not yet built). It mounts as a byte stream (create<Name>TwinStream), and its HTTP fetch servesGET /twinand its doors. - It replaces the runtime's package-named Redis backing (
redis-backing.ts), which reads the same facts from the package name; until the runtime readsmanagedServicefrom pack-facts, that is the declaration's interim implementation. MongoDB uses its own server as described above, outside this pack mechanism.
Where the evidence stops: managed-service dispatch resolves the installed Redis CLI by package name; descriptor-based dispatch is not implemented.
The real system. How a branch's entries reach the vendor and the vendor's state reaches the root (the plugin contract).
| field | declares | read by |
|---|---|---|
stateSystem | { perform, refresh?, ingest? }: perform a request's entries against the vendor, refresh a root from it, take a webhook; derived by packOf from the manifest and surface, never written | the head, stateSystemFor |
vendorBacked | { none: '<reason>' }, copied by packOf from the manifest (a vendor of lanes: the vendor's own manifest, a VendorManifest's vendorBacked): the pack has no vendor-backed half, packOf derives no state system, and binding a root to it is refused with the reason | adaptersFor; pack-standing.ts for a lane, from its vendor's manifest |
refresh | { every?, webhook?, onDemand?: { atMost } }: how a root is kept current | the served World |
pullPosture, pullPostureReason | how often the vendor's real API may be pulled, and why (required for continuous) | the v1 pull tooling |
rateBudget | the vendor's published limits, armed on registration; a widening re-declaration is refused (D8) | every live call |
auth | how the executor applies a sealed credential, when the vendor does not read a replaced header | the executor |
roundTrip, references, referenceTrip | a minimal write the branch round trip sends; which fields hold another subject's id; a write referencing the round trip's subject | the round-trip gate, the kernel's resolution |
shapeParity, parityOrigin, refreshRefusal | that the write path and the refresh store one shape; the origin a refresh reads its scope from; a refresh the parity gate cannot exercise, and its expected refusal | the parity gate |
engine | the module owning a second half of state beside the tree (a git plane, bytes, an SQL engine) | the engine-slot gate |
emitter | a pack not yet moved to the manifest's events that synthesizes signed deliveries | the deliver path; a derived pack declares events in its manifest instead |
conformanceFields | a vendored field map for the v1 conformance check | the v1 tooling; a derived pack's shapes are its generated surface |
Pack layout
Each checked-in journey can declare a separate scenario JSON file in journeys/. The form admits only the safe basename explicitly referenced by customer-life.json or vendor-examples.json, alongside the fixed files; an unreferenced extra remains outside the form. The World loads that journey's declared file. OpenRouter's published-example failure scripts need separate preconditions from its customer story, so one shared scenario.json would conflate the two walks. This is the first need for declared per-journey scenario files, not a second scenario grammar.
Protocol 3 and the vendor's spec determine every file of a pack: its name and what it holds. Two authors creating
the same vendor produce the same tree, and a file the list below does not predict is outside the form.
The package's published files includes every UNIT_FILES file its units hold (conformance reads
those files after installation), including each lane's manifest and spec and the vendor's journeys.
The form's layout criterion checks inclusions and exclusions against that package list.
<vendor>/ in twin-packs-p3
README.md package.json LICENSE CHANGELOG.md
vendor/<package>/ a client bundle the vendor publishes that the twin serves as published (Clerk's clerk-js), with
its SOURCE.md (upstream, version, sha256, licence)
spec/
<document> the vendor's spec as published (openapi.json[.gz], openapi.yaml[.gz], smithy.json[.gz],
discovery.json, rfcNNNN.txt, proto/, or openapi/ for one API in several documents)
SOURCE.md where each document came from, when, its version and sha256, the corrections
<writer>.ts the script that wrote the document from the vendor's reference page, when the vendor publishes
none and SOURCE.md names it (a tool, never hashed as a document)
patches.json the corrections, each with its reason and source (absent when there are none)
grouping.json how derive-pack groups the operations into families, `{ "by": "tag" }` (absent when by path)
sources.json the checked citations (check-sources.ts writes it)
doc-examples.json the vendor's published examples (vendor-examples.ts writes it)
recordings/ the vendor's own answers, where any were recorded
journeys/
customer-life.outline.md customer-life.outline.verdict.json
customer-life.json customer-life.verdict.json
vendor-examples.json vendor-examples.verdict.json
first-use.json the installed customer entry: pinned SDK files, assertions and retained-state readback
scenario.json the World's scripted turns for the customer life, when it uses them
<name>.json another walk's scripted turns, when its journey names the file (`"scenario"`)
demand.json what the applications using the vendor call: each application (its repository and
commit), and each operation, event or flow it uses with the file and line that shows it
decisions.json the decision table: each served operation, its demand and its mechanism
unreachable.json what no World reaches, each with its reason
score.json coverage.json the report (score-pack.ts --write)
src/
index.ts the descriptor and the exports
cli.ts the pack's bin
manifest.ts the vendor's facts no spec carries: error envelope, auth, ids, time, paging, the
resources modelled, refresh, references, roundTrip, engine, screens
fetch.ts a fixed file: createPackFetch over the pack's parts (the manifest, the surface, the
handlers, the doors, the screens, the clock); the kernel serves the discovery door, the
clock's catch-up, the doors, the screens and the API from them
server.ts a fixed file: the serve seam (serveHttp) around fetch.ts; a unit whose spec is a proto, serveGrpc over its generated schema (a lane's proto is refused: its vendor's seam is HTTP)
stream.ts for a byte-stream wire: create<Name>TwinStream, framing connections into its semantics
budget.ts when the vendor rate-limits a live token (D8)
generated/ the derived core, from the spec alone (derive-pack.ts writes it): surface.gen.json, and
proto.gen.json for a unit whose spec is a .proto tree
semantics/
states.ts every state machine, as data: each field's states, its initial state, each transition
with the operation or actor that makes it and its citation
index.ts the handler map, by operationId
<family>.ts the handlers of one family of the spec's operations: the first segment of their path
after a version (`/v1/organizations/{id}/invitations` is `organizations`), a dotted RPC
method's first part (`chat.postMessage` is `chat`), a line command's name
doors.ts the World's /_twin/ doors the manifest's `doors` declares (standing in for the vendor's
pages not yet built as screens): one export per door, named by its id, over the context
clock.ts one export, `clock(ctx)`: the vendor's own moves the World clock has made due (a rotation
starting, a file expiring), caught up by the kernel before any request is answered
answer-views.ts one export, `answerViews`: named views of an answer whose values the vendor's wire encodes (a
psdb result's rows, base64 sliced by per-column lengths), each `(body) => value`; a life's step
names one as its `view` and its `expect` paths read it (the walk's own `jwt:<field>` beside them)
scenario.ts one export, `scenario`: how the World's scenario decides a model vendor's turn — the operations
it decides, the request each is to it, the vendor's matchers and fault body (its adapter); the
kernel loads the scenario (`--scenario`), answers a fault and hands the decision to the handler
as `ctx.scenario`
gap.ts one export, `gap(ctx)`: the vendor's answer for what it does not serve where the manifest's
`gap` cannot say it (it depends on the request: Clerk's Frontend API answers `signed_out` to an
operation asked without a client, and Go's plain-text 404 to a path it does not have)
tenant.ts one export, `tenant(ctx)`: the tenant the caller acts in (its organization, team or account,
named by its credential), which the manifest's `tenant` scopes every resource the core serves by
seed.ts the pack's default data, as data: `seed: SeedCall[]`, calls to the vendor's own operations
(or its doors), which `volter-world init` copies into the World beside a runner that makes them
events.ts one exported `data(ctx, write)`: how the vendor renders an event's `data` for a write, when
it is not the written object (the manifest's `events` declares the rest)
sockets.ts one exported engine per socket the manifest's `sockets` declares (world-core `SocketEngine`):
what a session is sent when it opens, what it answers each frame, which writes it hears of
shared.ts what the pack's handlers share (a helper several packs would share is the kernel's)
around.ts one export, `around(ctx, next)`: what the vendor does to every request before and after its
handler (its credential check, its request id), when the manifest's `auth` cannot say it
graphql.ts the resolvers of a vendor's GraphQL API, where its spec is a schema
<family>.test.ts a handler file's tests, where its rules are tested beside the life
screens/<id>.tsx one per screen the manifest declares, named by its id, built from @volter/world-ui
screens/shared.tsx what two or more of the pack's screens share (its page shell, its skin, a flow two screens
walk), exporting no screen
engine/ the engine slot's module, when the manifest names oneA generated vendor front retains the DerivedFetch contract on its return type, including the optional upgrade seam, rather than narrowing it to a bare request function. Its server.ts can therefore use the same optional socket adapter as a single-unit server. Jira first exposed this during the package typecheck even though it declares no sockets.
A lane of a vendor that serves several APIs (lanes),
<vendor>/<lane>/, has spec/ and src/ with the same entries except server.ts, cli.ts and the descriptor,
which are the vendor's. A vendor whose every API is a lane (create-pack --init <vendor> --lanes a,b) holds at its root
the package files, the vendor's life (journeys/: one demand, outline, decisions and life for all its lanes), its
lanes, and in src/: manifest.ts (a VendorManifest: the lanes' routes, by host, path prefix or header, and the
descriptor), index.ts, fetch.ts, cli.ts, server.ts and semantics/shared.ts (what the lanes share, which each
lane's semantics/shared.ts re-exports). A file the list does not predict is outside the form, and so is a name the list
fixes written another way (<vendor>-server.ts for server.ts); the grader names each (The grade).
A lane whose state is its vendor's (its manifest names the vendor's service, and the vendor's src/server.ts serves
it: Clerk's Frontend API over the users, sessions and organizations its Backend API keeps) is one customer's life
with its vendor, not a second: the vendor's life walks it through the vendor's serve factory, and the lane has no
journeys/ of its own. Its hand-written lines and its state machines are measured in the vendor's coverage beside the
vendor's own (scripts/lanes.ts names such lanes; life-coverage.ts counts their src/, recorded unreachable as
../<lane>/src/<file>), and the grade reads the vendor's outline, life, examples and report for the lane. Its
handlers and shared.ts may import the vendor's src/semantics/states.ts, src/semantics/shared.ts and
src/manifest.ts (to open the vendor's context), and its manifest the vendor's states: the state they share, one home
for it. A lane over its own state (aws's Secrets Manager), or one the vendor's server does not serve, has its own life.
Where the evidence stops: the report's surface for such a lane is the vendor's, until the report counts each lane's
operations beside the vendor's.
spec/ and journeys/ are inputs to the build and the runner and are not published. Everything
the twin serves is under src/, so C2 and the publishing pipeline
apply unchanged. When the vendor publishes no usable spec, the SDK's types or recordings stand
in, and SOURCE.md says which.
Screens
An existing browser's request interception sees only the first request of a redirect chain. A World browser routing adapter fetches every HTTPS hop through the same World-attached transport before fulfilling the request, preserving methods and bodies according to HTTP redirect rules and removing origin-bound credentials on a cross-origin hop. The final HTTP application callback is left to the browser. The adapter places vendor pages under a World path namespace on the bound browser origin, passes that namespace through the existing World-place header and cookie seam, and maps HTML browser destinations into it; input values and published external JavaScript remain unchanged. This gives the World its own cookie paths in an existing profile. First need: Clerk's hosted OAuth sign-in through a same-site proxy must keep every redirect in the World and its client cookie separate from an existing profile cookie.
A screen host consisting of one named placeholder (<instance> or {instance}) captures
the whole routed hostname, including dots. A placeholder within a dotted host pattern
captures one DNS label. First need: Clerk's hosted OAuth sign-in and consent screens share
the instance's complete Frontend API hostname, including its same-site proxy host.
A vendor's screens are twinned for two jobs, and the job decides what the screen must hold to:
- Hosted flow: a screen an application sends its user through. An OAuth consent, Slack's install,
Stripe Checkout, the billing portal, Connect onboarding. The application never reads such a page;
it sends the user to a URL with parameters and receives a redirect back (a
codeandstate, orerror=access_denied), then an API call or a webhook. That round trip is the contract, and the vendor documents it: the entry URL and its parameters, the outcomes and the redirect each one sends, the form fields a submission carries, and the controls a person acts on, named as the vendor names them ("Authorize octocat", "Card number", "Pay"), so a test that finds a control by its role and name finds it. A submission is a transition by an external actor on a declared machine: hosted flows are where moves no API call makes enter the World. - Workspace: a screen people work in. A Slack workspace, a pull request, a dashboard, a board. A
person, a tester or an agent must be at home in it; nothing reads it but people. The console never holds a vendor's
paths: it frames a workspace at the address its pack's
GET /twinlists. Not built: a workspace opening a record asked of it by name (?show=<type>:<id>, the page that shows it, else where it lists the type, else its home); no pack's screens read it yet, so the console offers no link to a record on a vendor's screens.
The vendor's own acts are modelled as the vendor's moves: a check suite GitHub creates when a
commit lands, a payout Stripe pays two days after it is made, an event a webhook delivers. A pack's
/_twin/ doors stand in only for a screen not yet built or for a system outside the vendor's
surface that acts on it (GitHub's Actions runner finishing a run); a door that makes the vendor do
something its own rules would do is a missing move.
An operation that hands out a long-lived connection answers the twin's own socket, on the address the caller reached
the twin by (ctx.publicBase): Discord's GET /gateway/bot its Gateway, Slack's apps.connections.open its Socket
Mode link with a ticket the socket's engine takes once. The injector carries a WebSocket upgrade to a twinned host
to the twin's own listener as a real request (inject.cjs, upgradeToTwin): its stand-in for a vendor's request is
fetch-backed and cannot switch protocols. Where the evidence stops: a hosted World's front upgrades nothing yet (sockets), so there the link does not
open.
A built screen is declared in the manifest's screens (its id, its host and its path, {param} or <param> in
either) and written as screens/<id>.tsx, whose one answer, screen(ctx), the kernel calls for a request to that host
under that path (its page and the forms it posts), over a context it opens: the screen reads and writes the World as a
handler does, never around it. Where the vendor's site is itself an API the pack's spec writes down, its pages drawing
and its forms and links acting on operations at the same host and paths (Hacker News: the /login page posts its form to
POST /login, an upvote arrow is GET /vote, and the front page at / stands over every path), the screen declares
yieldsToApi: wherever the screen would answer (on its host, at a World's place, by its path alone), a request the
API names for its method is the operation's, and the screen draws the rest. Without it, a screen on a host it names
exactly takes every request under its path. A content screen never declares it (the kernel refuses the declaration):
on its host, and at <base>/@<host>/, it answers its host's requests alone. A screen neither job reaches is not built: a World's state is inspected elsewhere, never through a
generic resource browser posing as the vendor's UI (C1b's "never a fabricated dashboard"). A widget
the vendor ships into the application's own page (Stripe Elements from js.stripe.com) is not a
screen: it is a client library, twinned from its published types as an SDK surface is.
A screen is authored, never captured. Building a twin never copies a vendor's page: no DOM,
asset or traffic capture is a step of creating or maintaining a pack, because no process reaches
every screen a vendor has, and a pack whose screens depend on captures has a screen count set by
what someone happened to record. Every screen is written from @volter/world-ui's shared pieces
under the vendor's skin, and reads and writes only through the pack's operations; a hosted flow is
the same kind of screen with its round trip declared as its contract.
A page at a World's place. A workspace and a vendor's public page (a post as anyone sees it on X's
x.com/<handle>/status/<id>) answer at a World's place for the twin too (x-forwarded-prefix: a served or hosted
World's /<org>/<world>/<vendor>/…, its content plane), by path, as the console frames them. Their links, moves and
form actions name the twin's own base there, never the vendor's host a browser outside the World's proxy cannot reach;
a prefix of / is a place whose base path is empty, still a place. Every cookie a twin sets there is kept to the
twin's base path (world-core placedCookies: Path=/ and no Path become Path=<prefix>, which RFC 6265's path-match
gives the twin's root and every path under it and no sibling; Path=/x becomes <prefix>/x), so one twin's sign-in
never reaches a sibling twin served from the same pages origin. A __Host- cookie, which a browser takes only at
Path=/, is left as set, so a twin keeps no session in one; a prefix the World never sends (outside
/[A-Za-z0-9._~/-]*, which twinPublicBase refuses too) leaves every cookie as set. A browser that holds a twin's
cookie set at Path=/ before this keeps it until it expires: a sign-out answered now clears only the scoped one.
A page at a hosted site mints every URL it writes for its vendor's media, API or pages with
world-core twinVendorUrl(request, url): images, video sources, poster frames and thumbnails included.
Pass the request that rendered the page and the complete vendor URL, after signing it where the
vendor requires a signature:
import { twinVendorUrl } from '@volter/world-core';
const src = twinVendorUrl(ctx.call.request, media.url);The helper keeps the URL's path, query and fragment on the page's origin: its own host at the root,
another host of that vendor under /_host/<hostname>/. It uses the page's real host, which the
World supplies as x-volter-world-site, and its public origin from the forwarded host and scheme.
The router checks the target vendor; the helper is for the pack's own vendor URLs. Without the
hosted-site headers it returns the complete vendor URL unchanged, so a local proxy reaches the
vendor host as usual. At a World's path mount the pack places its media under the content route
below. A sibling hosted-site label has its own page session, so a media URL names the page's
origin through this helper.
At a World's path mount, what a page reaches on another of the pack's hosts, one a content screen serves (X's images and video on
pbs.twimg.com and video.twimg.com; the S3 bucket Reddit's media lease names, which its Create Post page posts an
upload form to), is named <base>/@<host>/<path> (world-core content-route.ts, pack-fetch.ts). At a World's place
the kernel reads such an address as a browser would have written it: a doubled slash collapsed, %40 for @, the
host's case, a trailing dot and a default port (80, 443) normalised away. When the host so read is one a built
content screen of the pack names exactly (a lane's, for a vendor of lanes), the request is that screen's, answered by
a direct call to the screen's answer over a context the kernel opens for the request as routed from that host
(x-volter-twin-original-host, with the pack's own routing of a host: its path prefix and host labels):
- a vendor of lanes, or a pack with lanes beside its own API, sends it to the lane whose content screen names the host, before any lane is picked by the request's host or path, through that lane's own fetch, wrapped as every request to the lane is (its correlation and perform, its request scope and read-only mark, its CORS, its cookies kept to the base);
- the path, slashes collapsed, is held to the descriptor's host rules read as the injector reads them (world-core
vendor-hosts.cjs,hostRules:host,suffixandhostPatternrules with theirpathPattern,excluderules, perkey), for a lane, which declares none, its vendor's descriptor as registered (getPack); a host no rule names takes any path its screen takes, and the screen's ownpathholds it too; - GET and HEAD reach any such screen; POST, its body carried, only a screen that declares the form it takes
(
takes: ['POST'], beside its demand); a read-only twin refuses that POST as it refuses any write; the vendor's clock moves first, as before any screen; - any other method, a port other than the default, a path the host's rules or the screen do not take: the pack's
not-found (the manifest's
notFound). Such a request never reaches the API, another screen, a door or the gap.
A /@<name>/ whose name is no host a built content screen of the pack names (an npm scope, /@socket.io/…; a profile
at /@handle) is not this route's: it is routed as any other path. First needs: X's posting flow and its post page in
the model editor's launch World, whose video and images are on X's content hosts (GET); Reddit's Create Post in the
same World, whose page posts a chosen video to the S3 host its media lease names
(reddit-uploaded-video.s3-accelerate.amazonaws.com), which a person's browser in a hosted World reaches only as
<base>/@<host>/ (POST).
Demand decides which screens exist, as it decides operations: a hosted flow where an
application's code sends its user to the vendor, a workspace where people do the vendor's job. Each
is declared in manifest.ts (screens: [{ id, kind: 'flow' | 'workspace', host, path, demand }]),
counted done or todo. A screen's control is accepted when driving it through its own click
performs the operation or transition it declares; a flow (an entry and one outcome) when the
redirect, its parameters and the state it leaves match the declared round trip. The life walks a
screen as a browser does, and its lines count toward the pack's coverage.
Where the evidence stops: that application code never reads a hosted page is measured for the six
hosted flows found in the ladder's applications (their code sends a user to a URL and handles the
redirect). Their end-to-end tests were scanned for vendor pages, capped at ten seconds, so partially:
26 applications' tests name a vendor-hosted page and five drive a browser near one. cal.com fills
Stripe Elements by the vendor's input names inside its frame (.StripeElement, then [name="number"],
[name="expiry"], [name="cvc"], [name="postalCode"]) and asserts only Checkout's URL; ToolJet
signs in on Google's page inside cy.origin; dub replaces js.stripe.com and checkout.stripe.com
with its own routes; the rest assert URLs or their own controls. So a vendor widget's field names are
part of its contract, and a hosted page's are not yet shown to be: a selector a scan finds is a
control the screen must carry.
Rules this replaces for a derived pack
- C1: the layout above; the grade's form section (
twin-standard grade <unit> --form) holds a pack to it. - T0 (the older repository's per-pack verification): for a derived pack it is the report
(The report,
scripts/score-pack.ts): the core regenerated byte-identically, the life walked with every check below and no check failing, the branch round-trip and shape parity. It runs once, when a slate is written (Creating a pack, step 11). - A0: generic CRUD is permitted for operations classed
crud, under the transition rule above. It never stands in for anaction. - D5 and Evidence: a pack's evidence is the report and the life. The branch round-trip and shape parity still run.
- D6 and D7: the derived refresh is the pull path. It observes through
observeResourceas D6 requires; there is no connector module.
The report
scripts/score-pack.ts <vendor> walks the pack's customer life through the pack's own fetch (and
stream) on an in-memory World and reports, into journeys/score.json:
- the life: whether a judge in another session found it plausible (one verdict per pack, which the life's later changes keep);
- correctness: every check on every step held, and the life answered byte for byte the same on a
second fresh World (REPLAYABLE). A step's checks are its status, expectations and captures;
SHAPE (
packages/twin-standard/src/walk/shape.ts): the answer carries only fields the spec gives what the operation answers (or the alternative it says it is, deleted forms included: by itsobject, or, where no alternative is the object it names, by the values its enum fields hold, a key'skty, the one it fits best where several do), every field the spec requires of it, and state values only those the spec lists, or for a line protocol only reply codes the spec gives the command; LEAK: no answer, event delivered or socket frame carries a_field the twin keeps for itself, nor an event or frame (which no SHAPE reads) the kernel'supdatedAt, where no schema of the vendor's spec names it, read from every delivery as it is made (setDeliveryObserver, world-coreevents.ts); TOOLS: a request or a socket turn whosetool_choiceforbids tools is answered with no tool call, in any vendor's form; and the walk's beats, ECHO and PERSISTS on a step thatcreatesand LANDED on one thatchanges; - coverage: what
scripts/life-coverage.tsmeasures of the hand-written code and the declared state logic (Journeys), against the standard; - breadth: the spec's operations (a line protocol's commands) by what serves each: a handler, the derived core, or the gap.
The report's proof is met when the life is judged plausible, no check fails, the vendor's published examples are replayed and judged, and coverage meets the standard; each is a criterion of the grade. Breadth's gap count is not a failure: it is the part of the vendor the twin does not model, answered as the vendor answers an unknown request, and the pack closes it by modelling, never by answering it otherwise.
Where the evidence stops: the report does not yet check a response's field types, the ISOLATED and UNKNOWN beats (no walk runs them), that a filter the spec maps to a field returns only matching subjects, that lists agree with gets, that a refused transition changes nothing on its subject, or that the API and the UI show the same tree. A passing step has held only the checks above.
The denominator is the whole spec, never the operations a twin serves: breadth counts every operation
of the spec, and one the twin does not model is a gap in it, closed by modelling it. Which fields are
state machines is judged case by case: semantics/states.ts declares each candidate a state field or lists it
in notState, with its reason beside it, and derive-pack.ts refuses a modelled resource that leaves a
candidate unruled. A union's discriminator (a field each alternative gives one value, Stripe's
object) is never a candidate. A GraphQL schema counts each field of each type.
Recordings and vendor-backed runs are welcome and never required. None is available to the exemplars: each rule's correctness rests on the source it cites, and perform, refresh and real heads (Layers) are specified and were exercised by no exemplar. What a done pack establishes: its surface behaves consistently and in the vendor's shape under the rules it claims, each with its cited source; not that the claimed rules are the vendor's.
The grade
A pack's standing is its grade, computed by twin-standard grade <unit>… (bun packages/twin-standard/src/cli.ts grade <unit>, run from the packs repository with the unit's directory) against a
standard: a module of @volter/twin-standard (packages/twin-standard/) that names its id and version and lists its criteria, each an id,
its section, what it asks, and a check that answers pass or fail with the reason. The grader is one engine.
Protocol 3 is the standard today (protocol-3, version 2); a later protocol, or a revision of this one, is
another module, and the grader grades every unit against the latest version of each standard, so a new bar shows
at once how far each pack stands from it. A criterion changed, added or removed is a new version, and a grade
names the version it was computed against, so a pack that regressed and a bar that rose read differently.
The unit graded is a pack, or a lane of one: the directory holding src/manifest.ts. A vendor with lanes shows
each lane's grade beside its root's.
The grader reads the checkout's files, those git does not ignore, and trusts only stamped evidence: a judge's verdict pinned to the
sha256 of what it judged (the outline, the life, the examples), the vendored spec's hashes in SOURCE.md, the
recorded source of each patch, and the reasons in journeys/unreachable.json. Checkout-file listings are snapshots only inside
an explicit withCheckoutFiles grading pass (the standing generator, gate and multi-unit grade callers). Nested calls
share that pass; independent passes and direct criterion checks read git afresh, so files written between checks are seen.
A status line, count or score the
tree carries (journeys/score.json, a capability count, an index row) is an output the grader rewrites, never an
input. It recomputes the report (The report) itself. The grader probes each directory with
git rev-parse --is-inside-work-tree: only Git's "not a git repository" failure selects the disk listing,
including an unpacked published tarball, excluding release outputs (dist/, generated/pack-facts.json) and runtime state
(node_modules/, .volter/) by the same rule as the lane-only root gate; any other probe failure or a failed Git file
listing throws with the directory, exit status, signal, process error and stderr.
Each unit has two numbers, which the grader prints (scripts/grades.ts commits them as generated/grades.json and generated/INDEX.md):
- grade: the criteria the unit meets over the standard's criteria, unweighted: how far the unit follows the method, and each unmet criterion names what is missing. Like test coverage it is a measure, not a bar: no standard requires 100%, and what blocks a change is a criterion it breaks (below), never a grade short of full.
- surface: of the operations the unit's spec declares, those a semantics handler or the derived core serves; the rest is the gap, answered as the vendor answers an unknown request. A pack with no derived core shows its spec census's served over in-scope operations instead, marked as a census.
Beside them the grade shows the unit's exemptions: the lines and state moves journeys/unreachable.json
records as no World reaching, each with its reason. They are never coverage; the count is published so a pack
whose coverage rests on them reads that way.
Protocol 3's criteria, version 2 (version 1 had no-legacy and none of handler-contract, manifest-data and
states-module; a criterion's id never changes meaning, and a changed criterion takes a new id; a check tightened to hold more
of what its criterion already asks keeps the id, and the change names what newly fails):
- form: the spec vendored under
spec/withSOURCE.mdnaming its source, and a sha256 inSOURCE.mdor the document's.sha256matching each vendored document's bytes (compressed or not); every patch with its reason and its source (a URL in the reason or itssource, or the place in the vendored spec it rests on), as correcting the spec admits;src/manifest.ts;derive-pack.tsregeneratingsrc/generated/byte for byte; the unit's files the Pack layout's closed list, withsrc/fetch.tsbuildingcreatePackFetch; the handler contract (what an author writes: each handler file its family's, each export an operation of it, no randomness, clock, environment, network or foreign import); the manifest as data; every state machine and state ruling insrc/semantics/states.ts, taken whole by the manifest; no read of the environment in the form's source outsidecli.ts, the budget and tests (the harness is counted once, as harness); no file kept whose every declaration is recorded unreachable and that nothing imports. - proof, recomputed by walking the life on an in-memory World: the outline and the life each judged plausible by a judge who is not among their authors, the verdict naming their current sha256; every check on every step holding, with no judge flag; every published example of a served operation replayed and judged; the life and the examples reaching every hand-written line and declared move, or the pack recording why no World can; every exemption with its reason.
- soundness, executed: the pack's typecheck and its own tests (at T0's per-test timeout). These are the only criteria whose result can vary with the machine, so the grader reruns a red among them once before it records it, as gates reads a red before it counts.
Named and counted in no grade until a version checks them: no handler that only does what the derived core does (its
operation run through the core answering and writing the same), no credential stored as it was issued, no hollow
capability beyond the gate's allowlist, the pack's row of the invariants matrix with every cell passing or not
applicable (scripts/invariants.ts computes the row; the grade does not read it yet), and a patch's quote re-found in
its stored source.
The grader runs when a slate is verified (Creating a pack, step 11), for the units the slate
touched (the pack when it has a manifest, and each of its lanes). Its committed record is generated/grades.json,
rendered as generated/INDEX.md (scripts/grades.ts --write, every unit graded with the life's report score-pack
recomputes), which the catalog site reads. The record ratchets: a criterion it holds as met, under the same standard
version, is refused when it is not met now (--write records nothing then; --check names each criterion that
changed). The whole catalog is graded when a slate is verified, never on a schedule.
A vendor whose APIs are several units shows each unit's grade; a vendor with one lane graded is not read as graded whole.
The standard is one versioned package. @volter/twin-standard (packages/twin-standard/) holds the standards'
criteria, the grader, the gate (twin-standard gate: every unit passes the form section) and the derivation the
grade reproduces (twin-standard derive, which scripts/derive-pack.ts calls). Every place a pack is judged runs it
at the version it pins, never a checkout's scripts: a pack repository's hook and workflow and its release
(pack-release), and twin-catalog's check of a submitted version, which evaluates the published artifact with its pinned released standard. So a
pack graded in its repository and the same version graded by the catalog get the same verdict. A change to a
criterion is a release of the package (a criterion's id never changes meaning; a changed criterion takes a new id
and a new standard version, above), and the package is a human-required path: no criterion is loosened, or
tightened, without a person's review. What only this repository has, the customer life walked on an in-memory World, is
scripts/score-pack.ts.
Conformance
A pack's conformance is what twin-standard conform <pack dir> (packages/twin-standard/src/conformance.ts)
decides by executing the pack: the one command a contributor runs before a pull request, pack-done runs at (d), and
the catalog runs on admission and on its regular re-assessment, so every place a pack is judged judges it alike. It
imports the pack and the kernel the pack itself resolves, serves the pack on its own from a scratch directory, and
reads every unit. No model judges any of it; what only a judge decides (the life's plausibility) is the grade's proof.
Each check names the defect it detects and fails when that defect is present:
-
decided: every operation the dispatch serves is in
journeys/decisions.jsonwith its mechanism and demand; every operation demanded, and every one a refresh scope reads, is served. Each unit is checked against its own operations (decisionTableProblems): a row decides the operation of the lane itslanenames, and a row naming none the vendor's own API's operation of that id, or, when its own API has none, the one lane's that has it; so a lane's operation whose id its root shares takes a row naming the lane, and stays the lane's gap while it has none. -
published: what a release publishes (
npm pack's file list) holds every unit as the checkout does, its manifest, spec and journeys, including the inputs each lane holds. -
cited: every vendor fact the unit cites (a handler's or the manifest's
// source:, a patch's source) is inspec/sources.jsonas a page that answered, each quoted line found on it: check-sources' record, read, not fetched. -
Vendor-backed acceptance also covers APIs with no customer mutation: publisher doors are World-only, so there is no deploy group on which to inject a transient failure. For these lives the vendor World alone receives the publisher steps; refresh must land their resources into an initially empty consumer World and remove an upstream deletion. Mutation lives retain the deploy failure/retry and adoption checks.
-
Acceptance planting must create a subject the consumer did not already hold. Deterministic twin ids can overlap when the vendor has fewer local subjects than the consumer, so a successful create alone is insufficient. The fixture repeats an append-style API create only while each attempt produces a new subject, bounded by the consumer's held subjects, until its returned subject is absent from that consumer. A name conflict or a create with no new subject stops that candidate. Resend's documented Send Email returns an id (Send Email); that returned id must be absent from the consumer before planting.
-
refresh: every stored resource of a vendor-backed unit (not a
_one, the pack's own bookkeeping) declares how a World rooted at the vendor reads it back, and a unit that sends events declares the ingest of the vendor's, or its vendor declares it once for its lanes (on theVendorManifest, as "What a pack declares" puts it and the kernel'sderiveVendorStateSystemreads it);STANDING.md's vendor-backed list reads it alike. Cloudflare's API lane sends the Notifications its vendor's oneingesttakes back. -
registered: a unit with resources that is not
vendorBacked.none, lane or not, reaches the registered pack's state system: itsperform, asked to send one of the unit's operations (to an executor that stops it first), finds the unit, and a unit declaring a refresh has one. Registration must carry every lane beside a pack's own API; the operation must resolve to its declared unit. -
allowance: a rate budget admitting more calls a minute than the fallback declares the vendor's documented allowance and cites its page on a
// source:line of the manifest. -
A raw TCP server's primary
portis its protocol listener; its optionalinspectPortis the HTTP control seam. Conformance sends HTTP discovery, boot probes and inspect life requests to that side port, retaining the primary port for the protocol. SMTP is the first need: HTTP at an SMTP listener yields a malformed response, even when both listeners serve correctly. The life's line steps open TCP connections to that primary port, as the application's mail client does, so a listener wired apart from the World (its stream given none of the server's options) fails the life. A line has drawn its replies once bytes arrived and 50 ms passed with no more, or 500 ms passed with none; these windows are timing, not protocol, and a reply slower than 500 ms is read with the next line's. A step with its ownconnectoptions is a finding there: those options configure only the in-process stream, which a served walk does not use. -
standalone: served by its
server.tsas a World runs it (its boot id set, so the seam answers the World's boot probe), the pack answers HTTP and the probe, and every socket its manifests declare takes an upgrade.serveHttppreserves the fetch's upgrade through boot-probe wrapping; OpenAI Realtime requires it when served through a World. -
client: the pack driven by the vendor's own client, its official SDK unmodified (
packages/twin-standard/src/ clients/<vendor>.ts), in a Node process as an application runs it (clients/run.ts), against the pack served as a World runs it: its credentials the pack's credential door issued, every connection through a stand-in for the World's proxy (worldProxy: the vendor's URL and Host unchanged, forwarded withx-forwarded-proto: httpsas the proxy that ends a caller's TLS forwards it, so the links and socket URLs the pack answers are the vendor's secure ones), an HTTP client's requests over it, a socket client's TCP connection made to it (the ws library'screateConnection), a client that takes an HTTPS agent given one whose connections reach it. Each case is a behaviour the vendor documents, cited, never the pack author's script. A vendor whose descriptor names npm clients (adoption.sdks) and has no cases fails the check (no case is no proof its SDK works against the twin), unless its descriptor'sclientCasesNonesays why none is owed:adoption.sdksis every npm client that names the vendor in an application (what the demand scan reads), and cases are owed only for an official SDK the demand's applications use (Discord maintains none, so discord.js and passport-discord name it without being its own); one its applications call with no SDK of its own (a raw HTTP catalog) passes it with that said.A present case module that executes zero cases fails; a module's existence is not execution evidence. The SDK entry in the customer outline uses default model selections and records the installed client version and methods exercised; its claim is limited to those methods and inputs. The ordinary CLI initialization, required infrastructure and retained-state resume are established by the final installed customer workflow, not by the stand-in proxy alone.
- PostHog's posthog-node, pinned with the demand's @posthog/core version: queued captures and group identification drained by flush/shutdown, person property updates and aliases, retry requests retaining their event UUIDs, typed HTTP refusals without retries, and flags interpreted from the server's versioned response. Readback uses the project's API with its issued personal key. A case may supply the SDK's documented fetch option to introduce a transient transport response before forwarding retries through the same World proxy.
- Slack's SDK cases discover the signed-in workspace through auth.test before requesting its app configuration token. Cases sharing one served workspace use separate channels, so a later case cannot fail merely because the earlier one created its channel. SDK clients retain their default HTTP agents and use the enclosing World's injector for API calls and returned upload URLs; Socket Mode event and upload assertions retain their vendor-documented expectations.
- Linear's official SDK at the dependency demand's 86.0.0 pin reads its current viewer, creates a team and issue, updates that issue and reads it back. A workspace, person and personal key are made only through their declared settings doors; team and issue data are written through the vendor's GraphQL API. The SDK keeps api.linear.app and the enclosing World's routing.
- Resend's official Node SDK at Dub's demanded 6.6.0 pin sends and reads a test email, sends a batch and reads each id back, and returns the documented error envelope for a missing required field. Credentials come from the pack's credential door; the SDK retains its api.resend.com URL and uses the enclosing World's injector. These cases assert stored mail and wire errors, never real delivery. Its exact dependency is held in the standard's separately locked client-sdks manifest.
- OpenAI's (openai-node), and every vendor on OpenAI's chat wire through it: a chat completion answered with the
World's scenario loaded,
tool_choice: noneanswered with a message, the scenario rewritten between two turns deciding the second; OpenAI's Realtime (its server-side client): the first eventsession.created, no function call undertool_choice: none. A socket refusal, ignoredtool_choiceor scenario cached across requests fails these cases. OpenAI's own cases use its SDK's documentedhttps://api.openai.com/v1endpoint. A descriptor's first host may belong to its Codex or account lane; that ordering never selects the API host for the official SDK's Realtime client. - Anthropic's (@anthropic-ai/sdk), on Anthropic's and merge's Messages: the same three, in each vendor's scenario
grammar, including no tool use under
tool_choice: { type: none }. - Slack's (@slack/web-api, @slack/socket-mode): a workspace made and a Socket Mode app installed through Slack's own pages, its client hears a person's message in its bot's channel as Slack documents the event, with no field Slack's message events lack; a raw row in the event fails it when put back; and an app's files.uploadV2, as Volter Harness's Chat SDK adapter calls it, sends a file's bytes to the upload URL the pack answers and completes it into a thread, where the file reads back with the size sent.
- Stripe's (stripe-node): an endpoint registered through the API, a customer made, the event Stripe sends verified by
constructEventwith the endpoint's secret, itsdata.objectthe customer as the API answers it; a customer list read two to a page by the client's auto-pagination; a create retried with its idempotency key answering the first one's customer, and the key with other parameters raisingStripeIdempotencyError; a missing customer raisingStripeInvalidRequestError(404,resource_missing); a portal session's url on billing.stripe.com opening the portal. - ElevenLabs' (@elevenlabs/elevenlabs-js): an agent made, its signed URL asked for, the conversation's socket opened at
it answering the client's initiation data with
conversation_initiation_metadata. - AWS's (
@aws-sdk/client-s3): the platform backup job's missing-bucket path.HeadBucketon a bucket not made rejects with HTTP 404; the job catches it and makes the bucket, after whichHeadBucketsucceeds. AWS's HeadBucket reference specifies the bodyless error and successful 200. The case is written during the pack procedure and run with conformance at the arc's final run. - Cloudflare's (
cloudflare, and aws4fetch for R2): a custom hostname registered on a zone, pending with its ownership verification and found by its hostname (Twenty's calls); a Turnstile widget whose detail answers the secret its creation did; an object put through aws4fetch at the account's R2 endpoint, listed and read back byte for byte. - GitHub's (@octokit/rest), as twin-world's on-call recipe calls it with the World's GITHUB_TOKEN: a repository made
by GraphQL's createRepository through Octokit's own GraphQL call, an issue filed, read back by its number and
answered with a comment, the issue then counting it; and a read of an issue the repository does not have raising
Octokit's HttpError with status 404. The applications the demand scan reads call GitHub through fetch,
ghand sign-in libraries; the on-call recipe is the one demanded caller of GitHub's own SDK. - Tinybird's official
@tinybirdco/sdkAPI wrapper is pinned at 0.0.84 for optional client cases. No demanded application uses it:adoption.sdksnames only@chronark/zod-bird, a third-party client, and the descriptor'sclientCasesNonestates that fact. The optional cases hold token overrides and typed API errors, Date rejection, NDJSON and JSON parameter serialization over the Classic/v0wrapper, with throwaway credentials and routed fetch. Sources: its API wrapper and README. - PlanetScale's (@planetscale/database, at the version Dub and Substrate pin), with the connection string the credential
door issued: SQL sent with
?parameters and its rows read back typed, as objects and as the arrays Prisma's PlanetScale adapter asks for (the serverless driver); a transaction whose callback throws rolled back, one that returns committed (database-js); and a duplicate key raising the driver'sDatabaseErrorwith MySQL's errno and sqlstate in its message,(errno 1062) (sqlstate 23000), which the adapter reads as a unique-constraint violation. - Upstash's (@upstash/redis 1.38.2, @upstash/ratelimit 2.0.6, @upstash/qstash 2.11.0, as Dub pins them), with the
credentials the credential door issued: commands given at once auto-pipelined and each answer decoded into the value
given (auto-pipelining); Dub's sliding window
allowing its limit and rejecting the next (algorithms);
a publish repeated with its deduplication id answering the first message's id
(deduplication); and a delivery's
Upstash-Signature, read through the deliveries door (the World's stand-in for the destination), verified by theReceiverwith the account's signing keys (signatures). The clients take no fetch of their own, so each case routes the global one as a World's injector does. A vendor with no cases yet says so. The case's host is the descriptor's first host that names one: a descriptor may lead with a suffix (PlanetScale's.psdb.cloud, a region's hosts under one domain), which no request can address, and its credential door is posted to the first named host; the case's own client reaches whatever host its credentials name, as an application's does. Volter Identity's demanded@volter/identityclient cases use its pinned SDK for discovery caching, issuer/audience verification against the twin's JWKS, and shared resource-token refresh with rotation. Client setup uses the operator API and sign-in wire, and a case's temporary shared-sign-in file is its own. SDK networking uses the harness transport, including the SDK's global-fetch JWKS and refresh calls; the SDK itself is unchanged.
-
life: the pack's life walked over HTTP against its
server.tsserved as a World runs it (boot id set, the World's root and the life's scenario its options, the vendor's host in each request's Host header), every check of the walk holding: status, expectations, captures, SHAPE and LEAK (no answer, event or socket frame carries the twin's own fields, including kernel bookkeeping such asupdatedAt), and every request answered within 10 s. Each HTTP step uses the SDK harness's Node HTTP transport with a fresh connection to the served port, retaining its vendor Host, method, body and manual redirects. The unchanged ten-second deadline covers the full response body and aborts the owned request when it expires; no request is retried. The transport does not use Bun fetch. First need: the Cloudflare release's Linux assessment recorded a handler returning 200 in 13.933 ms while pooled Bun fetch delivered no response before the unchanged 10 s deadline (source6bdb2f6, published Cloudflare 3.0.11, Bun 1.3.11, standard 1.0.64/core/runtime 3.0.72, manual Actions run37508338971through a disposable World;bun scripts/diagnose-served-transport.ts). The isolated served-only comparison at sourcedc3004cpassed three 268-request walks each for pooled Bun, fresh Bun and Node HTTP on Linux in run37510696539; it establishes working transports, not a reproduced pooling cause or full readiness. Fresh Bun fetch still timed out on a different GET after Chromium replay in Linux catalog run37512174242(published Cloudflare 3.0.11, Bun 1.3.11, standard 1.0.72/core/runtime 3.0.80; normal catalog assessment, no concurrent runner work). Its quick and Chromium replays and all three SDK cases passed. Reusing the harness's Node HTTP transport removes the failing Bun fetch path without changing the served pack, journey assertions or admission criteria.
The GraphQL wire awaits a resolver before applying the schema's Relay connection wrapping or enum conversion. A resolver that reads blob-backed state is asynchronous and has the same wire shape as one reading a row. First need: GitHub's pull-request commits connection reads commit objects and is queried with last: 1 by GitHub CLI's merge panel; its resolved list must supply nodes and pageInfo.
LEAK recognizes the response keys selected by a GraphQL document, including aliases and the schema introspection field __typename. These are client-selected wire fields, not stored bookkeeping. First need: GitHub CLI selects __typename to distinguish CheckRun and StatusContext in the status-check union. Undeclared underscore fields remain leaks.
Only the body-root query string of the pack serving that step (the journey vendor or its declared companion) with a generated GraphQL SDL opts into GraphQL document filling and retains its selection-set braces. A nested query, a header or multipart field, or a pack without an SDL uses ordinary capture filling. The journey toolkit parses the document and treats a selection such as {name} as GraphQL syntax, not a {name} capture; captures in variables and string literals still substitute normally, and missing captures still fail. A complete document held in a capture may be sent as that capture. First need: GitHub CLI's published GraphQL requests include compact field selections, which must reach the twin unchanged. This is journey tooling, not vendor behavior.
SHAPE exempts a binary representation from JSON resource checks only when the operation’s derived response media declares the returned media type for that response status and the request’s Accept permits it (including media ranges and quality-zero exclusions). Without Accept, only the operation’s default response media permits exemption: derivation records the JSON default when JSON is declared, or the sole declared representation; an ambiguous non-JSON set grants no exemption. The twin’s content-type alone grants no exemption: otherwise the ordinary resource shape is checked, and a binary-labelled scalar cannot stand for an object resource. Spec omissions are corrected by cited patches before derivation. An exempt step still asserts its media header and downloaded content. First need: GitHub’s release asset download documents Accept: application/octet-stream for bytes and application/json for metadata; BUILD.json’s bytes are not the release-asset resource.
SHAPE checks the HTTP request method before interpreting a response representation. A HEAD response must have zero response content, as RFC 9110 §9.3.2 requires. The walk passes the request method and raw response text: nonempty content fails, while an empty HEAD answer has no JSON body to judge, regardless of Content-Type or Accept. Those headers describe the corresponding GET representation. First need: Cloudflare R2's S3 HeadObject retrieves object metadata without returning the object; the represented object's binary Content-Type is not a binary HEAD body.
Journey expectations and captures accept JSON Pointer selectors starting with /, alongside dot paths.
Pointer segments decode ~1 and ~0, so a semver map key such as /versions/1.0.0/dist/shasum is
one literal segment. npm's metadata reference
uses version numbers as object keys; substring assertions cannot prove which version supplied a field.
An XML answer is decoded to its root element's members for SHAPE and for field selectors (Contents.0.Key), and kept as
it came for what reads the body as text: an expectation on path: "" asserted as text (equals, contains,
notContains or matches a string) and a text: capture read the document a client reads, as "Creating a pack" step 8
says (path: "" the whole body as text). First need: S3, whose
CreateMultipartUpload answers the
UploadId its parts are then sent with, captured from the XML. A World door the manifest declares (doors) is checked
as that door, never matched to a vendor operation whose path would also take it (S3's POST /{Bucket}/{Key} and
/_twin/app-credentials).
A journey's application stand-in may declare bodyError: its headers are returned, but reading the response
body fails with that transport reason. ctx.ask, which needs the complete body, treats it as unreachable.
First need: Tavily extraction reports the URL in failed_results when a source's body read fails after a 200
response. The fetch route/refusal wrapper covers obtaining the response; callers that consume its body must
also handle body transport failure.
The customer-life application stand-in may declare requestHeaders: exact values it requires on a request the vendor asks (ctx.ask). The walk compares them case-insensitively by header name and reports an APPLICATION failure if absent or different before returning the scripted response. This proves a configured synthetic workspace secret is carried to a custom LLM; credentials are never inferred from the vendor account key.
The tunnel client case uses the vendor’s unmodified createTunnel client to forward a
local HTTP server through the served relay: visitor method, body, application headers
and public Host, streamed output, bodyless HEAD/204/304, CORS preflight, embedding and
cache headers, and the documented 750000-byte request limit. The SDK’s own HTTPS
agent is given the same TCP routing seam; it retains the vendor URL and Host.
A served socket step waits for its declared response expectations and captures, within the same ten-second deadline as an HTTP step, before collecting its frames. A brief quiet interval alone cannot end a step that still expects a response: a custom LLM round trip or queued World write may answer after that interval. Steps expecting silence use the quiet interval; the runner never sends a vendor-specific acknowledgement. First need: ElevenLabs' custom LLM tool-result continuation can arrive after a brief quiet interval.
Where the evidence stops: the life walk does not establish that it catches every served scenario hang.
Journeys
Wire and recorded JSON integers beyond JavaScript's safe range are decoded as decimal strings, including RESP integer frames and published example values. Safe integers remain numbers; doubles retain their protocol semantics. The same decoder reads journey assertions and captures so an int64 value survives reading, recording and comparison without rounding. Stored integer fields and event versions are ordered as exact integers before any floating-point fallback.
A RESP replay compares the complete ordered reply sequence, including nulls and every nested array value, with the vendor's published output. A single-command example publishes one reply; a pipeline publishes its reply sequence. The last reply cannot stand for the sequence. Other wires compare literal text through the strict text reader, and examine every published array element. Structured generated values retain the documented type comparison; published nulls use the served field schema only for samples. Sample object collections and documented catalogues or stored-state lists compare every returned element rather than their illustrative length, as specified in Published examples. Request-produced arrays keep their length and ordered output keeps position; nested-array tuples and schema-fixed lengths remain exact. Placeholders and schema-rendered samples retain their existing meaning. JSON inside output stream frames uses the ordinary output comparison; event names, frame order and count, and terminators remain literal. Form reading retains repeated fields as arrays; multipart field comparison requires a complete, terminated body.
A life with no steps has no judged behavior and reads unjudged, regardless of a retained verdict. Reports identify authors and judges by their author names; process, branch and session identifiers are never copied into generated records.
An HTTP journey step can name defer: <key> to start a request while subsequent socket
steps answer it. A later HTTP step with await: <key> and the same method and resolved
path collects that request once and runs the ordinary status, shape, leak, capture and
expectation checks. The starting step carries no response assertions; those belong to the collecting
step. The collector uses the original body and headers (including version pins),
and cannot change them. Deferred state beats are unsupported and fail explicitly:
a collect cannot reconstruct the pre-write state after dispatch. Duplicate keys, unmatched collects, changed request
identity and requests left uncollected fail the walk. Pending requests have the walk’s
ten-second deadline and are aborted at teardown, which closes the walk’s own sockets.
This is journey tooling, not pack behavior. First need: the tunnel control protocol’s
request and response frames
require a client to answer a public HTTP request while its response is pending.
JSON request bodies and headers interpolate captured strings as JSON string values, preserving quotes, newlines, backslashes and braces. A value that is exactly a capture retains the captured JSON type; interpolated values are opaque and are not interpreted again as capture templates. This lets a tool loop echo the vendor's complete argument string or input object, as Merge documents sending a tool result. The journey toolkit owns this JSON construction; it is verification tooling, never runtime kernel code.
Journey expectations use the same decoded selectors as captures: json:<path> parses a JSON string field, and sse-json:<event>:<path> joins stream fragments before parsing. An expectation's shape checks the resulting JSON value's type, properties, required, items and additionalProperties, recursively; unsupported validation keywords are refused rather than silently accepted. This is structural proof, without pinning model-generated values. First need: AI Gateway structured outputs, whose caller supplies the schema and whose streaming example assembles a complete JSON string before parsing.
A large body is written short and sent whole: a step's bodyFill: { text, bytes } is text's UTF-8 bytes repeated
and cut to bytes bytes, sent as raw bytes whatever character the cut falls in, and inside a body a value
{ "$fill": { text, bytes } } is that string, refused unless the cut lands between characters (a cut character would be
sent as U+FFFD's three bytes, and the body would not be bytes long). The sizes are the vendors' limits:
PostHog's capture limit ("the entire request body must be less than 20MB by
default") and R2's part size ("Multipart part is below minimum
size (5 MiB), except for the last part."). The walk and the published-example replay expand fills before a step is
sent, signed or compared, so the journey file stays small and a judge can read it. This is journey tooling, not pack
behavior.
A capture next:<path> takes the integer at the path plus one: what a client asks for next from what it last read,
never the twin's sequence written as a literal (Telegram's getUpdates offset, "update_id of last processed update + 1",
Bot FAQ, as the Chat SDK's polling loop and Postiz compute it).
A presigned step (presign) signs each header its URL names among X-Amz-SignedHeaders with the value the step sends,
as the client that presigned the URL bound it (the kernel's presignSigV4 takes the headers; the verifier reads the sent
values). Dub presigns its uploads with Content-Type and Content-Length signed (its storage.ts); R2's
presigned URLs can bind Content-Type. Signed as empty, such
a URL's replay was refused.
A journey is an ordered, stateful story in the form packages/twin-standard/src/walk/journey-kit.ts defines: steps, captures
and expectations, as data, with no functions. Logic a check needs belongs to the runner, where every
pack gets it; the runner tells a refusal from a success by the step's declared status, which covers a
vendor that refuses with HTTP 200 (Slack's ok: false). A step is a request (method, path), a
line step on a byte stream (line protocols), a git
step, or a wait that moves the World clock. A git step is what git clone and git push send over
smart HTTP (packages/twin-standard/src/walk/git-client.ts, on the kernel's git library): a clone reads a ref's tip and its
paths; a push commits files on a ref's tip, rewrites the tip (amend, which only a force lands) or
deletes the ref, and its answer is the server's report (result: ok or the reason the ref was
refused). A customer of a git host moves code with git, so the life does too.
A pack whose data plane is the World's managed Postgres (the descriptor's managedDatabase) is walked over one, as a
World binds it: each walked World its own fresh database (walk/vendor-fetch.ts starts the World's containerless
Postgres, walk/walk-database.ts, on the World's first request), and the walk reads whether a pack declares one from
the pack facts. The walker declares that database as infrastructure in a task-owned World, with literal throwaway
database, user and password, and uses runWorld for boot and verified teardown. A registered waiting command holds
it while the walk runs; the runtime's existing supervisor handles caller loss. The walker obtains its connection
from that command's World environment and sets time through the runtime's clock API. It never starts, stops or
deletes a PGlite host by PID, and releases its reusable declared port only after the runtime confirms teardown.
The standard's served factory binds that same declared database to a pack's server and exposes its connection to
the SQL steps; the HTTP credential door and the native driver use the same database. Each request or SQL step
advances the backing to the walk's clock. The walk ends its SQL session before stopping the served pack and its
database World. This binding comes from the descriptor, without a vendor switch or an invented connection.
What the application sends that database over its own wire, not through the vendor's API, is a SQL
step (sql): its schema as its deploy lays it (Supabase's customers apply their migrations with supabase db push --db-url, a file at a time, over the database's connection) and the queries it sends, on one connection of the
application's own to the walked World's database (world-core databaseSession), held across the life's SQL steps so a
transaction it begins stays open into its next steps. A text alone is one simple Query; with values it is one
extended-protocol statement, the values its bind parameters, as a client's query(text, values) sends it. Its answer is
the last statement's command and rows (each row its columns by name, in Postgres's text format), which
expect reads. The runner compares the two fresh Worlds' answers byte for byte; database randomness is not pinned
and steps that depend on it do not satisfy R9. A refusal fails the step with Postgres's SQLSTATE unless the step names it as refused, the application's own catch. A table, a function or a bucket row the application's migrations make is so made in a life as it is
made in the application's deploy, never through a vendor operation the application does not call (first need: Supabase,
whose customers' tables and buckets come from their migrations, https://supabase.com/docs/reference/cli/supabase-db-push
"Pushes all local migrations to a remote database").
A life whose vendor calls another vendor as its own behaviour (ctx.vendorFetch, what an author
writes) names that vendor among its companions: packs of the same catalog, by
vendor. The walk loads each companion's fetch (walk/vendor-fetch.ts) over the walk's own root, on the same in-memory
World and World clock, so a replay of the life is as deterministic as one without them. A companion can be a sibling
source pack or an installed, explicitly pinned development dependency. The walker uses the kernel's installed-pack
discovery and the selected artifact's generated identity, host rules and schemas, independent of its npm scope or
package name. It never requires a private checkout, invents a provider response or treats companion coverage as the
candidate's own. Missing or ambiguous dependencies refuse the walk. First need: Clerk's demanded Google sign-in
when its published artifact is assessed outside Volter's source pack repository.
While the walk runs, the
vendor's ctx.vendorFetch reaches a companion in process: the walk registers the companions and the life's own fetch by their routing keys (the
host rules of their descriptors, vendor-hosts.cjs) through world-core's setInProcessTwins, which vendorFetch
consults before the instance's twins, and restores the previous registration after. A companion can call back the
life's vendor through the same route (A → B → A). Routing facts are read under the vendor before any slash in a
lane-style pack id, as the pack loader reads its descriptor; a host rule with no explicit key uses that vendor.
A step whose absolute URL a companion's host rules claim
(and the life's own vendor's do not) is served by that companion, as the application's request to the provider is
(the person consenting on the provider's page), and its answer is checked against the companion's spec (SHAPE, LEAK).
A companion is walked only where the life reaches it; its own coverage is its own life's. Without companions, or for a
host no companion claims, ctx.vendorFetch reaches the World's twins as before, and an in-memory walk has none. First
need: volteridentity's demanded social sign-in (RH2's Apple ID-token sign-in, Open Autonomy's GitHub sign-in reading
github_id), which trades the provider's code at the provider's twin and whose life could not reach one from an
in-memory walk (VendorUnreachableError). Volter Identity's life now names GitHub and Google as companions and walks their callbacks.
Each pack has one journey, its life: journeys/customer-life.json, one customer's whole life with the
vendor, from signup to leaving. The life pays for setup once and every later step lands on state it
already built. Time passes in wait steps; the World clock starts at 2026-01-01. A life walks on an
in-memory World (inMemoryWorld, packages/twin-standard/src/walk/behavior-journey.ts) in seconds; a slower walk is a defect
of the twin or the kernel. An operation only a vendor page reaches (a token made on a settings page) is
a missing screen: the pack builds the page and the life walks through it, posting what a
browser posts. packages/twin-standard/src/walk/behavior-journey.ts replays committed journeys deterministically, with no model
call: judgment happens when a journey is written, never when it is scored.
Writing a life
Plausibility comes before coverage, and every expensive stage is preceded by a cheap one that could reject it. Each stage ends on its test.
-
Outline. The customer, the people and programs acting for it, the arc from signup to leaving, and the time it spans, in prose with no calls, at
journeys/customer-life.outline.md. It lists every actor with the credential it acts with and that credential's grant, and its schedule keeps within the vendor's documented windows and quotas (how far ahead a send may be scheduled, how often a key may be rolled). Before its judge, the author walks it: per act, who makes it, with which credential, and does the grant allow it; per stated cause, where the story carries it out; per recurring or batch act (a digest, a newsletter), who it reaches at each date against who the story has then. A judge in another session rules on it against what a judge rejects and the rules the lives have taught, reading each item of the list for the outline's beats (a beat is a sitting; a refusal, a toggle and a read-back are judged in prose as in steps); a vendor's grant stated only in general words ("can only send emails") is read by its words, and an act plainly outside them is a ruling. The same judge re-rules as at stage 3. The dispatcher stamps its verdict atjourneys/customer-life.outline.verdict.jsonas a life's is stamped (sha256,judge.agent,authors,rounds). Test: the outline is accepted. Where the evidence stops: the credential list, the walk and the windows are drawn from one pack's blind run (resend), whose first outline drew two rulings for acts its key's grant did not allow and one for a cause never carried out, all three caught by that walk. -
Life. The steps, written from the accepted outline and the vendor's documentation. The author does not read the coverage report at this stage: a life written toward the report becomes a tour of it (of
score-pack.ts's lines the author reads the walk's checks and flags only). The life names its author session (author). Before the first step the author maps every act of the outline to the twin's door for it (an operation, a screen, a git or line step), one line per act in the unit's progress record (the door, what serves it today: core, handler or nothing, and whether it holds), and probes the doubtful ones: an act with no door, or one the twin answers unlike the vendor, becomes a twin fix decided before the life is written, or an outline change its judge re-rules. Test:score-pack.tsshows no check failing and no judge flags, and the author has walked every step against what a judge rejects reading what each sends (a push's code, an upload's bytes, a path's origin), not only its name, and every recurring or batch send's recipients against who the story has at its date (resend's first life left out an owner the story still had). Where the evidence stops for the self-walk: a fix handed over without it drew two new rulings of the same two kinds (github). Where the evidence stops for the mapping: drawn from three packs (slack, github, stripe), each of whose accepted outlines needed core acts the twin could not represent (joining a workspace, a pull request from a fork, a subscription's renewal) that lives written toward coverage had never asked for. -
Judge. A judge in another session, on another model where one is available, rules on the life as a whole and commits its verdict beside it, naming the life it read (its sha256). Agents dispatched from one session share its id, so the dispatcher, which alone knows both, stamps the verdict with the judge's agent (
judge.agent) and every agent that wrote the life (authors); a judge started as a session of its own (scripts/judge-brief.tsprints its brief) names its session too (judge.session). A verdict without them, one whose judge is among the authors, or one marked self-judged, is no verdict (@volter/twin-standard'snotIndependent, which the grade,score-pack.tsandvendor-examples.tsread alike). A judge rules against a step only for a defect its list names or a vendor contradiction it can point to in the spec; anything weaker it lists as a doubt. An outline and a life are each judged at most once, and the judge's feedback and the implementation are both exhaustive. So the one judging is exhaustive: the judge reads every step against the spec and the vendor's pages and rules on every defect it finds, each ruling naming the steps it concerns and exactly what they must become, never a sample of a few for a later round. And the answer is exhaustive: the author applies every ruling, checking each change against the spec (a fix that swaps one fact can break another that depended on it). There is no second judging, for the rulings or for anything after them: the verdict stands for the pack's life, its outline and its published examples from then on, and a change made later (the rulings applied, a review's fix, a step added for coverage) keeps it; the life is not judged again (the owner, 2026-10-03: "we need to pull back on judging"). Test: a plausible verdict from another session. Where the evidence stops: openai's life passed on the sixth round, the first re-ruled by the same judge; aws, smtp, slack, stripe and github, each re-ruled by its own judge throughout, passed in two or three rounds.Validating the judge: a judge from another model family (gpt, through
codex exec) was run once over all six lives the Opus judges had passed, and ruled against steps in every one, several rounds deep; those rulings became the list's newest items, the author's self-walk andscore-pack.tschecks, so the judge the procedure uses (Opus) reads for them. Planted defects measured the result: eleven defects of eleven kinds planted in a copy of a done life (smtp) were each ruled by both families judging blind, andscore-pack.tsalone caught eight; in a second round of twelve subtler defects in stripe's life (a price off by three dollars, a quarter's range running into May, a toggle of one portal feature, a read of a deleted account) the blind Opus judge ruled ten, the gpt judge eleven, and every defect was caught by a judge orscore-pack.ts. Where the evidence stops: two packs, two rounds; a second-family judge is an audit to run when the list is revised, not a stage every life passes. Where two judges disagree on a vendor fact, the dispatcher reads the page either cites and rules by its text, never by majority. -
Coverage. Only now the author reads
scripts/life-coverage.ts <vendor>. Each gap is closed by a step the accepted story already has a reason for, by a record, or by deleting dead code; a guard's refusal no customer of the story has cause to draw is recorded, never reached by a contrived step. Test: coverage meets the standard. One life runs a pack's own code only in part (55 to 65 per cent of the hand-written lines in slack, stripe and github); the rest is recorded, so a record's reason carries as much of the standard as a step does. Additions made for coverage are where a life drifts toward a tour: judges read them as "added for coverage" (one member placed in a workspace three ways in one sitting, one method called in both its forms in a row) and listed them as doubts, which the bar lets pass. Where the evidence stops: drawn from those three packs' coverage passes. -
Done. A plausible verdict against the current list whose judge is not among the authors, no check failing, no judge flags, the vendor's published examples replayed and judged (Published examples), and coverage meeting the standard.
A streamed answer can be retained without replacing it with guessed history. A capture
sse:<event>:<dot path> joins the string values at that path from matching server-sent events,
and sse-json:<event>:<dot path> parses the joined JSON fragments as a captured typed value.
The journey tooling decodes SSE framing, including CRLF and multiline data, independently of
vendor identity. First need: Claude's client streams text, thinking, signatures and tool input
(streaming); its following
turn must send the actual prior content and tool result, not a literal guessed by the life author.
An explained status difference on a vendor example compares the actual error expectations,
not that example's success-shaped sample body. The published request stays unchanged. First need:
the Claude reference renderer shows Opus 5 with top_k: 5 and top_p: 0.7, while its vendored spec
explicitly rejects both on models released after Opus 4.6. This exception applies only when the
status actually differs and exampleDiffers.status explains the vendor contradiction.
A generative pack's life names the World's scenario it walks under ("scenario": "scenario.json", a file beside it in
journeys/, in the form a World's handlers/<vendor>.json takes): the walk loads it as the World does, so the turns a
scenario scripts, its faults and the pack's own scenario code (its matchers, its checks of a handler, its fault's
body) are walked like any other line, and a life that scripts nothing walks the stub alone. What the vendor asks the
application (ctx.ask: the LLM endpoint an ElevenLabs agent is configured with, Stripe Issuing's real-time
authorization) is answered in a walk by the life's application: each answer the request it takes (its method and a
URL prefix) and the status and body the application gives, used in order; a question none takes is unreachable, as the cooperative HTTP/Fetch sandbox refuses an unlisted destination (transport coverage).
Published examples can name their own scenario file, apart from the life's (see the layout above): its scripts set fixture output, never stored mutations. First need: the Claude documentation's web-search example needs a server-tool answer, while the customer's life scripts its own client tools.
What the vendor asks the application (ctx.ask: the LLM endpoint an ElevenLabs agent is configured with, Stripe
Issuing's real-time authorization) is answered in a walk by the life's application: each answer the request it takes
(its method, POST unless named, and a URL prefix) and the status and body the application gives, used in order; a
question none takes is unreachable, as the cooperative HTTP/Fetch sandbox refuses an unlisted destination (transport coverage). The walk gives these answers to the pack's fetch it walks
(PackFetchOptions.application, which the kernel puts in the scope of every context that fetch opens, a write hook's
and an event's too), so they answer that walk's twin alone: no module holds them, so another walk running beside it,
another pack's fetch or a World's host is never answered by them. They are given within the question's window, as an
application's answer is. A hosted World gives its twins' fetches its own router through the same seam (what an author
writes, ctx.ask), which answers only a read of a site one of its twins serves, so a
life's answer for such a site stands in for what the World's twin of it would answer there.
A step posts an HTML form as a browser or an SDK's form encoder sends it with form ({ fields?, parts }), in place of
body or bodyBase64: one multipart/form-data body (RFC 7578) behind one fixed boundary, so a walk sends the same bytes
each time. fields is a template whose captures fill to a JSON [{name, value}] list ("{vFields}", a capture
holding the list as its JSON text), sent first and in its order: a POST-policy upload
sends every field its lease named, then the file (Reddit's media lease answers args.fields and the S3 form's action,
and its clients send them so: PRAW's _post_to_s3). parts follow in the form's order, each a text field ({ name, value }, its captures filled: a value the page showed, read off it by a capture, as a composer's Post as choice) or a
file ({ name, filename, type?, base64 }, or png: { width, height } for a real PNG of that size, an uncompressed RGB
gradient as heavy as an uncompressed export). A value read off a page goes in beside a file's bytes, never written as
a literal into them. A signed step signs the form as it is sent: SigV4 over its multipart bytes and type, OAuth 1.0a
with the multipart type, whose parts its base string never carries. First needs: Reddit's life, which sends a real video and poster through its lease and a
print-size card past the lease's 20 MiB range; LinkedIn's share box, a post as a company page with a video attached;
Telegram's chat_id beside the photo it sends (node-telegram-bot-api's form,
sendPhoto), an id an earlier answer gave, which a bodyBase64 could not
carry.
A capture written {encoded:<name>} is filled percent-encoded (encodeURIComponent), as a URL's path segment or query
value or a posted form field carries it: a URN a vendor's pages print encoded in a URL (LinkedIn's "URNs included in
the URL params must be URL encoded": /rest/posts/urn%3Ali%3Ashare%3A…), a sign-in's return path holding & that a
browser posts back.
What a judge rejects
A judge rules each action follows from the last for a reason a real person or application would have. It rejects:
- a refusal without a cause the story shows, or the same cause drawn twice; a run of refusals;
- a failing repeat of a request that succeeded earlier in the same sitting;
- a state switched away and back in one sitting, or returned with no reason the story shows;
- a stated cause that never happens, and a promise never kept (a container made "to analyse" data that never runs the analysis, a comparison started and never finished);
- a feature tour: one call per option of an endpoint in a row;
- a read-back with no need: a list or get of what the story already holds, unless someone who does not hold it reads it;
- the wrong actor or credential for an act, a date that contradicts the
waitsteps, a name or id used before it is made or after it is gone; - the wrong era: building on an API the vendor had deprecated by the World's date, or calling one past its documented shutdown;
- vendor behaviour the vendor's documentation contradicts;
- content that does not bear out the step's name: the code a push carries does not do what the story says it does, an upload carries no bytes of the file it names, a message the vendor never sends in that mode is said to arrive, an attachment lacks what its format requires;
- a call through a door the vendor does not document (a path the client builds instead of the one the vendor returned, a read the vendor would refuse for want of a grant the story never gives), even where the twin answers it.
A value a person could only have learned from a page, a mail or an answer (a join link, a hosted page's address, a token shown once, an id) is read off it with a capture, never written as the literal the twin's sequence would give; the judges catch this by reading, since a mechanical check that flagged literals an earlier answer had first returned raised sixty-seven flags across the six lives and none was a defect (vendor constants, the World's fixed ids, values a person reads and then types).
The list is the bar for lives judged after an item is added; a done life is not judged again for it.
Where the evidence stops for the last two: drawn from the cross-family judges' rulings on the six lives (code that returned nothing where a fix was claimed, uploads of JSON metadata, an upload path built from a file id, an org app reading a workspace it was never added to).
score-pack.ts flags the first three mechanically (REPEAT, RUN, TOGGLE; a sitting is the steps between
two wait steps), a time of day a step names that its hour on the World clock contradicts, a weekday named
with a date that falls on another, and a wait whose named date its clock does not reach (TIME: a
"morning" at 21:00, a "Wednesday 10 February" in 2026), and a life-wide default credential in a life whose steps act with several (CRED: a
default lends one actor's credential to every step that names none, including a server's call). The flags are a floor: they miss most toggles and everything below them, which only
the judge sees. Calendar and time-of-day flags use the life's startsAt when declared,
otherwise the World's default start, and apply its waits just as the walk does. Where the evidence stops: this list is what independent judges rejected in the first
six lives (openai, github, stripe, slack, aws Secrets Manager, smtp) and in one rewrite; it grows by the
same means.
Coverage
Lanes over one vendor store may import the same state-field declaration. The life coverage walk counts that shared object once for its vendor resource and field; separate declarations of the same field still refuse as a collision. TikTok’s open and Business posting calls share the publish status machine.
State coverage credits a move only when the kernel observes it on a committed write. Each observation carries that log entry and its stored subject key, field and checked transition actor; a legality check alone is no move, and neither an earlier observation nor an unchecked declaration can credit a later entry. At the write, the machine checks the actual stored source and destination, operation and actor (API by default, vendor for a vendor scope or clock, or the explicit actor a field's legality check supplied in that context). Context evidence is consumed by that field's write. An observation credits two resource declarations only when they share the identical transition object over the same service, stored record type and field. First need: Cloudflare's R2 custom-domain validation becomes active after external DNS proof; its stored subject key differs from the domain name in a request. GitHub's branch deletion closes associated pull requests automatically; that observed vendor closure cannot credit the separate REST merge transition, or another subject's change with the same endpoints.
Replay measurement pins the kernel's signing-key generation as well as byte draws. WebCrypto's subtle.generateKey uses its own entropy, so pinning getRandomValues alone does not give two Worlds the same issuer key. The walk substitutes held key pairs only for calls originating in world-core; direct pack randomness remains unpinned. Real serving still generates a fresh private key for each World. First need: Volter Identity issues EdDSA tokens (its discovery document); two otherwise identical walks must compare the same World keys without changing the advertised algorithm.
A refusal-only transition (refusal with no target) contributes a refusal item, not a fictitious move back to the same state. Coverage reads its refused observation; it never requires that refusal to authorize a move.
An acceptance root pairs its credential with the API host the customer actually used for that credential. If the descriptor supplies a credentialDoor and the life obtains its account credential there, root checks prefer that credential over a temporary integration grant. The loopback vendor receives the paired original host; it never needs relaxed site authentication. Jira first needs this because its account token uses a site host while its OAuth grant uses a scoped gateway, and the most-used grant is not the account credential declared by the descriptor.
A vendor whose APIs are all lanes has no root resource table or root service. Coverage treats that front's resource table as empty, measures its shared lanes' machines, and reads their common service when inspecting the log. A walk that cannot execute is a failed measurement, never evidence that only the imported manifests need coverage. First need: Jira's auth and platform lanes, whose root is the routing VendorManifest described in Pack layout.
What the life must cover is the pack's hand-written code and its declared state logic: semantics,
screens, presenters and doors, and every declared transition and guard's refusal. The generated core
is the generator's to test, once, and needs no journey. scripts/life-coverage.ts <vendor> measures the
lines of the hand-written files the life's walk loads (the real vendor's side, connectors and
rate budgets, is out of any World's reach and not counted), each never-run range named by its
declaration. A measurement stands only on a walk that held: a walk that fails measures only what loaded before it
failed, and a file in the pack's scope that declares code yet measures no line was not measured; either stops the
measurement as an error naming the walk or the file, never a file with nothing to reach. Loading only a manifest is insufficient to measure the pack's code. The scope is the pack's hand-written files on disk (its src/ and its sharing lanes', but generated/,
tests and a unit's src/cli.ts, the command line a person runs, which no World's fetch runs), never the files the walk
happened to load, so a screen not wired into fetch.ts is in scope and fails the
measurement. Data is not code: a file of only imports, re-exports, types, ambient declarations, and constants or a
default export whose values are data (literals, with no function, call, new, accessor or tagged template in them, as
manifest-data holds a manifest's) has nothing a walk could run, loaded or not: a unit's budget.ts (D8's data), a
lane's states.ts of no machine, and semantics/seed.ts (seed: SeedCall[]), which volter-world init copies into
a World and sends to the twin as requests, and which the twin's fetch never loads; the code its calls reach is the
pack's operations, measured where a life makes them. A function, a class, an enum or any other statement is code, so
no handler passes as data. A pack's code
is measured with its lanes' lives and examples as well, since a lane reaches
the pack's code through the pack's router (planetscale/api's life makes the branches psdb serves); each
lane's own code and state logic are measured with the lane. A move is reached when the machine allowed it on a step (the kernel's
observeTransitions) or the World's log shows the change; one write may carry a chain of declared moves
the machine walked, and resources stored as one type share their fields. A change to a state field no
declared transition allows is an undeclared move, and counts as missing. A refusal is reached when
the machine gave it, or a step on its operation was answered with it.
The standard is all of it, with no threshold: every hand-written line, every declared transition and
every guard's refusal is reached by a step the accepted story has a reason for, deleted as dead, or
recorded in journeys/unreachable.json (code: a file and its declarations; state: the item as the
report names it; each with its why). A record names why no World reaches the code, as its
world: host-process (the host process around the fetch), door (the twin's own doors), configured-otherwise (a
World configured otherwise: read-only, scripted, dated before a retirement), hostile-peer, bug-guard (the engine's own
bug guard), vendor-backed (state observed from a live account) or sealed-refusal (a sealed World's refused delivery).
A record naming none of these exempts nothing (life-coverage.ts lists it and counts its code as missing). A refusal or an option the vendor documents is reachable (any client may draw it), so
it is never recorded unreachable because no customer of this life has cause to use it: a step with a reason in the
story reaches it, or the vendor's published example does; until one does, it counts as missing. A step is never
contrived to reach a branch: the story gives it its reason.
A record is a claim, checked every time coverage is measured: one naming a file, a declaration or a
state item the pack no longer has, or one the walks now reach, is stale and counts as missing until
life-coverage.ts <vendor> --prune removes it (a record removed wrongly shows again as missing, so
pruning hides nothing). The walks are the life and the vendor's published examples
(Published examples).
A record is by declaration, so a large function is not recorded for one branch: the branch moves into
its own function first. Bun reports lines, not the branches inside a line, and marks a closing brace or
a lone fallback line unrun where the path ran; such a line is rewritten into an expression bun
measures, with the behaviour unchanged, never recorded. A screen's controls are counted by the lines
their handlers run, not by a click. Because bun reads lines, a branch moved out whose call shares a
line with code that runs (catch { return malformedJson(); }) reads as reached while only its function
is recorded: the condition that calls it is unmeasured, and the moved-out function's record is the
only account of it (github joined four such calls).
Published examples
Published-input comparison resolves captures from response headers and page text with the journey's own
capture selectors, as it resolves JSON fields. A Post ID returned in LinkedIn's x-restli-id, or a video
part's ETag, remains the World's id when replaying the vendor's request; a missing capture still fails
the walk. Capturing a header does not excuse any other field of the published request or answer.
A declared placeholder span matches the value the walk captured or sent for it literally when that value is known.
The published body chooses the comparison: when it parses as JSON, the answer must also parse as JSON or fail with
answer is not JSON. Published request comparison requires the same keys, types and array lengths;
object key order does not matter, and placeholders match only corresponding string leaves.
Only sample object collections and documented catalogues or stored-state lists omit illustrative
length comparison. This distinction comes from the operation and field semantics, never merely an
array or a read verb: an array minted from the request keeps its length, including advisory request
arrays and a domain's generated DNS records. Every returned element is compared. A catalogue or
stored-state member must match at least one published member under the ordinary rules; ordered
request output keeps position. A sample outside those lists compares each existing position with
the corresponding published member and each extra element with the first published member, using
the same ordinary rules. The served schema supplies shape only where a sample's published value
is null, under the sample-null rule below. A nonempty
published collection answered by an empty one fails with "nothing to compare: the collection is empty".
Stored scalar lists, such as comment IDs and SCAN keys, follow the same list rule as stored object
lists; other scalar arrays and nested-array tuples retain exact length comparison. Any array whose
served schema fixes its length with equal minItems and maxItems keeps that length.
These output rules do not change request arrays, literal text, stream event framing or terminators.
A span cannot leave its string leaf.
Renderer-created markers for an unfilled object or string retain their in-memory identity through request transformations;
vendor JSON cannot create one by spelling a field (any object) or a value string. First need: an invalid JSON escape inside a URL-host
placeholder and a vendor object whose ordinary (any object) field otherwise bypassed its required content.
URL-encoded inputs use the kernel's side-effect-free bracket-form reader, including its own-field handling.
The kernel has no SSE-event or JSON-lines wire reader: these comparisons retain line framing in tooling,
decoding each JSON document or SSE data line structurally.
Raw multipart uses the kernel's side-effect-free reader for boundary recognition, part splitting and MIME headers,
including transport padding and folded header lines. The published Content-Type names the boundary when present;
otherwise the same reader infers it from delimiter lines, including after a MIME preamble. Comparison preserves
raw preamble, epilogue, delimiter lines, header bytes, LF or CRLF separators and part count, then compares each
part's body by its media type. A delimiter line remains framing even inside a quoted part value. Record counts
and separators match exactly. On every text path, an unbound span cannot contain a published boundary line
(including its padded forms), blank-line event separator or data-prefix line; the enclosing multipart boundary
also guards spans inside each part. Multiline spans occur only inside a published quoted multiline value.
First need: a padded MIME delimiter that let a multiline quoted placeholder swallow an additional multipart
part, and folded MIME headers that must be interpreted identically by the request and comparison readers.
In other text, an unbound span excludes the first character of its following literal, unescaped double quotes inside
a quoted value, and line breaks unless that published quoted value contains them. Adjacent unbound placeholders
share a span; a final span reaches the end of its value. Surrounding literals and each whole string match exactly.
First need: rejecting a URL-host placeholder that swallowed a closing quote and extra JSON fields while retaining
AI Gateway's multiline private-key and data-URL placeholders
(advanced options,
images) and Gemini's captured model
text and sentence placeholders (generateContent).
Known bindings constrain span extraction before unbound spans are inferred, so adjacent placeholders preserve
each bound value instead of letting an unbound span consume it.
An unchanged published string with no bound substitutions already matches literally, including angle-bracket
markup; copying a bound placeholder's spelling still fails unless it equals that binding's value.
A step's exampleBindings names caller-chosen placeholder values (captures may fill them); they constrain the
comparison literally and cannot contradict an earlier capture. This keeps a configured URL base containing /
out of unbound inference. First need: Workflow's caller-selected failure URL and route origin
(failure URL,
route origin).
A thin deletion can be read back through the parent's embedded child collection as well as through a collection
endpoint. Its list names the child's collection in url, and its current data omits the deleted id. The replay
still asserts the list's membership; it does not require an additional diagnostic request to the child endpoint. First need: Stripe's subscription items, deleted from the subscription's embedded items list.
Examples published on routes differing only by a trailing slash are counted once when their operation id and example content match. Distinct content remains a duplicate-id error. Examples of operations removed by spec patches remain in the published total, counted separately as patch-removed and excluded from replay obligations.
A recorded vendor answer is also an example: spec/doc-examples.json names
source: "recording:<safe basename>", and copies its request, status and body from
spec/recordings/. Replay verifies those copies against the retained recording
offline; it never calls the vendor. An unknown request names operation: "gap"
and must match no spec operation. Its replay checks the recorded method, URL,
absence of Authorization where recorded, status and answer. This proves the
vendor's unknown-request refusal without adding an operation or a customer act.
Tripo's retained API-front 404 is the first such example.
Published request maps bind a placeholder key from the same published scalar positions as response maps. Polar's ad-hoc checkout example repeats productId in products[0] and as a prices key (Checkout API). Replay preserves the published request, maps only explicitly declared placeholder keys to the corresponding sent value, then compares the complete map values normally. An absent or conflicting binding remains unmatched; a wrong amount, currency or undeclared key still fails.
An expectation closing an unordered collection (members) fills captures in nested objects as well as scalar ids, and compares object fields without depending on JSON property order. Only collection order is ignored; membership and values must match exactly.
A spec or documentation streaming response example may be one decoded SSE frame, either a plain JSON event or an SDK wrapper with data and optionally event. Published-example replay decodes the HTTP stream and checks an actual frame against that sample, while the journey's text expectations still check the full framing. It never substitutes the sample for the answer; a missing or malformed frame remains a mismatch.
A model provider can fail after generation starts: OpenRouter publishes partial Chat Completions, failed Responses objects and terminal SSE errors. The scenario grammar therefore has an opt-in generation fault (status, message, canonical errorType, optional metadata, partialText and terminal event). A pack must validate its compute-only scope; other adapters reject it. The kernel carries that decision to the compute handler instead of answering an HTTP refusal. SDK wire helpers render actual error frames or partial JSON, with no successful stored mutation. Status faults may carry typed metadata as data, rendered by the vendor adapter; the kernel still owns status and Retry-After. This is deterministic failure simulation, never a claimed live provider output.
An OpenAPI application/json example may be published as a JSON-encoded string rather than an already parsed value. Replay decodes it once before comparing the answer; text examples remain text, and invalid JSON remains a visible mismatch. Jira's swagger-v3.v3.json is the first need: its response content examples contain JSON strings. The media type, not vendor identity, decides this decoding.
A life shows the twin behaves as a believable customer finds it; the vendor's own examples show it answers what the
vendor says it answers, with no judge reading documentation in between. Every example the vendor publishes for an
operation the twin serves is replayed against it in journeys/vendor-examples.json, a journey in the life's format
whose example steps name their example (<operation>#<title> for the spec's, <operation>#doc <title> for a page's in
spec/doc-examples.json). The step's example field identifies it;
when that field is absent, a step's name that exactly equals a published example id identifies it too, before
replay and decided-value checks. An explicit example takes precedence; a descriptive name alone counts nothing. When a page uses placeholder ids as request-map or response-map keys, replay binds
those keys by their published request positions to the captured request values; only declared placeholders are
translated, and conflicting bindings remain unmatched. Examples include the spec's own (Smithy's examples, an OpenAPI
response's, Swagger's, openai's x-oaiMeta, stripe's fixtures, a line protocol's RFC scenarios) and those the
vendor's documentation pages publish (a standard the vendor's page itself refers the reader to counts as the
vendor's documentation for that point: Discord's OAuth2 page, "please see RFC 6749", makes RFC 6749's error answers
Discord's), vendored with their URLs in spec/doc-examples.json (each
{title, source, operation, request?, input?, output?, means, status?}; a page's request is checked to route to
its operation, and its ids stand for the World's own, as a spec's do: a query value or path label the step fills from a capture, an S3
upload's UploadId, is the World's id and is not compared with the example's literal), including a rule a page states, replayed as
the refusal it causes. A field's example value is not an example: a spec that publishes none per operation sends the
author to the vendor's pages (resend's, one request and one answer per operation). An API the pages document and the
spec omits is the spec's lag, patched in with evidence when the pack models it and until then outside the count,
named in SOURCE.md. Setup steps make each example's precondition true or read back its effect,
and nothing else. scripts/vendor-examples.ts checks that each is sent as published, answers the published status,
and holds every key of the published answer with its type (a sample, which is what an object can hold rather than one
request's answer, may answer null where the served spec permits it; in the other direction, only a
sample's published null accepts null or a value conforming to the known served field schema's declared
type or one of its anyOf/oneOf alternatives, including enum membership. An admitted object is also
checked against the properties and required fields its schema declares. With no known field schema,
or a value of no admitted type, the sample's null remains a mismatch. A per-request published null
requires null: fix a wrong twin or its precondition, or give the reasoned exampleDiffers the vendor
text allows),
and counts every example: replayed, or recorded with its reason. An example the
twin answers otherwise is the twin's defect, fixed, unless the vendor's example is itself wrong or the server the twin
models answers otherwise where the vendor's text allows it (exampleDiffers, with the reason). An example of an
operation the twin does not serve is counted, not replayed: it comes in when the operation is served. Where the spec and a page
publish different shapes for one answer, the twin answers the spec's, the spec is patched where a page adds a field it
lacks (with the page's words as the evidence), and each remaining difference is an exampleDiffers entry naming both
(resend's pages and spec disagree on a list's empty fields and on two answers' shapes; the evidence is that pack's).
Form-encoded published requests are compared as decoded name/value fields, preserving repeated fields as arrays. The actual journey still sends the vendor's application/x-www-form-urlencoded bytes; comparing a wire string to a published object would reject the same request. Both a published form string and a journey form string are decoded under the request’s media type before comparison; an already decoded published object stays an object. First need: Tinybird’s Create Data Source publishes the same name and schema as form bytes that its replay sends, which must compare equal without changing the captured example.
Multipart replay decodes raw UTF-8 body strings as well as base64 bodies and structured journey parts.
When the published field is an object or array carried as JSON in a form field, comparison decodes
that field's JSON once; invalid JSON still fails. A single file remains a scalar when the published
field names one filename, and repeated uploads remain arrays. Snowflake-shaped ids in examples stand
for the World's own ids when the manifest declares snowflake ids, and only under an id-named key (id, ids, *_id,
*_ids, *Id, *Ids), custom_id and custom_ids excluded (values the caller chooses), as scripts/vendor-examples.ts reads them: another
field's digits (a permission bitfield) are no id. A UUID stands for one by its form alone where the manifest's ids are UUIDs.
First need: Discord's upload examples
carry individual text fields and a JSON payload with a referenced message id in multipart bytes.
A page's explicitly declared placeholders may occur inside a raw text or binary-text request, as well as occupy a JSON string field. The comparison substitutes only those named spans, retaining every other byte and the item's length. Sentry's full envelope examples place the receiving account's DSN inside their header line; the receiving World's DSN replaces that identity without changing either attachment or event payload. A declaration still carries the page's words identifying the value the caller supplies.
Multipart examples are compared to the text fields and file names decoded from the bytes the journey sends, including repeated file fields. A published file placeholder stands for a nonempty uploaded file, never a JSON filename in place of bytes. First need: ElevenLabs' Instant Voice Cloning example.
Two of the judge's recurring rulings are the script's checks: a value an earlier step or the step's own request sent or
captured, answered back on an example step, is asserted, or named in the step's undecided with the reason it only coincides (a default the vendor
applies, a value the vendor generates); and a write example whose answer is thin has its effect read by a later step.
Before its judge, the author walks the rest of the bar one item at a time: the values the setup only causes (a count, a
date a send was made, a status a verify leads to), which the script cannot see, are asserted; every setup step makes an
example's precondition true or reads back an example's effect; every exampleDiffers and notReplayed reason is checked
against its page for a twin defect it excuses; and nothing is asserted that the vendor generates or defaults, since a
value pinned to make a check pass states the twin's guess as the vendor's answer. --sweep adds advice (actor fields no
assertion pins, lists no expect closes). Where the evidence stops: resend's first examples file drew sixteen rulings,
thirteen of them the two checked classes; asserting openai's and slack's hits by value alone pinned a reasoning default,
generated counts, cursors and a stale block, which their judges ruled.
A vendor with an API at its root and lanes beside it replays the published examples of both: the root having a spec never hides its lanes. Each operation retains the schema dictionary of its own unit, so identically named components on different APIs cannot change an example's comparison. First need: fal's direct Model API and its Platform pricing lane; the latter's five published response examples must be visible to the vendor's one examples journey.
An independent judge rules on what the script cannot (padding, a decided value not asserted, a reason or a step name
untrue of its source, an answer that passes the checks and contradicts the vendor's text), in
journeys/vendor-examples.verdict.json, pinned to the file's hash, as a life's verdict is. Coverage counts what the
examples reach beside the life: a line only the vendor's own example reaches is reached for the reason the vendor
gives.
A vendor's examples span its history: one names a model released this autumn, another an API retired last summer. The pack holds the vendor's timeline as data (each model's release and shutdown, each API family's removal, each dated policy, cited to the vendor's changelog and deprecations pages), and the twin answers by it on the World clock: a model before its release or after its shutdown, and an endpoint after its removal, answer as the vendor documents, and a model the pack does not catalogue is unknown. The examples file is then a timeline too: each example is sent on a date when everything it uses is live and its values fall within the vendor's documented windows (a reschedule no further ahead than the vendor allows), and one no single date can hold is recorded with the dates that exclude it. A default the vendor gives (the model a request runs on when it names none) is data in one place, read wherever the request is gated, served and billed.
Where the evidence stops: proven on aws (54 examples: the spec's 24, the schedule page's 15 and its 15 stated rules; rotation schedules use the requested cadence and omit fields AWS leaves out), smtp (RFC 5321's 38 scenario turns; Postfix's own replies) and openai (147; the timeline, from its timeline prevents removed APIs answering after their removal date).
Rules the lives have taught the twins
- Every move the twin makes asks the machine, whatever made it: a handler, a read-time presenter, a
screen, another wire (GraphQL, a git push, a runner's report through a door), and a write the twin
stores in another shape than the machine's field (an overlay row, a
closedflag for astate). A move no step can ask for is removed from the machine and named in a comment beside it. An ask that names no target state takes the first transition that allows it, so a guard that lets a state stay is declared before a move sharing its actor and from-states. - An input the vendor's operation does not take is never a field on it: a twin's own act has a door under
/_twin/. Where the evidence stops: drawn from github (its old router took fields on vendor operations for its own acts, and answered Not Found to 834 operations it held). - An answer that was empty hides its shape: a step that fills a list for the first time meets SHAPE on its items, and the defects it turns up are the twin's, fixed where the list is built.
- A twin refuses what the vendor refuses at the edge: a parameter the operation's spec does not have (stripe had no such check, and a life's removed parameter passed until a judge read the changelog), a redirect the app never registered, an org-wide token missing the workspace it must name (slack). A lenient twin hides a life's stale or wrong calls from every mechanical check.
- Every credential resolves to the actor who was given it: a user token to the member who authorized the install, a bot token to that install's bot, revoked with the install (slack issued one fixed token of each, so an uninstalled app's reads still worked). A life carries each step's own credential and no life-wide default, which leaked one person's token into a server's call (github).
- A move the World clock makes (a renewal, a payout, a trial's end, a cancellation at period end) is stamped with the moment it fell due, not the request that caught it up; otherwise a quarter's books count it in the next quarter (stripe).
- A move the World clock makes happens when any request next arrives, never because a
/_twin/door was read: a twin's state cannot depend on being watched, and its doors answer through every router a World mounts (aws's scheduled rotation first started only when its invocations door was read, and its router refused the door's GET). - The customer's own program that the vendor invokes (a rotation function, a webhook receiver) is played by the life every time the vendor would invoke it within the life, and the outline picks the schedule so that stays a story (aws rotated every 90 days, four rotations shown, after a 30-day schedule played twice left a rotation stuck for nine months that no review noticed).
- A twin that serves one API version serves it on every World date, so a life dated before a parameter's release cannot use the vendor's name for that date (stripe's market stall moved a month for it); the account's own version is open on the board.
- A twin takes a payload only in the format the vendor documents: an upload's raw bytes with its content type, a real image or audio file, an upload URL it minted; a body in another shape is refused as the vendor refuses it (github took JSON for a release asset, openai took text for audio, slack took bytes at a path it never minted). A time-limited resource expires on the World clock (openai's batch files after thirty days).
- A twin's generated output (an image, speech, a model's text) is a labelled placeholder, so a life never claims what it shows or says, and never reasons from it; an input a person supplies is real content of its kind (speech that says what the step says, made with the host's text-to-speech), not a valid-but-empty file (openai's first real WAVs were a second of silence, one copied as the other).
- Where the evidence stops: each rule above is drawn from the one pack named beside it, found by an outline's door mapping or its life's judge.
What the exemplars settled
Each question the exemplars were built to answer, as a rule or a rejection with its reason. Where a rule's code does not yet hold, the section names the work it orders.
Moves no API call makes
Every move has a cause the vendor shows, and the twin makes it where that cause arrives:
- Time (a renewal, a payout, a trial's end, a file's expiry, a scheduled rotation): the pack's catch-up, a
semantics function its fetch runs before answering any request on a writable World. It walks every move due by
the World clock, in order, each stamped at its due time. There is no scheduler and no kernel pass; the World
clock is read, never watched. (stripe
advanceBilling/advancePayouts, openaiexpireFiles, awscatchUpRotations.) - The vendor reacting to state (Dependabot reading a manifest, a session completing when its page is paid): the handler of the act that caused it, in the same write, asking the machine for each move.
- A program or third party acting (a CI runner's check run or SARIF upload, a bank transfer arriving): its own call, through the door the vendor gives it (the runner's API, Stripe's test-mode helpers), played by the life as that actor with its own credential.
- A person on a vendor's page: the screen, which asks the machine like any handler.
- A third party outside the vendor (a recipient's mail server refusing mail, a reader clicking a tracked link
in their inbox): the World states the outside fact through a door (resend's
POST /_twin/recipients/:domain, a server that accepts no mail) and the vendor's own act meets it (its delivery bounces); a person outside reaches the vendor's own door as a browser does (the tracked link's host), reading what they hold through the World's door for it (the recipient's inbox,GET /_twin/mail?to=).
Rejected: one door that fires a named move. Each move above has a real cause the vendor exposes; a generic door lets a life skip the cause and a twin model a move no cause produces.
Where the evidence stops: the catch-up is drawn from stripe, openai and aws, the reaction and the third party from github and stripe, the screen from github, slack and stripe, and the third party outside the vendor from resend alone.
Events and webhooks
An event is the vendor's report of a write, and the twin announces it where the write is made: the handler that
makes the write (github's announce, slack's emit), or the manifest's onWrite, which the core calls after every
write it stores (stripe names the event from the write's operation). Its type comes from the vendor's published event
list, never coined; its payload is the object as the operation answers it, or the spec's webhook schema where the spec
publishes one. An announcement is stored where the vendor lists its events (stripe's /v1/events) and delivered to
every endpoint subscribed to its type, signed as the vendor signs; the cooperative HTTP delivery policy refuses an unlisted destination and says so (transport coverage), and
the event stays listed. What the vendor does on reading an event (a push running workflows and Dependabot) happens in
the same announcement (moves no API call makes).
Rejected: events derived from declared transitions. Most of the vendor's events report no transition (a push, a
posted message, a created object, an edit), and one write can report several (a stripe refund is refund.created and
charge.refunded); a transition-keyed derivation would miss the first and merge the second.
Where the evidence stops: drawn from stripe, github and slack; openai's webhooks and aws's events (EventBridge) are not built.
Fields the server assigns
A resource declares what the server assigns as data: its id's prefix (idPrefix), each field set on create
(assigned: the World clock's now, the subject's id, a fixed value, or a template over the stored fields, base64-encoded when the vendor's ids are — GitHub's node_id, 0<length>:<Type><id> in base64), a number
counted per parent (number, a repository's issue numbers), and the spec's defaults, which the core applies. A
handler assigns by hand only what no rule can say (an AWS ARN's suffix, six characters seeded from the name; a
GitHub node_id encoding type and number), and names the vendor's documentation beside it.
Lists ordered newest first break equal or absent creation times by the store's subject creation order, never by the lexical spelling of an opaque or caller-supplied id. Stripe coupons accept caller-supplied ids, and Stripe lists return reverse chronological order (https://docs.stripe.com/api/pagination); spelling a later coupon LOYAL5 must not place it before an earlier SPRING26.
Where the evidence stops: the rules serve every create the generated core makes in all six packs; handlers assign by hand in each, and no pack has been audited for a hand assignment a rule could say.
Embedded and expandable resources
A resource holds another by its id, stored as the id, and declares it in embeds (field → resource); the core answers
the id and, where the vendor has an expand parameter (expandParam: stripe's expand[]), hydrates each path the
caller asks for from the current stored object, at any depth, through that resource's own view. A stored copy of an
embedded object is never kept: it would answer yesterday's object. A field the vendor answers embedded on every answer, asked
or not (its schema gives the object with no id alternative: Stripe's issuing card's cardholder, an authorization's
card), is listed in alwaysEmbedded: still stored as the id, it is hydrated on every answer the core makes and on
every ctx.expand a handler makes, and a resource already being embedded is not embedded again inside itself (the cycle
answers the id). A handler that answers such a resource builds it with ctx.expand. A field the vendor names with a leading _ (GitHub's _links) is listed in
vendorUnderscored and answered like any field, a setting's values included; every other _ field is the pack's
bookkeeping and never answered. The kernel’s internal render filters a stored row with that resource’s declaration and retains the subject id when the vendor has no own id; the private renderOwn applies the same filtering to an already rendered or constructed vendor view. A handler uses ctx.render(row, resource?) to render a raw stored row or constructed vendor view. The context tracks rows returned by its raw reads, so a vendor field named updatedAt cannot be mistaken for kernel metadata. It resolves the declared resource (from the raw read or an explicit resource for constructed views), then applies that resource’s vendorUnderscored through the kernel render path. A handler never imports its manifest or supplies a vendor-wide exception. First need: GitHub's _links in pull requests.
A retained stored field absent from the vendor's response is named in the resource's viewOmit,
the kernel's one response-omission mechanism. Stored views and render share its filter;
expansion applies it at entry for every resource, including constructed bodies passed to ctx.expand
and objects already embedded in them. Thus get, rows, render and core answers omit it;
raw rows and ctx.own retain it, so parent scoping, refresh and conflict checks keep their stored names.
This is separate from underscore bookkeeping: renaming a retained parent to hide it would orphan existing rows.
First need: Cloudflare's stored DNS zone_id, absent from its record response. Before this declaration,
an authored view could omit fields and use ctx.list (Polar's customer views), but the generic core
had no resource declaration for omitting a non-underscore field without changing retained state.
A field the vendor answers as a list of another
resource's current children (Stripe's subscription items, its subscription items) is a collections entry (field →
{ resource, by, list }): built on every answer from the child rows whose by field holds the parent's id, in the
order they were made, each through the child's view and its own alwaysEmbedded (each item's price), inside the
list fields list names ({id} the parent's id, "{count}" the number of children); the parent stores no copy of
them. First need: Stripe's subscription items, its subscription items.
Where the evidence stops: embeds are drawn from stripe (15 embeds) and openai (5); collections rests on stripe alone,
and vendorUnderscored on github alone. github and slack embed objects in every answer
(a user in an issue, a channel's creator) through their presenters; whether each reads the current object rather than a
stored copy has not been audited.
Credentials, scopes, roles and tenants
The manifest declares the credential's wire once (auth: header, scheme, the vendor's answers to a missing and an
invalid one) and how a credential resolves to its actor (identity); every credential resolves to the actor who was
given it (rules the lives have taught). An operation's scopes are the spec's
(its security requirements, which the IR reads onto each operation); where a vendor's spec lists them, the twin
refuses a call whose credential holds none of them with the vendor's own refusal, before any handler runs, and a spec
whose scopes lag the vendor is corrected by a patch, never by a table beside it. The schemes a call is made with are
the spec's too (credentials on each operation of the surface: each requirement's scheme names, anonymous where a
requirement is empty, absent when the call takes none), so a vendor with several kinds of credential (Polar's
organization tokens and customer sessions) reads which one an operation takes from its operation, never from a list
copied out of the spec. Roles and tenants (an organization's
members and teams, a workspace an org-wide app was added to, a project a key belongs to) are the vendor's state, kept
by the pack's handlers; no spec declares them, and none is derived.
A key whose grant no spec declares (resend's full or sending access, optionally one domain) keeps its grant with the
key in the pack, and the pack's fetch refuses a call outside it before dispatch, as slack's scope gate does; a key
the vendor issued and then revoked, deleted or retired with its project is refused as the vendor refuses any key it
does not hold (openai, github, resend), while a key the twin never issued is the World's and is checked only for its
format. A World's first key, which the vendor makes on a dashboard, comes from a door standing in for that page
(github's token door, resend's POST /_twin/api-keys).
Where the evidence stops: scopes from slack alone, the only one of the six whose spec lists them (every operation); github's fine-grained permissions and openai's key permissions are not in their specs and are not modelled; key grants from resend alone.
Behaviour every operation shares
Declared once in the manifest and applied by the core before or around every handler (crossCutting): the
credential's wire and its refusals, the API version header and the answer to a malformed one, the idempotency header
(a repeated key answers the stored first answer, or the vendor's conflict when the parameters differ), a body that
does not parse, a write to a read-only World, the list envelope and its paging (after/before ids, an opaque cursor, or
numbered pages with the vendor's Link header), the error body, and headers every answer carries. A handler never
re-implements one of them.
The core retains same-named scalar list filters, as Stripe's customer list
filters by email. Numbered pages remain one-based: GitHub's pagination
uses page, per_page and Link relations; Polar's vendored OpenAPI source
defines its product list's page and pagination envelope. A declared list limit has a required maximum.
List configuration is optional: a unit serving no stored list declares none. Tremendous's public
prohibited-email-domain snapshot needs no paging or list envelope. The core owns a resource list only when
its manifest declares list configuration; a handler asking for a list without it receives the vendor-shaped gap,
never invented paging defaults.
A rate-limit answer comes only from a limit the twin models (the vendor's documented limit for the account, on the World clock); no derived pack models one, and none answers 429. A header that forces the answer is a twin's act on the vendor's operation and is refused by the rule that such acts have doors of their own.
Conditional requests (an ETag answered, If-None-Match answered 304) are not modelled: a client that sends
If-None-Match gets the full answer, which every client accepts. Where the evidence stops: no life has needed one;
github's REST answers both, and a pack that models it declares it here with the others.
The API version an account answers
A World serves one version of each vendor: its vendored spec's, on every World date, to every account. An account's own version (the one it was created on, answering its unpinned requests) is not modelled: it would need the vendor's history of every parameter and field by version, which no vendor publishes as data, and a twin that guessed it would answer shapes no spec checks. A request pinned to an older version is answered in that version's shape only where the pack renders it from the served one (stripe's versions before basil). A life is dated where the served version holds: nothing it sends was released after its step's date, and a judge rules on a step whose date precedes what it uses.
Rejected: an account version chosen by the World's start date. Where the evidence stops: stripe alone versions its API per account; github and openai version by header or path, slack and aws not at all.
Seeds
A journey observer receives the original declared step, together with the resolved path, body and answer. Expanding a short body may copy its value for execution, but keeps that declaration's identity at the observation boundary: scoring uses its position among the original waits to distinguish working sittings. First need: Stripe's life, whose expanded request bodies must retain their original step identity for sitting scores.
A seed is a journey: steps in the customer life's format (packages/twin-standard/src/walk/journey-kit.ts), played through the vendor's own operations
and doors on a fresh World, each actor with its own credential, with captures for the ids a later step or an app
needs. There is no second format and no write beneath the vendor's operations: state a seed cannot reach through them
is state no customer can have. A seed's steps are checked as any journey's are (status, SHAPE, expectations); it has
no judge, because it tells no story.
The copied runner uses one ordered pass for ordinary calls and browser calls, preserving each call's form encoding
and dated World clock. It restores the original clock before an undated call and when the seed finishes or refuses.
Slack's fresh local defaults create a synthetic workspace and owner through its existing slack.com/get-started
signup flow before the application's bot uses workspace-dependent APIs; credential issuance does not substitute
for creating that workspace.
State the account made before the World's day one, through an API that has since stopped making it (an Assistants
assistant built before OpenAI deprecated that API), is made through that API as it was then, never through a door: a
seed call carries the instant it was made (at), and the runner volter-world init writes makes that call with the
World's clock frozen there and puts the clock back after; a life that begins then declares its own start (startsAt) and
its waits move the clock on. The pack refuses such a call at the World's own time as the vendor does. First need: openai's
customer life, seeded through the vendor's API with the World's clock set before the deprecation.
A seed may call a /_twin/ door only for state no vendor operation reaches, as screens allows.
An application's database schema is laid through its migrations on the declared database, as the SQL steps do;
declaring a managed database does not establish support for a vendor's HTTP data plane or install its schemas.
The pack's current manifest and seed own the concrete behavior. The rule follows from the pack layout's "calls to
the vendor's own operations" and the lives, whose opening sittings are what a seed would be.
Who is on a screen
A scenario is its handlers in force, default data and browser states. A World may hold zero or more named browsers: the frontend state of its scenario, made through the twin's own
sign-in requests or loaded through the World's browsers door. A World's browser is a browser profile, anchored to
what a Chrome profile holds. Its portable form is { storageState, context }: Playwright storageState (cookies,
including optional partitionKey and Chromium ancestor metadata, and per-origin local storage and optional IndexedDB),
at the vendor's real domains and origins, plus the profile subset of Playwright's BrowserContextOptions.
That subset is permissions, geolocation, locale, timezoneId, userAgent, viewport, deviceScaleFactor,
isMobile, hasTouch, colorScheme, reducedMotion, extraHTTPHeaders and offline, with Playwright's names and
value shapes. Recording, proxy, base URL and HTTP credentials belong to execution, outside the profile.
Missing context reads as {}; a legacy top-level cookies/origins object reads as the profile's storageState.
Playwright 1.61.1 storageState does not carry session storage, so neither does the profile.
Hosted pages load cookies and local storage and the World applies the profile's Accept-Language header to forwarded
requests, falling back to locale when no explicit header is set. Permissions, time zone, location and device
settings take effect where a browser is created from the profile (Playwright or a dedicated browser), not inside
a board frame. The frame retains the person's browser settings. Browsers
are instance bookkeeping alongside the clock: outside the log, diff, changesets and push. Reset discards them and
runs the seed that makes the defaults; a branch inherits its parent's browsers as they stand when it is made.
Record reads cache validated state by store, path and version; a changed record is read again. Rewriting a
browser retires its earlier marker files, carrying their cookie paths into one record per site for the current
version. Loads preserve that union for the version's lifetime, so every real browser context expires the same
retired paths. Deletion removes active version markers and retains only cookie names and paths per site under
the deleted name; recreation carries them into its next version.
A browser names no person: one can carry credentials at different vendors for different people. Loading is one way;
subsequent browser activity reaches the World only through ordinary twin requests. The decision is
ADR 0016.
Names are 1–24 lowercase letters, digits and single hyphens, starting and ending in a letter or digit.
A seed imports browser(name) from @volter/world-core/browser, a dedicated subpath absent from the
root index. Importing or calling it installs no injector or clock; the side-effect-free host resolver is shared
with the proxy. WorldDoors and the HTTPS redirect and reflection fronts strip client-supplied browser routing
headers before forwarding, including raw upgrades; an invalid browser header at a twin's site-link helper is ignored. Awaiting it
reads an existing browser through GET, or creates an empty one only on 404; its fetch carries a standards cookie jar through the same host resolver as the proxy,
following redirects, its storage writes local storage, and context(options) replaces the profile settings without
changing storageState. state() returns the whole profile. Its forwarded requests use the profile's language
header. When the twin's URL has a path mount, the helper
maps response cookie Paths and URLs in Location, Content-Location and Refresh back to the vendor's paths
before storing cookies, following redirects or returning the response. The mount itself becomes /; paths
outside the mount are kept as given. Clearing cookies follows the same rule. This applies to hosted and
locally served Worlds; the ordinary local boot gives each twin its own listener without a path mount.
Under a path mount the twin's front states a Path on every cookie, so a cookie the pack sets without a Path
arrives at /, while a root twin's jar derives its default Path from the vendor URL.
Ordinary default calls are exported as seed;
browser calls are a separate browserSeed export read by the runner after ordinary calls, so an earlier runner
can load the ordinary data without interpreting browser URLs. A default SeedCall may name its browser client;
its path is then the real vendor URL. Init copies a pack's supplied runner into the project. When the pack supplies
call data without a runner, init copies that data and a generated runner; the project author owns the copied files.
Before a browser's first default call in each runner, the runner GETs its state once and keeps that snapshot;
404 means empty state. A call is skipped only when the snapshot has a cookie matching its hostname under the
jar's host-only/Domain rule, or an origin with that hostname. All calls use the same initial snapshot, so a
multi-request sign-in runs through and another pack can add state at a different host. A runner loads the
browser helper inside the branch for named browser calls; ordinary seeds have no runtime import of that helper. The project
must resolve that package from its seed; a missing helper reports the package to install, with no alternate lookup. Form bodies declare their ordinary content type; no vendor session is issued by init. Each state-changing call awaits a PUT through the
loader. Seeding serves that door on task-owned loopback for the subprocess and closes it in finally. The listener
startup allocates only process-local state and binds a loopback port, with request journaling disabled; it writes
no World file or record. Browser persistence starts only through explicit browser-door mutations.
The HTTP loader owns browser persistence.
Only the current local World materializes .volter/browsers/<name>.json under its state directory, regardless
of where the caller writes world.env, as the whole profile for
Playwright's newContext({ ...profile.context, storageState: profile.storageState, proxy }), using the World's proxy
and session CA. These are derived
files, not another loader or source of state; a small output index identifies their owning instance so switching
branches refreshes its recorded outputs after selecting the current World and purging an older instance cannot remove a newer branch's files.
The health endpoint /-/ping answers before page and site routing, including at site-shaped addresses.
Site routing rejects hosts outside the template before reading browser state or asking twins; it gathers
the host set once per request. Hosted requests and seed requests resolve the real host and path with
the proxy's shared include/exclude rules and key order; an excluded exact host is not listed as a browser site.
The compiler and resolver live in a pure module: passed host facts, routing rules, endpoints and claimed hosts,
they return a routing table or answer. They import no module, read no files or environment, and install no hooks.
The Node host-table loader supplies process facts; hosted routing supplies the runtime's in-memory facts. Browser routing
compiles one table per facts object; a pack overlay changes that object. Ordinary site reads and application
questions use only the claimed-host listing and do not compile or consult a browser routing table.
Caller and trace headers accompany browser-site forwarding only; ordinary site reads forward their existing
read-only header set.
A page's own bearer authorization reaches its twin. Redirects outside the World retain their original URL. A hosted browser's sites are <browser>--<site>--<world>--<org><suffix>, each subject to the DNS label limit. Sites whose labels cannot fit are skipped; a browser is refused only
when none of this World's sites fits. A World with no declared or claimed exact-host site is reported separately
from one whose site labels exceed the limit. Browser names that collide with a claimed site or another browser
address are refused at the loader. Routing checks an exact claimed-site label before browser labels, so a
site claimed after browser creation retains its address. An unknown local browser label never opens the World
on an alias origin.
A browser site's GET/HEAD navigation may come from the same site or a typed address. Other methods require
same-origin, including navigation requests. An ordinary site's navigation gate admits same-site or none
without a method restriction; its read-only site handler then refuses methods other than GET/HEAD with 405.
The site-opening endpoint with a browser admits only same-origin or none; another World's same-site page cannot
mint a browser pass.
After the existing page pass opens access, first navigation loads matching cookies host-only on that site and
local storage for its real host, including an origin with a port. The private load marker records the cookie
names and paths it set, so a later version expires every previous path before replacement. Bootstrap reloads the current document after applying the browser state, including paths beginning
with a doubled slash, without interpreting them as another host. Hosted site HTML is private and not stored in HTTP caches. A state-version marker makes loading one way, once per version; ordinary
requests afterwards are the twin's own. Real cookie-domain behavior holds with the proxy and CA at the vendor's
real hostnames. Relabelled sites keep an independent copy per site; no synchronization is performed. A page
on one relabelled site calling another site of the same browser has no page-access session there and is refused.
On a hosted site, /_host/<hostname>/<path> reaches another host declared or claimed by the same
vendor on the page's origin. The target host and path resolve through the shared include/exclude
rules and key order, and must resolve to the page's vendor; otherwise the request answers 404.
The original site's live page session and navigation gate authorize it, with the same browser,
read scope, caller and trace rules. Browser loading stays attached to the original site.
Opening each site through its own World page pass supplies only that site's session. A browser site without
a page session tells the person to open it from the World's board; it offers no site-opening link. An ordinary
site's opening link names its real host and landing path, without a browser parameter.
The one loader is GET /-/<org>/<world>/browsers (names) and GET | PUT | DELETE …/browsers/<name>
(the state, with unknown keys refused). The loader accepts and preserves Playwright's optional IndexedDB
and Chromium cookie metadata. Cookie values may contain spaces; control characters, semicolons and characters
above U+00FF are refused because they cannot be carried in a Set-Cookie header. Cookie paths must also fit
the header's byte characters. Its input policy permits at most 32 browsers per World and one MiB per PUT body,
refusing additional records or oversized state with the reason; these are input caps, not resource measurements.
Hosted bootstrap loads cookies and local storage; IndexedDB remains available in the native Playwright file. It uses the same read/write grants as the other World doors.
The shared dashboard session kit issues its opaque cookie from a new kernel bookkeeping subject and ctx.secret,
never from a count of prior rows, a person's email or the current time. Separate sign-ins remain separate after
a branch or deletion; a bearer session is not derivable from its public inputs. Intent: ADR 0010.
A person on a vendor's page is who signed in there: the pack serves the vendor's sign-in page at its own path (built
from @volter/world-ui's SignIn), the person signs in with the password the World gave them (a door under /_twin/),
and the vendor's session cookie names them to every page after (at a World's place, kept to the twin's base path:
"A page at a World's place"). A page asked for by nobody signed in sends its visitor
to the sign-in, which returns them there. A page never takes its person from an API credential, a query parameter or a
default, and a journey carries the cookie as a browser would, captured from the sign-in's answer
(header:set-cookie:<pattern>).
Built in github (src/screens/session.tsx: github.com/login, /session, GitHub's user_session cookie). Not yet so
(the work this rule orders): slack's pages take the person from the request's bearer token, openai's from a fixed
owner. Where the evidence stops: the rule is drawn from the lives' judges, who doubted a token standing in for a browser
in every pack with pages; signing out and a session's expiry are built when a life has cause to reach them.
Streams
OpenRouter's streaming reference requires a separate final usage chunk with non-empty choices, repeating the stream's finish reason before the closing sentinel. The shared chat wire supports this as optional separateUsageChunk framing; vendors that do not select it retain their existing framing. The OpenRouter Responses overview is stateless-only; its spec's Responses example also uses nullable sampling parameters and metadata. The shared Responses wire accepts vendor defaults for these fields, including null, so a gateway can select its documented defaults without changing the OpenAI wire's defaults.
Shared wire builders accept declared stream variations without vendor branching: a chat shape's interimExtra
replaces its normal top-level extras on interim chunks, retaining normal extras on the terminal chunk; the Messages
stream options can omit pings and send tool input in one JSON delta. Merge requires these distinctions
(Streaming: "Interim frames report service_tier: null", "No ping
events to use for liveness", and "Tool-use input arrives as one complete input_json_delta"). Vendor rules select the
options in the pack. Defaults preserve the original wire contracts.
A server-sent event stream is derived wire: the manifest declares its framing (sse, streamFor: named events, a
closing sentinel) and a handler answers ctx.sse(events); a journey step reads the frames as its answer (openai's
life streams in eight steps). A byte stream (a line protocol, a WebSocket) is the pack's create<Name>TwinStream,
walked on the in-memory World by a journey's connection steps (on, connect, send), with SHAPE checking each reply
against the spec's (smtp's life holds its whole session this way).
A WebSocket a manifest declares (sockets) is walked in socket steps (sockets).
Where the evidence stops: SSE from openai, a byte stream from smtp, Discord's Gateway; Slack's Socket Mode is served
and met by Slack's own client in a World, and its life does not walk it yet.
One representation of a spec
Every spec is read into one representation, the spec IR (packages/twin-standard/src/spec-ir.ts: operations,
resources, scopes, streams), by its own reader (fromOpenAPI for 3 and 3.1, fromSwagger2, fromSmithy, an RFC's
command-reply table, Protocol Buffers services (spec-ir-proto.ts), a command language's table (spec-ir-commands.ts),
and a vendor's own client (spec-ir-client.ts)), after the pack's patches, and one generator (derive-pack.ts) writes
every derived core from it. An answer is read through its vendor's envelope: the one object (or list) it holds under a named property is the resource, and the envelope's metadata beside it is not held (a list of messages, Cloudflare's errors and messages; a paging block, its result_info), so Cloudflare's { success, errors, messages, result, result_info } answers the record under result; an inline object that is one named schema (allOf: [$ref]) is that schema. A GraphQL schema is read beside it: its root fields are the operations a single endpoint
names in its body, and its types answer through the pack's resolver, not the IR's resources. A vendor that publishes no
spec but ships its own client is read from that client: each request the client makes (its method, URL, query and
body keys, typed by the client's own annotations) is vendored as data, spec/client-ops.json, with the extractor that
made it; the client says nothing of what an answer holds, so the vendor's reference pages give the answers, as examples,
and what the client builds as a raw string, as patches citing the page.
A client-call document may add resources in the IR's field-table format and each call may name
answers and resource. These are reference-derived response declarations, patched with citations just as
request paths are. The client alone cannot describe them. The reader classifies a declared GET response as a retrieve or list and preserves
its resource; a document-rewriting write remains an action. npm's metadata reference
needs this: its package and version responses contain vendor fields such as _id, _rev and _npmUser;
without response declarations the derived surface cannot distinguish them from World bookkeeping.
Where the evidence stops: OpenAPI 3 (github, stripe, openai, vercel), Swagger 2 (slack), Smithy (aws), an RFC (smtp), GraphQL SDL (github), Protocol Buffers (planetscale), a command table (upstash). A vendor's client (tinybird's Python client, 128 calls naming all 18 of Dub's Tinybird operations) is read, and no pack is derived from it yet.
What an author writes, and how
Most of a pack is generated, and none of it is written twice. From the spec, derive-pack.ts writes src/generated/
(every route, parameter, shape and the plain create, read, update, delete and list) and, from the manifest, the
pack's fixed files: src/fetch.ts, src/server.ts (the hosts, the discovery door, the doors and screens the
manifest declares), src/index.ts, src/cli.ts and src/semantics/index.ts (the handler map, from the handlers'
names). A generated file is never edited: the manifest or the spec is changed and the file generated again, and
derived-reproducible checks it byte for byte. The author writes exactly three kinds of thing, and each has one form.
The manifest (src/manifest.ts) is data: one exported manifest: DerivedManifest object literal, holding the
vendor's facts (error envelope, auth, ids, time, paging, idempotency, the resources modelled and their unmodeled
operations, refresh, references, roundTrip, engine, hosts, doors, screens), each with the citation it rests on in a
comment beside it. It imports its states from semantics/states.ts and nothing else but the kernel's types; it
declares no function, save the event mapping onWrite takes (an operation to the vendor's event type, as a table).
The states (src/semantics/states.ts) are data: one exported object,
export const states: Record<string, { state?: Record<string, StateField>; notState?: string[] }>, keyed by the
manifest's resource names. state holds each state field's machine (its states where the spec lists them, its
initial state, and each transition with the operation or the actor, external, vendor or time, that makes it,
the refusal the vendor answers where its from does not hold, and its source, a citation check-sources.ts
verifies); notState lists the candidates ruled not state fields, each with its reason in a comment. The manifest
takes each resource's whole: Invitation: { storedAs: 'invitation', idPrefix: 'inv', ...states.Invitation }.
Nothing else writes a state or rules a candidate, and the grade's states-module loads both modules to check it.
A handler is one exported async function <name>(ctx: HandlerContext): Promise<Response>, its name the
operationId with every character a JavaScript name cannot hold made _ (GitHub's pulls/create is pulls_create) and a
name JavaScript reserves given a trailing _ (an operation delete is delete_); the handler map (create-pack --index)
keys each by its operationId as the spec writes it,
in src/semantics/<family>.ts, the file of its operation's family. An operation's family is the first segment of its
path after a version (/v1/organizations/{id}/invitations is organizations; a bare number is a version too, X's
/2/users/me is users and /1.1/oauth/invalidate_token is oauth), a dotted RPC method's first part
(chat.postMessage is chat); an operation named in a header, whose path is /, is the family operations, and a
line protocol's commands are the family commands. (the board's create-pack card moves the rule to the spec's own grouping,
written by derive-pack.ts on each operation: a new version of the standard.) A
family named like a fixed file of semantics/ takes the suffix -ops (seed-ops.ts). A handler is written for an
operation the core and the states cannot serve alone: a rule across resources that no declaration expresses, a
computed answer, a flow. It holds to one contract:
- It reads state only through
ctx.rowandctx.rowsRaw(a row as stored),ctx.rowsandctx.get(as the vendor serves it),ctx.history,ctx.resolve(the id a subject has now) andctx.expand, and writes it only throughctx.write,ctx.createorctx.change; the bookkeeping the vendor never serves (a key's hash, an email sent) only throughctx.record('_<type>'). A lane over its vendor's state reaches the vendor's resources throughctx.over(<the vendor's manifest>), the same contract over that manifest.ctx.now,ctx.core,ctx.atomically,ctx.tree,ctx.writeDetailedand the tree's locationctx.rootare the kernel's and not a handler's: a handler is typedHandler, overHandlerContext(@volter/world-core), which does not carry them,semantics/index.tstypes its mapRecord<string, Handler>, andsemantics/never names the kernel's wideSemanticsContext,SemanticsorsemanticsContext. The type holds what a handler can name, not a cast: a cast is review's to refuse. The manifest's write hook reads the writing call throughcontext(), aWriteHookContext: the same contract withoutwrite,create,createUnique,change,issue,record,accumulate,atorover, so rendering an event never writes again. - A handler creates a stored occurrence through
ctx.create(resource, fields, operation): the kernel allocates its id and appends its fields under the same cross-process action lock.fieldsmay be a pure function of that id, so references and generated wire identifiers are bound to the allocated occurrence. A conditional update isctx.change(resource, id, decide, operation):decidereceives the current live stored row under the lock and returns fields, or undefined to leave it as it stands. Neither callback performs I/O. Both use the ordinary write's recorded vendor input, views, counts and notifications; a write hook cannot call either.ctx.issuerecords an occurrence the vendor issues and does not serve (a_bookkeeping row) under that same lock. Need: OpenAI's Files uploads, Responses and completions must have distinct identities for simultaneous requests, and a background completion must be claimed once (spec FunctionToolCall.call_id: "The unique ID of the function tool call generated by the model."). A blob is placed under the created subject's distinct key before success is answered; missing bytes are refused, never represented as a successful empty file. ctx.record(type, fields, id?, operation?)can name the operation that changes a bookkeeping subject. When named, the kernel observes its committed state move against the handler's legality checks, without recording the credential-bearing request. Its unnamed form keeps the bookkeeping record operation. First need: X's single-use authorization code, rotating refresh credential and OAuth 1.0a request token must report the exchange that consumed them, rather than a synthetic record operation. X's OAuth flow and OAuth API reference own those exchanges.- A write that moves a state field first asks the machine (
ctx.legal) and answers its refusal when it gives one; the machine instates.tsis the only place the move is allowed. - Ids come from
ctx.mint, which mints the next id of a declared resource from the subjects the tree holds, so it gives a new id only once the last one is stored; an id the vendor issues for what it does not keep (a completion's, a message's, a request's) isctx.issue(resource), which mints it and records its subject in the one step, so the next is another (a read-only request mints without recording). Instants come fromctx.occurredAt(andctx.atfor a move time made), the twin's own URL fromctx.publicBase(as this request reached it); a secret the vendor issues (a key, a client secret, a token) fromctx.secret(label), an HMAC under a seed the World makes once at random, and a key it signs tokens with fromctx.signingKey(label), an RSA pair the World makes once at random, never a value computed from ids or a key written in the pack, which anyone who reads either could reproduce; a credential the World's application holds is issued by the pack's credential door (the descriptor'scredentialDoor), which answers the same credential at every boot; a message to the application's own server the vendor decides by its answer (Stripe's real-time authorization request) throughctx.ask(url, init, within), its status, headers and body within the window or why there was none (the headers' first need: QStash fails a message at once only on a 489 withUpstash-NonRetryable-Error: true; a life'sapplicationanswer declares them asheaders). A pack's fetch given anapplication(PackFetchOptions) asks it first, within the same window: an answer it gives is the application's,unreachableis a question nobody answers, and a question it does not take goes on as any other, through the World's application route, its egress rule and the network. A walk's stand-in takes every question (its life's answers, else unreachable). A hosted World's router (world-runtimeWorldDoors.askInWorld, which apps/cloud gives every twin) takes only what the World can answer truthfully: a GET or HEAD of a URL whose hostname one of its twins serves (itshostsClaimed: a Cloudflare twin's Worker route or custom domain), answered as a browser reads that site in the World. Any other question, a POST to such a host included (Stripe Issuing's authorization, Slack's slash commands, QStash's delivery), goes on exactly as without the router, to the application or the network as the egress rule allows; event delivery never pass through it. First need: X's link card for model-editor.videogame.ai, a site the launch World's Cloudflare twin serves. A walk proves the asking twin's side by its life'sapplicationanswer for that URL; the router itself is the World's site door, driven in a hosted World; a git repository of the World isctx.git(name), its objects in the World's object store and its refs in its store, over the kernel's git library; a mail the vendor sends a person (GoTrue's links) goes throughctx.mail(route, mail), over SMTP to the route the vendor's settings name under the same egress rule, throwing when it cannot be delivered; randomness, the wall clock, the environment, the network and a module loaded at run time are never reached. A vendor's call to another vendor as part of its own behaviour (Clerk trading a Google sign-in's code at Google's token endpoint) isctx.vendorFetch(<the vendor's own URL>), which the kernel answers by the World's twin of that vendor (the host rules the injector routes by, the World's twins key) and refuses where the World runs none (VendorUnreachableError): a twin calls twins, never a vendor, and never reads another vendor's store (A3). A URL the vendor imports may also name the World's application:vendorFetchuses its declared application route (or the journey'sapplicationstand-in), with the same response bytes. An unregistered host is stillVendorUnreachableError. Redirects are resolved one hop at a time through these same routes, never followed directly by the network client. The shared redirect follower used byappFetchalso servesvendorFetch: each supplies its own per-hop route/refusal, while method rewriting, body removal, credential stripping, cancellation, redirect mode and hop count have one implementation. All failures while resolving or delivering a vendor URL (including malformed destinations, forbidden schemes, redirect limits, route/egress refusals and transport errors on any hop) leavevendorFetchasVendorUnreachableError, preserving the original cause. This is the vendor-call boundary;appFetchretains Fetch errors. First need: a Tinybird URL import must become the documented failed import job when any redirect hop cannot be fetched, rather than escaping its import error handling. The stand-in is the application's file response, never a successful mutation of another vendor's store. First need: Tinybird's URL imports, whose reader supplies a CSV at a URL; the published examples' file is held by the World's application during replay.
A twin this process answers itself comes
first: a journey walk's companions (journeys), registered in process for the walk (setInProcessTwins)
and cleared after, so an in-memory walk, which has no instance, reaches them; otherwise the instance's twins.
- A refusal is the vendor's: its documented status, code and message; where the vendor documents none for the case, its documented error for that class with a message stating the rule, and a comment saying where the documentation stops. A success is the operation's documented answer, which SHAPE checks on every walk.
- Every rule it writes (a refusal, an effect on another subject, a default) carries its citation beside it, a comment
line
// source: <citation>; a citation is a transition's: a pagecheck-sourcesfetched and found, orspec:<operationId or /json/pointer> "<words the spec says there>".check-sourcesfetches the pages these lines name, andderive-packrefuses a line whose citation does not hold. A handler's<url> "<quote>"has its quote looked for on the page (a transition's URL citation is checked for the page alone), and a quote in any other form is refused. - It imports the kernel's types, the pack's
statesandshared.ts, and nothing else. A write is a call, not a returned decision: a handler makes several writes and cascades in one operation, each through the same door. - A handler that only does what the core would do is a defect in the core, fixed there, and the handler deleted.
Citation collection covers every handwritten TypeScript file beneath a pack’s src/, including its screens and engines; generated source and test files are excluded. Source recording and derivation use the same collector, so a hosted flow’s vendor rule receives the same evidence check as a REST handler. First need: GitHub’s OAuth PKCE rule requires S256; a citation in its OAuth screen must be fetched, recorded and validated before derivation.
shared.ts holds only what two or more of the pack's own handler files use and no other pack would; the same
contract holds for it, doors.ts, seed.ts and around.ts, with no exception: no file of a pack reaches a
module but the kernel's root, the pack's own files and @volter/world-ui (the grade's kernel-contract), and no
file waits on a mechanism the kernel does not have (an // awaiting: marker fails handler-contract). What a pack
cannot write without such a reach is a kernel mechanism to build first. Tests are semantics/<file>.test.ts
beside the file they test and screens/<id>.uitest.tsx, driving the pack's fetch as a client would; the types of a
vendor SDK the tests drive are src/<name>.d.ts. A pack's proof is its life and its examples.
Every semantic has one home. What a vendor does beyond an operation's plain effect is written in exactly one place, and a pack writes no other code. Each row says how the form is held today: by the kernel's types, by the grade, or by review until the named work lands.
| What | Home | Written as | Held by |
|---|---|---|---|
| operations, shapes, plain create, read, update, delete, list | src/generated/ | generated from the spec | the grade (derived-reproducible) |
| the vendor's facts: errors, auth, ids, time, paging, idempotency, versions | manifest.ts | data | the grade (manifest-data) |
| who a credential is (its actor) | manifest.ts | data the kernel reads; until the kernel reads it, no pack declares one | the grade (manifest-data: no function in the manifest) |
| the hosts it answers on, its SDKs and environment names | manifest.ts's descriptor, which index.ts registers (packOf(manifest)) | data | review |
| resource rules that recur across vendors: a setting read and written at a path of its own, a delete's cascade, a count of children, a key-by-key merge, one subject per pair, a lookup by an alternate key, a list's order, search and filters, a create that makes a companion, a boolean parameter's spellings | manifest.ts, on the resource | data the kernel executes, served by the core and by the context: setting (a setting held once per parent at a path of its own, its GET and PATCH served by the core: its defaults until first written, an update merging and making it), cascade (ctx.remove), unique (ctx.conflict), update: 'deep' (ctx.merge), alternateKeys (ctx.find), orderBy and search (ctx.list), counts (a parent's count of its live children, excluding a child when any vendor field matches unless (so a refresh needs no private presence marker), with parentKey when the child's field names the parent by another vendor field rather than its stored id, and with | |
present and plus for one past the numbered ones: kept by the kernel on every child's write), and on the manifest the vendor's CORS (cors, served by withCors around the pack's fetch, a preflight to any path answered), a parameter's boolean spellings (booleans, ctx.flag) and a strict body (body.strict, ctx.fields: an undeclared field, a value of another type or a number outside the spec's bounds refused in the vendor's words); a list's filters from the spec's query parameters and a companion create wait on declared-semantics | the kernel; a rule it does not execute yet is a handler rule under the contract, unmarked | ||
| the events the vendor sends: which write sends which type, with which payload, signed by which scheme | manifest.ts (events), and semantics/events.ts for the payload | the manifest's events (world-core events.ts): the write's kernel operation to the vendor's event type, or to several (a Slack message sends message to the apps subscribed and app_mention to each app it mentions; the pack's values withholds a type the write does not warrant with $send: false), the envelope as a template ($type, $data, $id, $time.ms, $time.s, $time.iso, $request.client_ip, $request.user_agent, $endpoint.<field> filled for each endpoint it goes to, and a ... key spreading a value into its object: GitHub's body is the event's payload with the installation it goes to, { '...': '$data', installation: '$endpoint.installation' }), the scheme, or several each adding its headers (webhook-id: a message id, timestamp and signature header under the vendor's prefix, Svix's and Standard Webhooks'; timestamp-v1: one t=…,v1=… header, Stripe's, its key named by version where it is not v1 (ElevenLabs' v0); or one hmac header over the body, a {t}/{body} template of it (Slack's v0:{t}:{body}) or ${t}.${body}, SHA-256 or SHA-1; or one hash header, a plain digest of a {secret}/{body} template, HubSpot's v1 X-HubSpot-Signature over {secret}{body}, in hex, base64 or base64 without its padding (E2B's e2b-signature); or one secret header carrying the destination's secret as it is, Cloudflare Notifications' cf-webhook-auth), further headers as templates (GitHub's X-GitHub-Event, with $endpoint.<field> for the endpoint's own), where the World's endpoints are stored, one kind or several (live by a flag or a status value, their fields dotted paths into the row, taking types by a list with * and <family>.* patterns, by scope: whom the event is about, Stripe's Connect endpoints, and by match: fields that must equal the event's values (or be one of them, when the value is a list: the apps a Slack message mentions), a GitHub repository's hook its repository's; several matches, any of which takes the event, and a list field taking an event whose value it holds — a GitHub App's installation takes its own events, every event of its account when it covers all of the account's repositories, and a selected repository's events when it lists it — an endpoint kind limited to some event types (types) or leaving some out (skip), so a kind of the same rows takes one event under a match the others do not need (a Slack app is sent app_mention only when a message mentions it, every message its subscription names), with joins naming a related row an endpoint's fields read through, a GitHub installation's URL, secret and events its App's) and where each delivery is recorded; a vendor that names every write's event by its resource lists its types (known: <resource>.create sends <resource>.created), and one whose API serves its own events keeps each as a resource (store: Stripe's GET /v1/events), delivered or not. The kernel renders, stores, signs, delivers through the World's application route and egress rule, and records. semantics/events.ts exports data(ctx, write, type) over the write hook's read-only context (the type each event of the write is rendered as), bound as events.render, when the vendor's payload is not the written object, and values(ctx, write, type), bound as events.values, when the envelope carries more of the vendor's state than the write (the account an event is about, the API version it is rendered in; $send: false keeps the event but sends it nowhere). A message the vendor sends the application and decides by its answer is ctx.ask (its status and body within a window), not an event. | the kernel (the scheme, the store, the delivery, the record); the grade (manifest-data: events is data and its render and values the imports of ./semantics/events.ts; handler-contract: events.ts exports only data and values) |
| what the vendor does around every request to its API, one its surface names or not, that the manifest cannot state (a key checked before routing, a plan's limits, a quota counted after a success, a header the vendor adds) | semantics/around.ts | one exported around(ctx, next) over the contract's context (its operation the request's, or unmatched, whose params are still the path's template parameters, read from the most literal route of any method the path fits: a CORS preflight names its bucket without the pack parsing the URL), next() answering the request, or next(request) the vendor's own rewrite of it (a query token moved to its header); createPackFetch runs it | the grade (handler-contract: its one export) |
state machines and state rulings, the moves the clock makes included (actor time) | semantics/states.ts | data; the kernel runs a clock move when a request next arrives | the grade (states-module); the kernel run is declared-semantics |
| tokens and keys: an instance's signing key, a JWT, a webhook secret | the kernel, on the context (ctx.crypto, world-core signing.ts); the key itself is the pack's data (its PEMs or its secret, in shared.ts) | jwtSign (RSA or Ed25519 (EdDSA, Better Auth's default) with the pack's key, or HMAC with a secret, SHA-256 to 512; no ECDSA, whose signatures are random), jwtVerify, jwtDecode, jwks, keyId, hmac (SHA-256, or the SHA-1 or SHA-512 a vendor names, over a secret's text or its bytes), signWith (a private key's signature: RSA or Ed25519, never ECDSA), verifyWith (any scheme's signature, under a key or a certificate), publicKeyOf, certificateOf (an X.509 certificate's names, validity, key, and whether an issuer signed it), sha256, md5 (an ETag), digest (any hash a vendor names, in its encoding), uuidFrom, lettersFrom and base62From (an id derived from a seed), equalSecrets: one deterministic implementation for every pack; a credential the account must hold (a token its Tokens page made, kept by its SHA-256) is the manifest's auth.held, checked by the gate, and a vendor that checks it before routing calls authRefusal in front of its dispatch | the kernel; the grade (kernel-contract: no pack reaches node:crypto) |
| AWS-style request signatures and payload integrity | the kernel (sigv4.ts), used by each AWS-shaped wire | verifySigV4 authenticates the canonical request and checks any ordinary hexadecimal x-amz-content-sha256 against the exact received bytes before returning success. A signature over a claimed hash does not prove that those bytes arrived. UNSIGNED-PAYLOAD remains the explicit S3 opt-out; streaming payload markers follow their separate protocol and this rule makes no chunk-verification claim. The pack owns credential/policy scope and vendor error wire, never a second signature implementation. Intent and evidence: ADR 0008. | |
| a genuine algorithm: a flow, a computed answer, a rule no declaration expresses | semantics/<family>.ts | a handler, under the contract | the grade (handler-contract, layout) |
| state beside the tree a handler reads or writes (a project's SQL database, an object's bytes) | the kernel, on the context; a vendor's own server in front of the database (a PostgREST, a GoTrue) is the pack's engine slot (src/engine/, the engine slot) | the World's managed Postgres as ctx.engine (world-core managed-database.ts: batch — one transaction as a role with its settings, one Sync — and script), bound from the call's scope (database, the descriptor's managedDatabase, the fixed CLI's --database), on node-postgres loaded the first time one is bound (installed by the pack's package.json; the kernel's dependencies stay B1's); an object's bytes as ctx.blobs; the vendor's server over managedDatabase(url). A handler imports the pack's engine modules (../engine/<file>.ts) directly: they are the pack's own computation and nothing else — they take no context, read and write no state and make no answer (what does is a handler's, in its family's file or shared.ts), reach nothing of semantics/, screens/ or the manifest, and reach none of what a handler may not; a handler hands them what it read and makes the answer from what they compute | the grade (kernel-contract and engine-pure on the engine; handler-contract: a handler imports no module but these, its states and its shared helpers) |
| a twin's own act standing in for a dashboard page, and a delivery the vendor receives (an ingest) | semantics/doors.ts | a function per door, under the contract; declared in the manifest once create-pack gives it doors | the grade (the contract); the declaration by review |
| default data | semantics/seed.ts | data: calls to the vendor's own operations | the grade (the file's name, and the contract); its being data by review |
| a query language, an SQL engine, a stream or socket frames, anything not HTTP-shaped | engine/ | the engine slot | review |
| a live token's rate limits | budget.ts | data (D8) | review |
| the vendor's UI | screens/<id>.tsx | built from @volter/world-ui | the grade (layout: one per declared screen, named by its id); its content by review |
| which of the vendor's things a person keeps open, and where each is seen (the board) | semantics/board.ts | one exported board(ctx) over a read-only context, returning the frames from the state; createPackFetch serves it at GET /twin/board | the grade (handler-contract: its one export); its choices by review |
The pack contract exports verifyAttestationBundle for signed Sigstore bundles against explicitly supplied
certificate roots and transparency-log public keys. It uses Sigstore's offline verifier to check the signature,
log body, authenticated signing timestamps, inclusion proof and certificate validity, with at least one log witness.
The bundle schema and offline verifier are protocol libraries installed by the pack and lazily imported on first verification, as B1 requires; kernel tests declare them only as development dependencies. No TUF state or network client is opened by importing the helper. World-issued certificates have no CT service;
their explicit World trust policy requires Fulcio and Rekor evidence without an SCT. This is simulated semantics
only: the real head sends the publish to the real registry, whose own Sigstore policy remains authoritative.
First need: npm's publish verifier
compares the package subject and digest, then delegates bundle verification to Sigstore. Checking only a DSSE
signature would accept a removed or corrupted transparency entry.
A rule a handler writes that a row above gives another home is outside the form once that home exists: the
declaration is used, and where the kernel lacks it, the kernel gains it (the board's declared-semantics card) and the
handler's code is deleted.
Rejected: a handler that returns a write decision for the core to apply; a legacy fallback serving any operation.
Where the evidence stops: the grade checks the contract in the source (handler-contract), which catches what it names and
nothing a check does not, and the compiler holds a handler to HandlerContext's members. derive-pack refuses a
// source: line whose citation does not hold; that every rule carries one is held by review alone. The family
written on each operation, the fixed files' generation and the manifest's hosts, doors and events as data are the board's
create-pack and declared-semantics cards; a handler's imports of the kernel limited to its types waits on the pack-facing
SDK boundary (a card on the board, the platform release's dependencies: what a pack may import from @volter/world-core). Until
each lands, what it would enforce is held by the pack's review.
Attestation
A judgment is an attestation: a life's plausibility, a transition's source, a record that code or a move is unreachable, the review of a semantics change. Each is keyed by the hash of the content it depends on and is stale when that content changes:
| Judgment | Depends on | Stale when |
|---|---|---|
| a life is plausible | the life | the life changes |
| a transition's source holds | the transition and its source | the transition changes |
| code or a move is unreachable | the record and what it names | either changes |
| a semantics change is right | the changed transitions and handlers | a later change touches a transition or handler it covered |
A change to a pack's code does not stale plausibility. A spec update is diffed per operation, and only new or changed operations need a judgment. The mechanical checks (replay, spec validation, invariants, the report) rerun in full on every change and call no model.
A change's attestations ride in its changeset (.changeset/) and reach its package's CHANGELOG.md
with the release. Each carries the change's checklist (the core regenerated, with the spec's hash; every
handler and transition bound to an operationId; the report and its change; the life judged;
transitions sourced; checks held; what was not checked), the author's signature and an
independent reviewer's, from different sessions. The tool stamps model id, session and time; the
agent signs only its judgments. An attestation is a record, never a merge gate.
The share of a vendor's operations classed crud, and so the saving, is unmeasured until the
generator classifies a pack; the index then publishes it.
Where the evidence stops: each of the six packs built to Protocol 3 (openai, github, stripe, slack, aws Secrets Manager, smtp) has one life, and all six verdicts are self-judged by the session that wrote the lives, and say so: no judge in another session has ruled on a life yet. No change's attestations have reached a changeset.
Creating a pack
A Protocol 3 pack is created from the vendor's spec, in twin-packs-p3, never ported from an older pack: Protocol 3 is a new way to
create twins. Where an older pack of the vendor exists, the new one is
created beside it and replaces it whole, as a major version. No implementation or frontend is read or copied from
an earlier pack: screens are authored from the vendor's documented flow and the measured demand. Everything is written
from the spec, the vendor's documentation and the measured demand. The spec gives every operation, every shape, and plain
create, read, update, delete and list through the derived core, with nothing written by hand; the author writes only
what the spec cannot say: the vendor's facts (manifest.ts), its states (semantics/states.ts), its doors, and
handlers where an operation does more than the core and a state move can (semantics/<family>.ts). A handler that only
does what the core would do is a defect in the core, fixed there.
A vendor whose APIs are all in its lanes may keep root spec/sources.json for citations in its
shared helpers and descriptor. Metadata alone is not an API surface: create-pack and the form
gate recognize an own surface by its actual derivable spec or generated surface, not by the
presence of a spec/ directory. First need: Sentry's shared default-feature citations.
Where the work happens. twin-packs-p3 is checked out beside twin-world (on the orchestrator's box:
~/volter/twin-packs-p3 and ~/volter/twin-world); each entry of its node_modules/ links into that twin-world
checkout's (twin-packs-p3's AGENTS.md, Setup), so a pack runs the source of the checkout beside it, and every tool below is twin-world's, run from the packs repository's root with the unit
named relative to it (polyhaven, polyhaven/api), so a tool never resolves a path against the wrong checkout. A
unit is a directory holding src/manifest.ts: a pack, or one lane of a pack of lanes. T below is the twin-world
checkout (bun $T/scripts/derive-pack.ts polyhaven/api).
The cadence (AGENTS.md): while code remains to be written the one test is the journey walk (the life, the published examples and their coverage); nothing else is tested, judged or reviewed, but for the regression review after every 30 commits of a build. The other checks run once, when the slate is written (step 11).
-
Choose the vendor.
generated/DEMAND.md("Our products without a P3 twin") is the order: the demand scan (bun $T/scripts/demand-scan.ts --offline, rereading cached clones in~/.cache/twin-demand, or$TWIN_DEMAND_CACHE) reads the productsdemand/applications.jsonlists (only those volter.ai names, owner 2026-09-30) and finds each vendor by a P3 pack's facts or, before it has one, bydemand/vendor-identities.json. A host no identity names is listed unmapped until one does (orignore.hostsnames it). A confirmed first-party service whose hostname is under an application-owned domain declaresownService: truein its identity, so the scan counts that explicit claim before excluding ordinary application traffic. An identity is ruledretiredfor a discontinued service andnotDemandfor one our products name only where nothing calls it at run time (a release script, a mock placeholder), each with its reason: neither is built. AnotDemandstring rules the vendor globally; a repository-to-reason map rules only those applications, retaining other applications' demand. AdemandedByruling records a product use the application scan cannot see (such as people using the launch World's vendor pages): each entry namesowner(volter,customeroradoption),productandsourceevidence. It contributes that caller to generated demand alongside scanned calls, without adding or advancing a scanned repository. Keep these rulings when pack facts replace an identity's detection fields. Env evidence matches a vendor stem only as a whole variable-name prefix segment, after removing a framework's public prefix, and only when the name identifies that vendor's credential or endpoint. The longest matching stem owns the name; neither substrings nor unrelated suffixes count (X_FRAME_OPTIONSandX_CAL_SECRET_KEYare not X credentials). Credential and endpoint roles may have auth, transport or service qualifiers; feature flags, model choices and resource settings alone are not calls. Exact stems count only when they themselves name a credential or endpoint; explicit descriptor credential/endpoint wiring also establishes the role. A vendor platform's CI/runtime metadata (GITHUB_OUTPUT,GITHUB_RUN_ID,GITHUB_REPOSITORY,GITHUB_ENV,GITHUB_STEP_SUMMARY, Vercel deployment URLs) is not API demand. A fixture or mock website URL is never a call; Packs declare these detection rules in their manifest descriptors; their screens still declare the pages they serve. A host rule's optionaldemandPathPatternrestricts detection independently of its routingpathPattern;falseexcludes it from detection. Without it, detection readspathPattern. A path claimed for routing but excluded from detection is not an unmapped vendor host. Regenerate pack facts after narrowing a descriptor's detection hosts. Unclaimed production hosts remain gaps for every scanned application, including adoption products. First need: Twenty's default brand-image lookup has no twin; dropping adoption hosts concealed it. Claimed and unclaimed hosts use the same production-file gate and comment-free source, with original line numbers. Repository maintenance, release, translation, reference-fetching and build utilities, including helpers reached only by those utilities, supply no runtime request evidence. Server, client, job and shipped CLI sources do; a package entrypoint or an import from those sources retains a runtime module even under a scripts directory. Auxiliary package tools without such an entry or caller are excluded. Resolve that graph with the application's ts/jsconfig paths, base URL, inherited configs, workspace exports and statically declared bundler aliases, including re-exports, CommonJS and dynamic imports. A helper shared by a runtime entry and maintenance remains runtime evidence. Client addresses inherited from a base class or passed through a factory count only when traced to a request sink; keep both the literal's and request's source locations. Caller-scoped owner exclusions may rule a vendor without a pack whose sole non-optional caller is that application. Recompute the class from current callers: another demanded caller or a pack prevents that exclusion. First need for comment removal and fixture suffixes: adoption hosts otherwise reported end-to-end fixtures and Storybook mocks as runtime gaps. A pack's or a run's change always scans with--offline. On a machine with an empty cache, first runbun $T/scripts/demand-scan.ts --fill-cacheonce: it fills missing clones at the commitsgenerated/DEMAND.mdnames (recorded ingenerated/demand.json), never the applications' newest heads. Existing clones stay at their recorded commits, and a mismatching clone is refused. Filling the cache writes no generated demand and is not an advance. Then run--offline; a missing clone is refused before incomplete demand can be written. An online scan without these flags advances the scanned commits as its own change, re-reading each pack's evidence. -
The fixed files.
bun $T/scripts/create-pack.ts <vendor> --init <vendor>, or--init <vendor> --lanes a,bfor a vendor of several APIs or hosts (Cloudflare's API and R2, E2B's API and each sandbox's envd, Poly Haven's catalog and its download hosts). Each lane has its ownspec/, manifest, fetch andsemantics/(its ownstates.ts); what the lanes share lives in the vendor'ssrc/semantics/shared.ts, which each lane'ssemantics/shared.tsre-exports, and all lanes keep the vendor's one store (their manifests name the vendor'sservice; a lane that is an independent service of its own, as Sigstore's Fulcio and Rekor are, names<vendor>-<lane>). The vendor'ssrc/manifest.tsroutes requests to lanes (lanes.routes: by host, path prefix or header). The scaffold writes the descriptor'sprotocol: '3', the platform's protocol (the plugin contract). -
The spec. Vendored in
spec/(a lane's in<lane>/spec/) as the vendor publishes it, under the namederive-pack.tsreads (openapi.json,openapi.yaml, either gzipped as.gzwhen large;smithy.json; a GraphQL schema;rfcNNNN.txt), withSOURCE.md: the file, its upstream URL (and commit), its version, the sha256 of the vendor's bytes, the corrections, and who read it. A proto, Smithy or Discovery document is converted with$T/scripts/spec-{proto,smithy,discovery}-to-openapi.ts. A vendor that publishes no document gets one written from its reference pages and the host's answers, andSOURCE.mdsays so and lists every page and answer read (Expo, Tripo, ambientCG, Hacker News from its API README, Telegram from its Bot API page). The kernel's converters read a standard format; one vendor's own pages are never a kernel script, and a pack keeps no converter in itsspec/: the spec written from them is the vendored document. Correct a spec only by a patch with evidence.The Discovery converter keeps the vendor's contract: one OpenAPI operation per method's id, using its flatPath when available to distinguish collections that share a reserved-expansion template, request and response references, and component schemas. Schema fields with OpenAPI equivalents are translated; other Discovery metadata is retained as x-discovery fields. Each flat-path label keeps its original Discovery parameter: one reserved resource name can expand into several labels, each binding its part of that name. Upload protocol routes bind their own labels against the same parameter contract. A segment that can contain slashes uses the existing manifest spanning declaration (Routing a spanning parameter). The retained Discovery bytes and the explicit fetched date reproduce the converted document. Gemini is the first need: its models, dynamic and tunedModels generateContent and countTokens methods share reserved templates, while the inventory-only conversion discarded the schemas needed to check their answers.
-
The demand,
journeys/demand.json:measured(when and how the applications were read, and what was ruled out and why), and per application itsname,commit,client, and each call inbackend/frontendas{ call, operation, evidence: "<path>:<line>" }, the operation by the spec's operationId (or themethod_pathslug derive mints where the spec names none). Read each application at the commit the scan cloned (~/.cache/twin-demand/<owner>__<repo>). Only what the running product calls counts; the webhooks it handles and the UI flows it drives count too. It decides what the outline must reach and what the pack models. What it runs as it ships is the demand, and so is the path its card proves (boot and sign-in in a World); a provider it uses only when someone opts in is listed inoptionalas{ application, what, setting, evidence }, the setting that turns it on and the line that reads it, and is outside the demand. Before a vendor has a pack, its identity'soptInmaps application repositories to the setting and file:line evidence; the scan marks them (opt-in) as it does a pack'soptional. An owner's exclusion for an application is a separate repository-to-reason map,excluded, keeping the owner's words; the scan marks that application (excluded), outside demand, without claiming an operator gate. A vendor served by a World backing has an identity'sbackingdeclaration, also carried in generated demand JSON. Server-backing facts without that declaration refuse the scan; the report printsserverand omits these vendors from the missing-P3 count. Temporal's workflow server, MongoDB's native server and PostgreSQL/PGlite's managed database are such backings; driver and environment evidence still identifies the applications using them. Volter's own products are demand in full: every connector or integration they ship counts, whatever its setup default, because they run whole in Worlds. A runtime such a product pins and installs (Hermes for Open Autonomy) is demand as far as the product's configuration enables it, plus its defaults on the paths the product runs; what it does only when switched on, or what the product's allowlist excludes, is named inmeasuredand not served. -
The outline,
journeys/customer-life.outline.md: the customer's story in acts, each mapped to the operations and doors it uses, reaching every demanded operation and every refusal the demand can meet (Writing a life, stage 1). At this stage filljourneys/first-use.jsonfrom the demand-pinned official SDK and vendor quickstart, using the app's normal environment names, default selections, result assertions and read-only retained-state readback. For each act, name the identity, records and values it needs and the earlier API call, issued credential or documented synthetic input that supplies them. Use returned account and resource identifiers, never guessed identifiers; a credential opens an account but does not create the records a later call needs. A prerequisite API call belongs to this story's scope and decision table even when the quickstart omits it. Walk this customer entry while implementing, as described below.create-pack --initwrites an unfinished entry for a new vendor, including a vendor with lanes: its empty commands cannot pass the installed workflow validator. The author supplies the vendor facts and client code; a new pack does not silently inherit the standard's fallback for older immutable releases. -
The manifest and the states.
src/manifest.tsholds the error envelope, auth, ids, time, the resources the outline needs, the doors, the events, and the descriptor (hosts, adoption,credentialDoor: the door that issues the World application's key and the env names it fills, each from a field of its answer, orfile:<field>for a credential the application reads from a file, written beside the World's instance file and the name set to its path);src/semantics/states.tsholds every state machine and everynotStateruling, each transition cited and each a rule the twin executes (an ask naming no target takes the first transition that allows it; a refusal is its own transition,fromwithrefusaland noto, becausectx.legalthrows on an undeclared move). Declaring a resource hands its plain reads and writes to the core; every operation of a declared resource the core cannot serve faithfully gets a handler or is listed inunmodeled, which answers the gap. The error template's{status}is the number,{statusText}the same as text,{kind}the error's type (a vendor whose HTTP status differs from the one its body names declareserrorsAnsweredAs).answerHeadersare headers every answer carries; a value's{hex:N}is filled per answer from the World's count of such answers, kept in the World's own directory as bookkeeping (never a log entry), read and written under its lock (OpenAI'sx-request-id,req_and 32 hex: every answer its own; a resumed World counts on and never answers an earlier value written to the count; two Worlds made alike, with the same service and root, answer the same values in the same order; a World with no root, or a read-only request, which writes nothing and reads the count without its lock, counts on in its process past the kept count, so the answers given to read-only requests since the last write may repeat after a restart or from another process serving the same World; two writes never take one number), unless the handler set the header itself (a stored completion'srequest_id); a string template is a text or XML body.ids.templatecounts ({prefix}_{n}) or mints:{uuid},{letters:N},{hex:N},{ksuid}, each after the resource's prefix too ({prefix}{letters:16}), or after it and the separator the vendor's type fixes ({prefix}_{ksuid}, Clerk'suser_2…;{prefix}_{hex:48}, OpenAI'sresp_67cc…;{prefix}-{ksuid}, OpenAI'sfile-…), and{snowflake:E}.{ksuid}is a KSUID, the 27 base62 characters Clerk's and Svix's ids carry (user_2…): the World's instant in seconds past KSUID's epoch, then the type's mint count, then bytes derived from the resource and that count, so ids made in one frozen second still sort in the order they were made (a list whose order ties on two made in one instant keeps them in the order its rows arrive: the core's reads them in the tree's order, the order they were written, which is their creation order under every template, a hash-derived{uuid},{hex:N}or{letters:N}id included;ctx.listkeeps the order of the rows the pack passes it, the creation order when those arectx.rows; first need: Clerk's pending invitations, two made in one instant, "Most recent invitations will be returned first";{ksuid:N}at a vendor's own length, OpenAI'sproj_24 orchatcmpl-29: its first N characters, the order kept from 11 on and a narrower template refused, or base62 from the seed past 27). An id's length is read from a real id the vendor shows (a response example, its server's generator); a placeholder (key_abc,file-xxx) gives its prefix only, and the id keeps the template's default width, its comment marking where the documentation stops; a resource's ownidsoverrides it. An id the vendor gives an object the pack never stores or declares (a tool call inside an answer, an output item, a moderation) takes the vendor's form for that object too, derived from what the call carries (ctx.crypto'ssha256for hex,base62From(seed, N)for the opaque base62 characters OpenAI'scall_andchatcmpl-ids carry), never a counter. A path's named groups (pathPrefix) and the host's (hostParams) join the call's parameters. On a raw row (ctx.row,ctx.rowsRaw)id,type,updatedAtanddeletedare the kernel's; a vendor field of one of those names is stored as the vendor names it and read throughctx.own(row). A file's bytes are the pack's resource blobs (ctx.blobs.put/get), the row naming them; never base64 in a row. A webhook is the manifest'sevents, never a manifest's events. A refusal the vendor does not document takes the vendor's documented error for that class, with a comment saying where the documentation stops. -
The decision table, then the blitz. Read and decide once, before any handler: per operation served, what it does, its state moves, its refusals with their citations, the stored fields it reads and writes (a work aid in the scratchpad). Its outcome is kept in the pack,
journeys/decisions.json:about, anddecisionsof{ operation, demand, mechanism: handler | core | door, why }(a door asdoor:<id>;lanenames the lane whose operation a row decides when its root shares the id). For an SDK call, the decision'swhynames the call injourneys/first-use.json's app files and the assertions for its consumed answer and stored effect. Before writing the handler, read the pinned client's request and response contract alongside the cited vendor contract: the envelope, required and nullable fields, nested default selections, pagination, typed errors and returned URLs that this act consumes. Decide where those values come from; declaring a field or returning a successful status does not supply them. The outline records the normal credential form, host and transport, and any companion this act requires. These facts belong to the existing outline, decisions and customer entry, not a second checklist or a broader served surface.create-pack --indexchecks door decisions (door:<id>, mechanismdoor) against that unit's manifestdoors(first need: Sentry's account bootstrap and credential issuance, which have no vendor operation ids). A declared door belongs to the unit declaring it when no lane is named; API ownership maps do not contain door ids. The checker holds it against the dispatch and the demand, and stops on an operation served and not decided, decided and not served, or demanded and not served (an operation no demand names answers the vendor's gap, and one the core would serve that the pack keeps from it is listed inunmodeled); a family file that exports anything but handlers stops it too. A missing kernel mechanism is built on its first need, in the kernel, before the pack that needs it, with its intent recorded in this document first (sockets, machine pools, the Anthropic wire, path and host parameters were each built so); a pack that works around one is drift. Then blitz: write each family top to bottom from the table, no re-deciding, ordering the families by the first customer act that needs them. Derive and index the first loadable act and walk it before writing the families that depend on its result. Construction usescreate-pack --index --building: implemented operations must still match their decisions, while unfinished operations remain gaps. This mode establishes no completion or release evidence. The ordinary--index, conformance and final completion keep the complete demand and decision checks. -
The handlers,
src/semantics/<family>.ts, eachexport async function <operationId>(ctx: HandlerContext)(a dot in the operationId is_:Process_Start), and doors insemantics/doors.tsexported by the door's id. Every rule carries its citation on the line above,// source: spec:<operationId or /json/pointer> "<words of the spec>"or// source: <url> "<words of the page>": the words as the rendered page reads them, no Markdown, backticks or typographic quotes; where the documentation stops, a comment saying so. The derive refuses a citation that does not hold and the linesp3-rules.tsnames (an id or number from a row count, the twin's URL built from the request,node:crypto, bytes in a row, a secret computed from ids, a handwritten delivery where the manifest's events belong); a line that is the exception records why,// p3: <reason>. Seed data through the vendor's own API wherever it has one; a door only for what the API cannot do. Scope is the life, the vendor's examples and the demand: nothing beyond what they reach. The demand, the life and a declared refresh's read bring an operation in; a published example is replayed for each operation served and never brings one in by itself (a vendor publishes one for nearly every operation: Stripe's spec carries 595), so an operation only an example reaches stays the gap. -
The life,
journeys/customer-life.json, from the outline:{ vendor, author, host, about, steps }, each step{ name, method, path, status, headers?, body?, capture?, expect? }. A path is relative tohostor an absolute URL of another host (a download host, a lane);capturenames a value for later steps ({name}in any string) by a dot path into the answer orheader:<name>;expectitems are{ path | header, equals | contains | notContains | matches | exists | members | oneOf },path: ""the whole body as text. A socket step, a line step and a SQL step on the World's managed database arepackages/twin-standard/src/walk/journey-kit.ts's. -
Derive, check sources, index. For each unit:
bun $T/scripts/derive-pack.ts <unit>(writessrc/generated/, refuses a citation that does not hold or a state candidate left unruled);bun $T/scripts/check-sources.ts <unit>(fetches each cited page and recordsspec/sources.json; run it before the derive when a new page is cited);bun $T/scripts/create-pack.ts <unit> --index(each lane, then the vendor: writessemantics/index.tsandfetch.ts); then load it once:bun -e "await import('./<vendor>/src/index.ts')". -
Commit. In the packs repository
git add <vendor> && git commit(only your own paths; its hook regenerates and stages STANDING.md); then in twin-worldbun scripts/pack-facts.ts, remove the vendor's detection fields fromdemand/vendor-identities.jsonwhen it had them (the pack's facts now name its hosts), retaining sourced rulings,bun scripts/demand-scan.ts --offline, and commit those paths. Nothing is pushed or published unless asked (publishing isscripts/pack-release.ts). -
When the slate is written, once: per pack
bun $T/scripts/score-pack.ts <vendor> --writewalks the life through the pack's fetch on a fresh World (every check, the second-World byte match, the shape), measures what the life and the vendor's published examples reach (Coverage, Published examples; what no World reaches is recorded injourneys/unreachable.jsonwith why) and the spec's breadth; every defect a check shows is fixed in the twin, never in the life's expectations. Then one judge per life (What a judge rejects), one independent adversarial review per pack, the grade (twin-standard grade <unit>), and the blind walk; their findings reopen the slate. Where an older pack of the vendor exists, the same change deletes it and carries a major-version changeset naming what changes for its users.
The customer path starts the build. Before writing semantics, use the demand's pinned official SDK and the
vendor's published quickstart to choose the entry into the customer life: the environment names the app reads,
credential identity, default SDK methods and their complete selections, required backing services, and the result
the person will inspect. This is part of the existing outline and decision table, not another life or a second
orchestration engine. Walk this entry while building through runConsumerWorkflow; it runs only the customer
journey, not the final grade or conformance slate. The public twin-standard walk-consumer <pack dir> --prepared-consumer <options.json> --out <walk.json> command invokes that same runner, so a builder does not need
to write an evaluator or run assess for early feedback. Prepare its app and options with prepare-consumer,
install their dependencies, and run the walk inside a World. Each changed candidate uses a newly prepared archive
and app; its report proves only the supplied entry, never the final grade or the whole customer story.
An SDK case preserves its default vendor URLs, agents and
selection sets unless the application's recorded demand itself supplies an option. A narrowed GraphQL selection
does not prove the SDK's default model works. A required field needs vendor-shaped stored data or a contextual
vendor read; adding its name to a served-field list is not an implementation. Undocumented starter choices are
identified as synthetic choices, never claimed as vendor defaults. Unsupported operations remain gaps. Shared
reference, count, cascade, refresh, transport and backing mechanics are built in the kernel on their first need,
before a pack relies on them.
When the entry exposes a defect, retain the failing SDK call and result assertion while correcting it. A defect in
a shared seam is fixed in the kernel with its regression case; the builder does not narrow the entry to avoid it.
Walk the SDK as the story becomes runnable. The executable entry follows the outline's customer, rather than using an unrelated create-and-list example as proof of the whole story. Its script grows with the acts that use the SDK: default response selections, related records, pagination, typed refusals, returned URLs or event verification are exercised when the demanded story uses them. It asserts the customer-visible result and the stored effect; an accepted HTTP status alone does not establish either. A model's labeled stub is asserted as a stub, never as evidence of judgment quality. Calls outside the demand remain outside the build. As soon as the pack can load and an act can run, walk that act through the unchanged client before building on its assumed result. Fix what the walk exposes before using that act's captures or stored records in later acts. The decision table still precedes its handlers, and each family is written from that table; this changes when the builder gets feedback, not the vendor's rules or the final verification cadence. A passing early act establishes only that act under its recorded client and platform versions. A later changed act is walked again; the complete advertised entry and exact artifact remain requirements of final completion. The outline's prerequisite chain includes API response values used by later calls, not only operation names. The entry asserts these values when produced and uses them in the dependent call. It keeps authentication, required resource creation and retained-state readback in the executable path rather than relying on a checkout's seed or an assumed account. A multi-vendor tutorial additionally walks its actual composition and World sharing sequence; individual pack evidence cannot establish those platform and caller steps. An app addressing a served World's URL uses the existing hosted attachment command, which binds that World's manifest, endpoints and access key before running the unchanged client. Shared-URL tutorials walk that attachment and the served wire response, including the identity the real-system root returned. The served attachment manifest supplies the scalar SDK credentials the World already issued for its selected services. Names come from the selected service's credential door, and values must belong to the boot's recorded World environment. Ambient env and sealed real-system credentials are excluded; the manifest does not issue a credential or mutate account state. Local file credentials keep their existing file workflow.
Diagnose the failing layer before repairing it. Retain the command, client and platform pins, expected result and actual refusal with the failed walk. First confirm the intended vendor selection and World attachment, then use the runtime's doctor, request log and twin manifest to locate the failure. A client-side credential refusal needs the vendor's structurally valid throwaway identity; a routing failure belongs to the descriptor or injector; an incorrect vendor answer or stored effect belongs to the cited semantics or shared kernel mechanism. Missing installed assets or dependencies belong to packaging, and failed boot, resume or teardown belongs to lifecycle. An evaluator that cannot locate or execute its own case has missing measurement evidence, not a vendor success or a reason to change the twin's answer. Repair the owning layer and keep the original call and assertion. Shared defects get the kernel regression case already required above, rather than repeated vendor workarounds. These are the existing builder journey walks. They add no suite, judge, per-commit check or remote trigger.
The private pack authoring repository's commit hook regenerates and stages STANDING.md only. Derivation remains
an explicit construction step; typechecks, conformance gates and the completion run follow the final verification
cadence. A commit does not start those checks or silently rewrite the pack's generated source.
The final customer evidence follows the installation. Source conformance and a plausible life establish their
own scope. Completion of an advertised installed workflow additionally uses the exact prepared tarball in a fresh
consumer directory without a private checkout or development links: install it and its declared companions, inspect
its generated facts, initialize the World using the app's normal environment names, boot, run the unchanged client,
inspect its stored result, stop, resume and read the retained result without reseeding, then stop with lifecycle
evidence retained. A stateless vendor records why retained state is inapplicable. The runtime owns setup and
teardown; the caller's existing example owns the API sequence. An independent pack supplies the entry in
journeys/first-use.json, whose files contain the pinned app SDK, normal env names and unchanged client calls. Its
recipe has schemaVersion: 1, source, about, a files map of relative paths to text (including package.json
with exact registry SDK versions and .env.example), an argv run, and either retention: { readback: <argv> }
or retention: { none: <why> }. Scripts assert the observed result; readback inspects prior state without
reseeding. A time-dependent entry declares clock: { at: <iso>, advanceAfterRun?: <duration> }: the normal CLI
freezes time before the client runs and optionally advances and inspects the due result before capturing retention
evidence. Resume and retained readback keep that clock; wall-clock delay is never used to settle a deterministic
result. twin-standard prepare-consumer materializes the fixture and its assessment options without installing or
executing it. The standard also retains consumer recipes beside its existing client cases as
clients/<vendor>.first-use.json for immutable releases that predate the entry; a supplied pack recipe takes
precedence. Source completion requires its authored journeys/first-use.json; deleting the unfinished scaffold cannot
satisfy completion through a legacy standard fixture. A new publisher does not need a private platform checkout or a new vendor-specific kernel path to
contribute its workflow. Dependency preparation materializes a new app and installs its SDK, the exact candidate
and the CLI's exact kernel peer before offline evaluation. The app has its own locked candidate and kernel;
an evaluator ancestor's node_modules cannot satisfy that installation. Registry candidates use their exact
version; a publisher preparing unpublished bytes supplies its tarball. The installed journey verifies those app-local
identities and the candidate integrity before starting compute. The catalog's global-CLI instructions explicitly
install the kernel peer in the app as well, including when npm's peer auto-installation is disabled.
runConsumerWorkflow then invokes the normal CLI sequence and retains each command's result. It
uses the installed @volter/world package's declared volter entrypoint. Dependency preparation does not select a
flattened .bin/volter, whose name can also be claimed by another installed package; the runner still verifies the
selected entrypoint's product package identity and actual tool versions.
The runner invokes a compiled JavaScript product entry through Node rather than depending on executable bits left
by a colliding dependency shortcut; it records that actual command and Node version. A source entry keeps its own
declared interpreter. Neither path replaces the product CLI or weakens its package identity check.
It verifies that init selected the candidate's exact package and version, that resume did not change the application
log, that readback added no mutations, and that final status reports stopped compute. A new installed-workflow
result carries artifact integrity, behavior-input and recipe hashes; an absent or failed result cannot establish
installed readiness. The release receipt binds the pack bytes, source identity, locked client, standard and
CLI/runtime versions, commands and result. A passing source walk, a declaration of required infrastructure or an
HTTP replay through Chromium cannot stand in for that evidence. Browser DOM, cookies and cross-origin application
behavior are claimed only when the advertised browser flow was actually exercised.
A publisher prepares immutable release bytes before qualification and evaluates those bytes in a clean dependency
scope with the released standard; upload sends those same bytes. The catalog independently evaluates the
registry-confirmed artifact. Qualification does not borrow source siblings, assets, SDKs or credentials from a
publisher checkout. SDK execution must produce at least one case result when a case module exists; zero results are
absent evidence and fail. These are requirements of the existing final run and explicitly requested publication,
not new per-commit or automatic test triggers. The completion command consumes that exact installed assessment
through --installed-assessment <report.json> and compares its behavior-input digest with the current source pack;
older source, missing lifecycle evidence or a different candidate cannot satisfy the new completion step. SDK
fixtures remain the standard's and publisher authority remains the catalog's. Trusted-publisher admission keeps its
existing authority rules; trust does not change what technical evidence proves.
The completion command validates that supplied installed evidence, including the current source package's name and version, before starting its final source slate. Missing, stale or mismatched evidence is an actionable prerequisite failure; it never spends a final run establishing source properties for a candidate that cannot satisfy the installed requirement.
A pack's standing is generated. Which packs exist, which are in this form, what each holds outside it, which our
products call and what is left to build are facts of the repositories, so they are written by a script from the
repositories and read from its file: twin-packs-p3's STANDING.md (scripts/pack-standing.ts: each pack and lane
against the grade's form, by @volter/twin-standard's gate) and whether a pack is done (scripts/pack-done.ts, whose
DONE <vendor> is the only definition of done) and this repository's generated/DEMAND.md
(scripts/demand-scan.ts). No document states them in prose: a README, this architecture, a board card, a log or an
agent's instructions names the generated file and never lists packs, counts them or says which are done. A
hand-written list was wrong within a day (2026-09-30: github, rebuilt on Protocol 3, stood in a list of packs "carried
from the older catalog" and was read as unbuilt). A Protocol 3 pack STANDING.md marks outside the form is brought into
it (twin-packs-p3's RECIPE, "An existing pack"); a pack is never ported from an older protocol's implementation. For a separate packs worktree, pack-standing.ts --repo <path> reads and writes
that checkout alone and refuses a path that is not a git worktree root. A cached verdict names the whole vendor's committed
tree (including the code its lanes share) and the resolved kernel installation's real path and tree state; dirty
units and unavailable environments are never cached. pack-standing.ts is run in the change that touches a pack, and --check refuses a
committed file that is not what the repository holds.
A. Layering & boundaries
- A0 [review] The kernel owns vendor-independent execution and state mechanics.
Packs use the shared serve/write seams, tree readers and head's state system. Packs own their
vendor's API, resource schema, validation and query semantics. A common request lifecycle must
not force unrelated vendors into a common resource schema or replace vendor behavior with
generic CRUD; a derived pack's generic CRUD covers only operations classed
crud(the derived pack). Rebuilding log folds or confirmation suppression inside a pack is also drift. - A0b [review] Maximize utility reuse — don't reinvent. Before writing a helper, check
@volter/world-coreand@volter/world-tooling. Reuse the shared log, tree readers, checkpoints, branching, observation diffing, blob storage and verification machinery. Vendor-specific pagination, filtering and typed response construction remain the pack's responsibility. Review each new helper for duplicated mechanics and each abstraction for false vendor uniformity. - A1 [auto] Runtime layers: the shared libraries + operator control plane
(
packages/world-core,@volter/world-core), vendor packs (@volter/twin-<vendor>, in the pack repositories), and the optional world-profile orchestrator (packages/world-runtime,@volter/world-runtime). No shared vendor-logic library. The world runtime composes independently runnable twins; vendor packs do not depend on it. There is also a dev-only@volter/world-toolingpackage (packages/world-tooling) holding the conformance framework — it ships no runtime code and a twin runs without it (see E2). - A1b [review] Support/helper packages (
packages/world-*beside the kernel, e.g.world-browser-assets): reusable tools that support worlds but twin no specific vendor (no vendor API surface). They are not packs and are exempt from the vendor-pack shape (C1). They MUST NOT import a vendor pack (A3) and MUST NOT hardcode app-specific config in the kernel (A2). A vendor pack MAY additionally ship*-service-clibins that launch the REAL external service for that vendor's integration/real-media story (e.g.livekit's real media-server / egress / redis) — those still obey B3 (no vendor SDK at runtime) and carry no app-specific config; the launcher is infra, not twin behavior, but it lives with the vendor it serves. - A2 [auto] The kernel is vendor-agnostic:
packages/world-core/srcMUST NOT import any vendor pack or any vendor SDK, and MUST NOT branch on vendor identity or hardcode a vendor's routes/hosts/fields in control-flow (vendor-specific knowledge belongs in the pack'sTwinPackdescriptor, not a kernel switch table — e.g. the dev proxy reads each vendor's browser routing fromTwinPack.browserRouting). Vendor names may appear in human-facing help/error strings, never in logic — the guardrail allows the former and forbids the latter. ⚠️ The[auto]regex is necessary, not sufficient: it matches honest quoted/bare vendor names but can be defeated by obfuscation (e.g.['cl','erk'].join('')). Such evasion is itself a violation — the real rule is "no vendor/app glue in the kernel; put it in the pack descriptor,world-runtime, or a cookbook." The §-review must read kernel diffs for obfuscated evasion, and app-specific launchers (browser proxy wiring, etc.) belong inworld-runtime, not the kernel. - A3 [auto] A vendor pack MUST NOT import another vendor pack. No cross-vendor coupling.
One vendor's single store split across two packs (Google's credentials:
googleoauthissues the access tokens,webriskserves an API they authorize) is not coupling between vendors: the pack that owns the rows writes them and states their shape as a contract at its definition; the other reads them throughctx.ownerRow(owner, resource, id)(the kernel'sprojectOwnerResources), never writes them, and never imports the owner's code; an integration test runs the credential issuer and a declared reader fixture in one World (packages/world-runtime/src/cross-pack-read.test.ts), so a renamed row fails without requiring an unrelated vendor operation to leave its declared gap. The reader asserts the issuer's documented row fields in both process and colocated execution and after branching. Every (reader, owner) pair is declared in the reader's manifest (ownerReads), and a read outside it is refused (ctx.ownerRow:undeclared owner read). In a World each twin runs on its own root (<data>/<service id>, the data directory the runtime hands every service asVOLTER_WORLD_DATA), so the owner's store is not under the reader's: the kernel'sownerStoreRootsfinds it on the reader's root when that root holds it, else on the World's service roots that do. A read naming a subject (the token a request presents) takes the one store holding that subject, and is refused (OwnerStoreAmbiguousError, which the reader answers as an invalid credential) only when two hold it; none is an empty read. Only the declared cross-pack read resolves this way; a pack's reads of its own store never leave its root, and the read claims no journal identity. The owner's rows are memoized per store on itstreeStamp, so a per-request lookup refolds only after the owner writes. A derived handler reads a declared credential subject throughctx.ownerRow(owner, resource, id), whosemanifest.ownerReadslists the allowed owner and resource pairs. The kernel resolves the subject in the World's owner stores and returns its own fields, never the store root or a write seam. Its first need is Web Risk reading Google OAuth's issued tokens; ambiguity is a refused credential. The roots stay separate rather than one shared store root per World: a root keyed by service id keeps two services of one pack (or a process service's own files under--root) apart, and existing Worlds,branch(which forks each service's root) andcheckoutkeep their layout. - A4 [review] No cross-vendor "query engine" or shared query semantics. Each pack answers its vendor's queries with that vendor's own semantics (REST filters, JQL, GraphQL resolvers). Shared plumbing (pagination cursors, zod helpers) is fine; shared meaning is not — it manufactures false similarity.
- A5 [review] Vendor-specific behavior lives in the pack. If you're tempted to add a vendor's quirk to the kernel, that's drift — generalize it or keep it in the pack.
- A6 [review] The catalog is independent of the platform. The catalog owns submission, evaluation policy, moderator admission and index publication; it consumes released tools and immutable pack artifacts from many repositories, never a platform checkout. A vendor and its implementation package are distinct identities. Catalog readiness does not approve or merge a release, and catalog publication does not require a hosted build. Outside contributions require human moderator review. Maintainers may merge their own changes without a separate review; internal pack admissions still require current-head readiness, verified provenance and an authorized merge. A publisher App is recognized by its configured GitHub user ID and login together with the trusted source and authorized maintainer merge. Its scoped token proposes data and grants no approval or administration. The platform consumes approved index data and preserves explicit package/version pins.
World resource and diagnostics boundary
Coverage joins a managed pack's descriptor scheme to a booted World's service-owned discovered connection, so an SDK vendor served by managed infrastructure is not falsely missing. A global URL alone is no coverage evidence. Native connection selectors match both the protocol vendor and the precise protocol/environment row; exclusions on either identity win. First need: Twenty completed its Redis-backed signup, SDK jobs and CRM actions, while covers reported Redis missing and excluded PostgreSQL/Redis connection rows despite explicit vendor selection.
Containerless managed-infrastructure teardown follows its recorded pid files even when a declared kind cannot be booted by the installed backing. Capability validation applies to up and status, not down: a refused boot must still complete rollback and purge. First need: a World declaring PostgreSQL and Redis without an installed Redis backing refused before starting infrastructure, then repeated that capability refusal during rollback and explicit down.
Managed infrastructure failures retain the backend's diagnostic beside the capacity classification, including a spawn error when no output was produced. Credential values from the service environment and URL userinfo are redacted before publication. First need: Dub's native MySQL boot returned exit 125 while the operator saw only "managed infrastructure operation failed", concealing the container client's refusal and preventing diagnosis through World commands. Managed infrastructure failures retain the backend's stdout, stderr and spawn error after the capacity classification. First need: LibreChat's World-managed MongoDB failed during Compose startup, but a classification alone hid the backend reason needed to choose a repair. The same diagnostic contract applies to startup, readiness and teardown for every managed service. Container backing selection requires a successful probe with a nonempty server version; an empty formatted answer supplies no daemon evidence. First need: the Docker client available to LibreChat's World returned an empty successful version probe while Compose refused the daemon.
Boot-port identity probes have a wall-clock deadline and settle on request error or premature response termination. Boot cancellation destroys an outstanding probe and returns control to the boot owner for rollback; no readiness promise may prevent that owner from observing cancellation. A missing identity answer retains the existing legacy-service behavior, while an observed different boot identifier remains a refusal.
Co-located factories have separate preallocated roots and ports and receive fixed configuration,
so their imports and startup may overlap with at most eight operations in flight. On failure,
stop scheduling and join begun operations before rolling back every owned server or worker.
TCP readiness and boot-identity verification retain their phase order but check independent
ports concurrently. The co-located CLI publishes an atomic, boot-bound receipt only
after every declared factory has returned with its requested port validated and
shutdown handlers are registered. The runtime requires that receipt and verifies
its complete port set before identity confirmation. The matching receipt vouches
for every co-located port, and no port waits on a timer (D9); a foreign boot identity
remains a refusal (ADR 0012, ADR 0014). Service
records and environment merging retain declaration order. A later twin declaration starts
once the host's addresses are allocated, beside the host's startup, because a twin calls no
service while it starts; every other process and external service waits for the host and
every earlier service to be ready (ADR 0015).
The runtime's own managed infrastructure (an external service whose up is this runtime's
volter-world-infra up, run in its process) is started once the host is spawned. It does
not wait for the host or for earlier services to be ready, because it reads only the
World's own configuration and calls no declared service while it starts. Its result is
taken at its declared position, after the host's readiness as before, so its record, its
environment merge and up's return keep declaration order. A failure in it fails the boot, and rollback first waits for its
up to settle (ADR 0017).
Its volter-world-infra status, both the declared readiness probe and the status read whose
output discover maps, also runs in the runtime's process, as its up does. A separate process
would load the runtime again only to read a pid and connect to a port, and in a browser tab that
is a whole Node start. A readiness probe that fails is retried after 25 ms, doubling up to its
intervalMs, so a service that is nearly ready is not left waiting out a full interval.
up prints one progress line per phase to stderr: volter-world up <name>: +<seconds> s <phase>,
with the time since up began. The phases are the co-located host spawned, its receipt read, its
ports admitted, and its twins' credentials issued. For each service they are starting, spawned or
up returned (with the managed infrastructure's own phase lines), port answering, ready (naming the
readiness check that passed), status read, and environment merged. Then the environment is
written and up returns. These lines are the product's own record of where a boot spends its
time, not a debug mode. A closed stderr never fails a boot. ADR 0011
records the decision and its publication and measurement limits.
Gate startup never signals processes selected by command-line or temporary-directory patterns. Such matches establish neither ownership nor completion; process inspection is read-only. Cleanup belongs to the recorded World or the test's own retained child handles. Gate shard output is retained as it arrives, so an interrupted runner leaves diagnostics. An output-storage failure cannot produce a passing shard verdict. Tutorial cleanup discovers Worlds only inside its own scratch directory without following symlinks, and delegates teardown to the World runtime. A failed teardown retains the scratch directory and reports the failure; fixture removal never discards unresolved ownership records. Tutorial shells join their background jobs and exit before World teardown begins. A failed join retains the scratch directory. Repeated serve signals share one shutdown operation; its deadline reports incomplete cleanup rather than success. Explicit down may reclaim a valid boot marker only when the OS proves its owner absent. Likewise, when a World's instance metadata is gone, down releases its lifecycle record only when the record is this host's and this user's and its owner and every recorded process have exited: nothing remains to tear down. Any live, foreign or unreadable record is retained. Live, uninspectable and malformed boot ownership remains protected; marker reclamation never substitutes for stopping the recorded resources and confirming teardown.
Session TLS leaves are cached by signing CA identity and hostname. Separate Worlds and a
replacement CA at the same path must never reuse a leaf signed by another instance.
Session trust supplements public and caller trust. Variables that replace a client's CA store
receive a bundle of public/default roots, its configured certificates and the session CA;
NODE_EXTRA_CA_CERTS preserves the caller's supplemental certificates. Trust bundles are built
on use, content-addressed under the World's TLS directory, and separate from its signing CA.
HTTP routing through the scoped proxy preserves the configured twin URL's namespace path,
as the Node injector does. VOLTER_TWINS_KEY supplies x-twins-key only on requests routed
to configured twins; vendor Authorization is preserved within an origin. Injector redirect followers
compare each hop's vendor-facing URL origins: crossing an origin removes Authorization, Cookie
and Proxy-Authorization, along with internal routing headers, and later hops retain that reduced
header set. This also applies between distinct hosts of the same application. Authorization follows
Fetch HTTP-redirect fetch, step 13;
cookies and proxy credentials are not copied to a different origin. HTTPS twin endpoints retain
TLS verification even when the original client request uses plain HTTP.
The redirect proxy ends a caller's TLS to a vendor's host and forwards to the twin in the clear, so it
says so (x-forwarded-proto: https), and a twin's own origin (twinPublicBase) carries that scheme
with the request's host: a page a twin renders (a consent form) links and posts over https, as the
vendor's does. What it forwards is the request, not the caller's connection: hop-by-hop headers
(connection and the names it lists, keep-alive, proxy-connection, transfer-encoding, te,
trailer, upgrade) and expect stay with the hop that received them, and the body streams on to be
framed afresh, so a chunked body (a git push larger than http.postBuffer) reaches the twin as a
fixed-length one does.
A database twin is reached through the vendor's HTTP driver. An ORM whose client-engine build
requires a driver adapter (Prisma's, the build a browser tab runs and an edge client uses)
gets that adapter from the injector when the application constructs the client without one:
the pack declares prismaAdapter (the adapter package, its export, the URL variable the
endpoint template renders), the injector constructs it from the application's own
dependencies over that URL, and an application that passes an adapter or does not install
the package is left as it was. The World never installs the adapter; the fact is data on the
pack, compiled into pack-facts like the hosts.
A Cloudflare Worker an application runs in a World (wrangler dev, Vitest's Workers pool: workerd started through
miniflare) makes its outbound requests from workerd, not from Node, so neither the patched fetch nor the proxy sees
them. Every miniflare Worker takes an outboundService, the function its outbound requests are handed to; wrangler's
own sessions set it to Node's fetch (wrangler
4.137.0, createSession). The injector runs in the Node process that starts workerd, so it hands that process's
importer of miniflare a view of the module whose Miniflare gives each Worker that names none an outbound service, at
construction and at setOptions (the build's own export is a getter that can be neither assigned nor redefined;
miniflare 5 takes it as dev.outboundService: { type: 'fetcher', handler }, miniflare 4 inline). The handler is given
the Worker's Request whole (its method, headers and body are getters a copy as init loses): a host mapped to a twin
goes through the patched fetch, any other through the World's proxy, loopback direct. No binding is added to the
application, and a Worker that names its own outbound service is left as it was. First need: RH2's channel bridge,
whose https://slack.com/api calls reached only real Slack; measured under wrangler 4.146 and miniflare
5.20261001-alpha (routines, twin-world 85d68fc), the bridge's OAuth install completes against the Slack twin.
Proxy startup records its child before waiting for readiness. Timeout and publication failure
retire that child before fallback; uncertain retirement keeps its PID and lifecycle evidence.
A startup intent is persisted before spawning and retained until PID publication or confirmed
retirement. An unresolved intent blocks replacement, teardown completion and pruning, so a
failed PID write cannot make an unknown child disappear from the lifecycle contract.
PID-file updates share a mutation lock and preserve recorded live or uncertain processes,
including proxies not yet represented by published proxy state. A successful boot rollback
retains stopped metadata and a teardown receipt for inspection and explicit pruning.
Both normal shutdown and failed-boot rollback publish that receipt before releasing lifecycle
ownership; publication failure retains ownership. A zombie awaiting OS reaping is already retired and holds no World resources.
Runtime child processes, including native backing servers and their setup helpers, request windowsHide: true
so starting a World from a daemon does not open an incidental console window on Windows.
The runtime retires a child it spawned and still holds through its live ChildProcess handle; a POSIX group delivery additionally requires that held leader to remain alive. A PID from a file, a retained number, or a group whose held leader has exited requires a recorded birth token before any missing-leader exception, with verification repeated for escalation. Missing or unreadable tokens refuse signalling and retain cleanup evidence; the runtime never blesses a retained PID by recapturing its birth. Birth capture for durable ownership is asynchronous and bounded on macOS and Windows and uses /proc start ticks directly on Linux. On Windows the birth is the process's creation time, a UTC FILETIME of 100 ns ticks read through Windows PowerShell's .NET Process.StartTime; a boot requires every service's birth before it publishes the World, so a platform with no birth reader can start no service. Every backing retains its child before descriptor close or receipt publication, retires it on a failed publication, and confirms exit before clearing ownership. The identity check and subsequent signal are not atomic. The macOS lstart token has one-second resolution and cannot distinguish reuse within that second. First need: Cal.diy at scanned commit 54343aa685ae8f33159d2f485ec4a57bad5c574a, whose native PostgreSQL retirement exposed the distinction between held children and retained numeric ownership; the same audit reached managed hosts, command lifetimes, service recorders, tunnels and proxy cleanup.
World environment exports are outputs, selected with --env-out; --env-file is rejected
by operator up, run, and branch to prevent confusion with credential inputs. An existing
output is reusable only when the prior instance records that exact path and its contents still
match that instance's exports. A foreign or modified file is refused before starting services.
Explicit --force authorizes replacement with a private backup; it does not bypass filesystem
protection. Symlinks, hard links and nonregular outputs are refused. Publication rechecks the
opened file and writes only the admitted inode, or creates a new file exclusively.
World config stripEnv: ["*"] starts services and attached commands without the caller's
inherited environment. Declared config/service variables and World-generated injection remain.
The policy is retained with the instance and applies to shells and external lifecycle commands
as well; removing the source config cannot widen inheritance. This includes caller NODE_OPTIONS: stripping it must not remove World's own preload or let
an outer preload cross the boundary. Named and prefix filters keep their existing behavior.
This controls environment inheritance; it is not filesystem or OS credential isolation.
First need for filtering caller NODE_OPTIONS: Rallly at 3e63239dbdec pins next@16.3.3,
whose dist/server/lib/utils.js:196-231 joins multiple require preloads into one invalid module
path. Excluding the caller preload while retaining World injection lets its own build run.
Task-owned command lifetime is distinct from persistent service lifetime. up and
run --keep retain explicit teardown. A normal run owns teardown through command
completion and loss of its caller; the ownership channel and instance generation,
not an old PID or elapsed idle time, authorize cleanup. Failed teardown retains its
lifecycle evidence. Other registered attachments remain protected.
CLI env and attach register the command against the same instance. Cancellation
has a bounded grace period and reaches the command's owned process group on POSIX;
the group is private to the command. The synchronous SDK execution API retains its
existing compatibility and reports its untracked coverage. Lifecycle generation records persist independently of the original admitting PID.
Task cleanup is owned by a separate process before admission, linked to its caller by a private
IPC descriptor never inherited by commands. It never restarts services. Cancellation settles
owned startup commands before rollback; external teardown metadata is published before their
startup begins. Boot and teardown retain durable lifecycle ownership until successful cleanup. Each boot tracks its own service groups. Group retirement checks include descendants;
unconfirmed retirement retains ownership and cannot become an ordinary readiness miss.
External-only Worlds use their matching lifecycle generation for runtime status rather than an
inert holder process. Doctor still probes the declared external services. Unreadable instance
metadata refuses teardown before any signals; missing instance metadata with retained lifecycle
ownership also refuses teardown. Neither a missing file nor a missing PID is a cleanup receipt.
External status, teardown and readiness commands are asynchronous with bounded waits (60 seconds
for status/teardown, the declared remaining readiness budget for probes).
An explicit down --purge may surrender this generation's recorded state when an external
backend's teardown command is unavailable (an absent/unexecutable tool or its command parser
refuses the invocation) and every recorded process and owned process group has exited. It
requires readable, matching local ownership, no active or uncertain consumer, no proxy/share
or boot intent, and the existing parent-reference check. It reports the failed command as
abandoned, preserves its diagnostic in the lifecycle log, and releases ownership without
claiming the external backend stopped. Ordinary down, execution failures from a working
backend and uncertain process/ownership state retain their evidence. First need: Dub's failed
native-infrastructure boot left no live process, but an unavailable Compose parser prevented
the task owner from purging its disposable World through the lifecycle door.
The World is the lifecycle and diagnostics boundary. Local startup checks the actual state
destination and required backends; it does not reserve speculative machine-wide memory or
storage. Deprecated config resources values are neither admission requests nor enforced
limits. A declared service that creates subordinate compute MUST enforce explicitly configured
native limits, report unsupported requested limits, and forward causal failures into its log.
Known additional storage requirements may be checked against their actual destination without
promises about subsequent concurrent writes. Lifecycle identities, startup intents, consumer
records and unresolved cleanup block unsafe replacement of the affected World independently
of capacity accounting. Application callers and agents MUST NOT operate infrastructure behind
that boundary separately. Failures identify the World, service, actual operation, available
measurements and retained diagnostics; failed diagnostic writes preserve the cause on stderr.
A process service's World parent, and the co-located host's, is the service recorder. The service
writes its output straight to its log (a pipe through the recorder would lose a crashing service's
last output); the recorder caps the log (64 MiB, copied to <log>.1 and truncated in place) and
writes a time mark about every ten seconds while output flows, the time granularity a line has. It writes a start record and an exit
record (exit code or signal, pid, run time, whether it was asked to stop) into the log and into the
World's logs/events.jsonl, the one list of every service start and end (the proxy daemon records
its own there; volter-world events prints it). Each service is handed VOLTER_WORLD_SERVICE_LOG_DIR
(logs/<service>.d) for the log files it writes itself, which status lists; the World's own stops (down, a boot's rollback, and a SIGKILL after the grace) are recorded
there too, so a stop is attributed to the World or to an outsider. status and doctor report a
stopped service's end from those records with its last output and its log path; a start without
an exit says the parent died with the service. A service that ends during startup fails up at
once with that account, not after its bind or readiness timeout. A fresh up keeps the previous
run's logs as logs/previous (one generation), so a crash's record survives the restart after it.
A service's state is judged by its process group, apart from the World's ownership: status and
list say degraded while any service is down, even when the proxy daemon or a tunnel keeps the
World running, and attach names the down services before it proceeds. doctor re-runs each
service's declared readiness probe (an accepting port is not an answering service), checks the
proxy daemon, whose output is its own log, and names a dead tunnel. The proxy's refusals and
failed twin requests name the twin, its origin and the cause, and are written to that log. A
failed run names each service that ended on its own during it.
A recorded pid names its process only while that process's start time (read in a fixed zone and
locale, or Linux's start tick) matches the one recorded at spawn: a reused pid is never signalled,
never keeps a World running, and down names it as not signalled. Rollback never lets its own
failure replace the boot's error; cleanup failures and attachments that stay uncertain for two
minutes and block a task's teardown are written to the lifecycle log, and the task then stops
waiting on them. An --env-out the runtime creates is owner-only.
The CLI's up boots in an owner process outside the caller's process group, the shape of run's
task owner: the caller's interrupt cancels the boot, which rolls back; losing the caller (a closed
terminal) does not, and the boot's outcome is written to the lifecycle log.
When a service's port first answers, up confirms the answer is the service's by an answer, never
a timer (D9): a twin answers /__volter/world-boot (the kernel seam serves it inside a World) with
its boot's id, and this boot's id is the proof, while another boot's fails the boot by name as a
leftover World. A program that answers no boot identity is the service when it is still running
once its port answers: a program that lost its port to another process fails its bind and ends,
and the boot fails with its account.
TCP allocation holds its IPv4 wildcard probe while checking the same candidate on the IPv6
wildcard with ipv6Only. An occupied port in either family retries selection; an unsupported
IPv6 stack leaves the IPv4 result usable. Both probes close before selection returns, so this
does not reserve the port or establish exclusivity between Worlds. Application URL records
retain the caller's origin. First need: applications in separate Worlds sharing port 8938
across address families exposed the IPv4-only allocation probe's blind spot.
Resource ownership is explicit metadata, independent of a lifecycle holder PID. Local CLI
attachments register one consumer per command, bound to the running instance, with a host-clock
heartbeat; reports retain unknown legacy, detached, activated-shell and remote consumers as
unknown. A stopped or expired consumer never authorizes automatic World teardown. A command
whose runner died before recording completion stays uncertain until the owner resolves it with
consumers retire, which retires a record only on this host and instance when its runner, its
command and the command's process group are all gone (survivingOwnedGroups), and keeps it,
with the reason, otherwise. Retirement reconciles the recorded runner and command
against process existence, independently of signal delivery: only a signal-0 ESRCH proves
absence; permission failures and unavailable registry inspection stay unknown and are
reported as such. Reconciliation never signals a recorded PID; stopping held commands
retains the lifecycle's birth-qualified delivery checks. A delivery ESRCH on a
positive SIGTERM does not by itself prove absence or complete retirement. down
checks registered consumers before releasing lifecycle ownership, and prune still requires its
successful teardown receipt. Resource inspection reports actual filesystem capacity and lifecycle inventory in a
versioned schema, never subtracting declarations from already reduced free space. Physical
memory is not measured usage or a scheduling guarantee. Legacy combined reservation/lifecycle
records remain readable and inspection never deletes them. An unresolved legacy record blocks
only that World's migration and cleanup; corrupt unrelated records never block new startup.
Managed installations migrate at verified stop boundaries with all lifecycle entrypoints pinned
to the new major runtime. Hosted consumers with legacy resource declarations must review their
capacity contract before adopting it. Neither diagnostics nor disk pressure authorize stopping
another session's World, deleting shared caches, or removing parent history.
Inspection distinguishes instance creation from last observed command use, reports the
observation source and partial coverage, and never updates activity merely by reading it.
Completed attachments retain their latest observation for that instance; a new instance
does not inherit it. These timestamps inform ownership review, never automatic teardown.
Local command attachments set VOLTER_WORLD to the selected World even when the caller
inherited a different marker. Cooperative refusal for mediated HTTP/Fetch and guarded Node connections applies even to an
empty sandbox World (transport coverage); an unrestricted empty
local World leaves the client functions unchanged.
Routed fetch redirects use the checked redirect follower under an explicit network policy too;
each hop re-enters routing and egress authorization, with scoped credentials removed across origins.
First need: localrouter could not load the Hub twin's normal 307 config redirect in a World with no external grants for mediated HTTP/Fetch (transport coverage).
Fetch responses retain the caller-facing vendor URL. Local HTTP twin redirects resolve against that URL,
using the existing redirect follower and its shared twenty-hop bound across all followers and re-entries. First need: localrouter's HTTPS
artifact cache rejects a loopback HTTP Response.url after the Hub's resolve-cache redirect.
The injector rewrites at http/https and global fetch; net.connect/tls.connect carry
its backstop for clients that open their own sockets (npm undici's Agent). Attached to a World,
a client that bundles undici (wrangler) and sends through undici's fetch or request on the
shared global dispatcher is keyed as well: the injector watches Object.defineProperty for that
global and stands a view in for the dispatcher set there, whose dispatch adds x-twins-key to a
hop only when its origin is the World's and its path, read as written, is one of the World's own,
and sends that hop with maxRedirections: 0, so no redirect follower below it (an interceptor, a
retry or proxy agent around one) carries the key to another origin: the redirect comes back to the
caller. A hop the caller asked undici to follow redirects for is not keyed, nor are a client's own
Agent and the dispatcher's other methods. Independent Node connections encounter the socket
guards; native connectors remain outside preload enforcement (transport coverage). A proxy that does not tunnel sees the
key on a plain-http World's hop, as it sees the hop, and so does the one origin a global Client or
Pool is pinned to: the dispatcher's owner chose where the bytes go. restore() puts the dispatcher back, a view
kept by a caller stops keying, and Object.defineProperty is restored unless something wrapped it
since (the injector's layer then stands in nothing). A socket to a host
a configured twin claims is refused in every mode, naming the pack's endpoint env when it has
one, because it went around the rewrite; a socket to an untwinned host is refused where strict
egress or the network policy refuses it. Loopback, private addresses and configured twin origins
pass. The backstop refuses; it never redirects, since a raw socket carries no request to route.
The redirect proxy answers a host it refuses (sealed, or outside the World's network policy) itself: a CONNECT to port
443 of a DNS name is terminated with the World's TLS and a request on it is answered 502, naming the host, as a page for
a browser (the address bar keeps where the request was sent) and as text otherwise, unless its Host names a host the
World routes, which is forwarded there; any other refused CONNECT, and every one past the first 64 names a proxy has
answered so, is refused on the tunnel line. Each is logged when refused; nothing leaves the World. A name the World
lets through (its own .test names among them) whose tunnel cannot connect on 443 gets the same page, saying why
(a name that does not resolve), within the same 64.
The application's own production hostnames are routed to the application, as DNS routes them to its host in
production: volter-world app-url <world> --host <name> records them beside the application's URL
(app-url.json in the instance; with no URL recorded, the World's own app service wherever a boot puts it), and
after twins and the hosts twins claim, resolveTwin answers a recorded host with the application's origin. A World
holds any number of applications, each named (--app <name>; the one a command names without it is app), each with
its own URL or, with none recorded, the World's service of its name, and its own hostnames; a hostname belongs to one
application, and recording refuses one another application of the World answers. So several products run in one
World, each at its production hostnames, and reach one another by them as they do in production. The
injector and the redirect proxy route by that answer, reading the record from the instance their World env names,
at most once a second, so a host or URL recorded after the application started applies; the socket backstop refuses
a raw socket to such a host in every mode, as it does a twinned one. A routed request is a real request to where the
application listens (streamed, abortable, the caller's redirect mode), keeps its Host and carries
x-forwarded-host and x-forwarded-proto (https where the caller used TLS), so an application that serves several
hostnames (Dub's app, partners and api) answers each as in production and sets its secure cookies. A recorded
wildcard (*.dub.link) answers every name below its parent and never the parent, as a DNS wildcard record does, for
the names an application creates while it runs (a Dub program's acme.dub.link). A name a vendor's host rule gives its
twin is never an application's while that twin runs in the World: routing enforces it, and recording refuses a host
or wildcard that names a vendor whose twin the World runs (reading the descriptors' hosts, suffixes and pattern tails
and the hand table's names). A vendor the World runs no twin of can be one of its applications (the product itself,
where the catalog also twins it for its customers: Runhuman at runhuman.com), so its hosts are recordable while the
World runs, against the twins it runs then; a route never answers such a host while that vendor's twin runs. The host compiler and resolver have one dependency-free home (host-routing.cjs); Node's
vendor-hosts.cjs supplies their facts. The route and its request options (@volter/world-core/app-route) are
read by the injector and by the colocated twin host, which
runs without the injector: a twin's own outbound request (a QStash delivery, a webhook) asks appDestination, before
worldEgressRefusal since the application is inside the World, and deliverToApp makes it where the application
listens with the same Host, forwarded headers and World CA, never the World key; appFetch is that request in
fetch's shape, for a delivery that reads fetch's answer: it keeps the caller's redirect mode, routing each hop again,
and a hop that leaves the application goes out only where worldEgressRefusal lets it, without the World's headers;
the application's own origin meets the same rule (an app URL recorded outside the World), and a request is bounded as
fetch's is (300 s). A twin's delivery does not
consult the hosts twins claim. A name the World routes (a vendor's host, a claimed host, the application's) also resolves in a
process the injector is installed in. On a native host, code that looks a name up before
fetching it (an SSRF guard) meets a public-shaped answer: an address in the documentation ranges (203.0.113.7, 2001:db8::7), routed nowhere, a socket to which is
refused with the reason (a guard that refuses every non-unicast range, as ipaddr.js classes these, still refuses
them). In a browser Node guest, a routed name resolves through the engine's lookup to
the selected guest proxy's address, or its actual twin/application endpoint when no proxy
is selected. The injector owns this route decision; the engine owns address resolution.
The guest's loopback address is private, so a caller's private-address URL guard may
refuse it. DNS resolution grants neither an original-port socket route nor network
permission: HTTP/fetch keeps the name and crosses the World proxy, while raw socket
guards still refuse direct vendor connections. Every other name uses the host resolver;
a browser engine without an outbound resolver returns ENOTFOUND, never a wildcard
that can reach an unrelated local listener. First-read waits and an unresolved fetch's
one refresh batch have a 1.5 s ceiling. Measurement at runtime dfb9058, Node 23.11.0,
in build-policy mode with 15 co-located twins (14 Dub vendors plus a Cloudflare claim
provider), no application or concurrent build in that World: volter-world attach cookbook-dub-build --root "$T" --owner cookbook -- node "$T/cookbook/dub/claim-refresh.mjs" measured
five refresh batches with one claiming twin at 2.34–14.08 ms. The existing first-read
ceiling is retained for scheduling headroom. The host was shared, with other actors'
workloads not controlled or measured; these samples do not assert a universal
claim-door latency. The recipe
holds the complete output and executable setup. Each claim-door read also aborts after 1.5 s,
including body consumption; a timeout or failed read retains previous claims. A refresh joins
an in-flight earlier read before requesting a new snapshot, and concurrent misses share that
refresh. The batch ceiling bounds the caller's total wait even across those two reads.
On completion or expiry the fetch rechecks the returned claims, then checks its World grants,
then refuses an unmatched, ungranted destination; build-artifact grant precedence is unchanged.
The diagnostic channel volter.inject.claims-refresh records batch outcome, duration, ceiling
and claiming-twin count for the repeatable recipe measurement below.
First need: the Harness claim refresh could wait indefinitely on a hung claiming twin,
preventing an external fetch from reaching its grant check or refusal. Harness
deploys a Worker in a child process and immediately fetches its newly claimed hostname in the parent, whose first
read preceded deployment. Periodic discovery alone incorrectly refused that request. The application
is not a twin: it never receives the World key, and a redirect that leaves it drops the credentials and the World's
headers as crossing origins does. A vendor's host, the World's own origin and a twin's are refused when recorded,
and a host a twin claims while it runs takes precedence; nothing on these hosts is twinned or journaled. A hosting
twin's door also lists the names it serves as the customer's application (appHosts: a domain the app added to its
own Vercel project), and those answer as the application, recorded hosts or not, as the name's DNS sends it to the
deployment in production; a twin's delivery to one of them is not yet routed.
Command launchers default the injector's informational routing banner to quiet, inherited by
child processes. An explicit verbosity option overrides the admitted environment; otherwise
an existing quiet setting is preserved. Warnings, errors and routing are unaffected.
B. Dependencies
- B1 [auto] Core runtime dependencies are
zodandwsonly. Thewsimport is confined toserve-http.ts, the Node WebSocket host adapter. A wire's protocol library (graphql) is loaded byimport()the first time that wire is used and is installed by the packs that serve it, so importing the kernel loads nothing more ("Other wires"). Vendor SDKs remain forbidden. - B2 [auto] Each pack declares
@volter/world-coreas apeerDependency(so installed twins share one kernel version), not a harddependency. (devDependenciesmay also pin it for local dev.) A pack that needs a mechanism a platform package gained (a manifest field, a context member, a route) declares that package's first release carrying it as its range's minimum ("@volter/world-core": ">=3.0.19"; the same for@volter/world-uiamong its dependencies), so it never installs beside a kernel without it; a pack that needs nothing new keeps*. The number is the one main's publisher stamped on the release commit that carries the mechanism, written after that release, never guessed before it.pack-releasereads a range's minimum by npm's own semver (minVersion: any range npm reads,>=3.0.19 < 4and> 3.0.18and~>included; aworkspace:range without its protocol, annpm:alias by its range) and refuses to publish the pack from a checkout whose package is older, or when the minimum is the checkout's own version and the registry holds no such release (the platform is released first); otherwise it pins the range to the checkout's version, as it pins every platform range.@volter/world-uipeers the exact@volter/world-coreit was released with, so a pack declaring a world-core minimum declares world-ui's release of the same commit too. What is not checked: which mechanisms a pack uses (a content route its pages name at run time, a journey token, a manifest field's meaning) is no factpack-releasecan read against a version, so a pack that needs one and declares*, or a minimum older than the release carrying it, is published unrefused; the declaration is the pack's, held by its review. First need: the social packs (x, hackernews, reddit, linkedin), on the content route at a World's place,yieldsToApi, a content screen'stakesand a vendor of lanes'vendorBacked. - B3 [auto] The vendor SDK (
stripe,@linear/sdk,jira.js,@octokit/rest,@slack/web-api) is adevDependencyonly and is imported only in*.test.ts— never in runtime code. A twin fakes auth locally; it must run without the SDK installed. - B4 [review] A pack's runtime
dependenciesare limited to@volter/world-core(peer), the UI runtime (react/react-dom), and genuinely-needed vendor protocol libs (e.g. linear'sgraphql). Never the vendor SDK.
C. Pack shape (uniform across all vendors)
- C1 [auto] A pack ships the Protocol 3 layout (Pack layout); the grade's form section checks it.
A Protocol 3 pack twin-packs-p3's
STANDING.mdmarks outside the form is brought into the layout; a pack is never ported from an older protocol's implementation. - C1b [review] A pack's screens (
screens/) are mandated for every vendor except those whose product is the API. The test is NOT "does the vendor have a UI" (nearly all do, including OpenAI's console) — it is "when someone does this vendor's core job, do they open a browser or write code?" Design in a canvas, write in a page, talk in an app, drag tickets on a board → the UI is the product → build its screens. Call the API while the console is incidental key/billing tooling (LLM gateways, TTS, geo/weather/data APIs — OpenAI, Anthropic, ElevenLabs, Mapbox, …) → build no screens rather than fabricate a dashboard, and state the reason in the README; coverage is the API, not UI. Note that a thin or read-only REST API is evidence FOR screens — Figma's API is read + comments only precisely because the work happens on the canvas. Screens reflect the vendor's real surfaces (data-coupled, proportional to the actual product), reading and writing the same tree as the API (API↔UI parity; Screens). This is a judgment no check can make generically, so it is held at review. - C2 [auto]
package.jsonexports["."]→./src/index.ts;binexposesworld-<vendor>→src/cli.ts.
D. State & behavior invariants (the architectural contract)
-
D1 [review] State is the kernel's append-only logical log and checkpointed tree, including inherited entries and the branch's own entries. A pack uses the shared write path and tree readers; it MUST NOT keep a parallel mutable side-store as the source of truth.
- Durability: every event/action append is a completed
appendFileSyncwrite, so a process crash after it returns still leaves the record on the log. The remaining window is an OS crash / power loss between the write landing in the page cache and the filesystem flushing it to stable storage (a lost or torn tail record). SettingVOLTER_DURABLE=1closes that window byfsyncSync-ing after each append (the kernel'sappendDurablehelper), at the cost of an fsync per write. It is off by default — the process-crash guarantee is enough for local dev/e2e — and should be turned on where real pulled staging data lives in these files (purpose 2).
- Durability: every event/action append is a completed
-
D2 [review] A twin presents the vendor's exact API and response shapes. An operation that isn't modeled yet fails like the vendor (404/422/etc.) — never a fabricated success and never invented data. (This is the honesty rule; it's also the conformance contract.)
-
D3 [review]
readOnlyforbids local writes (the vendor-shaped 405/method-not-allowed), so a read-only twin serves reads and rejects mutation. -
D4 [review] Simulated execution is local and deterministic. Real vendor calls use the connector or the real head's kernel executor, with an injected client and sealed credential. The executor applies the credential BY STRATEGY (
executor.ts): header replacement by default; a query parameter, or anhmac-sha256/aws-sigv4signature computed per request, when the pack declares that vendor fact inTwinPack.auth. The executor dispatches on the strategy NAME and never on vendor identity (A2); a pack supplies only pure canonicalization (canonical,scope) and never holds the secret; a declared strategy with no secret FAILS CLOSED. Held byexecutor.test.ts, which for SigV4 checks the signature against an independent implementation (aws4). Pack request handlers never make direct vendor network calls. Reads use held state; refresh observes the vendor explicitly or on its configured schedule. Verification injects fakes. An explicitly selected local-generation capability may hand supported generation misses to a separately declared, World-owned local service. This is an optional extension, not simulated execution or a new World mode: unselected twins remain deterministic, scripted responses and faults take priority, and read-only refusals still apply. The pack validates and makes one scenario decision before handing off, without recording a preliminary stub. The local service owns inference and its generation accounting; twin history does not claim those results as replayable simulated state. The handoff preserves streaming, errors and cancellation, refuses redirects and hosted destinations, and sends no vendor credentials. Runtime retains service lifecycle ownership; neither the kernel nor the pack embeds an inference engine. Capability verification remains deterministic with injected local fakes; real inference is separate integration evidence. -
D5 [review] Capability
verify()predicates exercise the twin (local round-trip), never the real vendor. A manifest check must run offline and deterministically. -
D6 [review] A refresh MUST fold observed mutable resources through the kernel's
observeResource(the refresh observes, the kernel diffs each resource against the tree and appends to the root's log only what changed). A refresh that appends rows itself re-appends unchanged state and breaks the shadow-diff contract downstream consumers depend on. -
D7 [review] A pack's pull is derived, never written: its refresh adapter is the kernel's, from the manifest's
refreshscopes (the real-system adapters), and it observes each resource throughobserveResources. No pack exports a pull function or astateSystem. -
D8 [review] Every real call to a vendor that rate-limits a live token MUST go through one guarded path (the kernel's executor) carrying a persistent, fail-closed budget: a durable rolling-window spend ledger, consulted before the request goes out, that throws instead of calling past a conservative ceiling or while a
Retry-After/429 cooldown is armed. It persists across processes (a fresh process must not get a fresh allowance), charges at check time so a burst is refused rather than raced through, and treats an unreadable ledger as a full window — never as zero spend. Corollaries: raw vendor API calls outside that path are BANNED (no discipline inside the path can restrain them, which is the whole point); retries are off by default; and cache/quota logic is proven with an injected fake, offline (D5) — never by calling the live vendor to "check". Rationale: a real ~4.5-day Figma token lockout came from calls made outside an otherwise well-behaved client. The mechanism is vendor-agnostic and lives in the kernel (packages/world-core/src/rateBudget.ts, exported from@volter/world-core); each pack supplies only its numbers as data (TwinPack.rateBudget/declareRateBudget— the same "vendor knowledge in the descriptor" pattern asbrowserRouting, so A2 still holds). A vendor with no declaration is not unlimited — it falls back toDEFAULT_RATE_BUDGET, and there is deliberately no opt-out.When an installed CLI imports an app-local pack, it registers that pack's exported descriptor in its own kernel registry before constructing the real executor. The app and global CLI may resolve separate copies of the same kernel package; the pack's import-time registration in the app's copy cannot establish the runtime's budget, host or reference declarations. The runtime uses the descriptor's existing registration mechanism.
A declaration may be more permissive than that fallback only when the vendor's own documented limits justify it, and then the
reasonmust carry the number. Where a vendor publishes no scalar limit (Figma, OpenAI, Sentry, Slack's tightest tier) thereasonmust say so rather than presenting a guess as a vendor fact — and the ceiling stays at or under the fallback unless a different, stated sizing principle is given. Permissiveness is judged on two axes, not one: sustained calls/hour and the burst a single window admits (an hour-long window is 20× the fallback's burst even while being tighter per hour).A normalized policy keeps the optional allowance metadata when supplied. The fallback has no documented vendor allowance, so normalization never makes one up or requires one.
scripts/rate-budget-check.tschecks this across every declaring pack at once. It reads the roster of guarded vendors from the filesystem (every pack of the pack repository imported, its descriptor'srateBudgetread), not from the in-process registry: every budget the served packs declare must be listed. It fails if any pack's numbers change shape on the way through the shared mechanism, if any endpoint is priced free, if a window is shorter than the fallback's, or if two packs would share a ledger for the same credential.The burst check is arithmetic against the vendor's documented figure, never against prose. A pack that admits more calls in a minute than the fallback declares
rateBudget.allowance: the vendor's own documented per-minute allowance, in that pack's units, and the page that documents it, which its manifest cites on a// source:line as every vendor fact is (check-sources holds the page's words; review checks the number against them). ItsburstCeilingmust fit inside it. A pack's contributor reads the page and cites it; no table elsewhere carries it (vendor facts, rate-limit anchors included, are read from the vendor's page and cited on a// source:line, and review checks the citation). A declaration never supplies its own justification: neither its own longer window nor a per-minute phrase in itsreasonjustifies a burst; the allowance's justification is the vendor's page. The documented allowance is declaration metadata. The normalized enforcement policy requires its numeric controls and compiled selectors, without requiring this citation metadata; the fallback has no fabricated vendor allowance.A guarded factory validates an injected budget by METHOD IDENTITY, not
instanceof(assertBudgetGuardIntact).checkBudgetis an ordinary prototype method, so a one-line subclass — or aProxytrappingget— satisfiedinstanceofand disabled the ceiling entirely. The three functions a factory calls must be the very onesRateBudget.prototypedefines.A pack declares its budget on its descriptor (
rateBudget, stripe's the first); which packs do is what the check reads, never a list kept beside them. Cross-vendor isolation of the ledger is the kernel's (world-core/src/rateBudget.test.ts, "PER-VENDOR ISOLATION"). -
D9 [auto] A service is up when it answers its own protocol on its port; the answer is proven its own by a receipt, never by its output or by time.
upreturns when every service the World starts (a process, a twin, a co-located host's twins, the managed infrastructure's PostgreSQL, MongoDB and Redis) answers its own protocol on its port: an HTTP status, Postgres's SSLRequest, MongoDB's serverStatus, Redis's PING, or a declared command that asks the service. That is what Docker's healthchecks ask (pg_isready, an HTTP 200). Ownership, the guard against another process answering in the service's place, is a receipt the service's host writes itself once its listener is bound (the co-located host's all-started receipt, ADR 0012 and ADR 0014; the PGlite host's listener receipt with its pid, port and this boot's token), or the service's own protocol naming it (a twin's boot identity atWORLD_BOOT_PATH; mongod'sserverStatus.pid; Redis'sINFO serverprocess_id, which the kernel's Redis protocol answers as Redis does). A program that names itself no other way is its own when it still runs once its port answers: another holder of the port makes its bind fail and end it. Readiness and ownership never read a service's log, stdout or stderr, and never wait a fixed time for it to fail; a log is for people. A readiness probe that would match log text has no form in the World config:readyandreadyWhentakehttpUrlorcommand, and nothing else. Source: the owner, 2026-10-07: "do our twins have other fragility like this? This could be a whole class of problems - and we should make sure that this is disallowed in our process whatever process made these". First need: Rallly's docker boot in the tab, run 26c, where the PGlite host's port opened at 6.88 s andupwaited until the 10 s stop for a ready line that never reached the log file it scraped.
E. Tooling & framework placement
- E1 [auto] Repo tooling (
scripts/) is dev-only and not referenced by any pack's runtimeindex.ts/cli.tsor its publishedexports. - E2 [auto] Test and validation tooling is NOT shipped in the runtime kernel. The browser journey a UI test
walks lives in
@volter/world-tooling(a dev dependency), and the derivation, the evidence check, the grade and the gate in@volter/twin-standard, never in@volter/world-core. A twin runs without any of it, and neither holds vendor-specific logic (A2).
How this is enforced
- [auto] rules →
scripts/architecture-auto.ts(A1, A2, A3, B1, B2, B3, C1 throughpack-standing.ts, C2, D9, E1, E2, and no committed World state), over the kernel and every pack of the pack repository, run by the pre-commit hook. - [review] rules → the per-cycle architecture audit (this doc is the rubric). Each audit cycle and any structural PR should re-check the [review] rules, since they can't be fully grepped.
Portable host and immutable payloads
The HTTP host supports TLS and WebSocket upgrades on Bun and Node. Node loads ws only
inside serve-http.ts when an upgrade adapter is configured. Runtime fronts relay text
and binary messages without introducing vendor state. Remote attach composes the session
CA with an existing extra CA bundle, preserving both caller trust and session trust.
The shared HTTP seam retains Bun's 128 MiB default; served-World and World-view fronts
retain their 1 GiB baseline. A twin or vendor-server backing may declare maxRequestBodySize
in bytes in its descriptor or backing facts. The fetch carries the pack's cap to its serve seam,
and a World front admits the largest cap declared by the twins it serves. Vendor size refusals
remain the vendor's contract. A larger cap keeps its artifact-byte measurement, command and
product commit beside the declaration. First need: localrouter's qualified Qwen3.5 2B decoder
LFS upload to the Hub twin (1,088,892,928 bytes).
The Node HTTP host gives each Fetch request a signal tied to client disconnect and forced server shutdown. A completed upload is not a disconnect. Disconnect interrupts response reads and backpressure waits, cancels the owned response reader, and retires listeners; late handler responses are canceled rather than written to a closed socket. Body failures after headers destroy the transport, never append a successful ending or a second response. Scenario serving accepts an optional caller signal: an already-aborted request makes no decision; abort during a slow or drop fault clears its timer and prevents later realization. A decision already made keeps its once/phase accounting; cancellation does not replay it. These are shared transport and scenario mechanics, independent of vendor or inference service.
Binary remote requests retain their bytes through signing and transmission. Resource
payloads stay in the blob store and follow retained branch ancestry when absent locally;
a corrupt child payload must not be replaced silently with an ancestor's copy. Branches
reference parent state. Hosted blob reads, size probes and range reads search the World's own
R2 prefix, then its supervisor's recorded parent chain, rewriting the leading World data path
to each ancestor's id without copying bytes. A branch reads an ancestor's bytes for a key it
does not hold; the row decides which bytes the branch sees. Resource keys are pack-defined;
multipart file parts use files/<sha256>. Writes, listings and removals stay in the World's own
prefix. A hosted parent with branches already refuses deletion;
removing a branch deletes only its own blobs. Local host branches retain a local parent pointer
beside their downloaded history view, so the kernel's resource ancestry resolves their uploads
through the parent too. Local parent pointers register durable child references before publication;
removal and registration share a coordinator outside the removed tree. A multiplexing host
front leaves vendor request journaling to its downstream twins; post-response logging must
not recreate a retired World directory. Fresh boot, reset,
purge, prune, core scrub and host deletion refuse removal of a referenced parent, while ordinary
stop retains history. Parent generations and branch positions are validated on read. Known
legacy World trees are checked before cleanup; an undiscovered legacy fork outside those
trees has corruption detection, not guaranteed preservation. Raw filesystem removal and old
binaries bypass this protocol. No timestamp or absent process is authority to discard a
committed child reference.
Local blob writes may reuse identical bytes through hard links on the same filesystem,
within one OS user's ownership. Every World retains its own ordinary file path; replacing
a blob atomically breaks sharing for that path, and removing a World never removes another
World's bytes. A bounded, disposable per-user lookup index stores paths and digests only,
never payloads or liveness authority. Reuse verifies the linked bytes before publication;
missing, corrupt, inaccessible or unsupported candidates fall back to an ordinary atomic
write. Index loss or failure cannot lose data or fail an otherwise successful blob write.
Deleting the last owning file frees its bytes without a separate archive-cache cleanup.
Shared payload files are immutable outside BlobStore's atomic replacement API: direct
in-place filesystem writes or permission changes affect every hard link. This is storage
deduplication within one OS user, not a security boundary between hostile processes.
Observation cursors use vendor fields distinct from kernel metadata (observedUpdatedAt
for GitHub), preserving the catalog's ownFields convention.
Prune shares shutdown's liveness definition: an exited child awaiting reaping is stopped; an uninspectable process is retained. Only a matching successful teardown receipt permits pruning. Inspection never grants permission to stop a running world.
Published Node CLI entry points identify the executed file through its resolved path when
import.meta.main is unavailable. Importing the CLI as a library must remain inert.
First need: Upstash direct IPv6 endpoints authenticate by token. Pack routing and semantics
share the kernel’s readHostPort authority reader; a bracketed IPv6 literal is never split at
its first colon. Invalid authorities produce no host match.