Skip to main content

DESIGN.md: Bank8:eFintech — Detailed Design Document

Overview-level material lives in README.md; the business view lives in BUSINESS.md.

1. Scope & Context​

What eFintech owns: the recipient-side presentation layer; the client-side state composition (EFintechContext); the FDX recipient journeys (provider catalog, provider detail, consent flow/monitoring, consented accounts + transactions); the admin surface (instance overview, FDX recipient-side administration); the listing manifest (app8-listing.json, mirrored in app.json) and BFF scope whitelist (bff8/scope_whitelist.rs).

What eFintech does NOT own (delegates): identity and the hosted consent UI (Sentin8:Auth); the FDX data plane, consent enforcement, and retrieval (Bank8:FDX); persistence and object access (Sentin8:DB, storage — via BFF); session/token custody (Sentin8:BFF); any ledger/payments surface (a recipient never touches the provider's core directly).

2. Runtime Architecture​

Request lifecycle (consent start → poll → display data):

Request lifecycle (revoke a consent):

3. Data Model​

eFintech persists no tables of its own. All state is service-owned and referenced by id:

Domain objectOwned byeFintech access
Provider catalog entriesBank8:FDX (sentin8_app_store_listings / provider profile registry)read via GET /v1/recipient/providers
Consents (customer ↔ provider)Bank8:FDX consent registrystart/poll/revoke/refresh via /v1/recipient/consents*
Consented accounts + transaction snapshotsBank8:FDX retrieval of provider dataread via /v1/recipient/accounts(/{id}/transactions)

The client keeps only ephemeral UI state (selected provider, in-flight consent, current route). No local persistence beyond the browser session.

4. API Surface​

All backend calls are BFF-proxied (https://{bff}/proxy/{service}/path); the app itself has no listening port.

ConsumerMethod + path (via BFF)GateNotes
Provider discoveryGET /proxy/bank8-fdx/v1/recipient/providerscustomer session; bank8:fdx:recipient:readprovider catalog
Consent startPOST /v1/recipient/consents/startcustomer session; bank8:fdx:recipient:writereturns redirectUrl/authorization_url
Consent trackingGET /v1/recipient/consentscustomer session; bank8:fdx:recipient:readstatus polling + dashboard
Consent revokePOST /v1/recipient/consents/{id}/revokecustomer session; bank8:fdx:recipient:writerevokes sharing + linked tokens
Consent refreshPOST /v1/recipient/consents/{id}/refreshcustomer session; bank8:fdx:recipient:writere-authorization journey
Consented accountsGET /v1/recipient/accounts?consentId=…customer session; bank8:fdx:recipient:readconsent-scoped
Consented transactionsGET /v1/recipient/accounts/{id}/transactionscustomer session; bank8:fdx:recipient:readpaginated snapshots
Admin instanceruntime context onlystaffno backend calls
Admin FDXhooks as abovestaffrecipient-side monitoring

Event flow eFintech participates in (consumer side only; consent lifecycle events are emitted upstream by Bank8:FDX and reflected by polling):

5. Cross-Cutting Concerns​

  • Authentication / token custody. BFF session cookie only; OAuth tokens attach server-side. Provider access tokens never exist in this app — Bank8:FDX holds the retrieval credentials.
  • Tenant resolution. Fail-closed runtime-context bootstrap from own origin, identical to the other apps.
  • Authorization. Recipient routes gate on bank8:fdx:recipient:read/write; admin routes require staff authorization resolved by the BFF. Consent status of other customers is never fetchable through this app's surface.
  • Consent minimization. Only the requested FDX scopes are asked at start (accountbasic, accountdetailed, transactions reads); the app renders only what the consent allows.
  • Correlation. x-sentin8-correlation-id on every proxy call; shared AppLogger entries under the same id.
  • TLS. All calls are https://; consent redirects ride the hosted authorization domain over TLS.

6. Failure Modes & Recovery​

  • Bootstrap failure → error page (fail closed).
  • Consent poll exhaustion (30 × 2s without a terminal state) → surfaced error frame; the customer restarts the journey from the dashboard (the pending consent remains visible there).
  • Consent FAILED/TIMEOUT → the status is rendered with an explanatory state; a refresh or a fresh start attempt is offered.
  • Revocation race — revoke returns success upstream; the following list refresh removes the consent and its data from view even if the customer is on a stale page.
  • Provider unreachable (data plane) → retrieval snapshots fail upstream; the app surfaces empty/error frames — it never cache-serves stale financial data.

7. Concurrency & Performance Model​

  • Single Flutter web isolate; all I/O async through the three BffApiClients.
  • One bounded poll loop per in-flight consent (ConsentFlowPage); no global polling. Dashboard refreshes are page-enter driven.
  • Transaction pages are paginated; large histories fetch incrementally.
  • No SSE/WebSocket usage in this app (consent status polling is the only long-running interaction).

8. Invariants, Insecure-Defaults Rejected, Unsafe Rationale​

  • No direct external provider calls from the app — all FDX access is via the Bank8:FDX recipient APIs through the BFF (CI guard scripts/ci-guards.sh enforces; direct provider HTTP is a hard failure).
  • No OAuth or provider token is ever present in the client.
  • Data outside a consenting relationship is never rendered (consent-scoped reads only).
  • Unknown hostname / failed runtime context = fatal, never defaulted.
  • No unsafe code exists in this app (pure Dart, no FFI).

9. Testing Strategy​

LayerOwnershipTooling
Widget testspage rendering, consent-state visualsflutter_test (test/widget_test.dart)
Contract testsrecipient path composition + scope whitelist shapetest/contract_test.dart, bff8/scope_whitelist.rs
Integration testsconsent lifecycle against a stubbed fdx client (start → poll → active; revoke → data gone)test/integration_test.dart
Security testsno direct provider calls, no token on client, fail-closed bootstraptest/security_test.dart + scripts/ci-guards.sh
E2E (UI only)customer sign-up → consent flow → consented data display inside e2e-ts lifecyclePlaywright + Flutter semantics bridge

Not tested here (platform scope): consent enforcement internals, provider token custody, retrieval scheduling, CSDF minimization — covered by Bank8:FDX's own suites.

10. Open Questions​

  • Recipient-initiated payment (money movement write) is a future FDX capability — the current app is strictly data-read.
  • Background aggregate refresh of consented accounts may move to a platform-scheduled retrieval job instead of client-pull; undecided.

Design attribution: bank8:efintech and its underlying concepts were designed by Hamid Jahanzad Hamid@Jahanzad.com.