SIGN IN SIGN UP

Add portal playground mode: anonymous browser-pinned sessions (#38)

* Add portal playground mode: anonymous browser-pinned sessions

PORTAL_PLAYGROUND=1 lets one deployment serve as a public try-it
instance. An unauthenticated browser navigation mints an anonymous
playground-<random> principal into the ordinary portal_session cookie,
so every visitor gets their own scoped sessions, files, memory, and
sandbox through the same personal-scope isolation real teammates use.
Non-HTML requests without a session still get 401, so only real page
loads mint. /auth/login keeps the full OIDC flow for the one admin,
/admin refuses anonymous sessions outright, and signing out simply
starts a fresh identity on the next visit.

Minting is rate-limited per client IP through the core's durable
single-use claim store, failing closed to a 429 page when the claim
cannot be recorded, so restarts and blue-green deploys cannot reset the
budget. The claim-store helper moves from plugins/auth to the shared
chassis package now that both the auth broker and the portal consume it.

* Harden playground mode after independent review

Refuse to boot when playground is combined with a domain-wide cookie,
an apps domain, or deployment proxying, since those surfaces never see
the anon flag and would take an anonymous session at face value. Refuse
out-of-range mint knobs instead of silently serving 429 to everyone:
the core grants at most 64 claim slots per request and a 24-hour claim
horizon, so values outside those bounds brick minting. Bucket IPv6
minting per /64 so a routed prefix cannot rotate through fresh budgets,
and warn at boot when the socket address would make every visitor share
one bucket behind a reverse proxy. Refuse anonymous sessions the
connect and secret-drop flows so real OAuth tokens and dropped secrets
cannot be attached to a throwaway principal that a cleared cookie
orphans.
J
Joshua France committed
35c47a3fcbd83dca8f640bdba7fa47fa2d6ec271
Parent: e00d3c0
Committed by GitHub <noreply@github.com> on 7/30/2026, 11:47:44 PM