fix: session cookie over plain http, console onboarding gate, webkit qa coverage

- the session cookie was Secure whenever NODE_ENV=production, so a production build on http://localhost got no cookie back in Safari: every page after signup looked signed-out. It is now Secure only when X-Forwarded-Proto says https, or when COOKIE_SECURE=1.
- /app and /app/[id] now require an analyst and a Jev key, the same rule /api/analyze enforces with a 412. Closing the guide mid-way used to leave a console that could not run anything.
- Playwright runs the production server (pree2e builds first) and gains a webkit project for session survival; chromium-only QA could never see a Secure-cookie rejection.
- tests-e2e/session.spec.ts pins both; .env.example gains COOKIE_SECURE and stops shipping a blank APP_SECRET.
- e2e: 21 passed (setup, chromium, webkit, mobile).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
ahmed mohammed
2026-09-22 10:45:12 +03:00
co-authored by Claude Opus 4.7
parent 5160a00db5
commit 2ebfcfb7a6
13 changed files with 124 additions and 12 deletions
+9 -3
View File
@@ -40,6 +40,7 @@ bash scripts/smoke.sh # healthz, login page, signed-out redirect, analy
| --- | --- |
| `APP_SECRET` | ≥16 chars. Encrypts API keys at rest (AES-256-GCM). Changing it makes stored keys unreadable. |
| `DATA_DIR` | where `qai.db` (SQLite, `node:sqlite`) lives. Default `./data`. Docker uses `/data`. |
| `COOKIE_SECURE` | set to `1` only when the app is served over https without a proxy that sets `X-Forwarded-Proto`. The session cookie is `Secure` when that header says `https`; forcing it on plain http makes Safari refuse to store it. |
Model keys are **not** config. Each user types them in the app (first-run guide or `/settings`), so a second user
brings their own endpoint and their own spend. Blank key field = keep the stored key.
@@ -56,12 +57,17 @@ brings their own endpoint and their own spend. Blank key field = keep the stored
```bash
npm test # node --test on tests/*.test.ts, no test framework installed
npm run build # production build + type check
npm run e2e # Playwright: signup, settings, real analyst + real Jev run, copy, print, history, 4 widths
npm run e2e # builds first, then Playwright against the production server
```
The unit suites run offline: the analyst and Jev are exercised through injected `fetch`, so no key and no network.
`npm run e2e` is the opposite — it drives the real app against the real models, and reads `JEV_API_KEY` from
`.env.local`. Expect it to be slow; that is the models, not the harness.
`npm run e2e` is the opposite — `pree2e` builds, then Playwright drives `npm run start` (the production server,
not `next dev`: cookie flags and hydration differ, and that difference once hid a sign-out bug) against the real
models. It reads `JEV_API_KEY` from `.env.local`. Expect it to be slow; that is the models, not the harness.
Projects: `chromium` (everything except the session spec, reusing one signed-in session), `webkit` (Safari —
session survival only, because it is the one engine that rejects a `Secure` cookie over `http://localhost`),
and `mobile` (Pixel 7, layout only, so no model run is paid for twice).
`tests/digest.test.ts` skips unless a genuine plan exists at `fixtures/real.sqlplan`. Drop one there from SSMS and the
skip becomes the real check on ShowPlan attribute spellings.