Public: anyone can read this; the crew can change it.

The service worker turns a forge restart into a Safari error page: never answer a navigation with Response.error() #497

Merged opened this in answer to

Check · Review · Land · Live done — Landed and live in production.

Merged

`/sw.js` now sends its own Content-Security-Policy, so `fetch()` inside the worker is no longer refused. The check failure on pull #468 came from a flaky unrelated test, which I fixed. `test/fork-flow.test.ts` passed 6 runs in a row after the fix. I did not re-run the rest of the check list or `tsc`, which can't resolve the `bun` types in this sandbox.

shipped production is serving it deploy 20352e6

opened this

/sw.js now sends its own Content-Security-Policy, so fetch() inside the worker is no longer refused. The check failure on pull #468 came from a flaky unrelated test, which I fixed. test/fork-flow.test.ts passed 6 runs in a row after the fix. I did not re-run the rest of the check list or tsc, which can't resolve the bun types in this sandbox.

Service worker fix

  • src/web/sw.ts sets content-security-policy: default-src 'self'; connect-src 'self'; frame-ancestors 'none'; base-uri 'none' on the script response, including the FORGE_SW_KILL variant.
  • The cause was the floor's default-src 'none': a worker takes its CSP from its own script response, so connect-src fell back to 'none' and every worker fetch failed.
  • test/offline.test.ts asserts connect-src 'self' and no default-src 'none'. It would fail on the old header.
  • The worker's script bytes are unchanged, but they differ from the retire script, so the browser replaces the retire worker.

Flaky test (the fork flow > a mirror whose git is not ready yet…)

  • The test seeded the upstream repo on a 300 ms timer, and the repo takes two requests to make. When the retry landed between them, the retry fetched an empty repo, the job finished done, and the branch list was empty.
  • It now seeds once the job has moved past its first fetch, and raises firstFetchRetry.ms to 8 s for that test so the seed finishes before the retry fires. It restores the old value afterwards. The test was wrong; no production code changed.
  • Separate, not fixed: after a failed first fetch the job's "trying again in Ns" detail is never shown, because the stage has already moved past fetching.

Left out

  • Playwright Chromium and WebKit test: Playwright isn't in the repo and I added no dependency. Say if you want one and I'll add it with a README justification.
  • Skipping re-registration on every load (optional item): left alone, since register() is idempotent.
  • sw-retire.yaml removal: it lives in trevato-apps/gitops (the file and its ConfigMap/Deployment/Service/Middleware/IngressRoute objects). Delete it in that repo once this ships.

To try: run the app and curl -I /sw.js to see the policy. In a browser, register the worker, reload /, and confirm forge.css is served by the worker.

🤖 Generated with Claude Code

Closes #457

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs