diff --git a/HANDOFF.md b/HANDOFF.md
index 30abae6..e9c67a7 100644
--- a/HANDOFF.md
+++ b/HANDOFF.md
@@ -1,6 +1,6 @@
# Payment Intelligence Modules — handoff
-_Last updated: 2026-05-05 21:30 SGT_
+_Last updated: 2026-05-07 17:07 +08_
This is the next-session entry point for `Raafet57/payment-intelligence-modules`.
Read this file first, then check live `git` / GitHub state before acting.
@@ -9,22 +9,22 @@ Read this file first, then check live `git` / GitHub state before acting.
- Repository: `https://github.com/Raafet57/payment-intelligence-modules`
- Local path on Hermes: `/Users/Shared/AgentWork/repos/payment-intelligence-modules`
-- Active feature branch for SSI work: `feat/ssi-hybrid-instruction-flow`
-- SSI Control Tower is being folded into `apps/ssi-control-tower/` as a separate FastAPI/Jinja backend module. The root Vite/React browser suite remains static/browser-only, and root `src/` plus `scripts/privacy-audit.sh src` must stay scoped to the browser runtime.
+- Product baseline before the current branch: `c31575c` — merge commit for PR #11, which folded SSI Control Tower into `apps/ssi-control-tower/`.
+- Active feature branch for unified SSI UI integration: `feat/ssi-unified-ui-integration`
+- Deployment/release/tag/public announcement: **not done**
-## Previous handoff snapshot
+## Current branch scope
-- Repository: `https://github.com/Raafet57/payment-intelligence-modules`
-- Local path on Hermes: `/Users/Shared/AgentWork/repos/payment-intelligence-modules`
-- Branch to start from next time: `main`
-- Product code baseline at handoff: `1dae872` — merge commit for PR #10
-- Latest merged PR: [#10 — `feat: expand IBAN workbench`](https://github.com/Raafet57/payment-intelligence-modules/pull/10)
-- PR #10 branch commit: `45818c2 feat: expand IBAN workbench`
-- Post-merge `main` CI run: `25379147860` — `success`
-- Feature branch cleanup: `feat/iban-workbench` deleted locally and remotely after same-tree proof
-- Deployment/release/tag/public announcement: **not done**
+This branch adds the first unified-suite UI slice for SSI Control Tower while preserving the root static/browser-only privacy boundary.
+
+Implemented shape:
-After this handoff file is committed, there may be a later docs-only `main` commit. Treat `1dae872` as the shipped product-code baseline and the handoff commit as documentation only.
+- New root-suite route: `/ssi`.
+- New static page: `src/pages/SsiPage.tsx`.
+- SSI appears in the suite header nav and Home module grid with `Backend` status.
+- The `/ssi` page documents the folded FastAPI/Jinja app posture and local run path without coupling to the backend runtime.
+- No root-suite API call, iframe, storage, telemetry, external request, or clickable localhost link was added.
+- README and ROADMAP route lists/docs include `/ssi` and clarify root-browser-runtime boundaries.
## Modules currently in the browser suite
@@ -36,80 +36,54 @@ After this handoff file is committed, there may be a later docs-only `main` comm
| BIC Validator | `/bic` | Demo/snapshot-bound only; not current-production BIC Directory validation. |
| CBPR+ Readiness Checker | `/cbpr` | Local readiness/structure checks only; not certified/full XSD/MyStandards validation. |
| Payment Insights Lite | `/insights` | Local lifecycle insight over files the user provides; not live payment tracking. |
+| SSI Control Tower | `/ssi` | Static entry page for the folded backend module under `apps/ssi-control-tower/`. |
-## What was completed today
-
-1. **CBPR+ Readiness Checker** merged earlier as PR #4.
-2. **raafetchoukri.com design polish** merged as PR #5.
-3. **Storyteller operator workbench refinement** merged as PR #6.
-4. **Payment Insights Lite** added and merged as PR #7.
-5. **Payment Insights hardening/refactor** merged as PR #8.
-6. **Insights CI hotfix** merged as PR #9, restoring green `main` CI.
-7. **IBAN Workbench** merged as PR #10.
-
-## IBAN Workbench details from PR #10
-
-PR #10 upgraded `/iban` from a single validator/explainer into a four-panel workbench:
-
-- **Validate** — keeps offline IBAN syntax/length/structure/MOD-97 validation and registry samples.
-- **Build** — derives exact BBAN field templates from bundled registry data and computes ISO 13616 MOD-97 check digits locally.
-- **Catalogue** — browses bundled country IBAN/BBAN structures, examples, segment hints, and snapshot SEPA flags.
-- **Provenance** — explains generated registry metadata and no-live-service boundaries.
-
-Important guardrails:
-
-- No backend/API/upload path.
-- No telemetry, analytics, remote logging, cookies, `localStorage`, `sessionStorage`, or IndexedDB for payment data.
-- No IBAN-side `fetch`, XHR, storage, cookie, or telemetry APIs; `scripts/privacy-audit.sh` now checks this explicitly.
-- Build rejects missing fields, wrong lengths, and wrong character classes; it does **not** auto-pad BBAN values or infer domestic account data.
-- No claim of live BIC lookup, VOP, account ownership, account existence, current bank details, SEPA reachability, settlement status, or payment tracking.
-
-## Verification evidence for PR #10
+## Verification evidence for this branch
-Local verification before PR:
+Local gates run before handoff:
```bash
-git diff --check
pnpm verify
pnpm test:e2e
-npx -y @google/design.md lint DESIGN.md
+git diff --check
+cd apps/ssi-control-tower && make test
```
Results:
-- `pnpm verify` passed: 29 test files / 226 tests plus lint, format, typecheck, privacy audit, deterministic data checks, and build.
-- `pnpm test:e2e` passed: 20/20 Playwright tests.
-- `DESIGN.md` lint returned 0 errors, with existing unused-token warnings only.
-- Added-line security/browser scans were clean.
-- Codex implementation review returned `APPROVED`.
-- PR branch CI run `25377224769` passed.
-- Post-merge `main` CI run `25379147860` passed.
+- `pnpm verify` passed: lint, format check, typecheck, Vitest, privacy audit, deterministic IBAN/BIC/CBPR data checks, and build.
+- `pnpm test:e2e` passed, including the new `/ssi` Playwright smoke and the privacy-boundary route loop.
+- `git diff --check` passed.
+- `cd apps/ssi-control-tower && make test` passed as a sanity check that the folded backend module was not broken.
+- Static secret scan over changed/untracked files: 11 files scanned, 0 findings.
+- Independent read-only Codex diff review: `APPROVE`.
-## Design and product boundaries to preserve
+## Boundaries to preserve
Design source:
-- `DESIGN.md` is derived from `raafetchoukri.com` and remains the app-level design contract.
-- Use the existing light fintech/practitioner visual system: spacious white cards, subtle borders/shadows, blue/cyan accents, and strong browser-only/privacy-first messaging.
+- `DESIGN.md` remains the app-level design contract.
+- Use the light fintech/practitioner visual system: spacious white cards, subtle borders/shadows, blue/cyan accents, and strong privacy-first messaging.
-Payment/product claim boundaries:
+Root suite privacy/product boundaries:
-- BIC module must remain demo/snapshot-bound and use the caveat `found in bundled demo snapshot` when describing lookup hits.
-- VOP remains out of scope: `VOP requires a certificate and access to the EPC, which we won't have.`
-- CBPR+ checker is local readiness/structure checking only; not a certified validator, full XSD engine, or MyStandards usage-rule engine.
-- Payment Insights Lite is: `Local lifecycle insight over files you provide — not live payment tracking.`
-- Deployment to `payments.raafetchoukri.com` remains paused until explicit user authorization.
+- The root Vite/React app remains static and browser-only.
+- Root `src/` must not add backend coupling, telemetry, analytics, remote logging, browser persistence, external fetches, or payment-data upload paths.
+- `/ssi` is a static entry page only. It must remain fetch-free and iframe-free unless a separate architecture/security review explicitly changes the posture.
-## Next sensible options
+SSI backend module boundaries:
-When work resumes, first choose one path:
+- `apps/ssi-control-tower/` is a separate FastAPI/Jinja backend module with its own Python environment, tests, SQLite dev data, and synthetic fixtures.
+- Public SSI data must remain synthetic and masked.
+- Real SSI data, raw account numbers, proprietary workflows, credentials, and reversible redaction maps must never be committed.
-1. **Post-merge housekeeping only** — confirm the handoff docs-only commit is synced and, if a docs-only CI run exists, confirm it passed.
-2. **Deployment readiness** — prepare static hosting for `payments.raafetchoukri.com`, but do not run host-side Hetzner/Caddy commands without explicit authorization.
-3. **Product polish pass** — pick one existing module and do a narrow review-gated refinement.
-4. **Cross-module provenance/privacy pass** — extract reusable provenance components and make BIC/IBAN/CBPR+ boundaries even more consistent.
+Payment/product claim boundaries:
-Recommended next product pass if no deployment: a **cross-module provenance/privacy consistency pass**, because IBAN now has richer provenance and BIC/CBPR+/Insights could benefit from the same pattern without adding live-data risk.
+- BIC module remains demo/snapshot-bound.
+- VOP remains out of scope because certified EPC access is required.
+- CBPR+ checker is local readiness/structure checking only; not a certified validator, full XSD engine, or MyStandards usage-rule engine.
+- Payment Insights Lite is local lifecycle insight over files the user provides; not live payment tracking.
+- Deployment to `payments.raafetchoukri.com` remains paused until explicit user authorization.
## Fresh-session opener
@@ -136,8 +110,6 @@ Do not deploy or publish unless I explicitly authorize it.
cd /Users/Shared/AgentWork/repos/payment-intelligence-modules
git fetch origin --prune
-git checkout main
-git pull --ff-only origin main
git status --short --branch
git log --oneline --decorate --max-count=8
@@ -147,13 +119,3 @@ gh run list --branch main --limit 5 \
--json databaseId,status,conclusion,headSha,name,event,createdAt,url \
--jq '.[] | [.databaseId,.status,(.conclusion // ""),.headSha[0:7],.name,.event,.createdAt,.url] | @tsv'
```
-
-## Vault pointer
-
-The matching vault checkpoint is in:
-
-- `/Users/Shared/AgentWork/vault/raf-second-brain/Memory.md`
-- `/Users/Shared/AgentWork/vault/raf-second-brain/wiki/hot.md`
-- `/Users/Shared/AgentWork/vault/raf-second-brain/wiki/projects/payment-intelligence-suite.md`
-
-The vault should say PR #10 is merged, `main` CI is green, and this repo has a `HANDOFF.md` as the next-session entry point.
diff --git a/README.md b/README.md
index 2c6f8ac..d78add8 100644
--- a/README.md
+++ b/README.md
@@ -10,15 +10,16 @@ The root suite remains static and browser-only. Backend/product-control modules
## Modules
-| Module | Route | Status | What it does |
-| --------------------------- | -------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
-| **Scrubber** | `/scrubber` | Available | Strip personally identifying fields from `pacs.*` / `camt.*` XML before sharing with peers or vendors. Produces a privacy-safe mapping summary. |
-| **Storyteller** | `/storyteller` | Available | Turn a `pacs.002 / 004 / 008 / 009` or `camt.052 / 053 / 054` message into a plain-language narrative plus a structured field projection and quick insights. |
-| **IBAN Workbench** | `/iban` | Available | Validate, build, catalogue, and trace provenance for IBANs from bundled SWIFT IBAN Registry-derived data. Builder computes MOD-97 check digits from exact-length BBAN fields; no live BIC, VOP, account existence, or reachability checks. |
-| **BIC Validator\*** | `/bic` | Demo | ISO 9362 syntax checks plus a tiny bundled snapshot lookup. **Demonstration only:** bundled BIC data is not accurate/current enough for production, routing, compliance, reachability, or payment decisions. |
-| **CBPR+ Readiness Checker** | `/cbpr` | Available | Browser-only AppHdr / Document namespace / CBPR+ schema-profile coverage checks, plus UETR, BIC syntax, and IBAN syntax/checksum hints. Not a certified validator or MyStandards usage-rule engine. |
-| **Payment Insights Lite** | `/insights` | Available | Local lifecycle insight over ACK/NACK, `pacs.*`, and `camt.*` files you provide. Groups files by identifiers in memory; not live payment tracking, VOP, reachability, or settlement monitoring. |
-| Vault | — | Planned | Planned encrypted local export bundle: user-controlled download/import, no cloud vault, no server storage, and no persistent browser storage by default. Not built yet. |
+| Module | Route | Status | What it does |
+| --------------------------- | -------------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
+| **Scrubber** | `/scrubber` | Available | Strip personally identifying fields from `pacs.*` / `camt.*` XML before sharing with peers or vendors. Produces a privacy-safe mapping summary. |
+| **Storyteller** | `/storyteller` | Available | Turn a `pacs.002 / 004 / 008 / 009` or `camt.052 / 053 / 054` message into a plain-language narrative plus a structured field projection and quick insights. |
+| **IBAN Workbench** | `/iban` | Available | Validate, build, catalogue, and trace provenance for IBANs from bundled SWIFT IBAN Registry-derived data. Builder computes MOD-97 check digits from exact-length BBAN fields; no live BIC, VOP, account existence, or reachability checks. |
+| **BIC Validator\*** | `/bic` | Demo | ISO 9362 syntax checks plus a tiny bundled snapshot lookup. **Demonstration only:** bundled BIC data is not accurate/current enough for production, routing, compliance, reachability, or payment decisions. |
+| **CBPR+ Readiness Checker** | `/cbpr` | Available | Browser-only AppHdr / Document namespace / CBPR+ schema-profile coverage checks, plus UETR, BIC syntax, and IBAN syntax/checksum hints. Not a certified validator or MyStandards usage-rule engine. |
+| **Payment Insights Lite** | `/insights` | Available | Local lifecycle insight over ACK/NACK, `pacs.*`, and `camt.*` files you provide. Groups files by identifiers in memory; not live payment tracking, VOP, reachability, or settlement monitoring. |
+| **SSI Control Tower** | `/ssi` | Backend (folded) | Static entry point for the separate FastAPI/Jinja SSI readiness and governance cockpit under `apps/ssi-control-tower/`. Not part of the root browser runtime; run locally when needed. |
+| Vault | — | Planned | Planned encrypted local export bundle: user-controlled download/import, no cloud vault, no server storage, and no persistent browser storage by default. Not built yet. |
\*The BIC module intentionally does **not** perform live BIC Directory lookup,
current bank-directory enrichment, current SEPA reachability checks, SWIFT FIN
@@ -30,7 +31,7 @@ This is the load-bearing property of the suite, not a footer line:
- All parsing, validation, explanation, and rendering happens in the browser
using platform APIs and pure JS/TS.
-- There is no backend. No analytics, no telemetry, no error reporting, no remote
+- In the root browser-suite runtime, there is no backend API. No analytics, no telemetry, no error reporting, no remote
logging.
- No persistence — `localStorage`, `sessionStorage`, `indexedDB`, and cookies are
never written.
@@ -105,7 +106,7 @@ pnpm verify # lint + format:check + typecheck + test + privacy/data ch
- Tailwind v3 with light fintech tokens in `src/index.css`, derived from
[`DESIGN.md`](./DESIGN.md)
- React Router for `/`, `/scrubber`, `/storyteller`, `/iban`, `/bic`, `/cbpr`,
- `/insights`, plus a `*` fallback
+ `/insights`, `/ssi`, plus a `*` fallback
- Vitest + Testing Library for unit/page tests
- Playwright for end-to-end smoke tests
- ESLint + Prettier
@@ -138,6 +139,7 @@ src/
CbprPage.tsx
IbanPage.tsx
ScrubberPage.tsx
+ SsiPage.tsx # static entry point for the folded SSI backend module
StorytellerPage.tsx
NotFoundPage.tsx
lib/
@@ -179,10 +181,10 @@ To deploy on any static host, the host must:
- Serve the contents of `dist/` from the site root.
- For unknown / deep-link routes (for example `/scrubber`, `/storyteller`,
- `/iban`, `/bic`, `/cbpr`, `/insights`, or `/anything-else`), fall back to serving
+ `/iban`, `/bic`, `/cbpr`, `/insights`, `/ssi`, or `/anything-else`), fall back to serving
`index.html` so React Router can resolve the route on the client. This is sometimes called
"SPA fallback" or "rewrite all to index.html".
-- Not require any server-side execution. There is no API.
+- Not require any server-side execution for the root static SPA. There is no root API.
- Preserve the privacy posture: no analytics injection, no remote logging, and no
third-party scripts added by the host configuration.
diff --git a/ROADMAP.md b/ROADMAP.md
index 634764d..d573a33 100644
--- a/ROADMAP.md
+++ b/ROADMAP.md
@@ -20,16 +20,17 @@ the suite is browser-only, static-host friendly, and privacy-first.
### 0. SSI Control Tower folded backend module
-SSI Control Tower now lives under `apps/ssi-control-tower/` as a separate FastAPI/Jinja backend module inside this repository. It is not part of the root static browser suite runtime, and it must not weaken the browser-only privacy boundary for `src/`.
+SSI Control Tower now lives under `apps/ssi-control-tower/` as a separate FastAPI/Jinja backend module inside this repository. It is not part of the root static browser suite runtime, and it must not weaken the browser-only privacy boundary for `src/`. The root suite now includes `/ssi` as a static, fetch-free entry page that explains the backend module posture and local run path without coupling the browser app to the FastAPI runtime.
-Current branch scope is the approved hybrid unified SSI instruction flow, Phase -1 through Phase 2:
+Merged SSI foundation includes:
-- fold the module into this repo as ordinary tracked files;
-- keep CSV demo imports compatible while also creating canonical `SsiInstruction` records with source lineage;
-- make SSI Plus V3 imports use the same canonical ingestion path;
-- add instruction-level validation, instruction exceptions, and a unified control queue API.
+- folded module source as ordinary tracked files;
+- CSV demo imports compatible while also creating canonical `SsiInstruction` records with source lineage;
+- SSI Plus V3 imports on the same canonical ingestion path;
+- instruction-level validation, instruction exceptions, and a unified control queue API;
+- approval, export, and account-privacy hardening for the synthetic public prototype.
-Approvals, exports, readiness expansion, and full UI polish remain follow-up scope.
+Follow-up scope remains: deeper product UI polish inside the FastAPI app, deployment/publication decisions, and real-data/private-lab workflows only after explicit approval.
### 1. Deployment target decision
@@ -47,7 +48,7 @@ Acceptance criteria:
- Static build only; no backend or API.
- SPA fallback works for `/scrubber`, `/storyteller`, `/iban`, `/bic`, `/cbpr`,
- and `/insights`.
+ `/insights`, and `/ssi`.
- No analytics injection, remote logging, third-party scripts, cookies, or browser
persistence introduced by the host configuration.
- README hosting notes are updated with the chosen target.
diff --git a/e2e/smoke.spec.ts b/e2e/smoke.spec.ts
index 1b83afd..2121bc4 100644
--- a/e2e/smoke.spec.ts
+++ b/e2e/smoke.spec.ts
@@ -37,7 +37,16 @@ test("privacy boundary does not use external requests or browser persistence", a
const requestedUrls: string[] = [];
page.on("request", (request) => requestedUrls.push(request.url()));
- for (const route of ["/", "/scrubber", "/storyteller", "/iban", "/bic", "/cbpr", "/insights"]) {
+ for (const route of [
+ "/",
+ "/scrubber",
+ "/storyteller",
+ "/iban",
+ "/bic",
+ "/cbpr",
+ "/insights",
+ "/ssi",
+ ]) {
await page.goto(route);
await expect(
page.getByText(/runs entirely in your browser\. nothing is uploaded\./i).first(),
diff --git a/e2e/ssi.spec.ts b/e2e/ssi.spec.ts
new file mode 100644
index 0000000..2eb815e
--- /dev/null
+++ b/e2e/ssi.spec.ts
@@ -0,0 +1,21 @@
+import { expect, test } from "@playwright/test";
+
+test("SSI Control Tower overview is a static backend-module entry point", async ({ page }) => {
+ const requestedUrls: string[] = [];
+ page.on("request", (request) => requestedUrls.push(request.url()));
+
+ await page.goto("/ssi");
+
+ await expect(page.getByRole("heading", { level: 1, name: /ssi control tower/i })).toBeVisible();
+ await expect(page.getByText(/backend module/i)).toBeVisible();
+ await expect(page.getByText(/synthetic-data public prototype/i)).toBeVisible();
+ await expect(page.getByText(/apps\/ssi-control-tower/)).toBeVisible();
+ await expect(page.getByText(/make run/)).toBeVisible();
+
+ const origin = new URL(page.url()).origin;
+ const externalRequests = requestedUrls.filter((url) => {
+ const parsed = new URL(url);
+ return parsed.protocol.startsWith("http") && parsed.origin !== origin;
+ });
+ expect(externalRequests).toEqual([]);
+});
diff --git a/src/App.test.tsx b/src/App.test.tsx
index 32491c4..83e1d4e 100644
--- a/src/App.test.tsx
+++ b/src/App.test.tsx
@@ -41,6 +41,7 @@ describe("App", () => {
expect(screen.getAllByRole("link", { name: /iban/i }).length).toBeGreaterThan(0);
expect(screen.getAllByRole("link", { name: /cbpr\+/i }).length).toBeGreaterThan(0);
expect(screen.getAllByRole("link", { name: /insights/i }).length).toBeGreaterThan(0);
+ expect(screen.getAllByRole("link", { name: /ssi/i }).length).toBeGreaterThan(0);
});
it("renders the Scrubber page at /scrubber", async () => {
@@ -75,6 +76,13 @@ describe("App", () => {
await waitFor(() => expect(global.fetch).toHaveBeenCalled());
});
+ it("renders the SSI Control Tower overview at /ssi", () => {
+ renderAt("/ssi");
+ expect(
+ screen.getByRole("heading", { level: 1, name: /ssi control tower/i }),
+ ).toBeInTheDocument();
+ });
+
it("renders a not-found page for unknown routes", () => {
renderAt("/does-not-exist");
expect(
diff --git a/src/App.tsx b/src/App.tsx
index a7044a5..f47f1e8 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -7,6 +7,7 @@ import { IbanPage } from "@/pages/IbanPage";
import { InsightsPage } from "@/pages/InsightsPage";
import { NotFoundPage } from "@/pages/NotFoundPage";
import { ScrubberPage } from "@/pages/ScrubberPage";
+import { SsiPage } from "@/pages/SsiPage";
import { StorytellerPage } from "@/pages/StorytellerPage";
export function App() {
@@ -20,6 +21,7 @@ export function App() {
+ SSI Control Tower is not part of the browser-only runtime. It lives as a folded + FastAPI/Jinja application inside this repository, while this React page documents the + entry point, posture, and local operator workflow without calling an API. +
++ Synthetic public prototype disclaimer +
++ This project is a synthetic-data public prototype for SSI readiness and governance. It + is not affiliated with DTCC, SSImple, Swift, FMSB, ISITC, any custodian, or any market + infrastructure. It does not connect to production SSI utilities and does not contain + real settlement instructions, client data, account numbers, or proprietary workflows. +
+What it does
+Run it locally
+
+ {"cd apps/ssi-control-tower && make run\n# opens http://localhost:8000"}
+
+ + The local app owns its Python dependencies, SQLite development data, templates, APIs, + tests, and synthetic fixtures. Read the folded app README for command details and demo + flow. +
+Rule catalogue
+{item}
+