Files
collective-lists/docs/history/fase-6.md
Oier Bravo Urtasun 768b3cda58 docs: split CLAUDE.md into docs/{development,deployment,history/fase-*}
CLAUDE.md shrinks to rules + status + index (175 lines, down from 476).
Dev setup, prod deploy, and per-phase build records move to docs/.
Gotchas regrouped by domain (auth, frontend, PWA, testing, backend, deploy,
platform) and unnumbered so they don't drift as new ones land.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 22:21:20 +02:00

4.5 KiB
Raw Blame History

Fase 6 — what has been built

6.1 PWA injectManifest migration

  • apps/web/src/sw.ts — minimal custom SW: precacheAndRoute(self.__WB_MANIFEST) + skipWaiting/clients.claim. Named sw.ts (not service-worker.ts) to dodge SvelteKit's built-in interception (see the PWA gotchas in CLAUDE.md).
  • apps/web/vite.config.ts — switched @vite-pwa/sveltekit to strategies: 'injectManifest' with srcDir: 'src', filename: 'sw.ts', injectManifest.globPatterns for the built shell.
  • apps/web/src/routes/+layout.svelte — registers the SW via onMount() → import('virtual:pwa-register').registerSW({ immediate: true }). @vite-pwa/sveltekit does NOT auto-register; without this call the virtual module is a no-op and navigator.serviceWorker.getRegistration() returns undefined.
  • apps/web/tests/e2e/pwa.test.ts — P-01 (manifest shape) + P-02 (SW registration via waitForFunction poll).

Note: the prod build later switched back to strategies: 'generateSW' — see the PWA gotcha for the reason.

6.2 Manifest + iOS meta tags + placeholder icons

  • Manifest driven by vite.config.tsSvelteKitPWA({ manifest: {…} }). Icons declared 192×192, 512×512, 512×512 maskable. theme_color: #0f172a, background_color: #f7f9fb, display: standalone.
  • apps/web/static/icons/icon-192.png, icon-512.png, icon-512-maskable.png, icon-180.png placeholder PNGs (slate background + "C" glyph). Generator: infra/scripts/generate-placeholder-icons.sh (ImageMagick). Replace with final artwork before public launch — noted in static/icons/README.md.
  • apps/web/src/app.html — iOS PWA meta tags: apple-mobile-web-app-capable, apple-mobile-web-app-status-bar-style, apple-mobile-web-app-title, apple-touch-icon.

6.3 Kong rate-limit (invitations only)

  • infra/kong.ymlrest-v1-invitations route: POST /rest/v1/collective_invitations, rate-limit hour: 20, policy: local, limit_by: header (Authorization). strip_path: true on a full-table path sends / upstream which PostgREST rejects with PGRST117, so the route also carries a request-transformer plugin that rewrites the URI back to /collective_invitations.
  • infra/docker-compose.dev.ymlKONG_PLUGINS env var gains rate-limiting (was missing).
  • packages/test-utils/tests/rate-limit.test.ts — RL-02 only (21st invitation POST = 429). Gated behind RUN_RATE_LIMIT_TESTS=1 so it's excluded from just test-all (Kong counters are shared state). Run via just test-rate-limit, which also --force-recreates Kong to reset counters first.
  • Scope dropped during execution: Kong rate-limit on /auth/v1/token + /auth/v1/authorize removed. Reasoning: Keycloak already offers native brute-force protection on the realm (bruteForceProtected: true, maxFailureWaitSeconds: 900, failureFactor: 30 in keycloak/realm-export.json), and stacking Kong on top throttles legitimate PKCE code exchanges during E2E (the same /auth/v1/token endpoint handles both password auth and code exchange — Kong can't distinguish). See plan/fase-6-deploy-prep.md §6.3 for the full rationale.

6.4 JWT rotation + production env template

  • infra/scripts/rotate-jwt.sh — bash script signing HS256 anon + service_role JWTs with a freshly-generated 48-byte secret via openssl dgst -sha256 -hmac + base64url. Prints the three values (SUPABASE_JWT_SECRET, PUBLIC_SUPABASE_ANON_KEY, SUPABASE_SERVICE_ROLE_KEY) for the operator to paste into .env. Does NOT touch files.
  • Dev secret rotated as a dry-run of the procedure. .env + apps/web/.env.development updated together (see project_env_keys_trap memory — both files carry PUBLIC_SUPABASE_ANON_KEY). Deduped a pre-existing duplicate PUBLIC_SUPABASE_ANON_KEY= line in .env.
  • .env.production.example — committed template with placeholders + rotation cadence note.

6.5 Lighthouse + RLS audit tooling

  • Justfilejust lighthouse recipe (prod build → node server on :3000 → lighthouse --only-categories=pwa,accessibility,best-practices → HTML report). Requires just dev to be up so the real Supabase stack is reachable.
  • Justfilejust rls-audit delegates to infra/scripts/rls-audit.sh.
  • infra/scripts/rls-audit.sh — signs an Eva JWT (non-member of the seed collective) with SUPABASE_JWT_SECRET and GETs eight REST endpoints (collectives, shopping_lists, shopping_items, task_lists, tasks, notes, collective_members, collective_invitations). Complements rls-audit.test.ts; all 8 must return [].