# Config

`.volter/world.json` is the committed, portable intent for a World. `volter world init` writes
format 2; `up` accepts format 2 and legacy unversioned files. Resolved ports, process identities,
minted values and timestamps belong to the ignored instance record. Live credentials never
belong here.

```json
{
  "schemaVersion": 2,
  "metadata": { "id": "acme-web" },
  "discovery": {
    "selection": {
      "include": [{ "usage": "application" }],
      "exclude": []
    }
  },
  "runtime": {
    "isolation": "colocated",
    "environment": {
      "values": { "GITHUB_TOKEN": "twin-fake-github-token" },
      "strip": ["HOST_SECRET_*"]
    }
  },
  "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" }
    }
  ]
}
```

## The document

| field | meaning |
|---|---|
| `schemaVersion` | Required integer `2`. It versions this document, independently of twin protocol and package versions. |
| `metadata` | Required `{ id, description? }`. `id` is the World name and main branch. |
| `discovery.selection` | Saved rules used by both initialization and coverage. |
| `services` | Required ordered array. Service ids are unique; order is startup order. Empty is valid. |
| `runtime.isolation` | `process` (default), `colocated`, or `worker`. |
| `runtime.environment.values` | Variables given to services and `run`. `$mint` creates a structurally valid throwaway value at boot. `$issue:<service>` is a credential that twin issues from its credential endpoint once it is up (left unset, with a message, when it issues none; see [data and keys](../concepts/data-and-keys.md#vendor-credentials)). `$app-url` is the application's own origin (its base URL, its auth URL): a command given the World's env gets the URL `volter-world app-url <world>` answers (what its booter recorded with `--set`, or the World's own `app` service), and the name is left unset, with a note, when none is recorded; `init` writes it for such names. An empty value is left unset, so the app's own env files provide it; `strip` keeps a caller's variable out, and a service's own `execution.environment.values` can still set one empty. |
| `runtime.environment.strip` | Caller variables kept outside: exact names, prefixes ending in `*`, or `*`. Declared World values still apply. |
| `runtime.network.egress` | Canonical exact HTTPS origins, exact paths, or subtree paths ending in `/`. `/css2` grants only that path; `/artifacts/` grants descendants at that exact scheme, host and port. Paths decode once and resolve dot segments; encoded separators, malformed/nested escapes and backslashes are refused. Queries and fragments do not influence matching. An empty list refuses external requests through mediated HTTP/Fetch and guarded Node socket seams; sandbox mode applies the same cooperative refusal to untwinned destinations. Twin routing takes precedence. Native DNS, Bun native connectors/aliases, curl and addons require an enforced Machine boundary; see [transport coverage](../contributing/architecture.md#outbound-transport-coverage). Omission preserves existing routing behavior. This grants no ingress. |
| `runtime.network.phase` | `runtime` by default; `build` labels a separately owned build World's lifetime from `up` through the build command to `down --purge`. The runtime does not infer a program's purpose. |
| `runtime.network.passthrough` | Optional exact-origin list, requiring `phase: build` and the same origins explicitly in `egress`. Only these broad grants permit opaque TLS; paths cannot be listed. Other granted HTTP/global Fetch requests use the World's per-request proxy. Raw sockets, HTTP/2 and unmediated undici cannot use inspected grants; guest inspected egress is refused. |
| `runtime.network.ownLoopback` | `true` by default: a run may reach the listeners it opened itself on loopback, when it declares them (an image's `EXPOSE`, or a listener a host process opened under the injector). `false` refuses them. A parent World's `false` carries to its children. See [capability decisions](../contributing/architecture.md#capability-decisions). |
| `serving` | `{ mode?, name?, share? }`. Mode is `app` by default. `bare` requires `name: "<org>/<world>"`. |
| `remotes` | World name to URL or path. `origin` is the default; tokens are stored separately. |
| `changesets` | `{ approvals?, summary? }`: how this World's changesets are made and readied. `approvals`, a whole number from 0 to 10 (1 when omitted), is how many different people must approve a changeset's current contents before a person deploys it (a key's approval counts for the person it was made for; a vendor whose deploy policy is `auto` deploys a push at once, without one). `summary` is `required` (the default: whoever cuts says what it does and why) or `generated` (a cut with none takes the summary generated from its changes). Changed by a person or the World's token through the rules endpoint, never by a key. |
| `scenario` | Opaque `{ actors?, fixtures? }` metadata recorded for tests; it does not mutate twins. |
| `provenance.catalog` | `{ sha, protocol? }`: the catalog birth stamp written by init. |

Unknown structural fields fail validation. At the document or service level, keys beginning with
`//` are comments; nested sections do not accept comment keys. User-keyed maps and
scenario values are not treated as structural fields.

## Selection

Selection contains `include` and `exclude` arrays. A selector has `usage`, `vendor`, or both:

```json
{
  "include": [
    { "usage": "application" },
    { "usage": "deployment", "vendor": "cloudflare" }
  ],
  "exclude": [{ "usage": "dependencies" }]
}
```

Usages are `application`, `dependencies`, `build`, and `deployment`. Fields within one selector
must all match; selectors within a list are alternatives; exclusion wins. A vendor is selected
when at least one detected use survives. The default selects application uses only, and init
writes that default so later runs reuse it. A vendor-only selector addresses all uses of that
vendor. Native connection rows also match their protocol vendor (`redis` selects `redis:REDIS_URL`);
an exclusion on the protocol or precise row wins. Registry destinations are dependency use; twin-declared tools such as Wrangler carry
their declared build or deployment use.

Selection controls automatic proposals and coverage obligations. It does not authorize network
access, expose credentials, remove explicitly declared services, or excuse broken routing.
An excluded finding remains visible as excluded, never covered.

Regeneration keeps saved service bindings, comments and authored infrastructure stubs. Conflicting
new endpoint claims fail before writing files. Existing `seed.ts` files retain their contents and
ordering; init reports the imports and calls to add for newly introduced default seeds.

## Services

Every service has `id` and an explicit `type`: `twin`, `process`, or `external`.

| field | meaning |
|---|---|
| `description` | Human-readable wiring rationale; ignored by the runtime. |
| `source.package` | A twin package such as `@volter/twin-github`, resolved from the World root. |
| `source.version` | Package constraint: exact or `^major.minor`. It also applies to a command-backed checkout twin. |
| `execution.process` | `{ command?, args?, rootArg?, portArg? }`. Package twins derive their command and may append args. |
| `execution.colocate` | `{ module?, export, scenarioPath? }`: the twin factory used by colocated or worker isolation. |
| `execution.lifecycle` | External service `{ up, status?, down, readyWhen? }`. |
| `execution.cwd` | Working directory relative to the World root. |
| `execution.environment.values` | Variables for this service only. |
| `execution.preload` | Extra Node preloads. Relative paths resolve from the effective service cwd. |
| `execution.controlPlane` | World infrastructure: receives declared values but not app-side egress machinery. |
| `execution.branchState` | `"unsupported"` refuses World branching before child compute; ordinary stopped-state checkout and reset retain their existing lifecycle. |
| `endpoint` | `{ port?, portReason?, ready? }` for a World-owned listener. A numeric port requires `portReason`; otherwise use `auto`. |
| `bindings.injectEnv` | Export the service URL under one variable, such as `GITHUB_TWIN_URL`. |
| `bindings.injectEnvTemplates` | More variables computed from `${url}`, `${httpUrl}`, `${host}`, or `${port}`. |
| `bindings.cliRedirect` | Variables honored by the real vendor CLI, computed from the same templates. |
| `bindings.discover` | External lifecycle output mappings: `{ as, protocol?, source?, jsonPath? }` or `{ as, protocol?, source?, pattern? }`. `protocol` (`postgres`, `mysql`, `mongodb`, `redis`, `http`) is the wire the discovered connection speaks, so coverage binds it to the application's driver; `init` writes it for the World-managed infrastructure. |
| `root` | A twin's real-system root, described below. |

A twin has exactly one of `source.package` or `execution.process.command`; it may additionally
declare `execution.colocate`. A process requires a command and cannot declare twin source,
colocation, external lifecycle, or root. An external service requires lifecycle `up` and `down`,
uses `bindings.discover` for outputs, and cannot declare source, process, colocation, endpoint,
preload, or root. Invalid combinations fail before anything starts.

Under `colocated` or `worker` isolation, a `twin` declared outside the host starts beside the host's startup: its
co-located twins' addresses are in its environment already, but their ports open only when the host is ready. Such a
twin contacts them only to answer a request, never while it starts. A service that must reach one while it starts is
declared `process` and starts after the host is ready (ADR 0015).

### A twin root

`root` identifies the real vendor account behind a twin on a shared World. The credential is
sealed separately under `.volter/credentials/`.

| field | meaning |
|---|---|
| `url` | The vendor API or a served World's twin URL. |
| `scope` | The one resource the account represents, when the twin requires it. |
| `deploy` | `auto`, `gated`, or `hold`; defaults to `gated`. |
| `refresh` | `{ every?, webhook? }`: scheduled or webhook refresh posture. |

## Sharing

`serving.share` retains the existing share shape: `provider` is `cloudflare-quick` or `command`;
a custom provider supplies `command` and optional `args`; `ephemeral` records URL lifetime; and
`services` is an array of `{ id, verifyPath? }` targets declared in this World.

## Migration

```bash
volter-world migrate-config .volter/world.json
```

Migration accepts only unversioned format 1. It refuses unknown legacy fields, creates the
byte-for-byte backup `world.json.v1.bak`, validates a temporary format-2 file, then replaces the
manifest atomically. It preserves service order, path-resolution behavior, roots, remotes,
scenario data, comments and provenance. The old all-signals coverage behavior is materialized
as all four included usages; changing to the application-only default is a separate edit.
Deprecated `resources` metadata is retained only in the backup and reported as dropped.
Ordinary reads never migrate a file, and running instance records are never rewritten.

## The World directory

Beside `world.json`, committed files include `handlers/<vendor>.json`, `seeds/story.ts`, and
`checks/*.ts`. Running state is ignored: `worlds/<branch>/`, `world.env`, `token`, `credentials/`,
and `current`.

The instance record stores actual service URLs, pids, logs, data directories, resolved twin
versions, the live environment, and the selection snapshot used at boot. Coverage of a running
World uses that snapshot; editing the manifest does not retroactively change it.

Explicit build grants take precedence over twin routing for their matching public paths.
Redirects return to the client; each subsequent request is authorized separately.

Without a container runtime, World-managed infrastructure serves a declared PostgreSQL
with the machine's own PostgreSQL wherever it has one. The tool directory (`postgres`,
`initdb`, `psql`, `createdb`) comes from `VOLTER_WORLD_POSTGRES_BIN`, or else from the
directory of a `postgres` server on PATH. A World keeps the database server its retained data
was made by. Where no server is installed it is PGlite, and the infrastructure log names
PGlite's one shared session. Nothing installs PostgreSQL, and
`VOLTER_WORLD_INFRA_BACKING=native|pglite|docker` forces one. Native PostgreSQL
is the installed version with the installation's extensions, and it keeps machine time.
It serves the declared PostgreSQL through independent
sessions in a private loopback-only cluster, retained on ordinary down and owned
by the World lifecycle. It reads the generated definition's literal `POSTGRES_USER`
and `POSTGRES_DB`; synthetic local connections use trust auth. Unix sockets are
disabled. Native database time remains machine time. An occupied port or ownership
mismatch is refused; teardown evidence is retained if shutdown cannot be verified.
Other supported containerless kinds retain their existing implementation. Do not change
the infrastructure implementation over a live instance or treat retained PGlite files as native
PostgreSQL: stop and purge a disposable World before changing its infrastructure implementation.

For a browser or other client without the process injector, an issued URL can be
requested as `$issue:<service>@direct`. It retains the twin-issued URL's user
information, path and query and replaces its scheme and authority with that
service's endpoint. Opaque tokens, non-HTTP URLs and credential files are refused
in this form. For a URL embedded by a build, declare a stable endpoint port so
the same URL serves after closing build permissions and resuming runtime state.
