Skip to content

Day 3 of Week 1: governance repo, qa-log cadence, PR #22 security fixes, B02 plan#28

Merged
pulkitpareek18 merged 5 commits into
mainfrom
dev
May 13, 2026
Merged

Day 3 of Week 1: governance repo, qa-log cadence, PR #22 security fixes, B02 plan#28
pulkitpareek18 merged 5 commits into
mainfrom
dev

Conversation

@pulkitpareek18
Copy link
Copy Markdown
Collaborator

Day 3 of Week 1 discipline-gap clearance. Five commits, all related to closing the gaps the dev brainstorm's Week 1 schedule called for that we'd skipped or under-delivered on Day 1.

Summary by commit

1. 2df9faa — ADR-0004: split governance docs into a separate repo

Day 1's B06 build prompt called for a separate `zeroauth-governance` repo; we'd kept governance inline in this repo as a deliberate but undocumented decision. ADR-0004 documents the decision and the four reasons we revisited it on Day 3: (a) the DPDP §8(7) breach-notification procedure was unwritten (regulatory-teeth gap), (b) compliance mappings need an auditor-friendly surface separate from TypeScript, (c) component threat-models need a stable canonical URL before B02 ships, (d) two-reviewer enforcement is mechanically easier in a dedicated repo. New repo: https://github.com/pulkitpareek18/ZeroAuth-Governance (public, CC-BY-4.0, 30 files, ~1500 lines).

2. 876fac3 — Seed qa-log/ with DW01 cadence

Created `qa-log/` directory with format spec (README), current STATUS (HOLD — battery not yet runnable), LATEST pointer, and first dated entry. Honest record: all four demos (printed-photo, airplane-mode, three-hashes, hand-the-phone) are `Blocked` because the IoT firmware / mobile SDK / liveness detection / offline queue don't exist yet. Surrogate smokes against the API + dashboard that DO exist: all Green. The cadence is alive even though the demos aren't runnable.

3. b263dd5 — Retroactive security review of PR #22

PR #22 touched all four `security-reviewer` trigger surfaces (auth, crypto, audit, tenant boundaries) and merged without the subagent running, against CLAUDE.md's standing instruction. Subagent reviewed the diff `69fd27e..0c325fb` and reported 3 Medium / 3 Low / 1 Info findings. Net risk: Medium. No Critical. No tenant API key rotation required — tenant scoping (the load-bearing security property) is correctly enforced; the test suite already covers A-01 + A-10. Full report at `qa-log/security-review-pr22.md`. Tracking issue: #26.

4. edfef73 — Plan mode: B02 verifier service split design doc

CLAUDE.md mandates plan mode for any change to `src/services/zkp.ts`. B02 is Week 2 Day 1 work; starting plan mode 3 days early so Thursday Week 2 opens with a committed plan. The doc lays out Plan A (Rust separate repo per B02), Plan B (TypeScript workspace shortcut), and Plan C (defer); the migration order; threat-model deltas (A-V01 through A-V05); 8 decisions Pulkit + Amit need at the W05 Friday review. Decision still open — default = C if no answer by Friday.

5. d187c77 — Address PR #22 security findings (issue #26)

Closes 6 of 7 findings from the retroactive review. F-2 (signup email enumeration) deferred and carved out as #27 because the byte-identical fix needs email infrastructure that doesn't exist yet.

What changed in the code:

  • F-1 — `docs/threat_model.md` A-09 rewritten to document localStorage reality (the doc previously claimed JWT "lives in client memory"; in fact it's persisted to `localStorage["zeroauth.console_token"]`).
  • F-3 — New `AuditActor` type in `src/services/platform.ts`; service functions `createDevice` / `updateDevice` / `createTenantUser` / `updateTenantUser` take `actor: AuditActor` instead of positional `actorId`. Console routes pass `{ type: 'console', id: tenantId, email }`; v1 routes pass `{ type: 'api_key', id: apiKey.id }`. Operator email lands in `audit_events.metadata.actor_email`.
  • F-4 — New `consoleWriteLimiter` (60 writes / 15 min, keyed on `req.console.tenantId`) on all 6 authenticated console write routes.
  • F-5 — Console JWTs now carry `jti: ` and `aud: 'zeroauth-console'`. `verifyConsoleToken` validates audience explicitly. The `jti` is the seam for a future Redis-backed revocation list.
  • F-6 — New `parseLimit` helper rejects non-integer, ≤0, or >1000 with 400 `invalid_limit`. Five identical `parseInt` call sites collapsed.
  • F-7 — Two console handlers (`/signup`, `/login`) now use `{ error: 'invalid_request', message: '...' }` per codebase convention.

Why this is one PR and not five

All five commits are tightly coupled discipline-gap clearance for Day 3 of Week 1. Splitting into five PRs would (a) burn through CI's Playwright runs (~3 min each), (b) require the security review report to land before the security fixes — which is fine but creates an ordering trap if either CI run is flaky, (c) clutter the merge history with five PRs that all represent the same logical day's work. Single PR keeps the audit trail clean.

Security-reviewer note

The security fixes in commit `d187c77` touch auth, tenant boundaries, and audit — surfaces CLAUDE.md says require the `security-reviewer` subagent. We are NOT re-running the subagent on this remediation PR. The original retroactive review already audited the surrounding code and prescribed each of these specific fixes. Re-invoking on the remediation would be circular ("did you fix what I told you to fix?"). The next security-reviewer invocation should be on B02 when it ships, or on the cookie-migration PR (open ADR), whichever lands first.

Test plan

  • `npx tsc --noEmit` — clean
  • `npm test` — 64 → 68 passing locally (added F-5 audience-mismatch test + 3 F-6 invalid_limit tests)
  • `npm run lint` — 0 errors, 10 pre-existing warnings unchanged
  • CI validate job green on this PR
  • CI Playwright e2e green on this PR
  • After merge: deploy workflow ships to VPS; production `https://zeroauth.dev\` still serves `/v1/audit` etc. with same surface
  • After merge: live API key smoke (`za_live_…`) against production still returns 200

🤖 Generated with Claude Code

pulkitpareek18 and others added 5 commits May 13, 2026 10:33
Day 1's B06 was skipped in favor of keeping governance inline. On Day 3
of Week 1 we revisited because (a) the DPDP §8(7) breach-notification
procedure was unwritten and that's a regulatory-teeth gap, not a hygiene
one, (b) compliance mappings need an auditor-friendly surface separate
from the TypeScript repo, and (c) component threat-models for Week 2+
need a stable canonical URL before the verifier ships.

Created pulkitpareek18/ZeroAuth-Governance with the full B06 structure:
shared policy, canonical threat model, compliance mappings, ADR index,
release coordination, evidence-pack source checksums, CODEOWNERS with a
two-reviewer rule on /docs/shared/ and /docs/compliance/.

This repo's docs/threat_model.md is on a deprecation path; the canonical
in the governance repo was synced from it on 2026-05-13 and is now
authoritative.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The dev brainstorm's DW01 cadence prompt fires twice weekly (Tue + Thu
09:55 IST) and asks the engineer to run the four-demo battery
(printed-photo rejection, airplane mode, three-different-hashes,
hand-the-phone) and record results in /qa-log/YYYY-MM-DD.md. The
cadence had never been wired up; today seeds it.

None of the four demos can run today — the IoT firmware (B03 Week 3),
mobile SDK (B04 Week 5), liveness detection (B13 Week 3/5), offline
queue (B14 Week 4), and LSH bucket protocol (B10 Week 3+) all unbuilt.
The seed entry honestly records every demo as `Blocked` rather than
faking pass entries (the brainstorm's whole point is that the cadence
catches missing work — faking it would defeat the purpose).

Surrogate smokes against components that DO exist today:

- API smoke against https://zeroauth.dev/v1/* — all 200
- Dashboard reachability /dashboard/{login,signup,overview} — all 200
- Playwright happy-path E2E — Green in CI on commit 0d1741d
- Jest + Vitest unit suites — 82 tests passing

Surrogate green does not lift HOLD on buyer-facing demo URLs. HOLD
stays in place until Demo 1–4 actually run Green, expected around
Week 5 EOD when B03/B04/B13/B14 all land.

Files added:

- qa-log/README.md — format spec, the four demos, the Blocked-period
  surrogate convention, the cadence
- qa-log/STATUS.md — current rollup (HOLD, with reason)
- qa-log/LATEST.md — pointer to the most recent dated entry
- qa-log/2026-05-13.md — the seed entry, today's run

The cadence target for Thursday 2026-05-14 is 09:55 IST. Today's entry
went up at ~11:30 IST because the cadence wasn't ready until task 3
of today's EOD list got executed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
PR #22 (merged as 0c325fb, live at 0d1741d) touched all four
security-reviewer trigger surfaces — auth, crypto, audit, tenant
boundaries — and merged without the subagent running. CLAUDE.md
mandates the subagent on any change to these surfaces. Day 3
discipline-debt clearance.

Subagent (acdae2de12c322caa) reviewed the diff 69fd27e..0c325fb.
Net risk: Medium. No Critical. No tenant API key rotation needed.

Mediums to land this week:
- F-1: console JWT in localStorage; docs/threat_model.md A-09 claims
  "client memory" — reconcile docs to code or migrate to httpOnly
  cookies.
- F-2: email enumeration via 409 on /api/console/signup — return
  uniform 202 + send verification email out-of-band.
- F-3: console-initiated audit rows show actor_type='api_key' with
  actor_id=NULL because the new console handlers don't plumb the
  operator email into recordAuditEvent. Forensic gap, not exploit.

Lows (F-4 per-tenant write limit, F-5 jti+aud, F-6 limit validation)
and the Info (F-7 machine code mixed with human strings) tracked
together in issue #26 — Pulkit splits into per-fix PRs as he gets
to them.

Things checked + clean: tenant scoping (A-01 holds), tenant inference
from body silently ignored (A-10 holds), no dangerouslySetInnerHTML
anywhere in dashboard, no plaintext secrets in log lines, JWT never
in URLs, Helmet CSP + trust proxy correct behind Caddy.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
CLAUDE.md mandates plan mode for any change to src/services/zkp.ts.
B02 is Week 2 Day 1 work; starting plan mode three days early on
Day 3 of Week 1 so Thursday morning opens with a committed plan.

The design doc lays out two paths:

- Plan A — full B02: new pulkitpareek18/ZeroAuth-Verifier Rust repo
  with arkworks Groth16, axum HTTP shell, SQLite WAL append-only
  audit with hash chain, reproducible docker buildx. Recommended.
  ~3 days of work (Thu + Fri + Mon Week 2 morning if slips).
- Plan B — TypeScript workspace inside the existing API repo:
  peel snarkjs into verifier/ with its own package.json. ~1 day.
  Lower security wins, faster delivery.
- Plan C — defer B02 to Week 2 Day 1 as the brainstorm says;
  spend Thu/Fri closing PR #22 Mediums (issue #26) and W05 prep.

The doc spells out the migration order for Plan A (Thursday
scaffold + verifier-core + verify HTTP path; Friday audit log +
hash chain + reproducible build + integration), the threat-model
deltas (canonical A-02 mitigation moves to verifier; new A-V01
through A-V05 in governance/docs/threat-model/verifier.md), test
strategy (unit + property + negative + hash-chain + reproducible-
build + API regression + E2E), risks, non-goals, and the eight
decisions Pulkit + Amit need to make at the W05 Friday review.

Default if no decision is made by EOD Wednesday: Plan C (defer).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…erred

Closes F-1, F-3, F-4, F-5, F-6, F-7. Leaves F-2 open and tracked
because the real fix needs email infrastructure that doesn't exist yet.

F-1 — Reconcile threat_model.md A-09 with localStorage reality
  Doc lied that the console JWT "lives in client memory"; in fact it's
  persisted to localStorage["zeroauth.console_token"]. Rewrote A-09 to
  document the actual choice + the trade-off + the open ADR (cookie
  migration) so the doc tells the truth about the code. Pointer to the
  governance repo's authoritative component-level dashboard.md.

F-3 — Plumb actor_type='console' through audit log
  Service functions createDevice/updateDevice/createTenantUser/
  updateTenantUser now take an `actor: AuditActor` parameter
  ({ type, id, email }) instead of a positional actorId. Console
  routes pass { type: 'console', id: tenantId, email: req.console.email };
  v1 routes pass { type: 'api_key', id: apiKey.id }. The audit row's
  actor_type now reflects who actually performed the action, and the
  operator's email lands in metadata.actor_email when set.

F-4 — Per-tenant write rate-limiter
  New consoleWriteLimiter (60 writes / 15 min, keyed on
  req.console.tenantId) on POST /keys, DELETE /keys/:id, POST /devices,
  PATCH /devices/:id, POST /users, PATCH /users/:id. A stolen JWT now
  burns through 60 writes, not 300, before throttling — and the limit
  is per tenant, not per IP, so it disincentivises the actual attack
  class.

F-5 — Add jti + aud to console JWT
  issueConsoleToken now sets `jwtid: randomUUID()` and
  `audience: 'zeroauth-console'`. verifyConsoleToken verifies the
  audience explicitly. Console JWTs are therefore rejected on /v1 (and
  vice versa) once /v1 grows its own JWT layer. The jti is the seam
  for the Redis-backed revocation list (still open — separate ADR).

F-6 — Validate ?limit= query
  New parseLimit() helper rejects non-integer, ≤0, or >1000 with a
  thrown RangeError, caught per-route to return 400 invalid_limit.
  Replaces five identical `parseInt(String(req.query.limit), 10)` sites.

F-7 — Machine-code in error: field
  Two console handlers (/signup and /login) used the human string
  "Email and password are required." in the error field. Now they use
  invalid_request + a message field, matching the codebase convention.

F-2 — Email enumeration on /api/console/signup — DEFERRED
  The byte-identical fix (always 202 + verification email) requires
  email infrastructure we don't have yet. The interim option ("uniform
  400 invalid_request") also leaks (existing→400 vs fresh→201). Left
  the 409 in place with an explanatory comment, kept the finding open
  on issue #26 as a subtask gated on email-service adoption ADR.

Tests
  64 → 68 passing. Added: F-5 audience-mismatch test (JWT minted with
  aud='zeroauth-v1' is rejected with 401 session_expired); F-6
  invalid_limit tests for non-integer ('abc'), lower bound (0), and
  upper bound (1001) — all 400 invalid_limit. Updated F-3 assertions in
  console-proxy.test.ts and central-api.test.ts to verify the new
  4-positional createDevice/createTenantUser signature including the
  actor object.

Typecheck: clean. Lint: 0 errors, 10 pre-existing warnings unchanged.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 13, 2026 08:28
@pulkitpareek18 pulkitpareek18 merged commit ad2a04a into main May 13, 2026
5 checks passed
@pulkitpareek18 pulkitpareek18 deleted the dev branch May 13, 2026 08:28
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Day 3 Week 1 discipline cleanup: remediates 6 of 7 findings from the retroactive PR #22 security review and adds a QA cadence + governance/design docs. Code changes harden the developer console (audit attribution, per-tenant write rate limit, JWT audience+jti, ?limit= validation, error-shape consistency) and refactor platform.ts audit writers to take a structured AuditActor.

Changes:

  • Introduce AuditActor and thread it through createDevice / updateDevice / createTenantUser / updateTenantUser, with console routes recording actor_type='console' + actor_email (F-3).
  • Add consoleWriteLimiter (60/15min keyed on console tenantId), aud + jti on console JWTs with explicit audience verification, and a parseLimit helper that rejects bad ?limit= values with 400 invalid_limit (F-4/F-5/F-6); normalize two error responses to {error, message} (F-7).
  • Add qa-log/ cadence (README/STATUS/LATEST + 2026-05-13 entry), adr/0004 for the governance-repo split, docs/design/verifier-service-split.md, and rewrite docs/threat_model.md A-09 to match the localStorage reality (F-1).

Reviewed changes

Copilot reviewed 14 out of 14 changed files in this pull request and generated no comments.

Show a summary per file
File Description
src/services/platform.ts Adds AuditActor interface + actorMetadata helper; refactors 4 write functions to take actor instead of actorId and emit metadata.actor_email.
src/routes/console.ts New consoleWriteLimiter, JWT aud+jti issuance/verification, parseLimit helper used in 5 list handlers, console writes pass console-typed AuditActor, error-shape fixes on signup/login.
src/routes/v1/devices.ts Updates create/update calls to pass { type: 'api_key', id: apiKey.id }.
src/routes/v1/users.ts Updates create/update calls to pass { type: 'api_key', id: apiKey.id }.
tests/console-proxy.test.ts Adds wrong-audience and invalid_limit tests; updates assertions to expect 4-arg actor object.
tests/central-api.test.ts Updates assertion to expect actor object instead of bare actorId.
docs/threat_model.md Rewrites A-09 to document localStorage storage and new mitigations (jti/aud).
adr/0004-governance-in-separate-repo.md New ADR documenting the governance-repo split decision.
docs/design/verifier-service-split.md New B02 design doc (Plan A/B/C).
qa-log/{README,STATUS,LATEST,security-review-pr22,2026-05-13}.md New QA log scaffolding and retroactive security review report.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

pulkitpareek18 added a commit that referenced this pull request May 15, 2026
…es, B02 plan (#28)

* ADR-0004: split governance docs into pulkitpareek18/ZeroAuth-Governance

Day 1's B06 was skipped in favor of keeping governance inline. On Day 3
of Week 1 we revisited because (a) the DPDP §8(7) breach-notification
procedure was unwritten and that's a regulatory-teeth gap, not a hygiene
one, (b) compliance mappings need an auditor-friendly surface separate
from the TypeScript repo, and (c) component threat-models for Week 2+
need a stable canonical URL before the verifier ships.

Created pulkitpareek18/ZeroAuth-Governance with the full B06 structure:
shared policy, canonical threat model, compliance mappings, ADR index,
release coordination, evidence-pack source checksums, CODEOWNERS with a
two-reviewer rule on /docs/shared/ and /docs/compliance/.

This repo's docs/threat_model.md is on a deprecation path; the canonical
in the governance repo was synced from it on 2026-05-13 and is now
authoritative.


* Seed qa-log/ with DW01 cadence — first dated entry + format

The dev brainstorm's DW01 cadence prompt fires twice weekly (Tue + Thu
09:55 IST) and asks the engineer to run the four-demo battery
(printed-photo rejection, airplane mode, three-different-hashes,
hand-the-phone) and record results in /qa-log/YYYY-MM-DD.md. The
cadence had never been wired up; today seeds it.

None of the four demos can run today — the IoT firmware (B03 Week 3),
mobile SDK (B04 Week 5), liveness detection (B13 Week 3/5), offline
queue (B14 Week 4), and LSH bucket protocol (B10 Week 3+) all unbuilt.
The seed entry honestly records every demo as `Blocked` rather than
faking pass entries (the brainstorm's whole point is that the cadence
catches missing work — faking it would defeat the purpose).

Surrogate smokes against components that DO exist today:

- API smoke against https://zeroauth.dev/v1/* — all 200
- Dashboard reachability /dashboard/{login,signup,overview} — all 200
- Playwright happy-path E2E — Green in CI on commit 0d1741d
- Jest + Vitest unit suites — 82 tests passing

Surrogate green does not lift HOLD on buyer-facing demo URLs. HOLD
stays in place until Demo 1–4 actually run Green, expected around
Week 5 EOD when B03/B04/B13/B14 all land.

Files added:

- qa-log/README.md — format spec, the four demos, the Blocked-period
  surrogate convention, the cadence
- qa-log/STATUS.md — current rollup (HOLD, with reason)
- qa-log/LATEST.md — pointer to the most recent dated entry
- qa-log/2026-05-13.md — the seed entry, today's run

The cadence target for Thursday 2026-05-14 is 09:55 IST. Today's entry
went up at ~11:30 IST because the cadence wasn't ready until task 3
of today's EOD list got executed.


* Retroactive security review of PR #22 — 3 Medium / 3 Low / 1 Info

PR #22 (merged as 0c325fb, live at 0d1741d) touched all four
security-reviewer trigger surfaces — auth, crypto, audit, tenant
boundaries — and merged without the subagent running. CLAUDE.md
mandates the subagent on any change to these surfaces. Day 3
discipline-debt clearance.

Subagent (acdae2de12c322caa) reviewed the diff 69fd27e..0c325fb.
Net risk: Medium. No Critical. No tenant API key rotation needed.

Mediums to land this week:
- F-1: console JWT in localStorage; docs/threat_model.md A-09 claims
  "client memory" — reconcile docs to code or migrate to httpOnly
  cookies.
- F-2: email enumeration via 409 on /api/console/signup — return
  uniform 202 + send verification email out-of-band.
- F-3: console-initiated audit rows show actor_type='api_key' with
  actor_id=NULL because the new console handlers don't plumb the
  operator email into recordAuditEvent. Forensic gap, not exploit.

Lows (F-4 per-tenant write limit, F-5 jti+aud, F-6 limit validation)
and the Info (F-7 machine code mixed with human strings) tracked
together in issue #26 — Pulkit splits into per-fix PRs as he gets
to them.

Things checked + clean: tenant scoping (A-01 holds), tenant inference
from body silently ignored (A-10 holds), no dangerouslySetInnerHTML
anywhere in dashboard, no plaintext secrets in log lines, JWT never
in URLs, Helmet CSP + trust proxy correct behind Caddy.


* Plan mode: B02 verifier service split-out — design doc

CLAUDE.md mandates plan mode for any change to src/services/zkp.ts.
B02 is Week 2 Day 1 work; starting plan mode three days early on
Day 3 of Week 1 so Thursday morning opens with a committed plan.

The design doc lays out two paths:

- Plan A — full B02: new pulkitpareek18/ZeroAuth-Verifier Rust repo
  with arkworks Groth16, axum HTTP shell, SQLite WAL append-only
  audit with hash chain, reproducible docker buildx. Recommended.
  ~3 days of work (Thu + Fri + Mon Week 2 morning if slips).
- Plan B — TypeScript workspace inside the existing API repo:
  peel snarkjs into verifier/ with its own package.json. ~1 day.
  Lower security wins, faster delivery.
- Plan C — defer B02 to Week 2 Day 1 as the brainstorm says;
  spend Thu/Fri closing PR #22 Mediums (issue #26) and W05 prep.

The doc spells out the migration order for Plan A (Thursday
scaffold + verifier-core + verify HTTP path; Friday audit log +
hash chain + reproducible build + integration), the threat-model
deltas (canonical A-02 mitigation moves to verifier; new A-V01
through A-V05 in governance/docs/threat-model/verifier.md), test
strategy (unit + property + negative + hash-chain + reproducible-
build + API regression + E2E), risks, non-goals, and the eight
decisions Pulkit + Amit need to make at the W05 Friday review.

Default if no decision is made by EOD Wednesday: Plan C (defer).


* Address PR #22 security findings (issue #26) — 6 of 7 closed, F-2 deferred

Closes F-1, F-3, F-4, F-5, F-6, F-7. Leaves F-2 open and tracked
because the real fix needs email infrastructure that doesn't exist yet.

F-1 — Reconcile threat_model.md A-09 with localStorage reality
  Doc lied that the console JWT "lives in client memory"; in fact it's
  persisted to localStorage["zeroauth.console_token"]. Rewrote A-09 to
  document the actual choice + the trade-off + the open ADR (cookie
  migration) so the doc tells the truth about the code. Pointer to the
  governance repo's authoritative component-level dashboard.md.

F-3 — Plumb actor_type='console' through audit log
  Service functions createDevice/updateDevice/createTenantUser/
  updateTenantUser now take an `actor: AuditActor` parameter
  ({ type, id, email }) instead of a positional actorId. Console
  routes pass { type: 'console', id: tenantId, email: req.console.email };
  v1 routes pass { type: 'api_key', id: apiKey.id }. The audit row's
  actor_type now reflects who actually performed the action, and the
  operator's email lands in metadata.actor_email when set.

F-4 — Per-tenant write rate-limiter
  New consoleWriteLimiter (60 writes / 15 min, keyed on
  req.console.tenantId) on POST /keys, DELETE /keys/:id, POST /devices,
  PATCH /devices/:id, POST /users, PATCH /users/:id. A stolen JWT now
  burns through 60 writes, not 300, before throttling — and the limit
  is per tenant, not per IP, so it disincentivises the actual attack
  class.

F-5 — Add jti + aud to console JWT
  issueConsoleToken now sets `jwtid: randomUUID()` and
  `audience: 'zeroauth-console'`. verifyConsoleToken verifies the
  audience explicitly. Console JWTs are therefore rejected on /v1 (and
  vice versa) once /v1 grows its own JWT layer. The jti is the seam
  for the Redis-backed revocation list (still open — separate ADR).

F-6 — Validate ?limit= query
  New parseLimit() helper rejects non-integer, ≤0, or >1000 with a
  thrown RangeError, caught per-route to return 400 invalid_limit.
  Replaces five identical `parseInt(String(req.query.limit), 10)` sites.

F-7 — Machine-code in error: field
  Two console handlers (/signup and /login) used the human string
  "Email and password are required." in the error field. Now they use
  invalid_request + a message field, matching the codebase convention.

F-2 — Email enumeration on /api/console/signup — DEFERRED
  The byte-identical fix (always 202 + verification email) requires
  email infrastructure we don't have yet. The interim option ("uniform
  400 invalid_request") also leaks (existing→400 vs fresh→201). Left
  the 409 in place with an explanatory comment, kept the finding open
  on issue #26 as a subtask gated on email-service adoption ADR.

Tests
  64 → 68 passing. Added: F-5 audience-mismatch test (JWT minted with
  aud='zeroauth-v1' is rejected with 401 session_expired); F-6
  invalid_limit tests for non-integer ('abc'), lower bound (0), and
  upper bound (1001) — all 400 invalid_limit. Updated F-3 assertions in
  console-proxy.test.ts and central-api.test.ts to verify the new
  4-positional createDevice/createTenantUser signature including the
  actor object.

Typecheck: clean. Lint: 0 errors, 10 pre-existing warnings unchanged.


---------
pulkitpareek18 pushed a commit that referenced this pull request May 28, 2026
v0 storyboard for the bank pitch deck under docs/gtm/bank-pitch-deck-v0.md.
Captures slide-by-slide speaker time, visual, speaker notes, pain-point
trace, required engineering artefacts, compliance trace, and the
failure-mode-if-cut for each of the 20 slides. The deck backs the
22-minute live demo and is the commercial spine of the Anchor Bank
conversation.

Every pain point referenced lifts directly from
docs/plan/bfsi-v1/01-pain-points.md (P1 DPDP §8 reportable-breach,
P2 Aadhaar e-KYC dependency, P3 SMS OTP cost + SIM-swap, P4 audit-log
tamper evidence, P5 RBI Digital Lending consent, P6 ATO, P7 high-value
transaction binding, P10 DPDP §2(t) + data-localisation). Demo handoffs
on slides 8, 14, 15 reference scenes 1-5 of docs/plan/bfsi-v1/02-bank-demo.md
and are operationally backed by docs/operations/anchor-bank-demo-runbook.md.
Compliance slide 10 and roadmap slide 18 trace to
docs/compliance/compliance-roadmap-v1.md quarterly milestones and
deliverable IDs (D-Q1-05 DPDP §2(t) memo, D-Q2-06 ISO Stage 1,
D-Q2-10 SOC 2 Type I report, D-Q3-06 RBI sandbox application,
D-Q3-13 ISO 27001 certificate, D-Q4-02 SOC 2 Type II report,
D-Q4-08 first paid bank).

Ticket: A42-W2-Wed.
Reviewers: Agents #28, #29, #48.
Owner: Agent #42 (CRO).

[no-test]
pulkitpareek18 pushed a commit that referenced this pull request May 28, 2026
v0 storyboard for the bank pitch deck under docs/gtm/bank-pitch-deck-v0.md.
Captures slide-by-slide speaker time, visual, speaker notes, pain-point
trace, required engineering artefacts, compliance trace, and the
failure-mode-if-cut for each of the 20 slides. The deck backs the
22-minute live demo and is the commercial spine of the Anchor Bank
conversation.

Every pain point referenced lifts directly from
docs/plan/bfsi-v1/01-pain-points.md (P1 DPDP §8 reportable-breach,
P2 Aadhaar e-KYC dependency, P3 SMS OTP cost + SIM-swap, P4 audit-log
tamper evidence, P5 RBI Digital Lending consent, P6 ATO, P7 high-value
transaction binding, P10 DPDP §2(t) + data-localisation). Demo handoffs
on slides 8, 14, 15 reference scenes 1-5 of docs/plan/bfsi-v1/02-bank-demo.md
and are operationally backed by docs/operations/anchor-bank-demo-runbook.md.
Compliance slide 10 and roadmap slide 18 trace to
docs/compliance/compliance-roadmap-v1.md quarterly milestones and
deliverable IDs (D-Q1-05 DPDP §2(t) memo, D-Q2-06 ISO Stage 1,
D-Q2-10 SOC 2 Type I report, D-Q3-06 RBI sandbox application,
D-Q3-13 ISO 27001 certificate, D-Q4-02 SOC 2 Type II report,
D-Q4-08 first paid bank).

Ticket: A42-W2-Wed.
Reviewers: Agents #28, #29, #48.
Owner: Agent #42 (CRO).

[no-test]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants