Public: anyone can read this; nobody can change it.

A hosted child keeps its parent's scheme and port: a child of http://forge.lan:8080 is http://kid--forge.lan:8080, not https://kid--forge.lan #47

Merged opened this view on origin

Check · Review · Land done · Live — Landed; nothing deploys from this branch, so this is the end of its path.

Merged

`childPublicUrl` built every child's address as `https://<name>--<host>` from a host with its port stripped. Behind the platform (https on 443) that is right. For any parent on plain http or another port it was wrong in two ways: the child was told an https address nothing serves, and it lost the port. Its config `url`, the ledger, `/spawn` answers, the claim link, and its apps' `FORGE_URL` all po…

shipped merged

opened this view on origin

childPublicUrl built every child's address as https://<name>--<host> from a host with its port stripped. Behind the platform (https on 443) that is right. For any parent on plain http or another port it was wrong in two ways: the child was told an https address nothing serves, and it lost the port. Its config url, the ledger, /spawn answers, the claim link, and its apps' FORGE_URL all pointed nowhere.

  • parentOrigin(config) in spawn/placement.ts names the parent's public

origin once: its canonical url when it has one, otherwise http://<host>:<port> as main.ts names itself. It replaces parentHostOf in actions/spawn.ts, which was a copy of childHost.

  • childPublicUrl(name, parent) takes that origin and keeps its scheme and

port (a scheme's default port drops, as URL does). A bare host still means https with no port, so the platform's https://<name>--forge.open-platform.sh and every existing caller and test are unchanged.

  • Every place a child's address is made uses it: the spawn door's placement

and ledger seeding (door.ts), liveChildUrl, the child app's own url (host.ts), and its apps' forge URL (child-apps.ts). Matching a Host to a child (parseHostedChildName) already ignored the port and is unchanged, as is childHost for those host-only uses.

Tests: childPublicUrl and parentOrigin across http/https, explicit, default and missing ports. End to end, a parent at http://forge.lan:8080 spawns kid and the spawn result, liveChildUrl, childUrlOf and the opened child's own config.url are all http://kid--forge.lan:8080. On main the same parent gives https://kid--forge.lan.

Overlap: #2419 (a new or spawned forge starts with signup = invite) changes the child's signup setting, not its address. No open pull touches these files for it, so neither rebases on the other. #41 (first-admin) does not touch placement.

Note: the single-label shape is unchanged, so a child of http://localhost:3000 is http://kid--localhost:3000, which needs a hosts entry or wildcard DNS to resolve, the same as before but now on the right scheme and port.

Tests run by hand (bun 1.3.14, CI env)

bun install --frozen-lockfile, bun scripts/gate.ts, tsc --noEmit and bun scripts/hollow.ts are clean.

Before, on main: childPublicUrl("kid", "forge.lan:8080") gave https://kid--forge.lan.

bun test test/spawn-placement.test.ts test/spawn-host.test.ts:

(pass) hosted child Host dispatch > parent Host still serves the parent
(pass) hosted child Host dispatch > child Host reaches the child's forge
(pass) hosted child Host dispatch > a child's requests are logged in the child's data, never the parent's
(pass) a parent on plain http with a port > its child lives at http, on the parent's port, wherever its address is read
(pass) hosted placement > places a child as a subdomain of the parent, one agent, no SSH
(pass) hosted placement > refuses beyond the children limit, and retiring frees a slot
(pass) hosted placement > refuses past the nesting depth
(pass) hosted placement > refuses a duplicate name; retire is idempotent
(pass) hosted child hostnames > derive a single-label child URL from the parent host
(pass) hosted child hostnames > keep the parent's scheme and port: a child of an http parent is not sent to https
(pass) hosted child hostnames > the parent's origin: its canonical url, else the plain host and port it serves on
(pass) hosted child hostnames > parse a child name from Host
(pass) a hosted child's app label > is bounded by one DNS label, not by the child's name
 13 pass
 0 fail
Ran 13 tests across 2 files.

Every spawn suite and every suite that spawns, places or opens a child (test/spawn*.test.ts, client-home, client-list, first-login, following, new-forge, tui-newforge, usage-count-child, apps-adversarial, hollow, openapi; 27 files): 311 tests, 0 fail.

<!-- pipeline:validation -->

Validation

pipeline/validate on a7daea4d: baseline in 479s — bun 1.3.14, CI-shaped env (scrubbed vars, runner PATH), exact check.yml shard + forge.json commands.

stepresulttime
bun install --frozen-lockfileok (exit 0)0s
forge.json check.fastBASELINE (exit 1)43s
check.yml gate (1) pull_requestBASELINE (exit 1)13s
check.yml gate (2) pull_requestok (exit 0)238s
check.yml gate (3) pull_requestok (exit 0)185s
  • ⚠️ node v20.19.2 here, v22 on CI (tsc runs under node)
  • ⚠️ forge.json check.fast stopped early at a BASELINE failure (fail-fast): its later files were not run in this step
  • ⚠️ check.yml gate (1) pull_request stopped early at a BASELINE failure (fail-fast): its later files were not run in this step
test output (failures, totals)
▶ bun install --frozen-lockfile
  ok  exit 0  0s
▶ forge.json check.fast
(fail) egress, reported: an undeclared public host goes through and is counted; a private one never does > an undeclared public host is let through and counted [1.54ms] [shard 8]
Ran 407 tests across 38 files in 30.8s; 1 failed.
classify: reran test/contain.test.ts test/pull-variants.test.ts test/web.test.ts  alone on origin/main 18917b93 and on this branch
  BASELINE  exit 1  43s
▶ check.yml gate (1) pull_request
(fail) egress, reported: an undeclared public host goes through and is counted; a private one never does > an undeclared public host is let through and counted [1.20ms] [shard 2]
Ran 65 tests across 2 files in 11.9s; 1 failed.
classify: reran test/contain.test.ts  alone on origin/main 18917b93 and on this branch
  BASELINE  exit 1  13s
▶ check.yml gate (2) pull_request
Ran 1260 tests across 128 files in 238.1s (128 shards).
  ok  exit 0  238s
▶ check.yml gate (3) pull_request
Ran 1351 tests across 128 files in 184.9s (128 shards).
  ok  exit 0  185s
Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs