diff --git a/Dockerfile b/Dockerfile index 59b0720..72d8a79 100644 --- a/Dockerfile +++ b/Dockerfile @@ -56,6 +56,62 @@ COPY website/ ./ COPY docs/ ../docs/ RUN npm run build +# ── Build Stage — Verifier (B02 Plan B) ─────── +# The verifier is an npm workspace of the root package. We install via +# the workspace flag (resolves against the committed root lockfile so the +# build is reproducible) but copy only what the verifier needs to compile. +FROM node:20-alpine AS verifier-build +WORKDIR /app +COPY package.json package-lock.json* ./ +COPY verifier/package.json ./verifier/ +RUN npm ci --workspace @zeroauth/verifier --include-workspace-root=false --ignore-scripts +COPY verifier/tsconfig.json ./verifier/ +COPY verifier/src/ ./verifier/src/ +RUN npm --workspace @zeroauth/verifier run build + +# ── Verifier Production Stage ───────────────── +# Slim runtime image — just the compiled JS + production deps + the +# verification key. No source TS, no test deps, no snarkjs build tools. +# Bound to :3001 on the Docker network; the API container reaches it via +# its compose service name `zeroauth-verifier`. Loopback-only is enforced +# at the network boundary — no host port binding. +FROM node:20-alpine AS verifier-production +WORKDIR /app + +RUN addgroup -g 1001 -S zeroauth && \ + adduser -S zeroauth -u 1001 + +# Install verifier's prod deps in a flat node_modules. Deliberately uses +# `npm install --omit=dev` rather than `npm ci` because the verifier +# workspace doesn't have its own lockfile (it shares the root's via +# npm workspaces, which complicates a single-package install). Trade-off +# is acceptable for v0; full reproducible-build provenance is on the +# roadmap per ADR-0005 / the verifier design doc. +COPY verifier/package.json ./package.json +RUN npm install --omit=dev --ignore-scripts && npm cache clean --force + +# Compiled JS from the verifier-build stage +COPY --from=verifier-build /app/verifier/dist ./dist + +# The Groth16 verification key — read at startup. Hard-coded absolute +# path via VERIFIER_VKEY_PATH so cwd changes can't make the file +# unfindable. +COPY circuits/build/verification_key.json /app/circuits/build/verification_key.json + +USER zeroauth + +ENV NODE_ENV=production +ENV VERIFIER_VKEY_PATH=/app/circuits/build/verification_key.json +ENV VERIFIER_BIND=0.0.0.0 +ENV VERIFIER_PORT=3001 + +EXPOSE 3001 + +HEALTHCHECK --interval=30s --timeout=10s --start-period=15s --retries=3 \ + CMD wget --no-verbose --tries=1 --spider http://localhost:3001/health || exit 1 + +CMD ["node", "dist/server.js"] + # ── Production Stage ────────────────────────── FROM node:20-alpine AS production WORKDIR /app diff --git a/docker-compose.yml b/docker-compose.yml index 3de590a..f8cdac4 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -34,6 +34,30 @@ services: retries: 3 profiles: ['dev', 'test', 'prod'] + # ── Verifier (Plan B — TS, B02 Phase 2) ────── + # Standalone Groth16 verifier service. Loopback-only on the docker + # network (no host port binding). The API reaches it via the service + # name `zeroauth-verifier`. + zeroauth-verifier: + build: + context: . + target: verifier-production + container_name: zeroauth-verifier + expose: + - '3001' + environment: + - NODE_ENV=production + - VERIFIER_CIRCUIT_VERSION=v1 + - LOG_LEVEL=info + restart: unless-stopped + healthcheck: + test: ['CMD', 'wget', '--no-verbose', '--tries=1', '--spider', 'http://localhost:3001/health'] + interval: 30s + timeout: 10s + retries: 3 + start_period: 15s + profiles: ['dev', 'prod'] + # ── Development ────────────────────────────── zeroauth-dev: build: @@ -50,6 +74,9 @@ services: - USE_REDIS_SESSIONS=true - REDIS_URL=redis://redis:6379 - POSTGRES_HOST=postgres + # B02 Phase 2 — route ZKP verifications through the verifier service. + - VERIFIER_URL=http://zeroauth-verifier:3001 + - VERIFIER_TIMEOUT_MS=2000 volumes: - ./src:/app/src - ./public:/app/public @@ -63,6 +90,8 @@ services: condition: service_healthy postgres: condition: service_healthy + zeroauth-verifier: + condition: service_healthy profiles: ['dev'] # ── Test ───────────────────────────────────── @@ -100,11 +129,19 @@ services: - REDIS_URL=redis://redis:6379 - POSTGRES_HOST=postgres - TRUST_PROXY=true + # B02 Phase 2 — production cutover. Routes /v1/auth/zkp/verify + # through the verifier service instead of inline snarkjs. The + # inline-fallback in src/services/zkp.ts stays in the binary as + # a safety net but is no longer the default code path in prod. + - VERIFIER_URL=http://zeroauth-verifier:3001 + - VERIFIER_TIMEOUT_MS=2000 depends_on: redis: condition: service_healthy postgres: condition: service_healthy + zeroauth-verifier: + condition: service_healthy restart: unless-stopped healthcheck: test: ['CMD', 'wget', '--no-verbose', '--tries=1', '--spider', 'http://localhost:3000/api/health'] diff --git a/qa-log/2026-05-15.md b/qa-log/2026-05-15.md new file mode 100644 index 0000000..7eca19e --- /dev/null +++ b/qa-log/2026-05-15.md @@ -0,0 +1,94 @@ +# QA Log — 2026-05-15 + +**Run by:** Pulkit Pareek (alone) +**Time:** 12:30 IST (DW01 canonical slot is 09:55; today's entry is late because email-infra deployment ran through the morning — the runbook fix landed first, this entry second) +**Build:** + +- API (`pulkitpareek18/ZeroAuth`): `02a79d0` on `main` +- Governance (`pulkitpareek18/ZeroAuth-Governance`): `bad10e7` on `main` +- IoT firmware: **not built** (B03 — Week 3) +- Mobile SDK: **not built** (B04 — Week 5) +- Liveness detection: **not built** (B13 — Week 3 / Week 5) +- Offline queue: **not built** (B14 — Week 4) +- Verifier service (Plan B, TS): **built + tested + in repo**, **NOT yet wired into prod docker-compose** — that's today's B02 Phase 2 work, next task on the day's list. + +## Results — four-demo battery + +### Demo 1 — Printed photo rejection + +**Status:** Blocked +**Note:** Same as 2026-05-13/14. IoT hardware unavailable; liveness detection unbuilt. Unblocks Week 3. + +### Demo 2 — Airplane mode authentication + +**Status:** Blocked +**Note:** Offline queue unbuilt. Unblocks Week 4. + +### Demo 3 — Three-different-hashes for the same identity + +**Status:** Blocked +**Note:** LSH bucket protocol unbuilt. Unblocks Week 3+. + +### Demo 4 — Hand-the-phone (impostor) + +**Status:** Blocked +**Note:** Mobile SDK + on-device liveness unbuilt. Unblocks Week 5. + +## Surrogate smoke (while battery is Blocked) + +### S-1 — API reachability against production + +**Status:** Green + +| Endpoint | HTTP code | +|---|---| +| `GET /v1/audit` | 200 | +| `GET /v1/devices` | 200 | +| `GET /v1/users` | 200 | +| `GET /v1/verifications` | 200 | +| `GET /v1/attendance` | 200 | +| `GET /api/health` | 200 | + +### S-2 — Email send (NEW today, was Blocked previously) + +**Status:** Green +**Method:** Live signup against `https://zeroauth.dev/api/console/signup` at 06:51 UTC + duplicate-signup attempt at 06:51 UTC. + +| Path | Outcome | +|---|---| +| Fresh signup → welcome email | ✅ Sent, messageId `<244c9a1f-958c-db5b-7da5-c93492194084@zeroauth.dev>` | +| Duplicate signup → notice email to legitimate holder | ✅ Sent, messageId `` | +| SMTP transport | Brevo SMTP via `nodemailer 8.0.7`, IP `104.207.143.14` now allowlisted on Brevo | + +This is the F-2 partial mitigation from issue #27 actually operating in production. + +### S-3 — Playwright happy-path E2E + +**Status:** Green +**Reference:** Last CI run on the most recent merge commit (`02a79d0`). Not re-run today. + +### S-4 — Unit + integration suites + +**Status:** Green +**Result:** 228 tests passing on the backend (Jest) + 23 on the verifier package = 251 total across both. + +## Rollup + +**Overall:** **HOLD** + +Unchanged status — HOLD stays until B03/B04/B13/B14 ship. Today's notable progress is on the *email* surrogate (S-2 flipped from "not configured" to "delivering"), not on the demo battery itself. + +## Escalations + +None today. No regressions. Email infrastructure deployed cleanly after correcting the `docker compose restart` vs `up -d --force-recreate` gotcha — captured in [`docs/operations/env-vars.md`](../docs/operations/env-vars.md) so the next operator doesn't hit it. + +## Operator notes + +- This entry is the third DW01 run. The cadence is establishing itself. +- The W05 Friday gate review is scheduled for 16:00 IST today. DW10 engineering annex assembly happens at 15:30 IST — task #6 on today's list. +- Today's headline build: B02 Phase 2 — wire the verifier into the prod `docker-compose.yml` and flip `VERIFIER_URL` so production traffic actually goes through the new service (currently it still runs the inline-fallback path; the verifier package is shipped to the VPS but unused). +- Yesterday's email infra changes were a "below-the-fold" win — the marketing site doesn't show anything new, but a buyer's security team reading the threat model sees the F-2 mitigation in place. + +--- + +LAST_UPDATED: 2026-05-15 diff --git a/qa-log/LATEST.md b/qa-log/LATEST.md index 5aa8b50..1b6c14b 100644 --- a/qa-log/LATEST.md +++ b/qa-log/LATEST.md @@ -1,9 +1,9 @@ # Latest QA Run -→ [`2026-05-14.md`](2026-05-14.md) +→ [`2026-05-15.md`](2026-05-15.md) -**Rollup:** HOLD (every demo Blocked; surrogate smokes green; production stable on `ad2a04a`) -**Date:** 2026-05-14 -**Next run:** Friday 2026-05-15 at 09:55 IST (before the W05 review at 16:00 IST) +**Rollup:** HOLD (every demo Blocked; surrogate smokes green; email delivery NEW + working today) +**Date:** 2026-05-15 +**Next run:** Tuesday 2026-05-19 at 09:55 IST (W2 Day 1 — second-week metronome resumes Tue/Thu cadence) (This file is overwritten on every run. For history, see the dated files in this directory.)