···8484 (`platform:"browser"`, `noExternal`, `noNodeBuiltinsPlugin` guard, `node:timers/promises` stub).
8585- **Done:** `apps/gateway/src/main.ts` — reads `packages/verify/dist/sw.js` at startup (`SW_SCRIPT_PATH`
8686 override), wires `opts.sw = { path:"/sw.js", script }`. **Not yet typechecked/built** (blocked on bundle).
8787-- **BUNDLE BLOCKER — RESOLVED, alias map below (NEXT ACTION).** The browser build fails because
8787+- **✅ DONE (2026-07-17) — bundle builds, gateway wired, harness green, merged to `main`.** The browser
8888+ IIFE `dist/sw.js` builds browser-clean; `@minisite/verify` + `apps/gateway` build+typecheck; the Playwright
8989+ harness (`test/sw-harness.test.ts`) proves the compiled SW verifies (positive) and fails closed on tamper
9090+ (negative 502). Full gate: 1252 tests / 86 files. Bundle fix = `@atproto/common` alias stub +
9191+ `buffer` polyfill (resolver plugin past the guard) + `sw.js` output name — see `packages/verify/
9292+ tsup.config.ts` and `src/sw-shims/*.ts`. **Q-A answered:** reused the frozen fixtures (no new signing
9393+ helper); the SW's cross-origin reads are route-mocked in Playwright (confirmed: `context.route` intercepts
9494+ service-worker fetches). **Q-B answered:** the chain bundles clean once `@atproto/common` is aliased and
9595+ `Buffer`/`node:timers/promises` are shimmed. Remaining: SRI (deferred), deploy via `railway up` (separate).
9696+ Everything below is the historical blocker analysis.
9797+- **BUNDLE BLOCKER — RESOLVED, alias map below (was NEXT ACTION; now DONE per above).** The browser build failed because
8898 `@atproto/repo`'s barrel → `repo/dist/logger.js` → `@atproto/common` barrel drags in `node:fs`,
8999 `node:fs/promises`, `node:stream`, `node:zlib`, `node:crypto`. **All four are dead-but-imported on the
90100 verify path** (createHash lives only in `common/dist/ipld.js`'s `VerifyCidTransform`, never instantiated;
···466466read-back does not re-derive the pins from the stored records. Closing that needs a second write, not a
467467stricter comparison.
468468469469+✅ **SHIPPED (2026-07-17)** — **H12 — the verifier service worker is now wired, served, and proven in a real
470470+browser** (merged to `main`; NOT yet deployed — deploy is a separate `railway up`). The root-scope SW *is*
471471+the gateway verifier: after first hit the gateway is untrusted for content (§9.3/§9.4). What landed:
472472+- `packages/verify/src/sw-entry.ts` — the classic-script entry: `deriveTarget()` reverses the per-site
473473+ origin host (`<siteRkey>--<did-suffix>`) to the target AT-URI (passthrough when the host carries no
474474+ reversible identity); `lazyReaderForTarget()` builds the verifying `atpReader` (records + DID doc read
475475+ CROSS-ORIGIN DIRECT — plc.directory + owner PDS — never proxied through the gateway, that independence is
476476+ §11.3); `raslFetcher()` reads blob bytes same-origin from `/.well-known/rasl/<cid>` (re-hashed by
477477+ `VerifyingCache`, so trustless). Lives in `@minisite/verify` because it's the only package depending on
478478+ both the verifier and `@minisite/gateway`; the reverse edge stays forbidden.
479479+- `packages/verify/tsup.config.ts` — a browser IIFE build emitting `dist/sw.js` (a classic self-contained
480480+ script the injected `register("sw.js")` can load). The blocker (`@atproto/repo`→`@atproto/common` dragging
481481+ `node:fs/stream/zlib/crypto` into the bundle) is severed by aliasing `@atproto/common` → a no-op
482482+ `subsystemLogger` stub (audited: the verify path consumes only that export, and never invokes it); a
483483+ `Buffer` polyfill (`buffer` npm pkg, injected) covers `repo`'s `Buffer.concat`; the `node:timers/promises`
484484+ yield stays a microtask stub. Bundle verified browser-clean (0 `node:` builtins, 0 `process.env`).
485485+- `apps/gateway/src/main.ts createServerFromEnv()` — reads `packages/verify/dist/sw.js` at startup
486486+ (`SW_SCRIPT_PATH` override) and sets `opts.sw = { path: "/sw.js", script }`; `http.ts` serves it
487487+ `no-cache` (§9.5). SRI/integrity deferred (§9.5 SHOULD). Dockerfile already ships `packages/verify/dist/`.
488488+- **First wiring = first execution, and it's proven.** `packages/verify/test/sw-harness.test.ts` loads the
489489+ built `dist/sw.js` into headless Chromium (Playwright) on a derived per-site origin and asserts: (positive)
490490+ the SW serves the site's signed `/index.html` verified end-to-end; (negative) tampered same-origin blob
491491+ bytes → the SW FAILS CLOSED (502), never serving forged bytes. Frozen fixtures in
492492+ `packages/gateway/test/fixtures/sw/` (also driven in Node by `sw-fixture.test.ts`). Gate: 1252 tests / 86
493493+ files green. **The gate now requires a Chromium binary** (`pnpm --filter @minisite/verify exec playwright
494494+ install chromium`). Deferred: SRI; the SW on slug/custom-domain origins (needs the `<meta>`-target path —
495495+ `bootstrapHtml` emits it, still read by nobody).
496496+469497**H12 — Verifier SW never served; a dead `sw.js` registration is baked into every site's root CID; an author
470498SW is neutralized then registered at root scope (inverse of §9.3).** `packages/gateway/src/sw.ts
471499registerServiceWorker()` is never called; `apps/gateway/src/main.ts` never sets `opts.sw`;