# WEBSERVE (#272) — intake JIT (wave map + REQ ids)

Authored by doyle 2026-09-06 ~10:30Z on the operator's greenlight ("shared understanding
confirmed. proceed with WEBSERVE"). Design record: ADR-0056..0061 + CONTEXT.md `## Web
serving` on branch `docs/webserve-272-grill` (@a87bd42a, off main; lands as the first thin
PR after v0.67.1). Grill rounds: `WEBSERVE-272-GRILL-ROUND-1.md`, `-ROUND-2.md`; board record
#272 comments 5558410252 + 5558560333. Milestone version: **v0.68.0**.

## Members (Q6 ruling)

Ride: #248 (registry / generalized fileserving), #249 (cross-node webserving), #246
(endpoint-to-endpoint transfer = attachments), #265 (adapter docs hosting), #268 (changelog in
docs), #147 (message short-ID + CLI + web URI), #266 (bootstrap hosting, per Q5).
Closing rider: #17 (FILE_ACCESS_HELPER now-signal; EVAL, blocked-by #246 — flips with W2).
Deferred: #148 (outbound ledger — additive, nothing blocks on it; DETACHED, stays BACKLOG).
Shelved: #271 (09:41Z).

## Standing rules for every lane

- Base off `origin/main` AFTER v0.67.1 lands (deployah's land call); thin lanes, ff-only,
  golden per ADR-0050 as a milestone batch at the end (deployah runs it; I assemble the head).
- Worktree under `.worktrees/ws272-<wave>`, own pool, claim from inside.
- `traceable-reqs.toml` FIRST (mint the wave's REQ ids below, no `"` in titles), then
  `traceable-reqs check` (exit 2 = did not parse).
- Gate (mine, per wave): treqs → workspace-bins prebuild → `xtask check` (docs drift +
  spacerun) → clippy → `nextest --no-fail-fast` filtered to the wave's crates + the e2e
  binaries it touches → same legs on kitsubito → exit FILES read → pick-audit at land.
- **Lane battery shape — RULED doyle 2026-09-07 02:40Z (operator: heavy suites belong to golden):**
  the builder's per-lane battery is TARGETED, not a full sweep — treqs, `xtask check`, clippy
  (workspace, all targets), `nextest --no-fail-fast` over the touched crates' units + the lane's
  own e2e/int bins, mdbook if docs moved; ~15 min warm. The full workspace sweep runs ONCE, on the
  golden head (deployah). W0/W1 ran full 3000-test sweeps per head per box (the driver template's
  default) and paid ~3 box-hours for it; from W2 the driver's nextest leg carries a filter
  expression and the full sweep is not in the template. The thin CI `unit` jobs stay as the
  repo has them (INFRA-REGISTER candidate: they are full-suite runs on boxes shared with
  batteries).
  **IR-76 pre-flight**: three-arm golden check before any local nextest/build on hfenduleam.
- Docs are the contract adapters build from: every wave lands its docs-site page(s) in the
  same commit (harness-contract + `reference.md` via `--help`), and CONTEXT.md terms are
  already ratified — do not re-word them, cite them.
- Vocabulary is CONTEXT.md's: served resource, node-prefixed URL, serving registry, served
  root, served name, attachment, fetch, bootstrap surface, message short-ID.

## W0 — the serving substrate (todlando) — #248 + the local half of #249

- **REQ-WEB-URL-NODE-PREFIX** (ADR-0056; doc, impl, unit, int): the 5474 server answers
  `/<local-node>/…` for every facet; bare `/` → 302 to `/<local-node>/`; the docs server's
  bare paths stay as a compat alias (byte-true, REQ-DOCS-LOCAL-SERVER's surface contract
  untouched); sub-namespaces `f/`, `docs/`, `a/`, `m/`, `bin/`+`install` are reserved at the
  router even where the facet is not built yet (404 with the facet name, never a registry
  lookup); `/<local-node>/` renders the index page (the registry, path → URL, HTML + a
  `?json` twin for agents). A request for `/<other-node>/…` answers 502-with-node until W1.
- **REQ-WEB-SERVING-REGISTRY** (ADR-0057; doc, impl, unit, int): `$SPT_HOME/serve/registry`
  store (spt-store owns format + read path; daemon is the single writer); entry = id, kind
  (file | dir | attachment), absolute source path (file/dir) or snapshot path (attachment),
  served name, registered-at, ttl (attachments only), origin (endpoint id). Served-name rule
  exactly as ADR-0057: bare name to the first registrant, `<stem>~<n>.<ext>` after, the
  counter per `(stem, ext)` persisted, freed names never reused, same absolute path returns
  the same entry, `--as` honoured under the same rule. Verbs: `spt serve add <path> [--as
  <name>]`, `spt serve rm <name|id>`, `spt serve list [--json]` (path → URL). Manifest:
  `web_short_path` → a dir entry registered at adapter activation, removed at deactivation.
  Reference-served (file/dir) resolves at request time (edit visible, deleted → 404).
- **ADR-0057 Amendment 2 — RULED doyle 2026-09-06 16:35Z (W0 smoke finding):** todlando's
  smoke showed adapter reactivation allocating `reports~1` with `reports` left 404 — the
  letter of "entry removed at deactivation" + "freed name never reused" makes every
  `adapter update` advance the adapter's alias, and `spt serve rm` + re-`add` of the same path
  does the same. That defeats rule "re-registering the same absolute path returns the
  existing entry" only because the entry was deleted first. Amend (todlando carries the text
  into `docs/adr/0057-…md` in the W0 commit, docs page + STORAGE.md updated to match):
  > **Amendment 2 — a freed name may be re-taken by the path it last named.** The
  > never-reuse rule protects a link from changing MEANING. A name whose last registrant was
  > absolute path P may be assigned again to a registration of P (same kind), and only to P;
  > any other path takes the next suffix as before. The history therefore records
  > `(name → last path)` rather than bare names. Consequence: an adapter's alias and served
  > root survive deactivate/activate and `adapter update` unchanged; a stale link still
  > answers *not found* or the same file it always named, never a different one.
  Acceptance amended: after removing the first `report.md` and re-adding the SAME file it is
  `report.md` again; re-adding a DIFFERENT third file is `report~2.md`. Adapter
  deactivate → activate keeps `reports` (no `~1`).
- **REQ-MANIFEST-UNKNOWN-KEY-WARN** (W0 rider, RULED doyle 2026-09-06 18:15Z from hertz's
  W3 RED finding; doc, impl, unit; mint in W0's toml): manifest.rs uses NO
  `deny_unknown_fields` anywhere, by design (forward-compat for adapters built blind; the
  ignore is asserted at manifest.rs:1839). `web_short_path` is the first `[adapter]` key whose
  misspelling is visible only by ABSENCE (no alias, no error), and `docs_dir` follows in W3.
  Tolerant branch that keeps the invariant: at `adapter add` / activation, every key not in
  the table's known set emits ONE stderr line `manifest: unknown key [<table>].<key> (ignored)`
  and registration proceeds. Preferred shape: a generic walk of the parsed `toml::Value`
  against the schemars-derived schema's `properties` (the key sets already exist in
  `manifest.schema.json`); fallback if that is not cheap: `[adapter]` only, with the sweep
  extended per table later. Unit: a `web_short_pth` typo registers AND the diagnostic names it;
  a known key emits nothing. Docs: one paragraph in `harness-contract/manifest.md`
  ("unknown keys are ignored; the diagnostic names them"). Never refuse.
- **REQ-WEB-ACCESS-SURFACE** (ADR-0060; doc, impl, unit): mint the `WEB` row in the surface
  table (`default_on: true` within subnet — the DISCOVER shape; non-attributable until a
  sender stamp exists); the `--help` "Control surfaces" section grows it by existing
  (table-composed); the gate site is W1's stream family — in W0 the row exists and is
  rule-addressable, nothing dials it yet.
- Docs: new page `docs-site/src/serving/overview.md` (URL grammar, registry, served names,
  index page) + `reference.md` regen; api.md untouched in W0.
- Field acceptance (mine): browser opens `http://localhost:5474/hfenduleam/`, index lists a
  file I `serve add`; two files named `report.md` from two dirs list as `report.md` and
  `report~1.md`; removing the first and re-adding a third gives `report~2.md`.

## Register riders (hertz) — composed at the v0.67.1 close / #272 intake sweep, 2026-09-06

- **Floor trio, ONE workflow lane** (thin PR, off the golden path, lands BEFORE the WEBSERVE
  golden head so that head runs under it): IR-46 (weigh the disk-floor-instant remedies —
  the `golden.yml` touch at `04e32c8c` fired the trigger unweighed), IR-59 log-the-floor half
  (record free space beside every floor check), IR-73 (the 8 literal-first floor sites in
  `ci.yml`/`release.yml`). Entry text in `docs/INFRA-REGISTER.md` is the spec.
- **IR-66 — RULED SATISFIED 2026-09-06 (hertz measured, doyle ruled)**: the register body says
  BUILT at `d04b922d`/#161 and both cells are present at `17815c9c` (attach.rs:530/:538,
  :655). The earlier "residual — two one-line test edits" line here was a carried-forward
  fossil of the entry's own "Size:" line, which describes the fix that LANDED. No edits; the
  floor-trio PR carries one register status line, and the entry leaves the register at the
  WEBSERVE close sweep.
- **IR-12 residual** rides W3's drift-gate riders (already hertz's).
- IR-59's log-the-floor line ALSO goes into IR-76's three-arm pre-flight on W0's driver
  template (todlando).

## W1 — cross-node (todlando) — #249

- **REQ-WEB-CROSS-NODE-PROXY** (doc, impl, unit, int): a request for `/<node>/…` where
  `<node>` is a subnet peer opens an Iroh stream (new `StreamFamily::Web`), the OWNER runs
  `access_check(…, surface::WEB, …)` under the handshake-proven origin (the `serve_xfer`
  shape), streams the response back; the local daemon proxies with NO cache; Range headers
  pass through; owner unreachable → 502 naming the node; owner refuses → 403 naming the
  surface. Unknown node name → the docs compat 404 (ADR-0056 Am.1 order, as W0 landed it at
  webserve.rs:307 — RULED doyle 2026-09-07 00:50Z, replacing an earlier "404 naming it" here;
  a 502 is only ever for a KNOWN subnet peer). An owner that does not speak
  `StreamFamily::Web` (N-1 daemon) resolves to the same 502-naming-the-node, never a hang.
- Int: twohost — kitsubito fetches a file registered on hfenduleam through kitsubito's own
  5474; a WEB deny rule on hfenduleam turns it into 403; a Range request returns 206.
- Docs: `serving/cross-node.md` + the access-surface docs page gains the WEB row text.

## W2 — attachments + message identity (todlando) — #246 + #147, then #17 flips

- **REQ-WEB-ATTACHMENT-PULL** (ADR-0058; doc, impl, unit, int): `spt send --attachment <path>`
  snapshots the bytes into `$SPT_HOME/serve/snapshots/`, registers an `attachment` entry
  (ttl default 30 d, `--ttl <dur>`), and the envelope carries the node-prefixed URL
  (`attachments: [{name, url, bytes}]`, additive key); the daemon pulse reaps expired
  snapshots and logs the count. Nothing is pushed to the receiver.
- **REQ-WEB-FETCH-VERB** (doc, impl, unit, int): `spt fetch <url|<node>/f/<name>> [dest]`
  writes the bytes to `dest` (default: basename in cwd, refusing to clobber without `--force`),
  prints the path; exit codes: 0 / 3 refused (403) / 1 failed.
- **REQ-MSG-SHORT-ID** (ADR-0061; doc, impl, unit, int): 8-char base32 over the message hash,
  node-scoped, minted at commit, collision → lengthen that id; carried in the delivery
  envelope, in `MSG_IN`/`MSG_OUT` io-event rows, and rendered by `/<node>/m/<id>` (HTML +
  `?json`); `spt msg show <id>` renders one message with its attachment URLs; `spt send
  --reply-to <id>` carries the parent id.
- **#17 rider** (WIDENED by operator directive 2026-09-06 ~10:56Z, ADR-0058 Amendment 1):
  `FILE_ACCESS_HELPER` now-signal category. Two triggers: (a) a delivered message carries
  attachments → the signal emits the exact `spt fetch <url>` line(s); (b) a USER's message
  quotes a filepath that exists on the user's node → core AUTO-REGISTERS that path as a
  served resource with a **24h TTL**, **audience = the one endpoint that received the
  message** (a new per-entry field; enforced where the fetch origin is proven — the W1
  cross-node stream; loopback is the trusted machine), and the signal hands that endpoint
  the `spt fetch <url>` line. Delta discipline: once per (message, path). Guards: existence
  check on the user's node, absolute/`~` paths only, cap per message (5), entries enumerable
  in `spt serve list` with origin = the message short-ID. Same-node user+agent: no
  registration, the signal says the path is local. REMOTE user (the #17 case) needs a
  cross-node "register on my behalf" request authorized by the user's attach session —
  rides W1's stream family, so this rider lands AFTER W1 (W2 already does). Operator RULED
  reference-served (live file/dir entry + ttl + audience, never a snapshot; ADR-0058 Amendment 1).
  Flip #17 EVAL → attached-and-WIP at W2 dispatch (operator ruled it a closing rider, Q6).
- Docs: `serving/attachments.md`, api.md now-signal category list, `messaging` page for
  short-ID + reply-to.

## W3 — facets (todlando; hertz takes the drift-gate test riders) — #265 + #268 + #266

- **REQ-WEB-ADAPTER-DOCS-FACET** (#265; doc, impl, unit, int): `/<node>/a/<adapter>/…`
  serves an installed adapter's docs. **MEASURE FIRST**: where an adapter's docs live after
  `adapter add` (archive `docs/`? `strings/`? nothing today?) — stop-and-refer with the
  measurement if no adapter ships docs; the facet may need a manifest key (`docs_dir`).
  **MEASURED doyle 2026-09-06 16:15Z: none of the 7 installed adapters ships a docs dir
  (`manifest.toml` + exe + `strings/` + `state/` only). RULED: manifest key `docs_dir`,
  adapter-relative, inside-the-adapter-dir refusal like `[strings]` pointers; absent = no
  facet (404 with the facet name).** Drift-gate test half = hertz, spec
  `WEBSERVE-272-W3-DRIFT-RIDERS.md`.
- **REQ-DOCS-CHANGELOG-PAGE** (#268; doc, impl, **unit** — unit is hertz's): the docs site
  carries CHANGELOG.md as a page (`docs-site/src/changelog.md`, generated by `xtask gen` from
  a pure `changelog_page(&str) -> String`, diffed by `xtask check`, listed in SUMMARY.md +
  llms.txt, and JOINED to the internal-codes scan — so the generator strips the
  `<!-- [doc->REQ-…] -->` tags CHANGELOG.md carries, else the scan fires on run one).
- **REQ-WEB-LAN-BOOTSTRAP-LISTENER** (ADR-0059; doc, impl, unit, int): `spt serve lan
  --bootstrap` starts a SECOND hyper listener on `0.0.0.0:5470` (config/env override) whose
  router knows ONLY `/bin/<platform>/spt[.exe]` + `/install` (a one-liner page); off by
  default, `spt serve lan --stop`; prints the LAN URL; now-signal carries `LAN-EXPOSED
  <url>` while up (a NowSpec category — ride the ratified render order, append at END);
  REQ-DOCS-LOCAL-SERVER's never-0.0.0.0 stays true for 5474 (unit asserts the 5474 bind is
  loopback while 5470 is up). **MEASURE FIRST**: where the current platform binaries are on
  disk (`$SPT_HOME/bin`? the update-set stage?) — the listener serves the INSTALLED binary
  set, never builds one.
- Int (kitsubito → hfenduleam over LAN): `curl http://hfenduleam:5470/bin/…` = bytes match the
  installed exe's sha256; `curl …:5470/hfenduleam/docs/` = 404; `curl …:5474/` from kitsubito
  = connection refused (loopback proof).

## Close riders

- **XFER retirement** (ADR-0060; rider on #246's close, W2 or W3 tail): delete
  `StreamFamily::Xfer`, `serve_xfer`, `fetch_file`/`push_file`, their tests, the `XFER`
  surface row; `ACCESS_SURFACE_RETIRED:<id>:<surface>` reported at store load for any rule
  naming it; docs + `--help` regen; CONTEXT.md's access-surface entry already states it.
  REQ id: **REQ-XFER-RETIRED** (impl, unit).
- **#148** stays detached BACKLOG (deferred by ruling; keys on REQ-MSG-SHORT-ID when built).
- **IR-76** driver pre-flight is a rider on W0's driver template (todlando authors the
  battery; the three-arm leg ships in it).

## Sequencing (todlando's queue)

1. v0.67.1 land call (deployah) → rebase #276 → thin PR → my gate → land.
2. #277 (iolog seq reset) thin lane → my gate → land. (#278, #279 are small; fold #279 into
   #277's lane if the tree is open, #278 stays its own thin lane, any order.)
3. W0 → W1 → W2 (+#17) → W3 (+ XFER retirement) → golden head to deployah → v0.68.0.
   Start W0's registry design + store work NOW in `.worktrees/ws272-w0` off origin/main and
   rebase at (1); nothing in W0 depends on #276/#277.

## W3 pre-brief question (todlando 07:58Z, recorded by doyle before W3 is shaped — ANSWER BEFORE DISPATCH)
#266 serves the spt BINARY over the LAN (`<node>:5474/bin/spt.exe` → `.\spt.exe install`). That is an
executable-distribution surface on the same WEB gate whose DEFAULT-ON-inside-a-subnet posture produced W2's
serve_for hole (three individually-true refusals still let any subnet node expose any owner path to itself;
closed by F1's user's-own-words conjunct). To rule explicitly, not inherit: (1) ACCESS — under the default
posture, who may pull the binary, and is "every subnet member" an allowed answer? (2) INTEGRITY — the puller
EXECUTES what it downloads, so the bytes need a story that proves the exe is the node's own installed build
(hash published beside it from the serving daemon's own exe path, and/or the seal machinery), not just an
access rule. A wrong default ships a supply-chain shape. #265 (/adapter-docs/<adapter>) and #268 (changelog
first in local docs + `spt update` at the core-hosted URL) are ordinary surface work by comparison.
Check against CONTEXT.md + ADR-0058/ADR-0061 + docs/KNOWN-HAZARDS.md (WAN-origin-auth) before the W3 brief.

### RULED doyle 2026-09-07 08:25Z — #266 access + integrity (checked against ADR-0059, ADR-0015 trust roots, KH 7.5)
**Premise correction first:** #266 does NOT ride 5474 or the WEB gate. ADR-0059 (accepted 2026-09-06) puts the
binary on a SEPARATE opt-in listener, port **5470**, with its own process-level allowlist (`/bin/*`, `/install`)
and nothing else in that process. Its audience is BY DEFINITION "strangers who are not yet nodes" (no Iroh
identity → no access chain can run), so W2's serve_for hole has no analogue here: there is no default-on
subnet posture to inherit because there is no subnet subject at all.
1. **ACCESS — ruled:** while `spt serve lan --bootstrap` is UP, anyone who can reach the LAN socket may pull the
   binary; the ONLY gate is the operator's explicit start (off by default, `--stop`, LAN-EXPOSED in the
   now-signal for the whole window). "Every subnet member" is neither the answer nor the question — the
   listener has no membership concept and must not grow one (a fresh box has none to present). Never bind
   5470 by default; never expose it through 5474; never serve anything but `/bin/<platform>/spt[.exe]`,
   the sidecar below, and `/install`. Licensing already lines up (binary redistributable, docs private).
2. **INTEGRITY — ruled, three conjuncts, all required:**
   a. **Serve only a SIGNED-RELEASE artifact, never a bare `current_exe()`.** The served bytes are the
      relcache artifact whose `SignedRelease` metadata (`artifact_sha256` + Ed25519 signature under an
      ADR-0015 trusted key, `VerifyPolicy::production`) verifies at serve-start AND whose sha256 equals
      the running daemon's own exe (canonical path captured at t=0, brainproc.rs:1201 rule — never a
      per-request `current_exe()`). Mismatch (dev build, hand-copied exe, apply-in-flight) → `spt serve
      lan --bootstrap` REFUSES with a named line (`LAN_BOOTSTRAP_REFUSED:unsigned-exe` / `:sha-mismatch`);
      there is no "serve it marked unsigned" branch — that is the supply-chain shape the question names.
   b. **Publish the provenance beside the bytes:** `/bin/<platform>/spt[.exe].release.json` = the exact
      `SignedRelease` record (metadata JSON + signature hex, relcache `metadata_json()`/`signature_hex()`).
      `spt.exe install` on the puller re-runs `verify_metadata` + `verify_artifact` against its BUILTIN
      keys before touching disk, and refuses on any failure. (Circular alone — a tampered exe carries
      tampered roots — which is why (c) exists; but it makes provenance durable and catches every
      accidental corruption/truncation/wrong-platform pull for free on the existing substrate.)
   c. **Out-of-band anchor = the operator's eyes:** `spt serve lan --bootstrap` PRINTS the artifact sha256
      (and the LAN-EXPOSED now-signal line carries it); `/install`'s one-liner is `…\spt.exe install
      --expect-sha256 <hex>` with the hex baked into the page AND `install` prints the sha256 of what it
      downloaded before it verifies. The two machines are on one LAN; the human compares the two lines.
      This is the only conjunct that is not circular over plain HTTP, and it is what an HTTPS-less
      bootstrap can honestly offer. Optional rider, not W3: the serving daemon may also seal the sha
      (`;;…;;` wax-seal over the hex) so a later reader can prove the operator was the publisher.
   **REFINED doyle 2026-09-07 10:14Z (real clock) — after todlando's measure-first, re-measured by me:** `%LOCALAPPDATA%\spt-core\releases\artifacts\` holds THREE triples (windows-msvc, linux-gnu, linux-musl) under ONE `release.json`
   (`metadata_json` + `signature_hex`, key `rel-primary-2026`, set version 102, phase applied); `bin\spt.exe` sha256
   `d0ec895a…52db` == the windows-msvc artifact == its `artifact_sha256` entry (three-way, measured twice). So the
   signature is per SET, not per platform, and conjunct (a) is re-scoped from "bytes == running exe" to
   **"artifacts of the APPLIED signed set"**, which the exe-equality proves for the set and the signature proves for
   every triple in it:
   - **Set gate (whole listener):** the served set = the relcache set whose HOST-triple artifact sha256 equals the
     running exe (canonical path at t=0) AND whose applied-state reads `phase=applied` at that set version. Fail
     → `LAN_BOOTSTRAP_REFUSED:unsigned-exe` / `:sha-mismatch` / `:set-not-applied`, nothing served. A staged-but-
     unapplied newer set is never served: the stranger gets what this node RUNS, never what it is about to.
   - **Per-triple gate (per path):** `/bin/<triple>/spt[.exe]` serves the staged bytes only if they hash to that
     triple's `artifact_sha256` in the same signed metadata; a missing or mismatched NON-host artifact 404s with
     `LAN_BOOTSTRAP_TRIPLE_UNAVAILABLE:<triple>:<missing|sha-mismatch>` and the host triple keeps serving — a
     missing musl artifact must not block a Windows→Windows bootstrap. The host triple failing is the SET gate.
   - (b) unchanged: the sidecar is that one `release.json`, byte-identical on every triple path.
   - (c) per triple: `spt serve lan --bootstrap` prints one `sha256 <triple> <hex>` line per SERVED triple, the
     LAN-EXPOSED now-signal carries the host triple's, and `/install` bakes the PULLER's triple hex (chosen by the
     page's `<triple>` segment, never sniffed) into its `--expect-sha256`.
   - **Scope: all three triples ride W3** — the substrate is one file and one map; deferring two triples would add
     a "host-only" branch that is more code than serving them. Rig plan accepted as measured: the HAPPY path is
     witnessed on the REAL installed daemon (hfenduleam satisfies the set gate today: applied==staged==102), int
     arm kitsubito→hfenduleam pulling linux-gnu; the REFUSAL arms stay on the dev-bin rig (unsigned-exe by
     construction) plus one staged-set-tamper arm (flip a byte in a non-host artifact → that triple 404s, host
     still serves). New REQ text for REQ-WEB-LAN-BOOTSTRAP-INTEGRITY: "the applied signed set, per-triple sha-anchored".

   Int (kitsubito → hfenduleam): bytes sha256 == installed exe sha256 == `.release.json`'s
   `artifact_sha256`; `install` with a wrong `--expect-sha256` refuses, exit ≠ 0, nothing written; a
   truncated download refuses at (b); `serve lan --bootstrap` on a dev-built rig refuses at (a).
   ~~SUPERSEDED 10:14Z (see REFINED above; todlando caught the placement 10:17Z): "that last arm means
   the rig pair (dev bins) CANNOT witness the happy path: the int arm needs a rig whose daemon is a
   staged signed release (lab-signed set) else a `--allow-unsigned-TEST-ONLY` cfg(test) arm — pick the
   former."~~ **RULED INSTEAD:** the happy path is witnessed on the REAL installed daemon (hfenduleam
   satisfies the set gate today, applied==staged==102), int arm kitsubito→hfenduleam pulling linux-gnu;
   the refusal arms stay on the dev-bin rig. No lab-signed staging rig, no unsigned-TEST-ONLY arm.
   REQ split: REQ-WEB-LAN-BOOTSTRAP-LISTENER keeps access; mint **REQ-WEB-LAN-BOOTSTRAP-INTEGRITY**
   (doc, impl, unit, int) for (a)+(b)+(c). Docs: ADR-0059 Amendment 1 (this ruling) + the install page text.

### RULED doyle 2026-09-07 12:05Z — W3 shape questions (todlando 12:15Z-stamped, real 12:04Z; both claims re-verified at source before ruling)
**#265 mount = (B), beside the core root.** `/<node>/a/<adapter>/` already serves the adapter's registry web root (webserve.rs:455 `fn adapter` -> `serve_entry` on the entry whose `adapter == name`), and ADR-0056 bought a W0 URL its fixed meaning, so `docs_dir` mounts at `/<node>/a/<adapter>/docs/...`. The `docs` segment is RESERVED under the adapter facet, router-first like W0's reserved facets: with `docs_dir` set it serves that dir; absent it answers `NOT_FOUND: facet a adapter <name> docs` and NEVER falls through to a `docs/` subpath of the core root (no shadowing surprise, deterministic either way). Containment is NEW code (todlando measured: `validate_web_short_path` serving.rs:357 is a NAME check): `docs_dir` is adapter-relative, refused at manifest load if absolute, if it contains `..`, or if its canonical path is not under the adapter dir's canonical path (named refusal `MANIFEST_DOCS_DIR_OUTSIDE:<adapter>`); re-resolved at REQUEST time per ADR-0057 (a dir swapped for a symlink after load is caught at serve, same refusal name). Unit: the three refusal shapes + one inside-the-dir accept; int: a facet fetch of a real docs file + the absent-404 naming the facet.
**#268 strip = every SCANNED CLASS, not today's population.** `changelog_page` strips by span using the SAME predicate the scan uses (`forbidden_codes_in` or a sibling returning spans, never a second hand-written list): the `<!-- [doc->REQ-...] -->` comments (5 today), the `(releases#N)` citations (CHANGELOG.md:294, :305 today — a public page carries no internal issue refs, so the whole parenthetical goes, not the token), and any ADR-/F-/REQ-/M-block token that a future entry adds. Unit: `forbidden_codes_in(changelog_page(include_str!(CHANGELOG.md)))` is EMPTY on the committed file (the real population) AND on a synthetic fixture carrying every class once; generated-and-committed page, diffed by `xtask check`, listed in SUMMARY.md + llms.txt, joined to the scan path list.
**#266:** riding the REFINED 10:14Z ruling verbatim, no re-measure — correct; a second reading of the same box is not a second witness.

### RULED doyle 2026-09-07 12:27Z — #266 shape (todlando 12:26Z)
doyle RULING on the #266 shape (checked against the 08:25Z + REFINED 10:14Z rulings and servehost.rs at ff4b405d):

APPROVED with four pins.
1. Listener lives in the DAEMON, CLI reaches it through servehost::call — correct, and required: the set gate anchors on the daemon's OWN exe canonical path at t=0 (brainproc rule), so the daemon computes the shas and answers; the CLI only prints. ServeRequest/ServeResult are the local IPC envelope (KIND_SERVE_REQUEST), not a sister-project wire schema, so new variants are fine: LanBootstrap {port override} / LanStop, results LanUp {url, shas: one (triple, hex) per SERVED triple} / LanStopped / LanRefused {code}. The refusal is the NAMED line from the ruling (LAN_BOOTSTRAP_REFUSED:unsigned-exe|sha-mismatch|set-not-applied) carried IN-BAND; CLI prints it verbatim, exit non-zero. Keep call's existing bounded timeout, no retry arm.
2. Idempotence: --bootstrap while UP answers the SAME url + shas with LAN_BOOTSTRAP_ALREADY_UP (never a second listener, never a re-run of the gate producing a different set); --stop while DOWN answers LAN_BOOTSTRAP_NOT_UP, exit 0. Unit both.
3. Ephemeral by construction: no persisted up-state; a daemon restart never resurrects the listener (off by default is a property of every boot, not just the first). --stop tears the listener down AND clears LAN-EXPOSED from the now-signal in the same request; unit asserts both.
4. lanhost.rs beside docshost.rs, same thread + current_thread runtime pattern, shutdown arm for --stop — fine. Router knows ONLY /bin/<triple>/spt[.exe], its .release.json sidecar, /install; everything else 404 with a line naming the listener, never a fall-through into 5474 code paths. The 5474-stays-loopback-while-5470-is-up unit rides here (REQ-WEB-LAN-BOOTSTRAP-LISTENER text).

Acked: REQ-DOCS-CHANGELOG-PAGE unit stays unactivated until hertz's half lands (activate, do not pre-fail). #265 (B) and #268 span-strip as described match the 12:05Z ruling; the decoy-docs no-fall-through cell is the right falsifier. Keep fa286ffb unpushed until you push W2 F17; W2 lands first. Rule still ABSENT here at the stamp below; priority unchanged.
### RULED doyle 2026-09-07 12:58Z — #266 @781fa9ba: LanStatus + explicit-policy seams
doyle — #266 @ 781fa9ba, two rulings:
1. LanStatus APPROVED. The now-signal needs a live source and the pins forbid persisted up-state, so a bounded daemon ask (500 ms, answered before the registry writer lock) is the only honest one; a dead daemon reads as no line, never as an open door. Pin: LanStatus is read-only (no side effects, no gate re-run) and the timeout arm renders NOTHING, not a stale line.
2. Public policy/exe seams APPROVED with two pins, because an int arm that verifies against a key the test signed never exercises the production wiring: (a) the daemon's production entry is the ONLY non-test caller of the explicit-policy seam and it passes VerifyPolicy::production plus the t=0 canonical exe — assert that with a unit that calls the production constructor and checks the roots it resolved (a census grep is not the test); (b) the REAL-daemon happy path on hfenduleam (kitsubito pulling linux-gnu, sha == installed exe == release.json) stays a required int/field arm, not replaced by the test-signed arms. Not a bypass as built; these keep it from becoming one by a wiring slip.
Ack: LAN_EXPOSED standing at END of POST_V1, daemon-memory source over call_with_timeout — correct per the ratified render order. Push F17 first; W3 sha stays unpushed. Kitsubito is running my Linux control, leave it alone.
