@volter/world-runtime 2.0.1 → 2.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/dist/src/app-url.js +1 -1
  2. package/dist/src/catalog.js +1 -1
  3. package/dist/src/cli.js +3 -3
  4. package/dist/src/console-apart.d.ts +1 -0
  5. package/dist/src/console-apart.js +7 -0
  6. package/dist/src/covers.js +4 -2
  7. package/dist/src/host-worker.js +2 -9
  8. package/dist/src/host.js +2 -9
  9. package/dist/src/import-module.d.ts +1 -0
  10. package/dist/src/import-module.js +16 -0
  11. package/dist/src/index.d.ts +7 -2
  12. package/dist/src/index.js +4 -1
  13. package/dist/src/infra-cli.js +2 -2
  14. package/dist/src/init.d.ts +1 -1
  15. package/dist/src/init.js +26 -0
  16. package/dist/src/local-branches.d.ts +37 -0
  17. package/dist/src/local-branches.js +193 -0
  18. package/dist/src/pglite-backing.js +8 -5
  19. package/dist/src/prerequisites.js +1 -1
  20. package/dist/src/process-groups.js +1 -1
  21. package/dist/src/redirect-proxy.js +2 -2
  22. package/dist/src/root.d.ts +23 -0
  23. package/dist/src/root.js +22 -10
  24. package/dist/src/run-task.js +1 -1
  25. package/dist/src/runtime.js +15 -15
  26. package/dist/src/schema.d.ts +5 -0
  27. package/dist/src/schema.js +10 -1
  28. package/dist/src/served-world.d.ts +196 -9
  29. package/dist/src/served-world.js +847 -103
  30. package/dist/src/service-recorder.js +1 -1
  31. package/dist/src/storage-capacity.js +2 -2
  32. package/dist/src/up-task.js +1 -1
  33. package/dist/src/world-origins.d.ts +13 -0
  34. package/dist/src/world-origins.js +37 -0
  35. package/dist/src/world-view.d.ts +25 -0
  36. package/dist/src/world-view.js +110 -0
  37. package/package.json +6 -5
  38. package/src/app-url.ts +1 -1
  39. package/src/catalog.ts +1 -1
  40. package/src/cli.ts +3 -3
  41. package/src/console-apart.ts +7 -1
  42. package/src/covers.ts +4 -2
  43. package/src/host-worker.ts +2 -1
  44. package/src/host.ts +2 -1
  45. package/src/import-module.ts +9 -0
  46. package/src/index.ts +7 -2
  47. package/src/infra-cli.ts +2 -2
  48. package/src/init.ts +22 -0
  49. package/src/local-branches.ts +173 -0
  50. package/src/pglite-backing.ts +8 -5
  51. package/src/prerequisites.ts +1 -1
  52. package/src/process-groups.ts +1 -1
  53. package/src/redirect-proxy.ts +2 -2
  54. package/src/root.ts +27 -2
  55. package/src/run-task.ts +1 -1
  56. package/src/runtime.ts +15 -15
  57. package/src/schema.ts +11 -1
  58. package/src/served-world.ts +762 -85
  59. package/src/service-recorder.ts +1 -1
  60. package/src/storage-capacity.ts +2 -2
  61. package/src/up-task.ts +1 -1
  62. package/src/world-origins.ts +38 -0
  63. package/src/world-view.ts +100 -0
package/dist/src/root.js CHANGED
@@ -1,11 +1,3 @@
1
- var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
2
- if (typeof path === "string" && /^\.\.?\//.test(path)) {
3
- return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
4
- return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
5
- });
6
- }
7
- return path;
8
- };
9
1
  // A TWIN'S ROOT (docs/concepts/the-model.md): the vendor's real account
10
2
  // behind a twin on a shared world. `volter twin <vendor> root <url> --deploy <policy>` records it,
11
3
  // key-free, in `.volter/world.json` (committed) and, at boot, in the twin's state dir (core's
@@ -22,6 +14,7 @@ import { authStrategyFor, getActiveWorldStore, getPack, sealingKey, buildRemoteE
22
14
  import { loadWorldConfig, writeWorldConfig } from "./configs.js";
23
15
  import { changesetReadiness, collectObservations, foldObservations, loadChecks, openRootCredential, performEntries, rootCustody, protocolStanding } from '@volter/world-core';
24
16
  import { findWorldChangeset, listWorldChangesets } from "./changeset.js";
17
+ import { importModule } from "./import-module.js";
25
18
  import { statusWorld } from "./runtime.js";
26
19
  import { findInstalledPackage, twinPackageName } from "./catalog.js";
27
20
  // ── the user's sealing key ──────────────────────────────────────────────────────────────────
@@ -125,7 +118,7 @@ async function packModule(vendor, from) {
125
118
  if (registered)
126
119
  return { pack: registered };
127
120
  const entry = packEntryPath(vendor, from);
128
- return entry ? (await import(__rewriteRelativeImportExtension(entry))) : null;
121
+ return entry ? await importModule(entry) : null;
129
122
  }
130
123
  /** The pack's module itself, installed beside the World or in this checkout: its every export (a
131
124
  * mirror's shell and client), where the registry holds only the descriptor. */
@@ -144,7 +137,26 @@ export async function packMirrorExports(vendor, from) {
144
137
  return null;
145
138
  const dir = dirname(entry);
146
139
  const file = existsSync(dir) ? readdirSync(dir).find((f) => /-mirror-ui\.(ts|js)$/.test(f) && !f.includes('.test.')) : undefined;
147
- return (await import(__rewriteRelativeImportExtension(file ? join(dir, file) : entry)));
140
+ return importModule(file ? join(dir, file) : entry);
141
+ }
142
+ export async function packMirror(vendor, from) {
143
+ const installed = findInstalledPackage(from, twinPackageName(vendor));
144
+ const dir = installed ?? resolve(import.meta.dir, '..', '..', 'twin', vendor);
145
+ // the pack's mirror module, as the hosted build reads it (its index need not re-export every builder)
146
+ const src = join(dir, 'src');
147
+ const file = existsSync(src) ? readdirSync(src).find((f) => f.endsWith('-mirror-ui.ts')) : undefined;
148
+ const entry = file ? join(src, file) : installed ? packEntry(installed) : join(src, 'index.ts');
149
+ if (!existsSync(entry))
150
+ return null;
151
+ const mod = await importModule(entry);
152
+ const pick = (re) => { const k = Object.keys(mod).find((key) => re.test(key) && typeof mod[key] === 'function'); return k ? mod[k] : undefined; };
153
+ const html = pick(/^\w+MirrorHtml$/);
154
+ const client = pick(/^build\w+MirrorClient$/);
155
+ const styles = pick(/^\w+MirrorStyles$/);
156
+ const signIn = pick(/^\w+MirrorSignIn$/);
157
+ if (!html || !client)
158
+ return null;
159
+ return { html: html, client: client, ...(styles ? { styles: styles } : {}), ...(signIn ? { signIn: signIn } : {}) };
148
160
  }
149
161
  export async function adaptersFor(vendor, from) {
150
162
  const registered = stateSystemFor(vendor);
@@ -7,7 +7,7 @@ import { siblingScript } from "./sibling.js";
7
7
  * The task process is outside the caller's POSIX process group and owns boot through teardown. */
8
8
  export function superviseWorldRun(configId, command, options) {
9
9
  return new Promise((done, reject) => {
10
- const child = spawn(process.execPath, [siblingScript(import.meta.url, 'run-task-worker')], {
10
+ const child = spawn(process.execPath, [siblingScript(import.meta.url, 'run-task-worker')], { windowsHide: true,
11
11
  cwd: process.cwd(), env: process.env, detached: process.platform !== 'win32',
12
12
  stdio: ['inherit', 'inherit', 'inherit', 'ipc'], serialization: 'json',
13
13
  });
@@ -65,7 +65,7 @@ function spawnRecorded(serviceId, command, args, cwd, env, log) {
65
65
  recorderEnv.VOLTER_WORLD_SERVICE_NODE_OPTIONS = recorderEnv.NODE_OPTIONS;
66
66
  delete recorderEnv.NODE_OPTIONS;
67
67
  }
68
- const child = spawn(process.execPath, [siblingScript(import.meta.url, 'service-recorder'), command, ...args], {
68
+ const child = spawn(process.execPath, [siblingScript(import.meta.url, 'service-recorder'), command, ...args], { windowsHide: true,
69
69
  cwd,
70
70
  env: recorderEnv,
71
71
  detached: true,
@@ -614,7 +614,7 @@ function processStarts(pids) {
614
614
  }
615
615
  return starts;
616
616
  }
617
- const result = spawnSync('ps', ['-o', 'pid=,lstart=', '-p', pids.join(',')], { encoding: 'utf8', timeout: 1000, killSignal: 'SIGKILL', env: { ...process.env, LC_ALL: 'C', LANG: 'C', TZ: 'UTC' } });
617
+ const result = spawnSync('ps', ['-o', 'pid=,lstart=', '-p', pids.join(',')], { windowsHide: true, encoding: 'utf8', timeout: 1000, killSignal: 'SIGKILL', env: { ...process.env, LC_ALL: 'C', LANG: 'C', TZ: 'UTC' } });
618
618
  for (const line of (result.stdout ?? '').split('\n')) {
619
619
  const match = /^\s*(\d+)\s+(.+?)\s*$/.exec(line);
620
620
  if (match)
@@ -768,7 +768,7 @@ async function runExternalCommand(command, cwd, serviceId, phase, env = process.
768
768
  if (!commandExists(bin))
769
769
  throw new Error(`External service "${serviceId}": \`${bin}\` not found on PATH (the ${phase} command's tool is not installed)`);
770
770
  abort?.throwIfAborted();
771
- const child = spawn(bin, args, { cwd, env, detached: commandProcessGroup, stdio: ['ignore', 'pipe', 'pipe'] });
771
+ const child = spawn(bin, args, { windowsHide: true, cwd, env, detached: commandProcessGroup, stdio: ['ignore', 'pipe', 'pipe'] });
772
772
  const lifetime = commandLifetime(child, abort, true);
773
773
  let stdout = '';
774
774
  let stderr = '';
@@ -815,7 +815,7 @@ async function runExternalUp(command, cwd, serviceId, env, logPath, abort) {
815
815
  const chunks = [];
816
816
  try {
817
817
  abort?.throwIfAborted();
818
- const child = spawn(bin, args, { cwd, env, detached: commandProcessGroup, stdio: ['ignore', 'pipe', 'pipe'] });
818
+ const child = spawn(bin, args, { windowsHide: true, cwd, env, detached: commandProcessGroup, stdio: ['ignore', 'pipe', 'pipe'] });
819
819
  const lifetime = commandLifetime(child, abort, true);
820
820
  let failure;
821
821
  child.on('error', error => { failure = error; });
@@ -2237,10 +2237,10 @@ export function ownCommand(command) {
2237
2237
  return [process.execPath, siblingScript(import.meta.url, stem), ...args];
2238
2238
  }
2239
2239
  function onPath(command) {
2240
- return spawnSync('which', [command], { stdio: 'ignore', env: process.env }).status === 0;
2240
+ return spawnSync('which', [command], { windowsHide: true, stdio: 'ignore', env: process.env }).status === 0;
2241
2241
  }
2242
2242
  function resolveWithPublicDns(hostname) {
2243
- const result = spawnSync('dig', ['+short', '@1.1.1.1', hostname, 'A'], {
2243
+ const result = spawnSync('dig', ['+short', '@1.1.1.1', hostname, 'A'], { windowsHide: true,
2244
2244
  encoding: 'utf8',
2245
2245
  });
2246
2246
  if (result.status !== 0)
@@ -2276,7 +2276,7 @@ async function verifyPublicUrl(publicUrl, path, timeoutMs) {
2276
2276
  || (isIP(normalizedHostname) === 4 && normalizedHostname.split('.')[0] === '127');
2277
2277
  if (local) {
2278
2278
  const target = publicHealthUrl(publicUrl, path);
2279
- const result = spawnSync('curl', ['--noproxy', '*', '-sS', '-o', '-', '-w', '\n%{http_code}', '--max-time', String(Math.max(1, Math.ceil(timeoutMs / 1000))), target], { encoding: 'utf8' });
2279
+ const result = spawnSync('curl', ['--noproxy', '*', '-sS', '-o', '-', '-w', '\n%{http_code}', '--max-time', String(Math.max(1, Math.ceil(timeoutMs / 1000))), target], { windowsHide: true, encoding: 'utf8' });
2280
2280
  const separator = result.stdout.lastIndexOf('\n');
2281
2281
  const body = separator >= 0 ? result.stdout.slice(0, separator).trim() : result.stdout.trim();
2282
2282
  const status = Number(separator >= 0 ? result.stdout.slice(separator + 1).trim() : '0');
@@ -2307,7 +2307,7 @@ async function verifyPublicUrl(publicUrl, path, timeoutMs) {
2307
2307
  '--resolve',
2308
2308
  `${hostname}:443:${resolvedIp}`,
2309
2309
  target,
2310
- ], { encoding: 'utf8' });
2310
+ ], { windowsHide: true, encoding: 'utf8' });
2311
2311
  if (result.status === 0) {
2312
2312
  const output = result.stdout;
2313
2313
  const separator = output.lastIndexOf('\n');
@@ -2370,7 +2370,7 @@ async function shareWorldReserved(name, options) {
2370
2370
  // Each spawned tunnel owns one log generation. Truncating before spawn prevents a repeated
2371
2371
  // share from accepting the previous process's URL handshake as the new process's endpoint.
2372
2372
  const out = openPrivateLog(log, 'w');
2373
- const child = spawn(tunnel.command, tunnel.args, {
2373
+ const child = spawn(tunnel.command, tunnel.args, { windowsHide: true,
2374
2374
  cwd: root,
2375
2375
  env: process.env,
2376
2376
  detached: true,
@@ -2540,7 +2540,7 @@ async function probeExternalOnce(ready, cwd, env, logPath) {
2540
2540
  return { ok: false, message: `readiness command \`${ready.command}\` not found on PATH` };
2541
2541
  }
2542
2542
  const [probeBin, ...probeArgs] = ownCommand([ready.command, ...(ready.args ?? [])]);
2543
- const result = spawnSync(probeBin, probeArgs, {
2543
+ const result = spawnSync(probeBin, probeArgs, { windowsHide: true,
2544
2544
  cwd, encoding: 'utf8', env, timeout: DOCTOR_EXTERNAL_PROBE_TIMEOUT_MS, maxBuffer: 64 * 1024 * 1024,
2545
2545
  });
2546
2546
  if (result.status === 0)
@@ -2914,7 +2914,7 @@ export function runWithWorldEnv(name, command, root = process.cwd(), options = {
2914
2914
  throw new Error('Missing command after --');
2915
2915
  const attached = worldAttachedCommandEnv(name, root, options.verbose);
2916
2916
  return new Promise((resolveExit) => {
2917
- const child = spawn(command[0], command.slice(1), {
2917
+ const child = spawn(command[0], command.slice(1), { windowsHide: true,
2918
2918
  cwd: resolve(options.cwd ?? root),
2919
2919
  env: attached.env,
2920
2920
  stdio: 'inherit',
@@ -2998,7 +2998,7 @@ export async function attachWorld(name, command, root = process.cwd(), options =
2998
2998
  let retired = true;
2999
2999
  try {
3000
3000
  return await new Promise((done, reject) => {
3001
- const child = spawn(command[0], command.slice(1), { cwd: resolve(options.cwd ?? root), env: attached.env, stdio: 'inherit', detached: commandProcessGroup });
3001
+ const child = spawn(command[0], command.slice(1), { windowsHide: true, cwd: resolve(options.cwd ?? root), env: attached.env, stdio: 'inherit', detached: commandProcessGroup });
3002
3002
  const lifetime = commandLifetime(child);
3003
3003
  let failure;
3004
3004
  record.consumerPid = child.pid;
@@ -3099,7 +3099,7 @@ async function runWithWorldEnvLogged(name, command, root, options = {}) {
3099
3099
  let spawnError;
3100
3100
  let child;
3101
3101
  try {
3102
- child = spawn(command[0], command.slice(1), {
3102
+ child = spawn(command[0], command.slice(1), { windowsHide: true,
3103
3103
  cwd: resolve(options.cwd ?? root),
3104
3104
  env: attached.env,
3105
3105
  stdio: ['inherit', 'pipe', 'pipe'],
@@ -3206,7 +3206,7 @@ export async function shellWorld(name, root = process.cwd()) {
3206
3206
  process.stderr.write(`world '${name}' active in a subshell — vendor calls hit the twins (env-only; install openssl for ambient https redirect). type 'exit' to leave.\n`);
3207
3207
  }
3208
3208
  try {
3209
- const result = spawnSync(shell, ['-i'], { cwd, env, stdio: 'inherit' });
3209
+ const result = spawnSync(shell, ['-i'], { windowsHide: true, cwd, env, stdio: 'inherit' });
3210
3210
  return result.status ?? 0;
3211
3211
  }
3212
3212
  finally {
@@ -3314,7 +3314,7 @@ function startProxyDaemonOwned(name, root, caCertPath, envFile, boot, caller = p
3314
3314
  mkdirSync(dirname(proxyLogPath(root, name)), { recursive: true, mode: 0o700 });
3315
3315
  const proxyOut = openPrivateLog(proxyLogPath(root, name), 'a');
3316
3316
  try {
3317
- child = spawn(process.execPath, [proxyDaemonEntry(), name, '--root', root, ...(envFile ? [`--env-file=${envFile}`] : [])], {
3317
+ child = spawn(process.execPath, [proxyDaemonEntry(), name, '--root', root, ...(envFile ? [`--env-file=${envFile}`] : [])], { windowsHide: true,
3318
3318
  cwd: root, env: process.env, detached: true, stdio: ['ignore', proxyOut, proxyOut],
3319
3319
  });
3320
3320
  }
@@ -190,6 +190,11 @@ export type WorldServiceConfig = {
190
190
  * to loopback services directly while presenting a disguised world to the app/user.
191
191
  */
192
192
  controlPlane?: boolean;
193
+ /** The account a vendor's own screens (its mirror) open signed in as: its handle, username or email, which the pack
194
+ * resolves against the accounts this World holds (`<vendor>MirrorSignIn`). Nothing secret is written here. */
195
+ signIn?: {
196
+ as: string;
197
+ };
193
198
  injectEnv?: string;
194
199
  /**
195
200
  * Additional env vars exported into the generated world env after this service starts.
@@ -358,7 +358,7 @@ export function assertWorldSelection(value, path) {
358
358
  function serviceV2ToLegacy(value, path) {
359
359
  if (!record(value))
360
360
  throw new Error(`World service must be an object in ${path}`);
361
- assertKeys(value, ['id', 'type', 'description', 'source', 'execution', 'endpoint', 'bindings', 'root'], `Service ${String(value.id ?? '')}`, path, true);
361
+ assertKeys(value, ['id', 'type', 'description', 'source', 'execution', 'endpoint', 'bindings', 'root', 'signIn'], `Service ${String(value.id ?? '')}`, path, true);
362
362
  const source = value.source === undefined ? {} : value.source;
363
363
  const execution = value.execution === undefined ? {} : value.execution;
364
364
  const endpoint = value.endpoint === undefined ? {} : value.endpoint;
@@ -379,6 +379,13 @@ function serviceV2ToLegacy(value, path) {
379
379
  throw new Error(`World service must define string id in ${path}`);
380
380
  if (value.description !== undefined && typeof value.description !== 'string')
381
381
  throw new Error(`Service "${value.id}" description must be a string in ${path}`);
382
+ if (value.signIn !== undefined) {
383
+ if (!record(value.signIn))
384
+ throw new Error(`Service "${value.id}" signIn must be an object in ${path}`);
385
+ assertKeys(value.signIn, ['as'], `Service "${value.id}" signIn`, path);
386
+ if (typeof value.signIn.as !== 'string' || value.signIn.as.trim() === '')
387
+ throw new Error(`Service "${value.id}" signIn.as must name an account (a handle, username or email) in ${path}`);
388
+ }
382
389
  if (value.description !== undefined && value['//'] !== undefined)
383
390
  throw new Error(`Service "${value.id}" must not set both description and // in ${path}`);
384
391
  if (execution.cwd !== undefined && typeof execution.cwd !== 'string')
@@ -437,6 +444,7 @@ function serviceV2ToLegacy(value, path) {
437
444
  ...(bindings.injectEnvTemplates === undefined ? {} : { injectEnvTemplates: bindings.injectEnvTemplates }),
438
445
  ...(bindings.cliRedirect === undefined ? {} : { cliRedirect: bindings.cliRedirect }),
439
446
  ...(value.root === undefined ? {} : { root: value.root }),
447
+ ...(value.signIn === undefined ? {} : { signIn: { as: value.signIn.as } }),
440
448
  ...(external === undefined ? {} : { external }),
441
449
  };
442
450
  if (type !== 'external' && bindings.discover !== undefined)
@@ -573,6 +581,7 @@ function serviceToV2(service) {
573
581
  ...(Object.keys(endpoint).length ? { endpoint } : {}),
574
582
  ...(Object.keys(bindings).length ? { bindings } : {}),
575
583
  ...(service.root === undefined ? {} : { root: service.root }),
584
+ ...(service.signIn === undefined ? {} : { signIn: service.signIn }),
576
585
  };
577
586
  }
578
587
  /** Serialize normalized intent as the committed format-2 manifest. */
@@ -1,5 +1,6 @@
1
1
  import { type HistoryReference } from '@volter/world-core';
2
2
  import { type TwinStreamConnection, type TwinStreamSink } from '@volter/world-core';
3
+ import { type TrustedIssuer } from '@volter/world-access';
3
4
  import { type Changeset, type Receipt } from '@volter/world-core';
4
5
  import { rootForControlRoot } from './root.js';
5
6
  import type { WorldInstance } from './schema.js';
@@ -29,6 +30,11 @@ export type ServedWorld = {
29
30
  console: string | null;
30
31
  stop: () => Promise<void>;
31
32
  };
33
+ /** Where a World keeps its browser sessions (WorldDoors): a host that rotates the tokens without the doors at hand
34
+ * clears it itself. */
35
+ export declare function sessionsFile(worldRoot: string): string;
36
+ /** Where a World keeps its named keys (their hashes, never a key): `.volter/keys.json`. */
37
+ export declare function keysFile(worldRoot: string): string;
32
38
  /** The served name: `bare.name` (`acme/team`), else `<id>/<id>`. */
33
39
  export declare function servedName(worldRoot: string, configRef: string): string;
34
40
  /** A world MOUNTED for serving: its doors, its tokens, and a boot that writes the serve record once the
@@ -53,7 +59,11 @@ export type MountedWorld = {
53
59
  stop: () => Promise<void>;
54
60
  };
55
61
  export declare function mountWorld(name: string, opts?: {
56
- root?: string;
62
+ passIssuers?: TrustedIssuer[];
63
+ root?: string; /** the host's branches of this World, by its served name */
64
+ branches?: (served: string) => BranchDoors | undefined; /** the origin a browser reaches this World at, when the host gives each World one */
65
+ browserOrigin?: (served: string) => string | null; /** served on this machine's loopback: its own pages need no token (DoorHost.localTrust) */
66
+ localTrust?: boolean;
57
67
  }): Promise<MountedWorld>;
58
68
  /** Boot the world (state kept) and serve it on its own port. Announces and returns after boot; `stop` downs the world. */
59
69
  export declare function serveWorld(name: string, opts?: {
@@ -99,7 +109,78 @@ export type DoorHost = {
99
109
  /** Where this host reads a twin's scenario (the handlers document a generative twin serves from,
100
110
  * re-read on every request), or null when the twin takes none here. */
101
111
  scenarioPath?: (vendor: string) => string | null;
112
+ /** A twin's mirror as its pack builds it: `index.html` (the shell) or `assets/<file>`, or null when
113
+ * the twin has none here. Every host answers the same mount (`/<served>/<vendor>/mirror/`); only
114
+ * where the files come from differs (built at serve under Bun, prebuilt under Node, static assets
115
+ * on Cloudflare). */
116
+ mirror?: (vendor: string, file: string) => Promise<Response | null>;
117
+ /** Whether the twin has a mirror on this host, without building it. */
118
+ hasMirror?: (vendor: string) => boolean | Promise<boolean>;
119
+ /** The account and token a twin's screens open signed in as, resolved by its pack from the twin's state at `root`
120
+ * for the account the World's config names (`signIn.as`); null when the pack cannot, or has no such account. */
121
+ mirrorSignIn?: (vendor: string, as: string, ctx: {
122
+ root: string;
123
+ twin: (path: string, init?: RequestInit) => Promise<Response>;
124
+ }) => Promise<{
125
+ account: string;
126
+ token: string;
127
+ } | null>;
128
+ /** Where this World's clock lives in its store (the file every twin reads `worldNow()` from). */
129
+ clockPath?: () => string;
130
+ /** The origin a browser reaches this World at (`http://<world>--<org>.localhost:<port>`), when the
131
+ * host gives each World one: its session lives only there (docs/contributing/architecture.md,
132
+ * "Viewing a World"). Null, or absent, on a host whose Worlds share one origin. */
133
+ browserOrigin?: () => string | null;
134
+ /** The origin another World this host serves (a branch of this one) is reached at, where it has one of its own. */
135
+ originOf?: (served: string) => string | null;
136
+ /** This World is served on this machine's loopback, for the person at it: a page of this World, reached by a
137
+ * loopback name, is given the World's session without a token (`localSession`). A remote host leaves it absent,
138
+ * and its pages sign in with a token. */
139
+ localTrust?: () => boolean;
140
+ /** The platforms whose passes open this World (docs/contributing/architecture.md, "The hosted product"): a person a
141
+ * trusted platform signed a pass for is given a session of the pass's scope. Absent, or empty, no pass opens it. */
142
+ passIssuers?: () => TrustedIssuer[];
143
+ /** This World's branches, where the host can make them (a branch is another World, so making one
144
+ * is the host's act); absent, the branches doors answer 404. */
145
+ branches?: BranchDoors;
146
+ };
147
+ /** A branch as its parent lists it. */
148
+ export type BranchRow = {
149
+ name: string;
150
+ from: string;
151
+ at: {
152
+ instant?: string;
153
+ live?: boolean;
154
+ label?: string;
155
+ } | null;
156
+ createdAt: string;
157
+ expiresAt: string | null;
102
158
  };
159
+ /** What a host does for a World's branches doors. `create` makes a World cloned from this one at
160
+ * `at` (its history cut at that instant), removed when `ttlSeconds` runs out; `origin` is where the
161
+ * request reached this World (a host whose branches clone over its public URL uses it). */
162
+ export type BranchDoors = {
163
+ list: () => Promise<BranchRow[]>;
164
+ /** `parentKey`: the key the branch reaches this World with (never this World's own token), made for whoever asked */
165
+ create: (at: {
166
+ instant?: string;
167
+ live?: boolean;
168
+ label?: string;
169
+ }, ttlSeconds: number | null, origin: string, parentKey?: string) => Promise<{
170
+ name: string;
171
+ token: string;
172
+ readToken: string;
173
+ expiresAt: string | null;
174
+ }>;
175
+ remove: (name: string) => Promise<boolean>;
176
+ };
177
+ /** A World's browser-origin label, `<world>--<org>`, or null when its names cannot make one (a DNS
178
+ * label: lowercase letters, digits and single hyphens, 63 at most). A host maps a label back by the
179
+ * Worlds it serves, never by parsing it. */
180
+ export declare function worldOriginLabel(served: string): string | null;
181
+ /** The header the doors set on a read-scope request to a twin: the twin refuses the caller's writes
182
+ * and still makes the vendor's own moves. It only restricts, so a twin trusts it without a token. */
183
+ export declare const READ_ONLY_HEADER = "x-volter-read-only";
103
184
  export declare class WorldDoors {
104
185
  readonly name: string;
105
186
  readonly worldRoot: string;
@@ -112,6 +193,46 @@ export declare class WorldDoors {
112
193
  constructor(name: string, worldRoot: string, configRef: string, served: string, host: DoorHost, token: string, readToken: string);
113
194
  /** New tokens; a request presenting the old ones is refused from the next call on. */
114
195
  retoken(token: string, readToken: string): void;
196
+ /** BROWSER SESSIONS: the session cookie carries an opaque id, never a token, so a leaked cookie is one session and
197
+ * not the World's write token; rotating the tokens ends every session. Kept in the World's store, so a restart
198
+ * keeps them; each lasts 30 days from its opening. */
199
+ private sessionsPath;
200
+ private held;
201
+ private sessions;
202
+ private keepSessions;
203
+ /** A new session of `scope`: its id, the cookie's value. A token's lasts 30 days; a person's from a pass, `who`,
204
+ * lasts as long as the pass's platform says (twelve hours), so a person removed there loses it within the day. */
205
+ private openSession;
206
+ /** NAMED KEYS: what an app or a script holds (docs/contributing/architecture.md, "The hosted product"), each named
207
+ * when made, shown once, kept only as its hash, and revoked alone; the World's two tokens stay its first keys. */
208
+ private heldKeys;
209
+ private keys;
210
+ private keepKeys;
211
+ /** When a key was last used, to the minute (a key in constant use is not a write per request). */
212
+ private touchKey;
213
+ /** `GET|POST /-/<world>/keys`, `DELETE /-/<world>/keys/<id>`, `DELETE /-/<world>/keys?person=<sub>`: named keys are
214
+ * managed with write access by the World's token or a person's session, never by a key (a key cannot make a key
215
+ * that outlives it). Each records who made it and whom it is for; the token may give one an end and a person, and
216
+ * revoke every key made by or for a person (their leaving the org). */
217
+ /** The person a key is for: whom it was made for, else the person whose session made it. */
218
+ private keyPerson;
219
+ /** Revoke the keys `pick` names, and what they reach: the branches whose parent key goes, and the branches (with their
220
+ * keys) a revoked key made. Answers the revoked keys' ids. */
221
+ private revokeKeys;
222
+ private keysDoor;
223
+ /** Passes this World has already spent: each opens one session. */
224
+ private readonly spent;
225
+ /** A PASS opens a session: a person a trusted platform signed a pass for, into this World, at its own origin, asked
226
+ * from its own page (the console reads the pass from its address's fragment and posts it here). */
227
+ /** The org its host records as this World's owner (`<state>/owner`, written at its making or its claim), or null. */
228
+ private recordedOwner;
229
+ private passSession;
230
+ /** A named key that still opens this World: not revoked, not expired. */
231
+ private liveKey;
232
+ /** The named key a request presents, with the end of its life. */
233
+ private presentedKey;
234
+ private endSession;
235
+ private endSessions;
115
236
  /** The world's twins, by vendor, once booted. */
116
237
  twins(): string[];
117
238
  private up;
@@ -119,8 +240,29 @@ export declare class WorldDoors {
119
240
  * the injector's `x-twins-key` (a zero-edit app keeps its SDK's own Authorization), `Bearer <token>`,
120
241
  * GitHub's `token <token>`, or the password half of `Basic` (Jira's email:token). */
121
242
  private scope;
122
- /** The World token the request presented, in whichever form, and the scope it opens. */
123
- private match;
243
+ /** The request's scope, whether it came by a token the caller presented or by a browser's session
244
+ * (the cookie `POST …/session` set), and the token that opened it. Every form presented is a
245
+ * candidate: a mirror page sends the VENDOR's bearer (the token its sign-in holds) beside the World's
246
+ * session cookie, and the cookie is what opens the World. With a browser origin of its own
247
+ * (`browserOrigin`), a World honours its session only on that origin: a session cookie on any other
248
+ * (a host's shared origin, path-addressed) opens nothing. */
249
+ /** What this World issued, for world-access to decide a grant against: its two tokens, its named keys, its sessions. */
250
+ private issued;
251
+ private credential;
252
+ /** The session cookie's name where `url` reached this World, or null where a session opens nothing:
253
+ * on the World's own origin a host-only cookie (`__Host-` over https); without an origin of its own,
254
+ * the path-scoped cookie a shared origin has always carried. */
255
+ private sessionCookieName;
256
+ /** The session cookie for `url`, set to `value`: host-only over the whole origin on the World's own
257
+ * origin (a `__Host-` cookie must be), scoped to the World's paths on a shared one. Null where a
258
+ * session opens nothing (another World's origin). */
259
+ private sessionCookie;
260
+ /** A browser's session opens the World only to the World's own pages: a request the session cookie carries must
261
+ * say `Sec-Fetch-Site: same-origin` (a browser sets it; a page cannot), or be a read the person navigated to
262
+ * (`none`: a typed or bookmarked address). Another page of the same site (a sibling World's origin shares its
263
+ * suffix) is refused its reads too, so a twin that echoes the caller's Origin with credentials cannot hand one
264
+ * World's data to another World's page; a request with no header fails closed. Null when it may pass. */
265
+ private crossOriginSession;
124
266
  /** Open one connection on a byte-stream door for the holder of the World's (write) token; null when
125
267
  * the token is not the World's or the World has no such stream. */
126
268
  openStream(token: string, id: string, sink: TwinStreamSink, peer: string): TwinStreamConnection | null;
@@ -129,14 +271,24 @@ export declare class WorldDoors {
129
271
  private controlRoot;
130
272
  private stateOf;
131
273
  handle(request: Request): Promise<Response>;
274
+ /** THE MIRROR MOUNT: `/<served>/<vendor>/mirror/` and `…/mirror/assets/<file>`, keyless and GET only —
275
+ * the pack's own shell and client. The shell's <base> is the twin's place in this World, so the
276
+ * mirror reads and writes the World's wire with the browser's session (`POST …/session`); any
277
+ * other path under mirror/ is one of the mirror's own routes (a reloaded deep link) and gets the
278
+ * shell. A link is not a twin and has no mirror. */
279
+ private mirror;
280
+ /** A vendor's screens' sign-in (`signIn.as`), once per account every twelve hours while this World is served (rotating the
281
+ * tokens forgets them): the pack finds the account and mints its credential through the twin's own doors; a failure
282
+ * is not kept, so the next page asks again. An account or credential that is not plain printable text is refused. */
283
+ private readonly signIns;
284
+ private readonly signInAt;
285
+ private signedIn;
286
+ /** Whether a twin enforces a read-scope request itself (its manifest names `requestScopes: ['read']`):
287
+ * only then may the read token reach it with anything but GET or HEAD. Asked once per twin. */
288
+ private readonly readEnforced;
289
+ private enforcesRead;
132
290
  /** The vendor wire: `/<org>/<world>/<vendor>/<rest>` → the twin's own URL. */
133
291
  private wire;
134
- /** Whether the twin's pack ships a mirror (its vendor's UI over this World's state). */
135
- private hasMirror;
136
- /** THE MIRROR MOUNT, as the hosted World serves it (apps/cloud supervisor.ts): `/<org>/<world>/<vendor>/mirror/`
137
- * and `…/mirror/assets/*`, keyless, GET only — the pack's own shell and bundle. The shell's <base> is the
138
- * twin's place under this World, so its reads go to the keyed wire with the browser's World session. */
139
- private mirror;
140
292
  private linksPath;
141
293
  /** The World's links: name → the vendor origin it forwards to. */
142
294
  links(): Record<string, {
@@ -151,6 +303,41 @@ export declare class WorldDoors {
151
303
  private observedAt;
152
304
  /** The world's own doors under `/-/<org>/<world>/`. */
153
305
  private door;
306
+ /** A browser session for this World: the presented token as an HttpOnly cookie scoped to the World's
307
+ * paths, so a page served under it (a twin's mirror) reads the wire as the token's holder, in the
308
+ * token's scope. */
309
+ /** THE LOCAL SESSION: a World served on this machine's loopback never asks the person at it for a token. Any
310
+ * website can send requests to 127.0.0.1, so what stands in for the token is proof the request is this World's own
311
+ * page, as Vite and Jupyter check their dev servers: the Host is a loopback name (a rebound DNS name is not, so DNS
312
+ * rebinding fails), the browser's Origin is exactly this origin (another site's page cannot claim it, so CSRF
313
+ * fails), and the fetch says it is same-origin. A process on this machine can claim all three: a local World
314
+ * trusts this machine's user, as a local dev server does. The session is the write token's. A World with no origin
315
+ * of its own (a shared origin, where every World's pages are one origin) is not trusted so: its page asks for a
316
+ * token. */
317
+ private localPage;
318
+ private localSession;
319
+ private session;
320
+ /** THE CLOCK: the frozen instant every twin stamps from, kept in the World's store. It moves only
321
+ * forward once the World has entries (a twin's catch-up stamps each move at its due time, so a
322
+ * clock set back would put new entries before old ones); the past is reached by branching. */
323
+ private clockDoor;
324
+ /** THE BRANCHES DOORS: list (read), make one as of an instant, remove one (write). The host makes
325
+ * and removes them; a host that makes none answers 404, and a view offers no "as of". */
326
+ private branchesDoor;
327
+ /** The newest entry's instant across the World's twins (ms), or null when it has none. */
328
+ private newestEntry;
329
+ /** THE MAP: every twin and what it holds, by resource type, and whether it has a mirror. */
330
+ private map;
331
+ /** THE TIMELINE: the entries of every twin's log merged, newest first, by each entry's instant, then
332
+ * twin, then position. The World keeps no order across twins finer than its clock, and this claims
333
+ * none. `before` is the cursor the previous page answered; `twin` and `trace` narrow it. */
334
+ private timeline;
335
+ /** Each twin's timeline rows, kept until its logs change: a poll of an unchanged World reads no log
336
+ * (a twin is read again only when the size or time of its parent log, branch log or branch record
337
+ * moved), and the merged order is sorted again only when a twin was. */
338
+ private readonly timelineByTwin;
339
+ private timelineMerged;
340
+ private timelineRows;
154
341
  /** PUSH: see `landChangeset` — the door hands the body to it. */
155
342
  private push;
156
343
  }