Closed bug report

Private page + readme.html renders a blank Overview: sandboxed readme iframe cannot authenticate

John Lauer · 2mo ago ·closed by Colby Knox

A PRIVATE page with a readme.html shows an empty Overview. The readme iframe is sandbox=allow-scripts (opaque origin, no cookies), and its src is the bare /readme///?variant=public endpoint with no auth material, so for a private page the fetch hits the privacy wall: curl anonymously returns 404 PAGE_NOT_FOUND ('No such page, or it's private and you don't have access') while the same endpoint is 200 for public pages (verified against adom/orbital-lab, 200/192840B, vs adom/hero-deck, 404). Repro: create a private page, push a readme.html, view Overview signed in: iframe stays blank at the 600px fallback; document.querySelector('iframe[src*=readme]').src shows no token. Suggested fix: have the page embed a short-lived signed token in the iframe src (e.g. ?rt=) that /readme accepts for private pages, since cookies can never ride into an opaque-origin sandbox. Workaround used on adom/hero-deck meanwhile: renamed readme.html to demo.html, README.md (inline markdown renders server-side in the authed page) is the Overview, and the live demo opens via the page-file render URL.

2 Replies

John Lauer · 2mo ago

Update with a controlled experiment (throwaway page john/readme-probe-tmp, since deleted): the readme endpoint serves the REPO-layer readme.html with NO published package (public page: 200 anonymously), so the published-layer requirement I implied is wrong. Privacy is the ONLY wall: the same page flipped private returns 404, and preset preview also 404s anonymously. The licensed preset (page public, source sealed) renders the readme while source blobs 403, which is the workaround adom/hero-deck now uses. The signed-token fix in the original report remains the right ask for genuinely private pages.

Colby Knox · 2mo ago

Fixed and live on prod, using the signed-token approach from the report.

One correction to the diagnosis first, because it changes what to expect. Reproduced on prod with a fresh private page carrying a readme.html: anonymous requests to /readme 404 as you saw, but the SAME endpoint returns 200 for both a Bearer header and a session cookie. So cookie-authenticated browsing at wiki.adom.inc was never blocked; what actually broke was every context where the cookie cannot ride: the wiki embedded cross-site (a webview, where a SameSite=Lax session is dropped for the nested document) and any Bearer-authenticated client, since an iframe cannot send headers. Worth knowing so the next person does not chase a wall that is not there in a plain browser.

The fix (your ?rt=<signed> idea, built on the capability tokens that shipped for adom/wiki#73): a private page now mints a short-lived, page-scoped READ capability into the readme iframe's src, and /readme accepts it for the frame document. The same cap is injected as the frame's bootstrap, so the readme's own asset fetches (bundles, models, images) keep working inside the sandbox rather than trading one blank frame for another.

Boundaries: public pages carry no token and stay cacheable; the PRIVATE readme variant still requires real source access (a cap never unlocks it); caps are read-only, scoped to one page, expire in 15 minutes, and expired, foreign, or tampered ones are refused. Verified on prod against a live private page: the rendered page's iframe src carries the cap, the frame loads 200 with the cap alone and no other auth material, and it still 404s without it. 38 asserts in test/security/caps.test.mjs cover the contract.

adom/hero-deck can go back to readme.html whenever you like; the demo.html workaround is no longer needed.

Log in to reply.