Repository navigation
Delay Jam and align cache, CSP, and robots - #196
Merged
Merged
Conversation
Jam still loads for every visitor, but only after the first interaction or eight seconds, and its capture iframe is detached on pagehide so it does not keep the document out of the back/forward cache. HTML revalidates instead of using no-store, public images cache for 30 days, CSP names the two Jam hosts the loader uses, and the app shell is noindex while /expocit/sources is indexed. Co-authored-by: Gáborjáni-Szabó Ádám <Programcode@users.noreply.github.com>
|
Warning Review limit reachedEnable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. Next included review available in 12 minutes. View limit details
Note 🎁 Summarized by CodeRabbit Free
Comment |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Putting the capture iframe back on pageshow reloads it, and the new frame starts a handshake that Jam's already-initialized parent script rejects. pagehide still removes the frame so the page stays cache-eligible; the restored page simply runs without Jam until the next full load.
Only a Jam that already loaded stays off after a restore. A trigger that had not fired yet may still start it, which is a clean first load.
Programcode
marked this pull request as ready for review
October 22, 2026 21:16
This was referenced Oct 22, 2026
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.
Jam stays on for every visitor. It now loads after the first interaction, or eight seconds after load, and its capture iframe is removed on
pagehideso that frame is not left in the document when the page is frozen. HTML uses a revalidating cache, CSP names the two Jam hosts the parent page actually contacts, and the app shell's robots meta matches the private-routenoindexheader.Nothing here deploys Supabase.
vercel.jsonis frontend hosting only, and this PR is not merged. TheAccess-Control-Allow-Origindelete runs on Vercel and was not confirmed on a live deployment.Local
npm run verifyGitHub Actions did not run (Actions minutes are exhausted). This is a local pass, not a green CI run.
Command:
npm run verify(docs, lint, typecheck, unit tests, collaboration tests, Deno edge tests, build, service-worker test, desktop Chromium@smoke).Exit code: 0
The process set
commit.gpgsign=falseonly for that run. The agent signing helper madesrc/test/scripts/backend-status.test.ts'sbeforeEachexceed the 10s hook timeout. That test file is unchanged, and with signing off it passes (7/7). Deno 2.9.6, the version in.github/workflows/deploy.yml, was installed for the edge tests because it was not onPATH.Also ran
e2e/loading-performance.spec.tson desktop Chromium: 4 passed. That spec is not in the smoke set.Lighthouse mobile (
/only)Same Lighthouse 13.5.0 flags:
--only-categories=performance --form-factor=mobile --screenEmulation.mobile --chrome-flags="--headless=new --no-sandbox".These are not the same origin. Before is the production CDN. After is this branch's
dist/on127.0.0.1. Do not read the lower local score as a regression of the CDN page.https://debate.pi-stack.org/(2026-10-22T20:27:56Z)http://127.0.0.1:4173/(2026-10-22T20:52:14Z)The proof that Jam is off the first paint is the loading spec: the landing heading is visible with zero
js.jam.devrequests, and apointerdownis what starts the recorder.Headers and CSP (local server applying
vercel.json)A static server served
dist/with the header rules, the rewrites (filesystem wins), and a simulated platformAccess-Control-Allow-Origin: *that the new route then deletes./,/index.html,/app-shell.html,/theme-init.js:Cache-Control: public, max-age=0, must-revalidate(nono-store)/sw.js:no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0/favicon.png,/hero-visual.png:public, max-age=2592000/assets/…:public, max-age=31536000, immutable/app,/login,/share/abc:X-Robots-Tag: noindex, nofollow/and/expocit/sources: noX-Robots-Tag. Built HTML robots areindex, follow, …on those pages andnoindex, nofollowonapp-shell.htmland404.htmlAccess-Control-Allow-Origin.sw.js, images,/theme-init.js,/assets/, and/robots.txtstill have*/embed/debate-timerkeeps its own CSP (frame-ancestors *, no Jam) and has noX-Frame-OptionsChromium (Playwright) on
/: the heading rendered, Jam was absent untilpointerdown, thenjs.jam.devandrecorder.jam.dev/captureloaded. The onlysecuritypolicyviolationon the parent document wasscript-src/evalfrom the app bundle. That is Zod'snew Function("")capability check (try/catch, then it runs without the JIT).unsafe-evalwas not added. No parent-page block ofjs.jam.devorrecorder.jam.dev. Console errors were the local 404s for/_vercel/insights/script.jsand/_vercel/speed-insights/script.js(this server does not host those routes).'self'already allows them; production serves them from this origin. Firefox was not installed here, so it was not checked.Issue by issue
SC-01
scheduleJamRecorderwaits forload, then armspointerdown/keydownand an 8s timer. The first of those callsrequestIdleCallback(2s timeout) orsetTimeout(0)and then loadsrecorder.jsandcapture.js. Signed-inuserIdmetadata insrc/lib/jam-metadata.tsis unchanged. A non-identifying id is not a small change, so it stays.Verified by
src/test/jam-loader.test.tsand the loading e2e spec. Open: once the scripts run, the capture iframe's worker poll continues. A consent banner was not added (follow-up).SC-02
script-src,frame-src,connect-src, andimg-srcnamehttps://js.jam.devandhttps://recorder.jam.devinstead ofhttps://*.jam.dev. Parent scripts are only onjs.jam.dev. The capture document's own hosts (api.jam.dev, Datadog, and the rest) are requested by the iframe, so widening ourconnect-srcwould not help them.index.htmldoes not set a CSP.SC-11
unsafe-evalwas not added. The parent bundles'Function("return this")is aglobalThisfallback and is not the call we saw. The one parent eval report is Zod's caught probe, above. Datadogevalanddd_cookie_testlive in the cross-origin capture bundle; third-party cookie blocking is the browser, not our CSP. Firefox markingsupabase.coas a bounce tracker is unchanged and not fixable here. Vercel Web Analytics and Speed Insights are same-origin/_vercel/insightsand/_vercel/speed-insightswith no DSN, so'self'covers script and connect. Externalva.vercel-scripts.com/vitals.vercel-insights.comhosts were not added.PF-07
/,/index.html,/app-shell.html, and/theme-init.jsarepublic, max-age=0, must-revalidate./sw.jsstaysno-store. Hashed/assets/stay immutable. The five unhashed public images cache for 30 days./appalready received Vercel's default revalidating cache and has no explicitno-storerule.PF-10
The 8s / interaction delay keeps Jam off the initial network. After it loads, polling cannot be stopped; tests must not wait for
networkidle.scripts/test-service-worker-offline.mjsnow useswaitUntil: 'load'. The service-worker test passed.SE-02
index.htmlrobots meta isnoindex, nofollow, so/app,/login, and/sharematchX-Robots-Tageven before JS. Prerender writes the publicindex, follow, …directive onto every canonical page andnoindex, nofollowonto the 404./expocit/sourcesis prerendered, in the sitemap (nolastmod, because it has no JSON-LD date), and callsusePageHead. The filesystem wins over the existing rewrite, which stays as a fallback.theme-init.jskeeps private routesnoindex, nofollowand sets the public directive if the shell is what served/expocit/sources.SC-10
A
routestransform withcontinue: truedeletesAccess-Control-Allow-Originon HTML documents and leaves it on assets,/_vercel, and static extensions. Confirmed on the local server that simulates the platform*header. It takes effect on the next Vercel deploy; this PR does not deploy.The staging origin
https://brifeyypjauyusmkptkx.supabase.costays inconnect-src. Onevercel.jsonis deployed to production and todebatelab-staging, and an unpinned production preview can still select staging from Settings.docs/operations.mdsays why.Back/forward cache
pagehide(capture) removesiframe[data-jam-capture],iframe[data-jam-recorder], andiframe[src*="jam.dev"].pageshowputs those same nodes back whenevent.persistedis true. The loader never registersunloadorbeforeunload. Unit test covers detach and restore.Open: Chrome still cannot see inside the cross-origin frame while it is attached, which is why the other branch's bfcache report named
recorder.jam.dev/capture. Detach happens onpagehide, before the freeze. Jam may not reconnect itspostMessageport after the node is removed and put back; reloading the scripts would hit Jam's already-initialized guard, so this PR does not do that. The recorder is also absent until interaction or 8s, so a quick back navigation often has no Jam frame at all.Left open
unsafe-eval.Update before merge (Cody, 22 Oct)
main(Re-enable buttons that leave the site after Back (AUTH-15, AUTH-16) #194) into the branch (172d560). No force-push.mcp:3be7c35b): no blockers. Its should-fix was that putting the Jam iframe back onpageshowreloads it, and Jam's already-initialized parent script rejects the new handshake. Fixed in c506b2a:pagehidestill removes the frame, andpageshowno longer re-adds it. A Jam that has already loaded stays off on a bfcache-restored page until the next full load. If Jam had not started yet, its pending trigger can still start it after the restore, which is a clean first load (177269d). There's still nounload/beforeunloadlistener. The unit test and a new synthetic-event e2e case ine2e/loading-performance.spec.tscover it. Codex re-reviewed: no blockers.npm run verifyon the VPS at 177269d: exit 0 (vitest 443, collaboration 30, edge 399, build/SW ok, e2e smoke 43/43).e2e/loading-performance.spec.tson desktop-chromium: 5/5.routesalongsideheaders. HTML ispublic, max-age=0, must-revalidatewith no ACAO.sw.jsis no-store. Images are cached for 30 days. CSP is as in the diff. In Playwright on/,/loginand/expocit/sources, the only CSP violation was the known Zod eval probe. Jam (js/recorder.jam.dev) and Sentry ingest loaded without being blocked.