# WEBSERVE (#272) grill — round 2

Round 1 answered by the operator 2026-09-06 (in chat): Q1 agree · Q2 agree + a registry
disambiguation plan is required · Q3 unsure, premise "XFER is not used by any user/agent
surface" · Q4 agree · Q5 agree · Q6 agree, #271 → SHELVED (done, alchemy 09:41Z).

## Settled by round 1 (recorded on #272)

- **Q1** unified node-prefixed URL namespace; bare `/` redirects to `/<local-node>/`; docs
  paths stay as a compat alias under the local prefix.
- **Q2** registry is the primitive; a served folder is a registry entry of type dir.
  **Disambiguation (operator's shape, adopted):** the URL name is `<stem>~<n>.<ext>` when
  `<stem>.<ext>` is already taken — first registrant keeps the bare name, `n` increments per
  `(stem, ext)`, and the assignment is PERSISTED in the registry entry so a URL never changes
  for the life of its entry and a freed name is never reused (a stale link 404s, it never
  silently opens a different file). Re-registering the same absolute path returns the existing
  entry, not a new `~n`. `spt serve add <path> [--as <name>]` overrides the stem;
  `spt serve list` prints path → URL so agents never guess a name. Cross-node URLs are
  disambiguated by the node prefix already (Q1), so `~n` is per node, never fleet-wide.
- **Q3** measured, see below.
- **Q4** `spt send --attachment` pull-model + `spt fetch <url> [dest]`; FILE_ACCESS_HELPER
  emits that verb string.
- **Q5** `spt serve lan --bootstrap`, off by default, bin + install page only, loud while active;
  loopback-only ruling for docs/registry stands.
- **Q6** ride #248 #249 #246 #265 #268 #147; defer #148; #271 SHELVED; #17 closing rider.

## Q3 — what the measurement says (doyle, 2026-09-06, source at main 8a21a3b0)

Operator premise: *XFER is not used by any user- or agent-accessible surface; repurposing it is
harmless; WEBSERVE supersedes it.*

- **Initiator side: TRUE.** `spt_daemon::xfer::{fetch_file, push_file}` have ZERO production
  callers — every call site is a test (`tests/xfer.rs`, `tests/twohost.rs`, `tests/dispatch.rs`).
  No CLI verb opens the Xfer stream family. `spt shell send --file` uses the xfer PROGRESS
  record format locally (shell perch `files/`), never the network stream or the surface.
- **Serve side: LIVE.** The daemon's `serve_xfer` gates the inbound Xfer stream on the `XFER`
  access surface (`spt-daemon/src/xfer.rs:270, :341`, `dispatch.rs:1161`), and the surface is
  in the operator vocabulary: knock codes can be minted for it (guarded — "a grant admits the
  whole machine"), rules can name it (`--surfaces MSG,XFER`), `reference.md` lists it ×15
  (generated from `--help`, one source).
- **Grant census on this node: ZERO rows name XFER** in any store file. A rename would drift
  no existing rule HERE; other nodes unmeasured.
- **Design record:** CONTEXT.md:876 already ratifies that *"adapter package serving, webservice
  facets mint their ids when the capability itself is built"* — minting is the ratified path
  for exactly this milestone. CONTEXT.md:949 names XFER as the surface "expected next" to
  grow a sender stamp; repurposing it inherits that plan under a different meaning.

**Recommendation (not a ruling — operator confirms):** mint `WEB` (one row, no schema change,
per CONTEXT.md:876) and **RETIRE** XFER rather than rename it — as a close rider on #246,
once `--attachment` ships: delete the Xfer stream family, `serve_xfer`, its tests and the
surface row together; a stored rule naming a retired surface is REPORTED at load
(`ACCESS_SURFACE_RETIRED`), never silently dropped. Same end state as "repurpose", without
an existing id changing meaning under anyone's rules, and the code that WEBSERVE supersedes
actually leaves the tree. If the operator prefers rename-in-place, the cost is: the
attributability plan at :949 re-ruled, 15 help strings + ~6 test files re-spelled, and the
retired transfer code kept alive under the new name.

---

❓ **Q7** — **URL grammar under the node prefix.** Sub-namespaces, one per facet, so a path
tells you what it is: `/<node>/f/<name>` registry files (Q2), `/<node>/docs/...` the docs
site (compat alias at the old bare paths), `/<node>/a/<adapter>/...` adapter docs (#265),
`/<node>/m/<short-id>` message view (#147), `/<node>/bin/...` + `/<node>/install` bootstrap
(#266, LAN listener only). `/<node>/` itself = an INDEX page listing the registry (the
"what is this node exposing" audit, in a browser, same list `spt serve list` prints).

➡️ Adopt the five sub-namespaces and the index page. Cost: a facet added later needs a
prefix ruling; benefit: no two facets can ever collide on a name.

❓ **Q8** — **Registry semantics: reference vs snapshot, and lifetime.** (a) `serve add`
serves the file AT ITS PATH (edits visible, deletion 404s); (b) `--attachment` SNAPSHOTS the
bytes at send time into the registry store (a message's attachment is immutable, like the
message). Lifetime: `serve add` entries live until `spt serve rm`; attachments carry a TTL
(default 7 days, `--ttl`), reaped by the daemon's existing pulse, reap COUNT logged.

➡️ (a)+(b) as stated. The one number to rule: attachment TTL default (7d proposed).

❓ **Q9** — **Cross-node proxy mechanics.** A request for `/<remote-node>/...` at any daemon
opens an Iroh stream to the owning node, which runs the WEB access check under the
handshake-proven origin (exactly the `serve_xfer` shape) and streams the bytes back; the
local daemon proxies without caching (no stale copies, no cache-invalidation seam). Range
requests pass through for large binaries. An unreachable owner = 502 with the node name.

➡️ No-cache proxy, owner-side access check, 502-with-name. Cache only if a measurement asks.

❓ **Q10** — **#147 short-ID.** Format: 8-char base32 over the message's existing hash,
node-scoped (`/<node>/m/<id>`), so it is printable in a chat line and pasteable as a URL.
CLI: `spt msg show <id>` (renders one message + its attachment URLs), `spt send
--reply-to <id>` (envelope carries the parent id; adapters may render a thread). The short-ID
appears in `MSG_IN`/`MSG_OUT` io-events and in the delivery envelope.

➡️ Adopt; #148's ledger later keys on the same id.

❓ **Q11** — **Scheme.** Plain `http://` URLs only — every consumer (browser, curl, an agent's
fetch) already speaks it; no `spt://` scheme, no handler registration on any OS. `spt fetch`
accepts the http URL (and an `<node>/f/<name>` shorthand it expands locally).

➡️ http only.

❓ **Q12** — **Bootstrap listener shape (#266).** Separate port (5475 proposed) and a separate
listener process-level allowlist (`/bin/*`, `/install`), NOT a path allowlist on the 5474
server — a routing bug on 5474 can never expose docs on the LAN because the LAN listener
does not have them. `spt serve lan --bootstrap` prints the LAN URL and the install one-liner,
and the daemon's now-signal shows a LAN-EXPOSED line while it is up.

➡️ Separate port + separate allowlist + loud signal.

Answer any subset; round 3 (if needed) = wave map + REQ ids for the greenlit set.
