Skip to content

feat(ui-refresh): landing + bys-demo + web-app shell (Scope A)#29

Merged
ahmetabdullahgultekin merged 1 commit into
masterfrom
feat/ui-refresh-opus
Apr 22, 2026
Merged

feat(ui-refresh): landing + bys-demo + web-app shell (Scope A)#29
ahmetabdullahgultekin merged 1 commit into
masterfrom
feat/ui-refresh-opus

Conversation

@ahmetabdullahgultekin

Copy link
Copy Markdown
Contributor

Summary

Design-system refresh across three FIVUCSAS public surfaces. Zero functional change — every OAuth flow, postMessage event, SRI hash, handler signature, and DOM integration id is preserved.

landing-website/ — fivucsas.com v2

  • Full rewrite of src/App.tsx, src/index.css, tailwind.config.js, index.html
  • Custom inline SVG icon system; 10 auth-methods grid; architecture stack visual (Clients → Traefik → Services → Storage); trust-signals row (RS256 default, AES-GCM-256, KVKK/GDPR, partitioned audit logs, MFA rate limits); CLI mock showing FivucsasAuth.loginRedirect(...)
  • Working EN/TR toggle via localStorage + navigator.language detection
  • All JSON-LD blocks, OG tags, sitemap, robots.txt, favicon, .htaccess preserved

bys-demo/ — demo.fivucsas.com

  • styles.css redesigned only. Marmara crimson identity retained, modern type scale, premium FIVUCSAS gradient CTA with shimmer sweep, polished dashboard + callback states
  • Zero HTML/JS edits — every <script> tag, CSP meta, SRI integrity hash, FIVUCSAS_CONFIG, loginRedirect(), handleRedirectCallback(), sessionStorage read/write, every DOM id preserved byte-for-byte

web-app/ submodule bump: 4e4cdd3ff17a57

Pulls in Rollingcat-Software/web-app#24 — Scope A theme + shell refresh:

  • src/theme.ts rewritten with calibrated palette, Poppins display hierarchy, 8-tier shadow ramp, focus-visible rings
  • Sidebar / TopBar / DashboardLayout / PublicLayout polished
  • i18n additive only (nav.group.*, nav.badgeAdmin, nav.primary, sidebar.systemStatus)
  • 608/608 Vitest green, lint 0 errors
  • Note: web-app submodule pointer targets the PR-24 branch head. After PR-24 merges, this PR's submodule pointer will be rebased to the new main tip before merge.

Out of scope

  • No verify-widget/ or fivucsas-auth.js SDK changes (SRI hashes on bys-demo + all external integrators remain valid)
  • No backend / identity-core-api / biometric-processor changes
  • No feature-page redesigns inside web-app

Test plan

  • Parent repo: submodule pointer diff verified (4e4cdd3ff17a57)
  • Landing: npm run build clean; Turkish diacritics verified on 106 tr: strings
  • BYS demo: CSP meta, SDK <script> tag with SRI, FIVUCSAS_CONFIG, all integration function names + DOM ids verified present in index.html, dashboard.html, callback.html
  • web-app (via PR-24): 608/608 Vitest tests green, lint 0 errors, npm run build clean, deployed to production
  • All three sites already deployed to production (live verified):
    • https://fivucsas.com/ → 200, new bundle
    • https://app.fivucsas.com/ → 200, /models/ endpoint serving
    • https://demo.fivucsas.com/ → 200, 29 KB new styles.css, integration points intact
  • Reviewer: click through demo.fivucsas.com → FIVUCSAS login button → redirect round-trip → dashboard
  • Reviewer: merge order — PR-24 first, then rebase this PR's submodule pointer, then merge this PR

See CHANGELOG.md, landing-website/CHANGELOG.md, and web-app PR-24 for full per-repo detail.

🤖 Generated with Claude Code

Zero functional change. All OAuth flows, postMessage events, SRI hashes,
integration IDs, and handler signatures preserved byte-for-byte.

- landing-website/: v2 redesign. src/App.tsx, src/index.css,
  tailwind.config.js, index.html rewritten with new palette, Space
  Grotesk + JetBrains Mono typography, 10 auth-methods grid,
  architecture stack visual, trust-signals row, refined team + stack
  sections. Working EN/TR toggle with localStorage + navigator.language
  detection. All JSON-LD blocks, OG tags, sitemap, robots, .htaccess
  preserved.

- bys-demo/styles.css: redesigned. Marmara crimson identity retained,
  modern typography scale, red accent stripes on cards, premium FIVUCSAS
  gradient CTA with shimmer sweep, polished tables + schedule + GPA +
  callback states. Zero HTML/JS edits — every script tag, CSP meta, SRI
  integrity hash, FIVUCSAS_CONFIG literal, loginRedirect(),
  handleRedirectCallback(), sessionStorage read/write, and every DOM id
  preserved.

- web-app submodule bump: 4e4cdd3 → ff17a57
  Pulls in PR #24 (feat(web-app): Scope A UI refresh — theme + shell):
  - src/theme.ts rewritten (calibrated ink scale, Poppins display
    hierarchy, 8-tier shadow ramp, focus-visible rings, refined overrides
    across every MUI primitive)
  - Sidebar (grouped nav + gradient active indicator + admin chips +
    status tile), TopBar (glass AppBar + gradient avatar + polished user
    menu), DashboardLayout (ambient canvas + refined breadcrumbs),
    PublicLayout (glass AppBar + gradient logo)
  - i18n additive only: nav.group.*, nav.badgeAdmin, nav.primary,
    sidebar.systemStatus in en.json + tr.json
  - 608/608 Vitest tests green, lint 0 errors

Out of scope: verify-widget/ and fivucsas-auth.js SDK untouched (SRI
hashes on integrators remain valid); no backend change; no feature-page
redesigns.

See CHANGELOG.md, landing-website/CHANGELOG.md, and web-app/CHANGELOG.md
for full detail.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings April 22, 2026 17:07
@gitguardian

gitguardian Bot commented Apr 22, 2026

Copy link
Copy Markdown

️✅ There are no secrets present in this pull request anymore.

If these secrets were true positive and are still valid, we highly recommend you to revoke them.
While these secrets were previously flagged, we no longer have a reference to the
specific commits where they were detected. Once a secret has been leaked into a git
repository, you should consider it compromised, even if it was deleted immediately.
Find here more information about risks.


🦉 GitGuardian detects secrets in your source code to help developers and security teams secure the modern development process. You are seeing this because you or someone else with access to this repository has authorized GitGuardian to scan your pull request.

@ahmetabdullahgultekin ahmetabdullahgultekin changed the base branch from main to master April 22, 2026 17:12

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Design-system / shell refresh across public surfaces plus a new verify-widget packaging flow, along with infra/test script and documentation updates to reflect the fivucsas.com domain move and production hardening.

Changes:

  • Added verify-widget NGINX + Docker packaging, plus an asset sync script to stage built bundles before image builds.
  • Updated NGINX gateway + docker-compose/security/monitoring configs and added/updated operational scripts (health checks, verification E2E, Cloudflare tunnel).
  • Refreshed landing + BYS demo static surfaces and updated repo-level docs (README/SECURITY/CONTRIBUTING/LICENSE), removing several audit/report documents.

Reviewed changes

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

Show a summary per file
File Description
verify-widget/sync-assets.sh Stages built verify assets into verify-widget/html/ before Docker build
verify-widget/nginx.conf NGINX config for widget hosting + caching/headers
verify-widget/html/index.html Widget SPA entry HTML (with CSP meta)
verify-widget/html/fivucsas-auth.js Bundled/minified SDK artifact served at widget origin
verify-widget/html/assets/poppins-latin-ext-700-normal-cby-RkWa.woff2 Font asset
verify-widget/html/assets/poppins-latin-ext-700-normal-DctTR6Tg.woff Font asset
verify-widget/html/assets/poppins-latin-ext-600-normal-CAhIAdZj.woff2 Font asset
verify-widget/html/assets/poppins-latin-ext-500-normal-CK-6C4Hw.woff2 Font asset
verify-widget/docker-compose.prod.yml Production compose for verify-widget behind Traefik
verify-widget/Dockerfile Widget image build (nginx:alpine + static files)
scripts/test/run-backend-tests.sh Backend test script now targets api.fivucsas.com by default
scripts/test/backend-test-plan.md Test plan doc updated for new API base URL
scripts/test-verification.sh New end-to-end verification pipeline test script
scripts/test-health.sh New API health check script
scripts/setup-twilio.sh Updated SMS enablement property names + domain URLs
scripts/security-audit.sh Updated security audit script target base URL
scripts/run-tests.sh Updated test runner behavior for identity-core-api
scripts/deploy/setup-laptop-gpu-wsl.ps1 Updated Cloudflare tunnel DNS targets (bio.fivucsas.com)
scripts/deploy/DEPLOYMENT_GUIDE.md Updated production URLs and deployment steps to new domains
scripts/cloudflare-tunnel.sh New helper for Cloudflare tunnel lifecycle for biometric processor
scripts/add_diagrams_to_docx.py Hardening: disable shell=True in subprocess call
scripts/README.md New index of scripts and their purpose
nginx/nginx.conf Gateway config tuning: rate limits, headers, upstream names/body-size
monitoring/docker-compose.monitoring.yml Monitoring stack tightened (bind localhost ports, require Grafana password)
landing-website/tailwind.config.js Tailwind tokens/palette/fonts/animations refresh
landing-website/src/index.css Updated global styling, effects, reduced-motion, scrollbars
landing-website/public/sitemap.xml Added sitemap for fivucsas.com
landing-website/public/robots.txt Added robots + sitemap reference
landing-website/public/pgp.asc Added release signing PGP public key
landing-website/index.html Updated SEO/meta/JSON-LD + dark theme + fonts
landing-website/README.md Added landing site documentation
landing-website/CHANGELOG.md Added landing site changelog
landing-website/.htaccess Added download route mapping + tightened security headers
docker-compose.yml Local compose hardening + env var parameterization
docker-compose.prod.yml Prod compose hardening for monitoring containers
docker-compose.dev.yml Removed compose version header (modern Compose)
bys-demo/test-elements.html Added web components test page
bys-demo/robots.txt Added robot rules to block callback/dashboard/test pages
bys-demo/dashboard.html Added demo dashboard page
bys-demo/callback.html Added demo OAuth callback page
bys-demo/.htaccess Added HTTPS enforcement + security/caching headers
auth-test/styles.css Removed auth-test CSS (file deleted)
auth-test/.htaccess Removed auth-test CSP htaccess (file deleted)
TODO.md Removed TODO list (file deleted)
SECURITY.md Added security policy doc
README.md Major README refresh: stack, prod URLs, status, test commands
PERFORMANCE_AUDIT.md Removed performance audit report (file deleted)
LICENSE Added MIT license text file
INFRA_REVIEW.md Removed infra review report (file deleted)
FRONTEND_COMPARISON_REPORT.md Removed frontend comparison report (file deleted)
CONTRIBUTING.md Added contribution guidelines
API_EDGE_CASE_AUDIT.md Removed API audit report (file deleted)
.pre-commit-install Added helper to install pre-commit across submodules
.pre-commit-config.yaml Added parent repo pre-commit (gitleaks)
.gitignore Updated ignores (env/prod, Claude metadata, verify-widget artifacts)
.github/workflows/deploy-landing.yml Updated landing deploy target path for new domain
.github/workflows/ci.yml CI validation updates for compose/nginx
.claude/settings.local.json Redacted token placeholders + updated domains
.claude/commands/test-gaps.md Added Claude command template
.claude/commands/security-audit.md Added Claude command template
.claude/commands/perf-review.md Added Claude command template
.claude/commands/docker-review.md Added Claude command template
.claude/commands/arch-review.md Added Claude command template
Comments suppressed due to low confidence (1)

landing-website/index.html:101

  • downloadUrl points to https://verify.fivucsas.com/sdk.js, but this repo’s widget/SDK artifacts are served as fivucsas-auth.js (and the NGINX config explicitly matches that name). If sdk.js is not a real endpoint, this JSON-LD will be inaccurate for SEO and integrators. Update it to the actual public SDK URL you serve.
      "url": "https://verify.fivucsas.com",
      "downloadUrl": "https://verify.fivucsas.com/sdk.js",
      "description": "Embeddable biometric authentication widget and SDK. Drop-in MFA with face, voice, fingerprint, NFC, TOTP, QR and more. Works like reCAPTCHA or e-Devlet for identity verification.",
      "offers": {

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

@ahmetabdullahgultekin ahmetabdullahgultekin merged commit 0589bb0 into master Apr 22, 2026
5 of 6 checks passed
ahmetabdullahgultekin added a commit that referenced this pull request Apr 25, 2026
ahmetabdullahgultekin added a commit that referenced this pull request Apr 25, 2026
identity-core-api → 2adfa90 (PR #29 audit_logs.tenant_id fix + V46 backfill)
biometric-processor → bdd8203 (PR #52 config validator)
client-apps → e840de0 (PR #31 MFA dispatch + PR #32 polish v2)

Plus prod deploy of identity-core-api with V46 backfill applied:
829 NULL-tenant audit rows → 0; 104 anonymous failed-login rows
correctly stay NULL by design.
ahmetabdullahgultekin added a commit that referenced this pull request May 11, 2026
…52)

* chore(deps-dev): bump vite from 6.4.1 to 6.4.2 in /landing-website (#28)

Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 6.4.1 to 6.4.2.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/v6.4.2/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v6.4.2/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-version: 6.4.2
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* docs(changelog): 2026-04-24 evening — 12 more PRs merged, puzzle-page split, dashboard 403/500/422 remediation

* docs(changelog): PR #38 — frontend RBAC gating (Rules 2+3) live on app.fivucsas.com

* chore(submodule): bump identity-core-api → 4bee6d7 (4 PRs landed 2026-04-25)

Pointer bump for the 4 PRs that landed on identity-core-api/main today:
  efb8228  feat(rbac): V45 TENANT_ADMIN permission baseline (#27)
  4794595  fix(auth): session cancel + dead-end prevention + method switch (#25)
  9e2df8e  feat(tenants): multi email-domain support (V44 + entity foundation) (#26)
  4bee6d7  feat(register): resolve tenant by email-domain via tenant_email_domains (V44 wire-up) (#28)

V44 + V45 migrations both ship in this bump. Next prod boot
(\`docker compose up -d identity-core-api\`) will apply them.

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

* chore(submodule): bump web-app → 1689177 (PR #39 biometric-puzzles)

Pointer bump for the always-succeed bug fix:
  1689177  feat(biometric-puzzles): real per-challenge face + hand detection (#39)

14 face puzzles now run pinned ChallengeType through the BiometricPuzzle
engine. 9 hand puzzles use the new HandLandmarker + per-challenge
detector module. Regression guard ensures every registry entry
references a unique component (prevents the bug-pattern from recurring).

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

* chore(submodule): bump biometric-processor → 4a9383d (PR #36 anti-spoof core)

PR #36 (liveness_score) — rPPG analyzer + temporal consistency + light
challenge service. ~1 KLOC of additive anti-spoof analysis, 3 new
test files, no rewiring of existing code paths.

Note: the broader liveness_capture branch (Aysenur's 6-commit superset
with demo-ui artifacts + dev OpenCV tool) was NOT merged — 12 KLOC,
7 real conflicts in central wiring; needs cleanup.

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

* chore(submodule): bump web-app → 9d7f0c2 (PR #40 lint sweep, 78→17 warnings)

* chore(submodule): bump client-apps → acd395d (PRs #27/#28/#29 — Android CI fix, iOS CoreFoundation, APK i18n)

* chore(submodule): bump client-apps → 2af501f (PR #30 V16 i18n — 12 login screens)

* chore(submodule): bump biometric-processor → ee1e870 (PR #51 anti-spoof pipeline integration, Aysenur)

* chore(submodule): bump web-app → 07f34d0 (PR #41 CI fix unblocks #39+#40 deploy)

* chore(submodule): bump biometric-processor → bdd8203 (PR #52 config validator)

* chore(submodules): bump for today's afternoon-evening wave

identity-core-api → 2adfa90 (PR #29 audit_logs.tenant_id fix + V46 backfill)
biometric-processor → bdd8203 (PR #52 config validator)
client-apps → e840de0 (PR #31 MFA dispatch + PR #32 polish v2)

Plus prod deploy of identity-core-api with V46 backfill applied:
829 NULL-tenant audit rows → 0; 104 anonymous failed-login rows
correctly stay NULL by design.

* chore(submodule): bump identity-core-api → 07b6bcf (PR #30 V47 enrollment scores)

* chore(submodules): bump for next wave (4 more PRs landed + 2 deploys)

identity-core-api → 12e8010 (PR #30 V47 enrollment scores + PR #31 auth-sessions admin list)
biometric-processor → 8c5423f (PR #53 demographics router gating, ~400 MB savings)
web-app → b59005c (PR #44 auth-sessions admin page wiring)
client-apps → f4bdc40 (PR #33 MFA PR #25 endpoints — DELETE cancel + switch-method + NEEDS_ENROLLMENT)

Prod deploys:
- identity-core-api rebuilt + restarted with V47 → live, GET /auth/sessions verified
- biometric-api rebuilt + restarted with demographics gating → ~400 MB freed

* chore(submodules): bump for 3 Dependabot security/patch merges (web #42 postcss XSS, bp #48 dotenv, bp #46 next-demo)

* chore(submodule): bump biometric-processor → f6c6fcb (PR #55 gesture Phase 1, supersedes #50)

* chore(submodules): bump client-apps + biometric-processor (post-PR-#34 + bp #54)

- client-apps → 5e756d8 (PR #34: KMP LoginScreen reads active auth flow + dynamic primary step)
- biometric-processor → a29812d (PR #54: dependabot uuid removal + @sentry/webpack-plugin 5.1.1→5.2.0)
- docs/practice-and-test pointer refresh (no-op in tree)
- .env.example: BE-H1 RS256 JWT signing config (carried over from prior wave)

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

* chore(submodule): bump web-app → 0c61076 (PR #31 GestureLivenessStep + i18n)

PR #31 — gesture Phase 2 web (landmarks-only client-side detection).
Closes the deferred-draft items: liveness.gesture.* keys (17 × en/tr)
landed; build + lint green.

* chore(submodule): bump client-apps → 0104d15 (PR #35 polish: i18n + UX sweep)

PR #35 — TR translation refinements (HW_TOKEN_*, MRZ_BACK_OF_ID_OVERLAY),
22 new i18n keys (OTP countdown + a11y + Profile sweep), TOTP auto-submit
on 6 digits, Email/SMS OTP 30s resend countdown, ProfileScreen +
EditProfileScreen full i18n. 447/447 desktopTest still green.

* docs: strip iOS/macOS from forward roadmap (out of scope, no Apple hardware) (#36)

* chore(submodule): bump client-apps → 0104d15 (PR #35 polish: i18n + UX sweep)

PR #35 — TR translation refinements (HW_TOKEN_*, MRZ_BACK_OF_ID_OVERLAY),
22 new i18n keys (OTP countdown + a11y + Profile sweep), TOTP auto-submit
on 6 digits, Email/SMS OTP 30s resend countdown, ProfileScreen +
EditProfileScreen full i18n. 447/447 desktopTest still green.

* docs: strip iOS/macOS from forward roadmap (out of scope, no Apple hardware)

Apple platforms (iOS / iPadOS / macOS) are permanently out of scope as of
2026-04-26 — the product owner has no Apple hardware available for
development, signing, or testing. Android APK + Windows + Linux desktop
cover the demonstration target. Apple-platform users are served via the
hosted login page (verify.fivucsas.com) in their system browser.

KMP `iosMain` source directory remains in the codebase as part of the
Kotlin Multiplatform compile structure but receives no further
engineering work. This is a docs-only PR, no source code is touched.

Files touched in this repo:
- ROADMAP.md: scope-note banner at top.
- ROADMAP_V2.md: scope-note banner; Phase 7 retitled "Desktop"
  (iOS bullets dropped); Phase summary table row 7 updated; WebAuthn
  browser-OS support table iOS/macOS rows flagged "n/a — native app
  out of scope (browser support remains for hosted-login users on
  Apple devices)"; ASCII core-design diagram updated to remove
  "NFC on iOS/unsupported" phrasing.
- MASTER_PLAN.md: scope-note banner at top; INTEGRATION PATH 4 SDK
  callout drops Swift Package, keeps Android AAR.
- PLATFORM_STATUS.md: scope-note banner at top; KMP source-structure
  bullet updated to keep `iosMain` listed but flagged as out of scope
  (in-tree for compile structure only).
- MOBILE_APP_COMPREHENSIVE_REDESIGN.md: scope-note banner at top.
  Architectural diagrams referencing `iosMain`/`iosApp`/`IosTokenStorage`
  preserved as KMP architectural reference (not engineered against).
- CHANGELOG.md: single Unreleased entry under a new ### Docs heading.

Submodule bumps (forward to docs-branch tips of each submodule's
companion PR):
- web-app → a84f9791 (PR #46)
- identity-core-api → 2af82343 (PR #34)
- biometric-processor → 65d2ea50 (PR #56)
- docs → a3c012e9 (PR #11 — substantive rewrite of CLIENT_APPS_PARITY.md
  and PATH_TO_20_20.md)

`client-apps` submodule deliberately NOT bumped — concurrent agent active
in that repo and per cleanup plan, that submodule was excluded from this
sweep. Its iOS-related docs (ROADMAP_CLIENT_APPS.md, CHANGELOG.md, README)
will be cleaned up separately.

History preserved: any CHANGELOG entries that mention past iOS work are
left as-is. Only forward-looking content is updated. Pre-pivot Appendix A
in CLIENT_APPS_PARITY.md is also preserved as historical reference.

* chore(submodules): bump pointers to post-merge mains (post doc-strip)

Sub-repos' doc-strip branches squash-merged; bump parent pointers to the
new main SHAs so this parent PR lands a coherent tree:
- web-app → main (PR #46 + PR #47)
- identity-core-api → main (PR #33 + PR #34)
- biometric-processor → main (PR #56)
- docs → main (PR #11)

* docs: refresh CLAUDE.md + add login surfaces comparison + close-out scope

- CLAUDE.md: stale rollingcatsoftware.com URLs → fivucsas.com domains;
  CX33 → CX43 (16 GB); add verify.fivucsas.com + demo.fivucsas.com to
  subdomain table; refresh "Last verified" to 2026-04-26.
- docs submodule bump: new LOGIN_SURFACES_COMPARISON_2026-04-26.md (3-way
  compare of app.fivucsas/login, verify.fivucsas/login, verify.fivucsas/widget)
- FIVUCSAS_INCOMPLETE_2026-04-26.md: refreshed with 8 PRs shipped,
  iOS/macOS DROPPED, V42/V43 audit-finding correction

* chore(submodule): bump identity-core-api → 82b3a48 (V48 drop biometric_data)

PR #35 — biometric_data table removed (V48 migration applied live on
Hetzner; flyway_schema_history rank 48 = success). 9 files of dead code
deleted: BiometricData entity + 2 repository declarations + legacy
service.BiometricService + 4 application service callers + EnrollmentController
references. Net -390 / +97 LOC. Zero data loss (table empty since 2026-04 pivot).

* chore(submodule): bump web-app → $(git -C web-app rev-parse --short HEAD) (PR #48 perf: MUI vendor split)

PR #48 — Phase E2 MUI vendor chunk split. mui-vendor 555 KB → mui-core
517 KB + mui-icons 37 KB. E1 Recharts lazy was already in place. 678/678
tests still green; lint 0/0; single file change (vite.config.ts).

* chore(submodule): bump identity-core-api → 462c062 (PR #36 PKCE D5a/b)

PR #36 — Phase D5a/b shipped + DEPLOYED. New AuditAction.PKCE_FAILURE +
PkceFailureReason enum + PkceVerificationException + per-clientId
Bucket4j (30 failures / 5 min) + Retry-After on exhaustion. Audit row
written even when rate-limited (full attack pattern visibility for SOC).
+3 net tests (929→932 passing).

* chore(biometric): bump submodules + roadmap docs

- biometric-processor: Faz 1-3 — centerface/.env.prod, liveness wired
  into /enroll+/verify, pose quality fields, adaptive threshold config
- web-app: FaceLandmarker primary detector, passive liveness pre-filter,
  enrollment renewal UX, i18n keys added (678/678 tests pass)
- BIOMETRIC_PIPELINE_AUDIT_2026-04-28.md: full audit doc
- BIOMETRIC_ROADMAP_2026-04-28.md: fix roadmap
- CLAUDE.md: biometric pipeline section updated

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore(submodule): bump biometric-processor (compose centerface+anti-spoof+liveness)

* chore(submodule): bump biometric-processor (mtcnn fix)

* chore(submodule): bump web-app (CI env fix + FaceLandmarker + passive liveness)

* docs(2026-04-28): refresh CLAUDE.md + add session roadmap and client-apps plan

- CLAUDE.md: replace stale "biometric pipeline aging" note with verified
  post-fix prod state (Facenet512, mtcnn, anti-spoof on, UniFace passive
  liveness, MediaPipe FaceLandmarker, adaptive threshold). Refresh
  Last verified line and Next Steps to point at today's branches and
  the V42/V43 missing-migration P0.
- ROADMAP_2026-04-28.md: canonical record of today's findings (8 user-
  found bugs C1-C11), 6 morning fixes verified live, and a final-state
  table for the 4 afternoon teams.
- CLIENT_APPS_PARITY_PLAN_2026-04-28.md: research-only plan for Team D
  (Compose UI parity + APK release workflow). Ready for user review;
  needs keystore + 4 GitHub secrets before action.

Companion memory updates (in ~/.claude/projects memory):
  feedback_no_hard_delete_users.md
  feedback_readonly_rootfs_cache_dirs.md
  feedback_liveness_hybrid_vs_passive.md
  project_session_20260428.md (READ FIRST ON RESUME)

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

* chore(submodules): bump web-app + identity-core-api + biometric-processor (2026-04-28 wave)

web-app 47f7077:
- fix(sidebar): exact-route highlight (no prefix collision)
- fix(enrollment): TOTP/EMAIL_OTP/SMS_OTP/QR_CODE correctness sweep
- fix(web): rescue stashed agent work (biometric tools network error,
  puzzles polish, MFA double-step defense)

identity-core-api 8ba4ee7:
- fix(enrollment): drop SMS_OTP/QR_CODE/EMAIL_OTP from AUTO_COMPLETE_TYPES
- fix(enrollment): auto-create ENROLLED EMAIL_OTP+QR_CODE rows on first list
- fix(mfa): exclude completed methods from next step's available list

biometric-processor 9444018:
- fix(prod): give UniFace MiniFASNet a writable model cache (HOME=/tmp,
  UNIFACE_CACHE_DIR=/app/uniface-cache, named volume biometric_uniface)
- fix(liveness): use passive UniFace only — hybrid mode vetoed every login

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

* chore(submodule): bump identity-core-api → c25b731 (users-list lastLogin fix)

Wrong audit action constant ('USER_AUTHENTICATED') made the Users
list always show "Never" for Last Login. Switch to 'USER_LOGIN' and
fall back to User.lastLoginAt when audit is empty.

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

* chore(submodule): bump identity-core-api → 5446d57 (V42 + tenant-lock)

Two changes:
- Restore Flyway V42 (TOTP secret encrypted-at-rest CHECK
  constraint) from the security/phase-1-auth-hardening branch,
  applied manually to prod since out-of-order=false.
- fix(auth): tenant-lock OAuth-initiated logins to the client's
  tenant. demo.fivucsas.com (marmara-bys-demo OAuth client) now
  rejects users from other tenants.

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

* docs: multi-email / multi-tenant identity design note

Captures the architectural answer to the 2026-04-28 question:
"I have ahabgu@gmail.com on Fivucsas and ahmet.abdullah@marun.edu.tr
should be on Marmara — same person, two emails, two tenants."

Today's reality:
- The schema already supports two distinct user rows in two
  tenants. tenant_email_domains (V44) auto-routes registration by
  email domain (marmara.edu.tr → Marmara). Today's tenant-lock at
  OAuth login (commit 5446d57) enforces "right account at right
  surface."
- glsm's 2-row situation explained — soft-delete + new tenant
  registration. No 500 risk after morning's findByEmail filter; do
  not hard-delete the soft-deleted row (FK cascade lesson).

Three architecture options laid out:
- A. Status quo — works for today's use case
- B. Identities + memberships — week-long, true human-centric model
- C. Email aliases on user row — cheap, partial

Recommendation: A now. B if/when concrete business need surfaces.
Phone-as-primary-login folded into B at that point.

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

* docs(archive): move 16 superseded reports into archive/2026-04-pre-roadmap-2028/

Top-level was carrying 22 markdown files, most pre-dating the
2026-04-28 production hardening day. Canonical "current state"
docs now sit at root:

  CHANGELOG.md
  CLAUDE.md
  README.md
  ROADMAP_2026-04-28.md
  CLIENT_APPS_PARITY_PLAN_2026-04-28.md
  MULTI_EMAIL_TENANT_DESIGN_2026-04-28.md

Archived (preserved with full git history via git mv):
  API_EDGE_CASE_AUDIT.md
  ARCHITECTURE_ANALYSIS.md
  BIOMETRIC_PIPELINE_AUDIT_2026-04-28.md   (morning audit, items shipped same day)
  BIOMETRIC_ROADMAP_2026-04-28.md          (morning roadmap, items shipped same day)
  CLIENT_SIDE_ML_REPORT.md
  FIVUCSAS_INCOMPLETE_2026-04-26.md
  FRONTEND_COMPARISON_REPORT.md
  INFRA_REVIEW.md
  MASTER_PLAN.md
  MOBILE_APP_COMPREHENSIVE_REDESIGN.md
  NFC_READER_REDESIGN.md
  PERFORMANCE_AUDIT.md
  PLATFORM_STATUS.md
  ROADMAP.md
  ROADMAP_V2.md
  TODO.md

Each of these is referenced from archive/2026-04-pre-roadmap-2028/README.md
with a one-line "why archived" rationale.

Also unignore /archive/ — it was previously gitignored which would
have silently swallowed this commit. Future archive waves can keep
this same shape (wave-name folder + README index).

README.md updated with version 4.0.0 + pointer to ROADMAP_2026-04-28.

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

* chore(2026-04-28-evening): 4 audit reports + bump submodules

Audit reports captured at root:
  AUDIT_2026-04-28_BASIC.md     — design correctness on happy paths
  AUDIT_2026-04-28_EDGE.md      — race / extreme inputs / FK cascades
  AUDIT_2026-04-28_SECURITY.md  — authn / authz / OAuth / secrets / transport
  AUDIT_2026-04-28_OPS.md       — backups / containers / network / DR

Bumps:
  identity-core-api → 24d3784:
    - V42 TOTP encrypted-at-rest CHECK (restored from unmerged branch)
    - users-list lastLoginAt fix (action 'USER_LOGIN', not 'USER_AUTHENTICATED')
    - OAuth tenant-lock (commit 5446d57 — refuses cross-tenant logins)
    - MFA double-consume race fix (PESSIMISTIC_WRITE on session lookup)
    - Cross-tenant by-id user access fix (enforceTenantScope guard)

  biometric-processor → 9d4481f:
    - Strip bio.fivucsas.com public Traefik router. Internal-only via
      backend docker network (per CLAUDE.md design intent).

P0 audit findings closed today (5):
  ops-P0a  bio.fivucsas exposed       → 9d4481f
  ops-P0b  disk 91%                   → docker builder prune (-23 GB)
  edge-P0a MFA double-consume race    → 24d3784
  edge-P0b cross-tenant user by-id    → 24d3784
  basic-P0 demo creds in prod bundle  → false positive (already DEV-gated)

P0 findings deferred (need coordinated rotation / SPA refactor):
  sec-P0a  secrets in git history      → TODO Phase C1a-f
  sec-P0b  VITE_BIOMETRIC_API_KEY in   → expand today's useFaceSearch
            SPA bundle for non-search     reroute to all biometric callsites

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

* docs(audit): 2026-04-29 ops follow-up — closes 2 P0 + 2 P1 from yesterday

P0 closed yesterday (verified today):
  1. bio.fivucsas.com publicly exposed → router stripped (9d4481f)
  2. Disk 91% → 86% via builder prune (-23 GB)

P1 documentation shipped today:
  3. Observability runbook → infra/observability/RUNBOOK_OBSERVABILITY.md
  4. DR runbook → infra/RUNBOOK_DR.md (first drill instructions inside)

P1/P2 still open: WAL/PITR, password rotation, image SHA pinning,
shared_buffers tuning, PgBouncer, offsite long-term retention.

Top 3 next actions for the user:
- Run the first DR drill (validates RTO/RPO + the runbook itself)
- Bring up observability (~5 min once Grafana password chosen)
- Move postgres password out of backup.sh into .env (chmod 600)

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

* chore(submodule): bump web-app → eef1657 (Sec-P0b — biometric API key out of bundle)

Closes the second of yesterday's audit-security P0s. SPA no longer
ships VITE_BIOMETRIC_API_KEY; all biometric calls now route through
identity-core-api with the user's Bearer JWT.

Bundle re-verified post-merge:
  X-API-Key            0 hits
  VITE_BIOMETRIC_API   0 hits
  bio.fivucsas.com     1 hit (cosmetic help-text)

Combined with yesterday's biometric-processor public-router strip
(commit 9d4481f on the bp side, parent submodule pointer 5199952),
bio.fivucsas.com is now fully internal — no public DNS reach, no
public credential leaked in any browser bundle.

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

* chore(submodule): bump identity-core-api → d4c4d43 (5 P1 carryovers + 9 tests)

Closes 5 P1s from yesterday's audits:
  EDGE-P1 #3  startEnrollment race → 500
  EDGE-P1 #4  /audit-logs unbounded size param
  EDGE-P1 #8  V43 migration version gap
  SEC-P1 #4  /auth/mfa/step missing rate-limit bucket
  SEC-P1 #3  JWT default-algo locked to RS256 on prod profile

V43 row pre-inserted into prod flyway_schema_history before rebuild
(out-of-order=false in prod), mirroring yesterday's V42 pattern.

9 new tests across 5 suites (mvn -DskipTests package green):
  ManageEnrollmentServiceTest        +2 (race + winner-vanishes)
  AuditLogControllerTest             +3 (size 0/100/10000000)
  MigrationChainContiguityTest       +2 (V43 reservation + chain)
  RateLimitInterceptorMfaStepTest    +3 (30/min, 31st 429, IP isolation)
  JwtServiceProdAlgoLockTest         +4 (prod+HS512 throws, etc.)

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

* chore(submodule): bump web-app → c641d4e (basic-audit P1 sweep)

Closes 9 P1 + 2 P2 from AUDIT_2026-04-28_BASIC.md:
- 23 Turkish translations for biometricPuzzle hints (full tr/en parity)
- document.title localized via pageTitles namespace
- 6 raw err.message sites routed through formatApiError
- TOTP enrollment hardcoded English (3 sites) + QR alt text localized
- UserService / AuthService re-throw ZodError; formatApiError
  extended to map ZodError → t('errors.validation')

63 new keys per language, +5 KB bundle, 678 vitest pass.

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

* chore(submodules): bump api → 69bfa09 + web-app → c580822 (5-team wave)

API (69bfa09) consolidates Teams A + B + C:
  A — fix/test-mocks-2026-04-29 (dcf50d6)
      7 mock failures from yesterday's 24d3784 fixed.
      946→966 tests, 0 failures.
  B — fix/biometric-adapter-tenant-and-embedding-forward (70e3e4d)
      BiometricServicePort gains 5-arg overloads forwarding
      tenant_id + D2 client_embedding(s) to bio. Backward-compat
      via default methods. 6 new adapter tests.
  C — fix/tenant-soft-delete-and-totp-convert (69bfa09)
      Tenant @SQLDelete + @SQLRestriction (Edge-P1 #5: tenant
      hard-delete trap closed). User.two_factor_secret @Convert
      (Edge-P1 #7: defense-in-depth for V42 CHECK). V49 idempotent
      reverse index applied to prod. 14 new tests.

Full suite: 966 tests, 0 failures, 0 errors.

Web-app (c580822):
  E — fix/loginpage-face-tile-cleanup
      Removed dead face-tile state from LoginPage (broken pre-auth
      since SPA reroute; tile was already invisible). +3/-32.
      Deployed to Hostinger.

Production deployed at 04:48 UTC: api rebuilt + restarted,
V49 in flyway_schema_history, smoke-tests green (login OK,
cross-tenant client_id still rejected at the marmara-bys-demo
gate).

Team D (ops P2) committed to /opt/projects local repo (no remote);
Sec-P0a runbook at /opt/projects/infra/RUNBOOK_SECRET_ROTATION.md
captures the operator-coordinated history rewrite + secret
rotation.

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

* chore(deploy): tag :latest images with :sha-<short> after build (Ops-P2 #7)

Mirror the rollback-friendly tagging pattern from /opt/projects/infra/deploy.sh
(commit e3e9056) into the FIVUCSAS dev-driven deploy scripts. After the remote
`docker compose up -d --build`, the script now SSHs back in to walk
`docker compose images`, find each repo carrying `:latest`, and re-tag it as
`:sha-<short>` derived from the local identity-core-api git HEAD. This lets a
broken deploy roll back to the previously-built image without rebuilding.

- deploy-identity-core-hetzner.sh: new step 4b
- deploy-identity-core-hetzner.ps1: new step 3b (mirrors bash via ssh heredoc)
- header comments cite the audit (Ops-P2 #7) and the reference SHA

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

* chore(audit): close DRAFT audit PRs api#32 + web#45 as superseded

Both audit PRs were authored 2026-04-26 and contained only an .md report
file (no code to merge). Today's hardening wave closes the substantive
findings; the reports remain in the working tree for history.

api#32 closed-via-comment: V42 restored (bad7262), V43 slot reserved
(29aa007), test compile fixed (dcf50d6, be30dc7), JWT RS256 lock
(65415f3), MFA-step rate limit + Retry-After (3670932).

web#45 closed-via-comment: i18n sweep (c641d4e), biometric API key
elimination (fc79de6, 5ac4a97, 2a3820b), CI .env.production write
(15aab67), face-tile login removal (c580822).

Closing comments on each PR list closed vs. deferred items with SHAs.

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

* docs(changelog): add 2026-04-28 + 2026-04-29 hardening wave entries

Single comprehensive entry per day covering:
- 2026-04-28: 6 morning fixes (LoginPage 401 i18n, UserRepo soft-delete,
  optional MFA-step skip, Fivucsas tenant contact_email, UniFace passive
  liveness, useQualityAssessment bbox fallback); enrollment correctness
  sweep across TOTP/EMAIL_OTP/SMS_OTP/QR_CODE; fingerprint-twice MFA fix;
  4 audit reports; FK-cascade incident note.
- 2026-04-29: Sec-P0b biometric API key elimination from SPA bundle
  (fc79de6, 5ac4a97, 2a3820b); biometric adapter telemetry forwarding
  (6ad1d91); JWT RS256 lock on prod profile (65415f3); MFA-step
  rate-limit + Retry-After (3670932); audit-log size cap (8075c11);
  Tenant @SQLDelete + TOTP @Convert (2e05457); V42 restore (bad7262)
  + V43 reserve (29aa007); test compile fix (dcf50d6, be30dc7); Ops-P2
  image-SHA tagging in scripts/deploy/*; 5 runbooks; audit DRAFT PRs
  api#32 + web#45 closed.

Each line cites the specific commit SHA that landed the fix.

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

* chore: bump submodules — bio→22563fd (file-size guard), web→5b8f876 (face demo)

biometric-processor: MAX_FILE_SIZE guard wired before API-key auth (Z3).
web-app: public /face-demo page showcasing 7 face capabilities (Z5).
ROADMAP_2026-04-28: add Round 2/3 + doc archive notes.

Closes the Z-wave (post-AUDIT_2026-04-28 follow-up). Z1/Z2 still open
pending Anthropic rate-limit reset.

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

* chore(submodules): bump api → 30371e8 (Z1) + web-app → 9e151bf (Z2)

api: refresh-token rotation family revocation (Sec-P2 #6) + OAuth2
HostedAuthorizeCompleteRequest @Valid (Sec-P2 #7) + MFA expiresAt
boundary alignment (Edge-P2 #6).

web-app: VitePWA navigateFallback + cleanupOutdatedCaches (Edge-P2
#9) — kills stale-shell 404s after Hostinger deploys.

Closes the post-AUDIT_2026-04-28 Z-wave. Sec-P2 #8 (audit-log HTML
escape) deferred — log-file-only today, only relevant if a future
UI renders details unescaped.

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

* docs(claude): refresh status to 2026-04-29 — Z-wave shipped

Last-verified line, In Progress section, and Next Steps order
all reset to today's reality. The post-AUDIT_2026-04-28 Z-wave
(Z1 refresh-token family revocation + OAuth2 @Valid + MFA
boundary, Z2 VitePWA navigateFallback, Z3 bio MAX_FILE_SIZE,
Z4 ops housekeeping, Z5 /face-demo) is closed. Operator-only
items (Sec-P0a rotation, DR drill, observability bring-up,
V50 prod apply) remain.

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

* chore(submodule): bump web-app → 9231f4d (Z2 vitest exclude)

Adds .claude/** + archive/** to vitest test.exclude so the baseline
reflects real source (678 passing, 0 failing) instead of stale
agent-worktree drafts.

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

* chore: bump docs+bio submodules + ignore verify-widget/html build artifacts

- docs: import AUDIT_2026-04-19 + PRECOMMIT_HOOKS reference
- biometric-processor: import AUDIT_2026-04-26 read-only verification
- .gitignore: verify-widget/html/ is build staging, not source

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

* chore(submodules): bump api+web to ship gitleaks CI

- identity-core-api da9b7c4: add gitleaks workflow (Sec-P0a §8)
- web-app 0256e58: add gitleaks workflow (Sec-P0a §8)

Pairs with GitHub-side secret-scanning + push-protection now enabled
on both repos via API.

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

* chore(submodules): bump api+web — gitleaks CLI fix + allowlist

- identity-core-api 8b5fdde: switch to gitleaks CLI (no paid license) + allowlist 10 known test/doc false positives — CI green.
- web-app 4566b19: switch to gitleaks CLI — CI green.

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

* docs(claude): mark all reachable Z-wave + ops follow-ups as completed 2026-04-30

V50 applied + api recreated, observability live, DR drill OK, gitleaks CI on both repos, secret scanning + push protection enabled. Sec-P0a actual rotation + DNS A record + Grafana contact point remain operator-only.

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

* docs(claude): record biometric-API-key rotation + Grafana ops-email + finding that .env.gcp leaked GCP creds, not Hetzner

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

* chore(submodule): web-app 7365003 — CI SKIP_MODEL_FETCH

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

* chore(submodule): web-app c791923 — fix dashboard/profile mismatches

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

* chore(submodule): web-app 91b1b6e — face mesh + hand skeleton overlays on puzzles

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

* chore(submodule): web-app 0654b27 — close P3 session-count UX

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

* docs(claude): record late-day profile UX polish + puzzle landmark overlays

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

* refactor(landing): strip Marmara University from buyer-facing copy (P2.5) (#37)

* refactor(landing): strip Marmara University buyer-facing branding — keep in /about + README (P2.5)

The hero badge and footer on fivucsas.com lead with "Marmara University -
Engineering Project" / "2026 FIVUCSAS. Marmara University.", which signals
"student demo" to prospective tenants and undercuts the SaaS positioning.

Strip the academic framing from the two buyer-facing surfaces:
- Hero badge → "Multi-Tenant Biometric Identity Platform"
- Footer → "2026 FIVUCSAS. All rights reserved."

Marmara University attribution is preserved verbatim in the Project Team
section (lines 423 + 471 — Project Team subtitle and Supervisor row), which
is the academic-history equivalent of an /about page. README.md is also
untouched.

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

* chore(landing): strip Marmara from meta keywords for SEO consistency (Copilot review)

Refs FIVUCSAS#37

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

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* chore(submodules): bump api+bio+web — 7 PRs landed (Phase 1+2 + Copilot)

api 8b5fdde..20cc504:
  PR #37 — validate-on-migrate + statement_timeout + MDC + Maven cache
           + Copilot fixes: CORS expose, X-Request-Id validation, constants
  PR #38 — Tx/Async correctness (readOnly defaults, REQUIRES_NEW, proxy-bypass,
           bounded pool), JPA equals/hashCode, audit-log HTML escape
           + Copilot fixes: TenantContext across @Async (RLS-safe),
           userAgentV2 escape, AuditEscapeTest + AuditLogAdapterTest escape
  PR #39 — fingerprint placeholder removed (legacy biometric path deleted;
           WebAuthn FingerprintAuthHandler retained)

bio 6e6ee88..f0d997c:
  PR #57 — asyncio.to_thread on UniFace + QualityAssessor + cv2.imread
           (event-loop unblock, P2.11)
  PR #58 — fingerprint placeholder hash_embedder removed

web f649ef3..7dae2c9:
  PR #49 — central dateFnsLocale helper across 9 list pages (P3.2)
           + Copilot fix: locale-aware skeletons P/PP/PPP/p/pp instead of
           literal MMM dd, yyyy / HH:mm:ss patterns

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

* docs(triage): USER_BUGS_2026-04-30 — face-no-gate / YOLO wrong class / puzzles broken

Three direct user reports surfaced 2026-04-30 16:50 (TR):

1. Face enrollment auto-advances even when no face is in view; failure
   surfaces only at server save. Front-end has the detection signal but
   does not gate progression on it. Branch fix/face-enrollment-no-face-gate.

2. YOLO card-type classifier picks the wrong TR ID variant. Likely
   label-order / preprocessing / no confidence floor. Defensive fix
   even if model is the root cause: ≥0.6 floor + manual-select fallback.
   Branch fix/card-type-yolo-classification.

3. Math + shape-drawing biometric puzzles silently broken. Likely
   string/number validation, missing onSolve wiring, pointer-event /
   canvas-size races. Branch fix/biometric-puzzles-math-shape.

Each has a separate background fix branch + Vitest. This file is the
canonical triage doc; supersedes ad-hoc TODO entries.

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

* chore(submodules): bump api+bio+web — USER-BUG-1 + 3, JVM heap, post-merge Copilot

api 20cc504..9973ee2:
  PR #40 — perf(jvm): drop -Xmx512m silent override, MaxRAMPercentage=75
           (PERF #1; ~1.5 GB heap on 2 GiB container; 40-80 ms p99 saving)
  PR #41 — chore: align FINGERPRINT delete log with PR #39 removal
           (Copilot post-merge; FACE/VOICE arms keep the info-log,
           FINGERPRINT explicit no-op with debug-only)

bio f0d997c..b1fe294:
  PR #59 — fix: 4 Copilot post-merge findings on PR #57 / #58
           A. _model typed via TYPE_CHECKING uniface.spoofing.MiniFASNet
           B. _ensure_model_loaded → asyncio.Lock + double-checked locking
           C. asyncio.to_thread → get_thread_pool().run_blocking()
              so ML_THREAD_POOL_SIZE is honored
           D. README Voice line corrected — Resemblyzer GE2E still in prod
              (numba conflict resolved by librosa==0.9.2 pin, not removal)

web 7dae2c9..6ce1f7d:
  PR #50 — fix(face): hard-stop face enrollment when no face is detected
           (USER-BUG-1; useFaceChallenge.ts hard-timeout removed,
           center-crop-fallback removed, early gate on detection.detected,
           en/tr i18n, 33/33 vitest)
  PR #51 — fix(puzzles): smooth finger-count + index-extended check
           (USER-BUG-3; HAND_MATH / HAND_FINGER_COUNT / HAND_SHAPE_TRACE /
           HAND_TRACE_TEMPLATE were jittered out by strict count===target
           comparator on noisy MediaPipe output; FingerCountSmoother
           500ms dominance ≥60% + isIndexExtended TIP-above-PIP;
           21/21 hand-challenge + 27/27 puzzle suite pass)

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

* chore(submodules): bump api+bio+web — USER-BUG-2 closed (server+client) + USER-BUG-4 SMS OTP

api 9973ee2..cc8ed23:
  PR #42 — fix(sms-otp): branch on VerifiableSmsService for Twilio Verify;
           prod uses SMS_PROVIDER=twilio-verify but OtpController was
           unconditionally going through local Redis OtpService. Fix:
           skip otpService.generate when smsService instanceof
           VerifiableSmsService; verify via verifiable.verifyCode.
           Plus normalizeCode (NFKC + ZWSP/LRM/RLM/BOM strip + trim)
           applied to email + SMS verify input.
           5 new test cases including "sendShouldNotGenerateLocalCode"
           that would have caught the original bug; 13/13 pass.
           USER-BUG-4. Needs identity-core-api container rebuild.

bio b1fe294..de5584e:
  PR #60 — fix(card-type): server-side defensive tightening on
           YOLOCardTypeDetector. Confidence threshold 0.5 → 0.65;
           OCR validation now runs on every detection (not only the
           two confusable pairs); borderline calls (conf in [0.65,
           0.75) AND OCR no_evidence) return detected=False rather
           than committing to a guess. _ocr_validate returns
           (chosen_class, evidence) for diagnostic logging.
           akademisyen_karti added to supported_card_types (model
           class was present, list was missing it).
           Companion to web-app PR #52 (client defense-in-depth).
           USER-BUG-2 server-side, the user-visible path.

web 6ce1f7d..8ea8186:
  PR #52 — fix(card-type): client CardDetector labels.json (canonical
           training-time class order) + FALLBACK_CLASS_NAMES corrected;
           CARD_HIGH_CONFIDENCE=0.7 gate; i18n cardDetection.classLabels
           so CardDetectionPage no longer leaks raw slugs; vitest pinning
           order against alphabetical regression.
           USER-BUG-2 client defense-in-depth.

USER_BUGS_2026-04-30.md status table updated.

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

* docs(user-bugs): all four closed; operator rebuild + E.164 phone follow-up noted

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

* chore(submodule): identity-core-api → 567ce25 — ShedLock for SoftDeletePurgeJob (P2.9, PR #43)

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

* chore(submodules): bump api+web — auth-methods-testing real APIs (USER-BUG-5) + V51 renumber

api 567ce25..8165311:
  PR #45 — chore(db): renumber V53__shedlock to V51 to fix
           MigrationChainContiguityTest after PR #43 left a gap
           (V51/V52 reserved on feat/v51-forbid-hard-delete-p1-7).

web a1f1adb..bb30a4a:
  PR #53 — fix(auth-methods-testing): server-mediated puzzles call real APIs
           (USER-BUG-5 part 1). Deleted StubAuthRepository entirely.
           EmailOtp/Sms/Totp/QrCode now hit real /auth/2fa/* endpoints
           against the admin's own session, surface real errors.
           Password puzzle was never a discrete second factor — left
           excluded from registry by design.
  PR #54 — fix(auth-methods-testing): biometric/WebAuthn puzzles wrap
           production step components (USER-BUG-5 part 2). Face/Voice/
           Fingerprint/NFC/HardwareKey now hit real biometric +
           webauthn endpoints, auto-enroll on 404, every onSuccess
           gated on server-confirmed verdict. setTimeout fakes removed.

USER-BUG-5 (auth-methods-testing page mocks) fully closed. Total this
session: 18 PRs merged across api/bio/web/root.

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

* chore(submodules): bump api+bio — Copilot post-merge round 2 fixes (round 2 of 3)

api 8165311..2fe3f03:
  PR #44 — chore: address Copilot findings on PR #42, PR #40
           OtpController: PROVIDER_REJECTED label clarified, normalizeCode
           docs aligned with NFKC, .strip() instead of .trim() to handle
           U+3000-class whitespace, verifySmsOtp UserNotFound now consistent
           across both modes. Dockerfile: ENTRYPOINT now uses exec for
           SIGTERM forwarding.

bio de5584e..9b81a39:
  PR #61 — fix: address Copilot findings on PR #59, PR #60
           Combined yolo_card_type + uniface + quality_assessor fixes
           (borderline-reject + ocr_unavailable, _ocr_validate confidence
           arg removed, _CONFUSABLE_PAIRS dead code removed, docstring
           sync, ThreadPool injected via constructor instead of static
           import to break infra→container coupling, _model type
           narrowed via local cast, ImportError guidance aligned with
           uniface>=3.0.0 pin, async-lock concurrency test added).

Web-app post-merge fixes round 2 still pending — single agent in flight.

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

* docs(user-bugs): USER-BUG-5 (auth-methods-testing mocks) closed; status table updated

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

* chore(submodule): web-app → f2930ca — Copilot post-merge round 2 (PR #55)

PR #55 — fix: address Copilot findings on PR #50, #51, #52
  CardDetector: __dirname → import.meta.url for ESM; smoothing-buffer
    impl/comment alignment; JSDoc shape 5+C → 4+C; UNRECOGNISED_CONFIDENCE_THRESHOLD
    extracted to remove the 0.6-duplicated-3x smell.
  useFaceChallenge: cropFace-null path now also resets stageStartRef
    so the stage-timer race is closed end-to-end (not just hold-timer).
  handChallenges: HAND_FINGER_COUNT target<0 short-circuit; FingerCountSmoother
    "immediately" docstring corrected; HAND_FINGER_COUNT-specific jitter
    regression test added; "4 fingers" comment aligned with twoFingerFrame.

Test verification (per agent): vitest 5/5 + 23/23 on touched specs; eslint 0 errors.

Closes Copilot post-merge round 2 (web-app). All 22 findings across the
3 repos addressed (api #44 + bio #61 + web #55).

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

* chore(submodules): bump api+web — Copilot post-merge round 3

api 2fe3f03..04b44bf:
  PR #46 — chore: address Copilot post-merge findings on PR #43+#44 (round 3)
    V52__shedlock_timestamps_tz.sql — ALTER lock_until/locked_at to
      TIMESTAMP WITH TIME ZONE so shedlock matches the rest of the
      schema's TZ-aware convention (rest of identity_core uses TIMESTAMPTZ).
      Picked V52 because MigrationChainContiguityTest requires no gap;
      V54 would have failed.
    VerifiableSmsService.java — closed unclosed Javadoc <p> tag (doclint).
    TwilioVerifySmsService.java — log.warn now passes the exception so
      SLF4J emits the stack trace (was string-only before).
    OtpController.java — switches on VerifyResult so the summary log
      says "SMS OTP provider error" on PROVIDER_ERROR vs "SMS OTP
      mismatch" on INVALID_CODE.
    OtpControllerTest.java — Logback ListAppender attached, asserts
      both WARN reason branches plus negative regression guard.

web f2930ca..e90aa35:
  PR #56 — chore: address Copilot post-merge findings on PR #53 (round 3)
    QrCodePuzzle.tsx — onError moved into useEffect so it no longer
      fires during render (kills "Cannot update a component while
      rendering a different component" warning).
    EmailOtpPuzzle.tsx — header comment fixed (OTP sent on click,
      not on mount).
    useTestVerifyApi.ts — JSDoc paths standardised on apiBase-relative
      form to match AuthSessionRepository.generateQrToken; file header
      documents the convention.
    AuthMethodMode.tsx — docblock rewritten to use the actual
      AuthMethodModeKind values (real | test | stub) instead of the
      stale "live" reference.

Test verification (per agent):
  api  — mvn -Dtest=OtpControllerTest,MigrationChainContiguityTest → 17/17
  web  — npx vitest run src/features/auth-methods-testing → 47/47
         npm run lint → 0 errors / 2 pre-existing warnings (cap 90)

23 PRs merged this session total. Round 3 closes the Copilot review
backlog. Operator container rebuild (Task #25) still required for the
server-side fixes to land in prod.

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

* chore(submodule): biometric-processor — Copilot post-merge round 4 (PR #62)

I-prefix for IThreadPoolExecutorPort, dead test helpers removed, asyncio.sleep
event-loop smell fixed.

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

* chore(submodule): identity-core-api → V53 forbid hard-delete trigger landed (PR #47)

PR #47 — feat(db): V53 BEFORE DELETE trigger on users + tenants
  RAISE EXCEPTION on hard DELETE; bypass via SET LOCAL
  app.allow_hard_delete='on' for legitimate GDPR Art. 17 purge.
  SoftDeletePurgeJob.purgeBatch now sets the GUC inside the TX so the
  trigger lets the row delete proceed. Trigger is idempotent
  (CREATE OR REPLACE FUNCTION + DROP TRIGGER IF EXISTS).
  Closes the residual hard-delete vector flagged in DB review #1
  (DB_REVIEW_2026-04-30.md) and codifies feedback_no_hard_delete_users.md
  at the database layer.

  Renumbered to V53 because V51 = ShedLock (PR #43) and V52 = ShedLock
  TIMESTAMPTZ alignment (PR #46) merged first.

  Ahabgu incident regression (2026-04-28 careless `DELETE FROM users`
  cascading across 13 FK children) is now engine-level impossible.

  3 commits: migration SQL + purge bypass + Testcontainers integration
  test (RUN_INTEGRATION=true gate, CI exercises).

  Effective on next container rebuild (Task #25).

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

* chore(submodule): web-app — wrong-password error fix (USER-BUG-6, PR #57)

PR #57 — fix(login): show 'invalid credentials' on wrong password
  Root cause: AuthService.login() was wrapping the axios 401 in
  UnauthorizedError, stripping response.status/data, so formatApiError
  fell through every branch to errors.unknown.
  Fix: re-throw axios error untouched; formatApiError now reads
  backend errorCode (INVALID_CREDENTIALS, NEEDS_ENROLLMENT, MFA_REQUIRED)
  and routes to distinct i18n keys; 401 on /auth/login returns
  errors.invalidCredentials not the misleading errors.unauthorized
  (session-expired) message.
  720/720 vitest pass.

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

* chore(submodules): bump web+bio — face login cold-start (USER-BUG-7)

web-app PR #58 — perf(face-login): preload MediaPipe + idle-time engine warmup
  index.html — preconnect to cdn.jsdelivr.net + storage.googleapis.com,
    prefetch vision_bundle.mjs + vision_wasm_internal.wasm +
    face_landmarker.task. Browser warms DNS/TLS/cache while React parses.
  LoginPage.tsx + verify-app/LoginMfaFlow.tsx — BiometricEngine.initialize()
    fires inside requestIdleCallback while user types email/password.
    By MFA-dispatch the FaceLandmarker is loaded; "MediaPipe (CDN)" badge
    shows immediately and detection runs on the first frame.
  vite.config.ts — mediapipe-vendor manualChunk so the npm fallback path
    isn't in the main login bundle.
  Estimated 1.5–2s shaved off "land on login → face badge visible".

bio PR #63 — perf(bio): warm UniFace at startup + per-stage timing logs
  container.py — UniFace MiniFASNet constructed inside
    initialize_dependencies() (was first-request lazy, costing 1–2s for
    every end-user after every restart). Failures non-fatal.
  verify_face.py + check_liveness.py — per-stage timing logs:
    "face/verify: decode=… detect=… quality=… embed=… fetch=… total=…ms".
    Future debugging will show which stage dominates.
  First /verify after deploy: ~3–4s → ~700–900ms.

Remaining bottleneck (per agent): DeepFace Facenet512.represent + mtcnn
inside _extractor.extract() costs ~250–400ms on CX43 CPU. New timing logs
will quantify. Further gains require ONNX export of Facenet512 or a model
swap (out of scope for this perf pass).

Effective on next container rebuild + Hostinger deploy (web auto via CI).

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

* chore(submodules): bump api+web — admin-pages bug sweep (USER-BUG-8/9/10)

api 8c7f6d2..6bb853f:
  PR #49 — fix(admin): SUPER_ADMIN cross-tenant + missing default-flow
           transition + verification stats aggregates
    Bug A — Guest endpoints route through TenantScopeResolver so
            SUPER_ADMIN sees platform-wide invitations; cross-tenant repo
            methods added; IllegalStateException → 409 in
            GlobalExceptionHandler (was generic 500).
    Bug B — ManageAuthFlowService.updateFlow now dethrones the prior
            default flow in the same TX when isDefault=true.
    Bug C — AuthSessionController + DeviceController accept null tenantId
            for SUPER_ADMIN as platform-wide. Paged repo methods
            findAllByStatusIn / findAllByUserId added; listAllDevices()
            use-case.
    Bug D — VerificationController /stats now computes
            completionRate, failureRate, avgTimeMinutes,
            statusDistribution, last-30-days dailyVerifications,
            top-10 failureReasons (was returning {total, completed, …}
            while FE rendered a different shape — every tile = "Veri yok").
            Verification flows kept distinct from auth flows
            (flow_type=VERIFICATION subset, KYC/KYB/AML).

web b0c9888:
  PR #59 — fix(admin): wire Star/StarBorder default-flow toggle, surface
           SUPER_ADMIN cross-tenant flag, replace hardcoded English in
           DevicesPage with t() (new devices.* keys EN+TR), pipe Guest
           toast through formatApiError.
    AuthSessionsPage + DevicesPage send empty tenantId when current
    user is SUPER_ADMIN; banner explains the cross-tenant view.
    AuthFlowsPage gets a Star icon button + confirmation dialog
    that calls updateFlow({isDefault: true}).
    GuestsPage error path now humans-readable.
    useVerification refactored to read the new dashboard shape.
    702/702 web vitest pass; 3 backend tests updated to reflect the
    "null tenantId = platform-wide for SUPER_ADMIN" contract.

USER-BUG-8 / 9 / 10 all closed. Effective on next container rebuild
(Task #25) for backend; web auto-deploys via Hostinger CI.

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

* chore(submodule): identity-core-api → V54 E.164 phone validation (PR #48)

PR #48 merged: @Pattern E.164 on CreateUserRequest + UpdateUserRequest;
V54__users_phone_number_e164.sql backfills existing non-E.164 rows + adds
CHECK constraint. Renumbered V53 → V54 after rebase to avoid collision
with V53 forbid-hard-delete trigger (PR #47).

Web-side auto-prefix +90 + strip-non-digits deferred (Task #46) —
agent rate-limited before reaching the FE form work.

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

* chore(submodule): identity-core-api → AuthController.verifyMfaStep extraction (PR #50, P2.9 second half)

PR #50 — refactor(auth): extract verifyMfaStep into VerifyMfaStepService
strategy. AuthController.java 1526L → 1162L (-364, -24%); verifyMfaStep
itself 380L → 25L pure HTTP shim.

New under application/service/mfa/:
  VerifyMfaStepHandler (interface), MfaStepResult, VerifyMfaStepRequest,
  VerifyMfaStepResponse, VerifyMfaStepService (orchestrator, 390L).
New under .../mfa/handler/:
  10 @Component handlers (one per AuthMethodType: PASSWORD, EMAIL_OTP,
  SMS_OTP, TOTP, FACE, VOICE, FINGERPRINT, HARDWARE_KEY, QR_CODE,
  NFC_DOCUMENT) + WebAuthnVerifySupport shared by FINGERPRINT/HARDWARE_KEY.

Tests: 1050 → 1053 (zero regressions, 18 new VerifyMfaStepServiceTest).
Wire contract preserved: request/response shape, status codes,
audit keys (MFA_STEP_COMPLETED/FAILED/COMPLETE), RFC 8176 amr ordering,
pessimistic-lock (findBySessionTokenForUpdate + @Transactional), WebAuthn
challenge two-phase flow.

Pure refactor, no production rebuild needed. Closes master-roadmap P2.9
second half.

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

* chore(submodules): bump api+web+bio — Copilot post-merge round 5 (26 findings)

api 19d76ff..cb3ea1a:
  PR #51 — chore: address Copilot post-merge findings on PR #49 (round 5)
    7 findings:
    - UserController guest-invite: SUPER_ADMIN no longer falls back to
      currentUser.tenant; missing tenantId now hard-errors 400.
    - Guest list / device list platform-wide paths capped at
      MAX_PLATFORM_WIDE_GUESTS / DEVICES = 1000 rows.
    - AuthSessionQueryService javadoc documents the tenantId=null
      SUPER_ADMIN platform-wide contract.
    - GlobalExceptionHandler: new DomainStateConflictException narrows
      409 handler; 15 domain throws migrated; 9 test assertions
      updated. IllegalStateException no longer silently demoted to 409.
    - ManageVerificationService.getStats: N+1 step-result fetch
      replaced with findAllBySessionIdInOrderBySessionIdAscStepNumberAsc
      batched query.

bio 16a03b2..41c85db:
  PR #64 — chore: address Copilot post-merge findings on PR #63 (round 5)
    4 findings:
    - container.py: liveness warm-up now runs
      UniFaceLivenessDetector.warm_model_sync() on the cached detector
      instance (was a throw-away MiniFASNet() that didn't actually warm
      the production code path).
    - container.py: explicit info-log when liveness backend skips warm-up.
    - container.py: generic exception handler uses exc_info=True.
    - check_liveness.py: renamed mid-flow timing to
      total_until_liveness; added true end-to-end total on completion log.

web b0c9888..0bc801f:
  PR #60 — chore: address Copilot post-merge findings on PR #57+#58+#59 (round 5)
    15 findings:
    - AuthService.ts comment now references real i18n keys
      (errors.invalidCredentials, errors.mfaRequired).
    - formatApiError.test.ts test descriptions renamed to match.
    - index.html: MediaPipe CDN pinned to @0.10.18 (matches package.json).
    - index.html: prefetches now use as="fetch" + crossorigin.
    - LoginPage warm-up comment clarified (timing optimization, not
      chunk split).
    - BiometricEngine.initialize() single-flight via shared _initPromise
      (kills duplicate-init race between LoginPage + LoginMfaFlow).
    - useAuthSessionsList: test exercises crossTenant=true with empty
      tenantId.
    - AuthFlowsPage: success auto-clear timer in ref, cleared on
      unmount + before re-scheduling.
    - AuthFlowsPage: Star IconButton aria-label toggles between
      setAsDefault / alreadyDefault.
    - DeviceRepository / VerificationRepository: signatures require
      explicit { crossTenant?: boolean } flag for empty tenantId; throws
      otherwise.
    - useVerification: derives isSuperAdmin; non-super-admin callers
      wait for tenantId instead of falling through.
    - AuthFlowsPage Set-default Dialog onClose ignores backdrop/Escape
      while request is in flight.
    - tr.json grammar fix: "...ayarlansın mı?" → "...ayarlamak ister misiniz?".

35 PRs merged this session total. Round 5 closes the Copilot review backlog
on the last 5 user-bug PRs. Operator container rebuild (Task #25) still
required for server-side fixes to land in prod.

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

* chore(submodule): identity-core-api → Copilot round 6 on PR #50 (PR #52)

PR #52 merged: WebAuthn action=challenge short-circuits substitution guard;
completeMfa/advanceToNextStep wrapped (AuthFlow lookup or token-mint
failures emit MFA_STEP_FAILED orchestration-error, not 500);
VerifyMfaStepHandler javadoc reflects actual wiring (List → EnumMap).
15/15 VerifyMfaStepServiceTest green.

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

* chore(submodule): web-app — phone E.164 auto-prefix (USER-BUG-4 part 2, PR #61)

PR #61 merged. New web-app/src/utils/phoneNumber.ts utility:
  normalizePhoneInputE164 strips non-digits, auto-prepends +90,
  preserves typed +CC, dedupes accidental 905... prefixes;
  isValidE164 matches backend Pattern ^\+[1-9]\d{7,14}$.

Wired into SettingsPage profile-form phone field and EnrollmentPage
SMS-OTP phone-prompt dialog. Replaces legacy length<10 check. Submit
payload uses normalized form. en/tr i18n key phoneNumber.e164Required.
26 new tests; 747/747 vitest pass.

USER-BUG-4 fully closed end-to-end: server PR #48 rejects non-E.164
with 422; client PR #61 prevents that 422 from reaching the user by
normalizing on input.

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

* chore(submodule): identity-core-api → Copilot round 7 on PR #52 (PR #53)

PR #53 merged: challenge action double-call fixed (contract violation
surfaced as 400 instead of re-invoking handler); e.getMessage() removed
from user-facing strings + audit reasons (e.getClass().getSimpleName()
only — full exception still in server log); RuntimeException catch +
containsInterrupted(Throwable) cause-chain walk re-sets
Thread.currentThread().interrupt() so cooperative cancellation isn't
lost when an HTTP client wraps InterruptedException as RuntimeException.

15/15 VerifyMfaStepServiceTest green.

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

* docs(session): 2026-05-01 status snapshot + 4 historical 4-lens review docs

SESSION_STATUS_2026-05-01.md: canonical session-state doc. 39 PRs merged
across 4 repos this session; all 10 user-reported bugs closed end-to-end;
7 rounds of Copilot post-merge cleanup; 9 senior reviewers deployed
(5 delivered 2026-04-30, 4 in flight 2026-05-01).

Operator container rebuild (Task #25) is the gating step — server-side
work is on main but not running in prod.

Adds the 4 lens reviews from 2026-04-30 morning that were left untracked:
- BACKEND_REVIEW_2026-04-30.md (Senior BE)
- ENGINEERING_REVIEW_2026-04-30.md (Chief Engineer)
- FRONTEND_REVIEW_2026-04-30.md (Senior FE — static lens; superseded by
  the in-flight 2026-05-01 redrive that explicitly covers working-feature
  dimension after user feedback that USER-BUG-6/7/8/9/10 weren't caught)
- PRODUCT_REVIEW_2026-04-30.md (Product Owner)

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

* chore(submodule): identity-core-api → P0 cross-tenant breach fixed (PR #54)

PR #54: TenantBindFromAuthFilter binds TenantContext to JWT-derived
tenantId after JwtAuthenticationFilter populates SecurityContextHolder.
Forged X-Tenant-ID headers from non-SUPER_ADMIN users are now overwritten
with the authenticated user's true tenantId + AUDIT-logged for SIEM.
SUPER_ADMIN keeps the legitimate cross-tenant override.

Closes SECURITY_REVIEW_2026-05-01.md §P0-1 (Task #57). Application-layer
half of multi-tenant isolation; DB-layer half is Task #27 (FORCE ROW
LEVEL SECURITY + non-superuser app role).

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

* chore(api-submodule): bump to security wave 2026-05-02 (P0-SEC-2/4 + P1-1)

- 7aee607 OAuth2 confidential client_secret + RoleController tenantId ownership (#55)
- 8d03566 hash refresh-token secret on write, dual-read legacy plaintext (#56)

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

* docs(2026-05-02): session status + proctoring/amispoof.com design memo

SESSION_STATUS_2026-05-02.md — 8 P0/P1 from senior reviews shipped in 5 PRs
across 2 repos. Operator-only residuals (rebuild + env vars +
JWT_SECRET decision + GDPR fixture force-push) listed in strict order.

RESEARCH_PROCTORING_AMISPOOF_2026-05-02.md — design memo on extracting
proctoring work into its own submodule + standing up amispoof.com as
public demo + research data flywheel. Recommends option B (extract now,
defer demo). User picks A/B/C. No code in that direction yet.

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

* chore(api-submodule): bump to WebAuthn fix wave (PR #57)

cf398b9 — WebAuthn origin allowlist (registration + assertion paths),
clientDataJSON required, sign-counter validated.

Operator must set WEBAUTHN_ALLOWED_ORIGINS in .env.prod before rebuild.

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

* chore(2026-05-02): api bump + optimized roadmap

api submodule bumped to 9cf4367 — includes #58 backend security batch
(JJWT alg/kid bind + iss/aud + HS512 gated + PKCE hard-reject + audit
escape + MfaSession Jackson), #59 test coverage backfill (F4 RLS + F5
refresh-token family revoke + F9 ShedLock concurrency), and #60
P0-critical CustomUserDetails wiring fix.

ROADMAP_OPTIMIZED_2026-05-02.md replaces stale ROADMAP_2026-04-28.md +
sprawling Phase-A–L tracker. Single source of truth for what's open:
T1 operator-only urgent, T2 awaiting user decisions, T3 in flight,
T4 leverage-ranked open work, T5 long-running scheduled.

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

* docs(session-status): record CustomUserDetails blast-radius + upgrade rebuild to P0 URGENT

PR #60 surfaced that the broken principal-type wiring in
CustomUserDetailsService had been silently nullifying — for at least
8 days — not just PR #54's cross-tenant filter, but also 5 methods on
AuthorizationService and AuditLoggingAspect's user/tenant attribution.

Operator container rebuild is now P0 URGENT.

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

* chore(submodules): bump web-app + biometric-processor to 2026-05-02 main

- web-app: e02c32f — PR #61 phone E.164 + PR #62 VITE_API_BASE_URL +
  PR #63 ErrorHandler/useVerification i18n + Redux removal +
  PR #64 round-8 MediaPipe CDN centralization + BiometricEngine dispose race.
- biometric-processor: 9f0999f — PR #66 round-8 UniFace warm-up cache +
  audit-doc import + Traefik public-router strip + MAX_UPLOAD_SIZE guard.

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

* chore(submodule): web-app → F3 Playwright tags + nightly cron (PR #65)

Default Playwright run no longer mutates PROD.

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

* chore(submodule): web-app → P1-FE error-surfacing batch (PR #66)

Mutation hooks now setState({error}) before re-throw across 5 features.
DeveloperPortalPage setTimeout leak fixed.

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

* chore(submodule): identity-core-api → backend quality + YAML hotfix (#61, #62)

PR #61 — P1-Q5/Q6/Q9 dead helpers + adapter cleanup + controller @Transactional move.
PR #62 — hotfix dup `app:` block in application-prod.yml (boot blocker).

Live state post-deploy 2026-05-02 17:58 UTC:
  api  HEAD 27d7423 + hotfix c53ce53 baked locally; main is now d8a4d33.
  bio  HEAD 9f0999f + manual schema/backfill applied (Alembic 0005 not in container yet).
  Soak window: APP_SECURITY_JWT_ALLOW_HS512=true, iss/aud empty, until ~2026-06-01.

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

* docs(2026-05-02): rebuild executed + JWT/User analysis + soak plan

- SESSION_STATUS: late-session rebuild section, two-retry post-mortem,
  follow-up Tasks #80/81/82.
- ANALYSIS_2026-05-02_USER_DOMAIN_AND_JWT_ROTATION: deep-dive professional
  recommendations on (a) Hexagonal User-domain bifurcation — gradual
  migration with ArchUnit guard rather than delete-or-park; (b) JWT_SECRET
  defence-in-depth via kid-based key registry rather than hard rotation.
- ROADMAP_OPTIMIZED: late-session adds line.

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

* chore(submodules): land PR-#63/#64/#67/#68 — backend/bio quality batch (2026-05-04)

- identity-core-api #63: ArchUnit user-domain-boundary guard (P0-Q3)
- identity-core-api #64: JWT kid-based key registry (T3.C)
- biometric-processor #67: F2 + F10 CI hygiene (drop pytest swallow + remove stale tests)
- biometric-processor #68: alembic in runtime image + backfill async-iter fix (Task #81)

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

* chore(web-submodule): T-FRONTEND-HYGIENE P3 batch

Bumps web-app to 319b457 (PR #67):
- index.html title brand-neutralized
- 9 setTimeout cleanups (TOTP/WebAuthn/NFC/StepUp/FaceVerify/etc.)
- NotificationPanel polling pauses on hidden tab
- CSP comment drift fixed in vite.config.ts
- tfhub.dev / kaggle.com stripped from connect-src (post-MobileFaceNet)

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

* docs(2026-05-04): roadmap refresh + CHANGELOG entry for Wave 1

- ROADMAP_OPTIMIZED_2026-05-02.md: post-deploy refresh (operator rebuild
  EXECUTED 2026-05-02 17:50–17:58 UTC; T2.1 proctoring direction = B;
  T2.2 user-domain decision = ArchUnit guard).
- SESSION_STATUS_2026-05-02.md: T3.C kid-registry rollout instructions
  added to §5 (operator post-merge env steps).
- CHANGELOG.md: Wave 1 batch — api PR #63/#64, bio PR #67/#68, web PR #67,
  CI/CD status, Copilot post-merge nit on NfcStep.tsx:96 queued.

T-LOGIN-EDGE and T-SEC-TAIL still in flight; submodule pointer for api
deliberately not bumped here.

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

* chore(api-submodule): T-SEC-TAIL DeviceController boundary + P2 cleanup

Bumps identity-core-api 2d958c5 -> 19366c0:

  * fix(security): route WebAuthn credential writes through service
    boundary (T-SEC-TAIL §T4.4) (#66)
  * fix(security): reject ID-token replay against /oauth2/userinfo
    (SECURITY_REVIEW_2026-05-01) (#67)
  * feat(db): V57 — hand audit_logs partition lifecycle to pg_partman
    (concurrent agent's work, included in fast-forward)

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

* docs(review): senior UI/UX designer review of verify + app — 2026-05-04

Comprehensive design review covering app.fivucsas.com (admin dashboard),
verify.fivucsas.com (hosted login + embeddable widget), demo.fivucsas.com
(Marmara BYS showcase), and fivucsas.com (brand cohesion). 35 prioritized
findings (1 P0, 4 P1, 20 P2, 11 P3) with file paths, effort sizing, and
agent-actionable flags. Verified against HEAD 319b457 to avoid restating
items already shipped (Profile date-i18n, tenant hardcode, dead Redux,
PWA navigateFallback, USER-BUG-1..10).

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

* docs(review): senior DB engineer deep review — 2026-05-04

Comprehensive storage-tier review covering schema design, indexes,
relations, views, functions, performance, backup, security, migration
hygiene. 30 findings prioritised P0–P3.

Headline P0s:
- V57 on disk without V56 will brick Flyway on next deploy
- User entity still missin…
ahmetabdullahgultekin added a commit that referenced this pull request May 16, 2026
Submodule spoof-detector 806b291..e6cd5d4 (PR #29):
  feat(prover): passive-only proctoring mode — track every movement, no challenges

  - 3 new passive movement axes (additive, no breaking change):
      eye_motion_points / 12, mouth_motion_points / 10, face_motion_points / 8
    sourced from LandmarkVarianceAnalyzer eye_var + mouth_var and
    TemporalAnalyzer motion (data was already computed, just unscored).
    Passive max 75 → 105; 60-pt proven-live threshold reachable from
    natural webcam observation alone.
  - Made 3 prover gates tunable via constructor options (Python defaults
    preserved): expressionRatioGate, rotationThreshold, landmarkVarThreshold.
  - SpoofDetector gains enableLivenessChallenges + livenessProverThresholds
    pass-through options.
  - amispoof switched to proctoring profile:
      enableLivenessChallenges: false
      livenessProverThresholds: { 0.4, 2.0°, 0.5 }
    UI hides ACTIVE CHALLENGES section + active-challenge banner; adds
    Eye/Mouth/Face motion proof-panel rows.
  - LivenessProver tests 10 → 16; full suite 133 → 139, all green.

Verified live at https://fivucsas.com/amispoof/ at 2026-05-16 16:45 UTC.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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