Skip to content

Tighten the Relay bind warning, frame cookies, device codes, and the Hosted release check - #1533

Merged
nedtwigg merged 16 commits into
mainfrom
relay-and-hosted-tightening
Nov 3, 2026
Merged

nedtwigg merged 16 commits into
mainfrom
relay-and-hosted-tightening

Conversation

@nedtwigg

@nedtwigg nedtwigg commented Nov 3, 2026 •

Copy link
Copy Markdown
Member

Tightens five behaviors across the Relay, Hosted, and the frame origin, and makes the published guarantees say what each one rests on.

What changes

  • Relay bind host. A Relay started with an https: origin and no DORMOUSE_BIND_HOST now warns at startup that plain HTTP listens on every interface. The unset default stays, for containers (S219). The published row now says the promise holds under a self-host installer.
  • Local networks only. The published guarantee and the security page now say "address ranges you allow", and docs/specs/security.md states that the level checks addresses, not which network holds them. The Settings copy is unchanged.
  • Frame cookies. The frame bootstrap page expires every cookie it can see on the frame domain, nameless and partitioned ones included, before the frame's page loads. A cookie on that frame's own origin stays. docs/specs/security.md -> "Known gaps" states what remains until the domain is on the Public Suffix List. A frame's closing is now worded per origin, as the code does it.
  • Device codes. A device code is now expiry || random || truncated HMAC under its purpose's key, in the same 32-byte bearer shape. A poll answers expired on a bad tag before the database, and begin still writes nothing.
  • Hosted deploy. The production verify job now reads each pgstencil package's signer, source commit, and signed payload, then that commit's place on pgstencil main and its security-audit runs. One tested script, hosted/scripts/pgstencil-provenance.mjs, now holds these checks and npm's verdict, replacing the inline jq.

Before merging or deploying

  • Codes in flight at the deploy. A device code issued before this ships has no tag, so it answers expired, which every shipped Burrow already handles. Enrollment started within ten minutes before the deploy has to begin again.
  • A failed pgstencil audit now holds a deploy. verify applies S120 as written, so any failed security-audit run on the installed commit refuses the deploy until that run is rerun or a new release ships.
  • Not yet run against real data. The new step reads npm's real attestation bundles and GitHub's API, which only CI reaches. Its tests use self-signed certificates shaped like Fulcio's. Please dispatch Hosted production release with promote off once before relying on it.

Testing

  • pnpm test: everything passes except lib/src/lib/lath/group-drop.test.ts, which timed out at 5 seconds at a load average above 50. It passes with a longer timeout, and this branch doesn't touch Lath.
  • pnpm --filter dormouse-hosted test passes, as do tsc -b in lib and tsc --noEmit in standalone and hosted.
  • Each new test was checked by removing the line it covers.
  • The cookie expiry was run in Chrome 156 against a loopback server with the real frame policies and sandbox: shared, nameless, and partitioned cookies were gone before the next page read them, and a frame's own cookie stayed.

🤖 Generated with Claude Code

nedtwigg and others added 6 commits November 2, 2026 19:36
… and state Local networks only by address range

A Relay started by hand with an https: origin and no DORMOUSE_BIND_HOST now
says at startup that plain HTTP listens on every interface. The default stays,
for containers. The published rows state what each promise rests on: the
installers for the plaintext port, and address ranges for Local networks only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…t frames share

Frame origins sit under one domain, so a page could leave cookies there for its
siblings. The bootstrap page now expires those it can see before the frame's
page loads, leaving that origin's own. The security docs state what remains
until the domain is on the Public Suffix List, and that a frame closes when its
surface leaves the page's origin.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A device code is now expiry, random bytes, and a truncated HMAC of both under
its purpose's key, in the same 32-byte bearer shape. A poll answers `expired` on
a bad tag before the database, so begin still writes nothing. A code begun for
one purpose is no code for the other.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The verify job took npm's verdict on each pgstencil package, leaving the signer
identity, source commit, and audit result to that night's audit. It now reads
them itself before deploy: the Fulcio SAN and source digest, the signed payload,
the commit on pgstencil main, and its security-audit check runs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ookies too

One tested script now holds the Hosted release to npm's verdict as well, so the
workflow holds no inline gate or second package list. The device code reuses the
shared byte helpers. The frame bootstrap also expires a partitioned cookie, a jar
of its own that the first delete missed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Nov 3, 2026 •

Copy link
Copy Markdown

Deploying mouseterm with  Cloudflare Pages  Cloudflare Pages

Latest commit: 06e699e
Status: ✅  Deploy successful!
Preview URL: https://3c8a7300.mouseterm.pages.dev
Branch Preview URL: https://relay-and-hosted-tightening.mouseterm.pages.dev

View logs

@dormouse-bot dormouse-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The nameless-cookie case the new test covers doesn't actually expire anything in a browser. Inline below.

nedtwigg and others added 3 commits November 2, 2026 20:25
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A nameless cookie is cleared by a value with no `=`, since a browser drops an
empty name and value without touching it. The bootstrap now always clears the
nameless cookie, whose value can also read as a name, and its test models how a
browser files a write rather than recording the string.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
nedtwigg and others added 2 commits November 2, 2026 21:40
The Settings line goes back to "networks you choose", with its original wrapping.
The published guarantee and docs/specs/security.md keep the precise statement.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
nedtwigg and others added 2 commits November 2, 2026 21:49
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@nedtwigg
nedtwigg merged commit 080cb6d into main Nov 3, 2026
19 checks passed
@nedtwigg
nedtwigg deleted the relay-and-hosted-tightening branch November 3, 2026 07:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants