Three screens, nothing more: a public read, an email-OTP login, and an owner-filtered read. The proof is screen 3 — two identities see two different sets of orders and a signed-out visitor sees none at all, from an origin that is not the API’s.
GET /api/{site}/products — no auth, no credentials. Works signed out, and
works offline from the service worker cache.
POST /api/{site}/auth/otp/send then /verify, naming the
members guard. The server sets the signed per-project auth cookie on the
verify response — which only reaches this origin because the request carries
credentials: 'include' and the API allows it.
Running against a local stack? The code is delivered by email — open Mailhog on port 8025 to read it. A local Revma host also returns the code in the send response, and this demo fills it in for you.
GET /api/{site}/orders with credentials: 'include'. Guarded by
members with owner-filter: Customer, so the row set is scoped to
whoever the cookie says you are. Sign in as each identity in turn and watch the
count change; sign out and it becomes a 401.
This demo registers its own ./sw.js and links its own
./manifest.webmanifest, because a service worker can only be registered from
its own origin — the Revma host's generated pair is out of reach from here by
construction. The static worker mirrors the generated one's contract, so what it proves is
the same contract.
Offline check: load this page once online, then go offline and reload — the shell and screen 1 still render, with a “showing cached data” banner.