# Host worlds for a team

Many worlds under one URL, each with its own token, and a console that shows them all.

This page supplies an executable walkthrough for `packages/cli/src/journeys/tutorials.test.ts`.

A host serves many shared worlds by `<org>/<world>`. Every world keeps its own token and its own
history; the host keeps one admin token that provisions and removes worlds, and mounts the console
when it is installed beside it. This page runs a host on your machine and points an app at one of
its worlds. Volter runs the same host for you when you would rather not: sign in, create an org,
provision a world — the console, the command and the twins are the same packages. For orgs,
members and sign-in through your own provider (Okta, Entra, Google Workspace, GitHub) instead of
tokens, run the platform in front of the host: [Self-host the platform](./self-host-the-platform.md).

## The app

```json file=package.json
{ "name": "acme-web", "private": true, "type": "module", "dependencies": { "@octokit/rest": "^21" } }
```

The app declares the credential it reads. The World issues a throwaway token for its GitHub account,
`world`; that account is separate from the platform org `acme`. The seed creates `world/web` through
Octokit before any issue is written. It can run again without creating a second repository.

```text file=.env.example
GITHUB_TOKEN=
```

```js file=.volter/seed.ts
import { Octokit } from '@octokit/rest';
const github = new Octokit({ auth: process.env.GITHUB_TOKEN });
const { data: { login: owner } } = await github.users.getAuthenticated();
try {
  await github.repos.get({ owner, repo: 'web' });
} catch (error) {
  if (error.status !== 404) throw error;
  await github.repos.createForAuthenticatedUser({ name: 'web' });
}
```

```js file=file-issue.mjs
import { Octokit } from '@octokit/rest';
const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN });
const { data: issue } = await octokit.issues.create({ owner: 'world', repo: 'web', title: process.argv[2] ?? 'Launch checklist' });
console.log(`filed #${issue.number}`);
```

```bash
npm install
npm install -g @volter/world
npm install -D @volter/twin-github
```

## The host

A host's directory holds the host, the console and the twin packages its worlds use, and one
bare world per `<org>/<world>`, laid out as the self-hosted image is. Install them, make the
first world, then serve the directory: the host prints the address it is reached at, its admin
token, and the console's URL. The admin token opens the host's own endpoints and nothing under a
world; keep it where you keep any team secret.

```bash
mkdir -p ../worlds && cd ../worlds
npm install @volter/world-host @volter/world-console @volter/twin-github
volter world init --bare acme/team --twins github --world acme/team
npx volter-host serve --dir . --port 4400 --console-port 4401 &
cd ../acme-web
```

```text
host ready  http://127.0.0.1:4400  1 world
admin token  tok_a_
console      http://127.0.0.1:4401/-/console/
```

## Provision a world through the HTTP API

A second world does not need a shell on the host. `POST /-/worlds` with the admin token makes it,
and `GET /-/worlds` lists every world the host serves with its base URL and its token.

```bash
curl -s -X POST http://127.0.0.1:4400/-/worlds -H "x-volter-token: $(cat ../worlds/.volter-host/admin)" -H 'content-type: application/json' -d '{"org":"acme","world":"staging","vendors":["github"]}'
curl -s http://127.0.0.1:4400/-/worlds -H "x-volter-token: $(cat ../worlds/.volter-host/admin)"
```

```text
"name":"acme/staging"
"name":"acme/team"
```

## Point your world at one of them

A world on a host is a shared world: `remote add` records its URL and token, and `push` sends
your changes to it as a changeset. Nothing about the app changes because the world moved onto a
host.

```bash
volter world init
volter remote add origin http://127.0.0.1:4400/acme/team --token "$(cat ../worlds/acme/team/.volter/token)"
volter world up
volter world run -- node file-issue.mjs
volter world changeset -m "The launch checklist"
volter world push
```

```text
filed #1
changeset  the-launch-checklist  2 changes
pushed  the-launch-checklist  2 changes → origin
```

## The console

Open `http://127.0.0.1:4400/-/console/` and paste the admin token: every world the host serves,
with a form to provision another. Open a world and its twins are listed; a twin opens on its log
and its tree, the same log `volter world log` prints. A world's own token opens the console on that
world alone.

## Remove a world

`DELETE /-/worlds/<org>/<world>` stops the world and removes its directory.

```bash
curl -s -X DELETE http://127.0.0.1:4400/-/worlds/acme/staging -H "x-volter-token: $(cat ../worlds/.volter-host/admin)"
```

```text
"removed":"acme/staging"
```

## Stop the host

The host was started in the background; stop it when you are done. Its directory keeps every
world, so the next `volter-host serve` brings them all back.

```bash
kill %1
```

