Repository navigation
Session origins: {name}.p.httpeers.net shell, demo, infra - #3
Merged
Merged
Conversation
This was referenced Sep 18, 2026
A relay page and a ServiceWorker that answer every non-shell path of a session origin -- index.html included -- over the MessagePort a ghost app hands relay.html. The ghost side is webrun-http-browser's relay mode (newRemoteRelayChannel + initHttpService), wrapped as openSession() in the package's ./client export. What the shell adds to the library's relay worker: routing at the origin's root instead of /~<key>/, first-live-registrant-wins registration from relay.html only, a referrer check on navigations, frame-ancestors on every worker-made response, and an origin check on the relay's CONNECT that refuses other sessions. The relay bridges to registration.active rather than the page's controller: Firefox 155 leaves a second relay page uncontrolled and the library's bridge then never answers. Caddyfile: a *.p.httpeers.net block with its own wildcard certificate through the DNS-01 snippet, frame-ancestors on the shell's files (a test compares it with the worker's copy), and a Host rewrite so every session name reads one storage prefix. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Firefox has no Request.prototype.body (checked against 155), so forwarding request.body sent every POST on with no body, silently. The body now streams where the runtime can and is read whole where it cannot. A test hides the property on one request to stand in for that browser. The README states what contain is not -- a boundary against a hostile app, measured 2026-09-15 -- and points untrusted apps at session origins. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
deploy.mjs writes the one p.httpeers.net prefix every session name reads: refuses an unmounted bucket, creates the directory only with --create, deletes only stale _shell/ files, writes relay.html last. live-check.mjs checks fresh random names on the real domain: DNS, the *.p.httpeers.net certificate and its issuer, byte-identical files with frame-ancestors on two names, .site/ not served, and in Chromium and Firefox that another session cannot hand a relay its port and a foreign site cannot frame a session. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The hub serves a small app at /spa and advertises it (kind "app"); the app page's 'Open in a new session' opens it in a fresh <random>.p.httpeers.net through the session shell's openSession, with httpeers-ghost's pinnedPeer as the port's handler -- the app reaches the hub and nothing else. This replaces the same-origin ghost iframe, whose frame could read the viewer's storage and DOM. session-smoke.mjs runs it on the real domains in Chromium and Firefox: the app renders in two session origins, served by each session's worker, with a root-absolute /api/hello reaching the hub; storage, cookies, IndexedDB and worker registrations are isolated between the sessions and from the viewer; parent DOM access and top navigation are blocked; a top-level visit is refused. The shared Vite config drops emitted assets nothing references: the webrun-http-browser entry's new URL(..., import.meta.url) defaults made Vite ship a dead 91 KB copy of the library with the app page. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…y model deploy/README: the A *.p record and why it is required (the empty non-terminal the ACME challenge creates), checking the *.p certificate with a never-used name, applying a Caddyfile change in place with a reload, and that p.httpeers.net is the shared session shell. security-model: session origins as the provisioned own-origin variant, what was measured for them, and how the demo builds its broker's calls from the pinned peer alone. Root README: layout lists the demos and the session shell. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…n's own header The rebase onto main crossed two changes this branch predates. `@statewalker/webrun-http-browser` 0.5 (main moved every other package to it): the session shell now asks for ^0.5.0 too, so the lockfile carries one copy rather than 0.4.2 beside it. 0.5 only adds to 0.4.2's exports -- the relay mode the shell is built on (`newRemoteRelayChannel`, `initHttpService`, `callChannel`, `handleChannelCalls`, `sendHttpRequest`) is untouched, and its own `SwHttpAdapter` recovery work is not on this path. The membership token travels in `x-httpeers-token`, never `Authorization` (5fa3dcf): `pinnedPeer` sets it through `setMeshToken`, so the session demo's test reads the token from that header instead of asserting `Bearer TOKEN` on `Authorization` -- which is now the app's own header and passes through. Also from the rebase, in the commits themselves: the demos' join form is the shared widget's, so `session-smoke.mjs` fills `.hp-join-input` and presses `.hp-join-submit` (`#invite`/`#join` went away with the old form). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
mkotelnikov
force-pushed
the
feat/session-domains
branch
from
September 20, 2026 08:22
24a4292 to
00416d8
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Session origins: every
{name}.p.httpeers.net(123123.p.httpeers.net,foobar.p.httpeers.net, …) serves the same static shell under one wildcard certificate, and each name is its own origin (storage, cookies, ServiceWorker). A ghost app on*.httpeers.netframesrelay.html, hands it aMessagePort(webrun-http-browser relay mode:newRemoteRelayChannel+initHttpService), and from then on every request inside the session —index.htmlfirst — is answered over the port.apps/session-shell/(new):relay.html, the workerrelay-sw.js, a fallbackindex.html, and a./clientexport,openSession({ handler }). The worker speaks the library's relay protocol, with these differences: it serves the app at the origin root, the first live registrant keeps the session, onlyrelay.htmlmay register, navigations must come from the session or a ghost origin (the referrer check), and it addsframe-ancestorsto every response it makes. The relay page refuses a port from another session, and it bridges toregistration.activebecause Firefox 155 leaves a second relay page uncontrolled.apps/demos: the hub serves a small app at/spa, and the app page's Open in a new session shows it in a fresh session origin, withpinnedPeeras the port's handler. This replaces the same-origin ghost iframe.packages/httpeers-ghost:pinnedPeerdropped every request body in Firefox because Firefox has noRequest.prototype.body. It now buffers the body there.deploy/: a*.p.httpeers.netCaddy block with its own DNS-01 wildcard certificate,frame-ancestors, and aHostrewrite to one storage prefix. The README adds theA *.precord (required: the ACME challenge turnspinto an empty non-terminal), SAN checks, and the in-place Caddyfile change procedure.Already live (applied from this branch; nothing merged)
A *.p.httpeers.net → 163.172.46.87, TTL 3600./opt/httpeers/Caddyfilereplaced in place by this branch's version (backupCaddyfile.bak.20260918-before-session-domains), thencaddy reload. No container was restarted. Let's Encrypt issued*.p.httpeers.net.p.httpeers.net/. Thehub.andapp.demo pages were republished from this branch (backups in the umbrella underworktrees/session-domains/backups/).Verified
apps/session-shell,pnpm run browser-test, local, Chromium + Firefox: 21/21. Covers the protocol, POST bodies, a worker restart, shell-path bypass, a second ghost refused, a top-level visit refused, and the session freed once its ghost is gone.pnpm run live-check, real domain, fresh names: 15/15. Covers DNS, the SAN and Let's Encrypt issuer, byte-identical files on two names,frame-ancestors,.site/not served, another session refused (both browsers), and a foreign site unable to frame a session (the Firefox half is weaker: the frame just never answers).apps/demos,session-smoke.mjs, real domains, Chromium + Firefox: 42/42. The mesh app renders in two session origins through their workers, and/api/helloreaches the hub. localStorage, cookies, IndexedDB and worker registrations are isolated between sessions and from the viewer. Parent DOM access andtopnavigation throwSecurityError. A top-level visit is refused. The existinglive-smoke.mjsis still green.Decisions to review
httpeers.netrather than on a separate registrable domain. That allows cookie tossing onto.httpeers.net; nothing relies on cookies today. To move them, changeSESSION_ZONE/GHOST_ZONEtogether with the Caddy block and the DNS record.*.httpeers.netsite may act as a ghost app. The allowlist is one label underhttpeers.net, excludingp. It does not include localhost; a shell served from localhost accepts localhost parents, so local tests work.notFound), which differs from the design note'sspa: true. With a connected session, the worker answers every path anyway.Not verified: Safari/WebKit; the NXDOMAIN window without
*.p(predicted by RFC 4592, not observed); cookie tossing.🤖 Generated with Claude Code