## [0.68.0] - 2026-09-08

Web serving. Files, directories, adapter documentation and the changelog are
reachable at node-prefixed addresses on the local server, and an address that
names another machine in the subnet is answered by the machine that owns it.
Messages can carry attachments the receiver pulls on demand, every message
gets a short ID to show or reply to, and a machine with no spt on it can be
handed the binary over the local network. The `XFER` access surface is retired.

### Added

<!-- [doc->REQ-WEB-SERVING-REGISTRY] -->
- `spt serve add`, `spt serve rm`, and `spt serve list --json` manage live file
  and directory references. Same-name registrations receive stable numbered
  suffixes; removing an entry never deletes its source. Only the same absolute
  path and kind may reclaim a retired name.
- Every registered adapter gets a core-owned `web/` output directory.
  `[adapter].web_short_path` optionally gives it a short URL alias. Removing
  an adapter stops serving its output without deleting the files.
- `spt adapter add` names each manifest key it does not know on stderr
  (`manifest: unknown key [<table>].<key> (ignored)`) and still registers;
  a misspelled optional key is no longer visible only by its absence.
<!-- [doc->REQ-WEB-ACCESS-SURFACE] -->
- `WEB` joins the access-control vocabulary. It is open by default within the
  subnet, but explicit WEB denies still govern. Existing file transfer is
  unchanged.
<!-- [doc->REQ-WEB-CROSS-NODE-PROXY] -->
- A served resource's URL now works from every machine in the subnet: a
  request for `/<peer>/…` on the local loopback server is answered by the
  owning node through the local daemon. The body streams and nothing is
  cached; `HEAD` and `Range` requests are honored by the owner. An owner
  that refuses answers 403 naming `WEB`; an owner that cannot be reached
  answers 502 naming the node, within a bounded time.
- `spt send --attachment <path>` sends a file with a message. The file's bytes
  are captured as they are at send time, so later edits or a deletion do not
  change what arrives, and the receiver pulls them with `spt fetch` when it
  wants them. `--ttl` sets how long an attachment stays available (default 30
  days, a unit is required); expired attachments answer 404 immediately.
- Every message now carries a short ID. `spt msg show <id>` prints a message by
  it, and `spt send --reply-to <id>` marks what a message answers. Messages
  from another machine resolve through the machine that holds them.
- `spt serve lan --bootstrap` hands the spt binary to a machine that is not yet
  a node, over the local network on port 5470. It is off by default and off
  again after every daemon restart, serves only the binary, its release sidecar
  and an install command, and refuses to start by name when the applied update
  set is not signed or does not match. It prints a checksum per platform, and
  `spt install` gains `--expect-sha256` and `--release-json` so what was
  downloaded can be compared before it is run. The documentation port is
  untouched and stays on loopback.
- `[adapter].docs_dir` publishes an adapter's own documentation at the `docs`
  segment of its address, beside the adapter's output directory rather than in
  place of it. A missing key, an unreadable manifest, a path that escapes the
  adapter's directory, and a directory that has since been removed all answer
  the same 404 naming what was asked for.
- The changelog is now one of the pages the local documentation server offers.
- A harness is told when a message it receives refers to a file the reader
  cannot open — an attachment to pull, or a path that belongs to another
  machine — instead of leaving the reader to discover it.

### Changed

<!-- [doc->REQ-WEB-URL-NODE-PREFIX] -->
- The loopback HTTP server now has a node-prefixed resource index with HTML
  and `?json` views. `/` redirects to `/<node>/`; canonical docs URLs live at
  `/<node>/docs/`, while existing bare docs paths remain compatibility aliases.

### Removed

- The `XFER` access surface is retired: attachments replaced the transfer it
  gated, so the entry leaves the access-control vocabulary along with the
  transfer itself. An existing rule that names `XFER` is kept and reported at
  load rather than dropped silently; it no longer governs anything. The shell
  channel's own transfer progress is unaffected.

### Fixed

<!-- [doc->REQ-IO-MSG-EDGES] -->
- Messages delivered by `spt api listen` now reach the receiver's incoming-message
  history and last-message state, for both queued backlog and live TCP delivery.
  A later hook poll does not record those deliveries again; filtered notifications
  are not recorded as delivered messages.
