Skip to content

Architecture

Antonio Membrides Espinosa edited this page Jul 24, 2026 · 32 revisions

Architecture Overview

This document describes the structure of the FSI (Financial Services Industry) PCI DSS (Payment Card Industry Data Security Standard) Payment Security Demo: what data it stores, how that data is protected, and how the components interact. It is written for security reviewers, compliance officers, and FSI prospects who want to understand the system without reading source code.


Table of Contents

  1. System Architecture
  2. Data Architecture
  3. Sensitive Fields and Encryption by Collection
  4. Collection Relationships
  5. Encryption Architecture
  6. Role-Based Access Model
  7. Provider Groups (Integration Hub, SD-193)
  8. Collections by Core Module

1. 🧱 System Architecture

This demo is one microservice in a broader MongoDB FSI ecosystem. Its role is the Payment Security service: it owns PCI DSS-scoped storage and encryption, fraud-case lifecycle management, and the RBAC (Role-Based Access Control) controls that govern who can read which encrypted fields. It does not simulate or duplicate payment execution or fraud detection, those responsibilities belong to the two peer systems it integrates with.

The architecture is also designed to serve as the foundation for an Open Finance / Open Banking extension. The stable REST (Representational State Transfer) API (Application Programming Interface) boundary, the field-level QE (Queryable Encryption) model, and the DEK (Data Encryption Key)-per-access-tier pattern are the three technical primitives that Open Banking consent-scoped access requires. See Open Finance / Open Banking readiness below.

Status note (kept current). Several passages in this section were written for the early (v1–v3) card-only prototype and describe later capabilities as "future" or "v4+". They are now implemented and are documented in the newer sections of this page: a full OAuth 2.0 / OIDC authorization server with CIBA passwordless (see Β§6 and Β§7), the payment gateway and bank transfers (ACH/SEPA/SWIFT via PISP, see Β§2 and Β§7), the Integration Hub (SD-193) with internal-first provider dispatch (Β§7), the event-driven timeseries audit (Β§2), and the external merchant app (see the topology diagram below). Where the older prose says "planned" or "v3/v4 stub", treat the newer sections as authoritative.

Components and integration context

+-----------------------------------------+     +-----------------------------+     +-----------------------------+
|  Sec4 Pay | PSP | Frontend | UI/UX      |     |  Leafy Bank                 |     |  Agentic ThreatSight360     |
|  Next.js / React.js  (port 3000)        |     |  Core banking platform      |     |  AI fraud-detection agent   |
|                                         |     |  (card issuance, accounts)  |     |  (autonomous investigation) |
+----------------+------------------------+     +-------+---------------------+     +------------+----------------+
           HTTPS |                               HTTPS  |       ^  HTTPS                         ^  HTTPS
            REST |                                REST  |       |  Webhooks                      |  Webhooks
                 |          payment initiation requests |       |  REST                          |  REST
                 |          and read card data          |       |                                |
                 +--------------------------------------+       |                                |
                 |                                              |                                | 
                 v                                              |                                |
+--------------------------------------+  REST / Tool calls     |                                |
|  Sec4 Pay | PSP | Backend | API  | ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─  β”΄ ─ ─ ── ─ ─ ─ ─ ─ ─ ─ ─ ── ─ ─ β”€β”˜
|  Authentication, business logic,     |                 +------------------+                    |
|  Fastify, QE MongoDB client          | <-------------> |  AWS KMS         |                    |
+----------------+---------------------+                 |  Customer Master |                    |
                 | MongoDB Wire Protocol                 |  Key (CMK)       |                    |
                 | (TLS 1.3)                             +------------------+                    |  HTTPS
                 |                                                                               |  Webhooks
                 v                                                                               v  REST
+-------------------------------------+                                   +-------------------------------------+
|  MongoDB Atlas  (M10+)              |                                   |  Leafy Wallet                       |
|  Stores only ciphertext for         |                                   |  Payment Service on the Edge        |
|  protected fields. Atlas cannot     |                                   |                                     |
|  decrypt without the CMK.           |                                   |                                     |
+-------------------------------------+                                   +-------------------------------------+

Ports reflect the local/docker topology: PSP frontend 8080, backend 8081, merchant app 8082. External Providers, bank rails and the CMK provider (AWS KMS or a local key for dev) are all reached from the backend; the Integration Hub answers internally first and substitutes an external provider only when configured.

How each system contributes

The Payment Security service plays the role of the Payment Gateway in the standard card payment chain: Merchant backend β†’ Payment Gateway β†’ Processor β†’ Acquirer β†’ Card Network β†’ Issuer. Card-lifecycle events (authorization responses, settlement notifications, chargebacks) arrive at the gateway from the card network side; they are not sourced from Leafy Bank. Leafy Bank is the merchant backend: it initiates payment requests and receives authorization outcomes.

System Role in the ecosystem Integration with Payment Security
Leafy Bank: Transactions Service (:8001) Merchant backend. Owns ACID (Atomicity, Consistency, Isolation, Durability)-safe fund transfers, account management, and payment order initiation. Calls the Payment Gateway to create, confirm, and capture payments. Sends payment initiation requests (POST /payment/create, confirm, capture) to the Payment Security API. Authorization outcomes and settlement status flow back from the gateway to Leafy Bank, not the other way around. Card-lifecycle events (auth responses, chargebacks) originate from the card network / processor downstream of the gateway.
Leafy Bank: Open Finance Service (:8003) AISP (Account Information Service Provider) in PSD2 (Payment Services Directive 2) terms. Aggregates financial data from external institutions on behalf of the customer. Reads customer transaction and card data from the Payment Security API using consent-scoped tokens (OAuth (Open Authorization) 2.0 / FAPI (Financial-grade API) flow, planned as a future iteration). This is the primary Open Finance integration point.
Agentic ThreatSight360 Financial crime detection and AML (Anti-Money Laundering) / KYC (Know Your Customer) compliance. Runs a 6-agent LangGraph pipeline (Triage β†’ Case Analyst β†’ Trail Follower β†’ SAR (Suspicious Activity Report) Author β†’ Compliance QA) with human-in-the-loop checkpoints. Calls the Payment Security API as a level2_investigator service account, reading QE-encrypted fields, updating fraud-case status, and writing audit events. It does not bypass RBAC or encryption; it operates through the same API surface as human investigators.
Payment Security (this demo) Payment Gateway. PCI DSS-scoped storage and access control. Owns Queryable Encryption, the two-DEK key hierarchy, RBAC enforcement, gateway orchestration, and the fraud-case lifecycle. Receives payment requests from Leafy Bank upstream, routes to processor/acquirer downstream (simulated in v1–v3, implemented in v4), and exposes fraud investigation endpoints consumed by ThreatSight360.

Key points:

  • The Payment Security service is the single source of truth for PCI DSS-scoped data. Leafy Bank and ThreatSight360 consume it; neither duplicates its storage or encryption logic.
  • The frontend never talks directly to the database.
  • The Backend API decrypts protected fields in its own process memory using keys fetched from AWS KMS at request time. No other system in the ecosystem can decrypt these fields.
  • Atlas stores binary ciphertext (BSON (Binary JSON) subtype 06) for all QE-protected fields. A MongoDB admin with full cluster access sees only opaque bytes.
  • TLS 1.3 protects all data in transit between the API and Atlas.
  • All callers, the Demo UI, Leafy Bank webhooks, and ThreatSight360 tool calls go through the same API endpoints and are subject to the same RBAC and DEK-access rules.

Open Finance / Open Banking readiness

Open Banking regulations (PSD2 in Europe, CDR (Consumer Data Right) in Australia, and emerging Open Finance frameworks globally) require three technical capabilities: a stable API boundary that third-party providers (TPPs) can call with scoped tokens, field-level access control tied to customer consent rather than internal roles, and an immutable audit trail of every TPP data access.

This architecture addresses all three without requiring structural changes to the storage or encryption layer:

Open Finance requirement How this architecture supports it
Stable versioned API (FAPI / PSD2) The Backend API is the only entry point. No caller reads Atlas directly. Adding OAuth 2.0 / FAPI token validation is a middleware addition, it does not change the storage or encryption layer.
Field-level consent-scoped access QE uses two DEKs today (DEK-lookup for searchable PII, DEK-sensitive for high-sensitivity fields). This model extends naturally: a DEK-consent-<scope> can be issued per customer consent grant, restricting a TPP to exactly the fields the customer authorized. Atlas never sees which DEK a caller holds enforcement is entirely in the API process.
Immutable audit trail fraudDiagnosisCaseEvents is already an append-only collection. A future consentAccessLog collection follows the same pattern: every TPP field access writes a signed event, satisfying PCI DSS Req 10 and PSD2 access-log obligations simultaneously.
Consent revocation Revoking a consent grant means revoking or rotating the corresponding DEK in AWS KMS. The revocation is cryptographic and immediate: the TPP can no longer decrypt previously authorized fields, even from cached Atlas ciphertext.

What a future Open Finance module would add:

  • OAuth 2.0 authorization code flow (FAPI 1.0/2.0) as an alternative auth path in the API middleware
  • consentAgreement and consentAccessLog collections (maps to BIAN SD-36 Information Provider Operations)
  • A DEK-consent-<scope> per customer per TPP, provisioned and managed in AWS KMS alongside the existing DEKs
  • Leafy Bank's Open Finance Service (:8003) as the first TPP consumer of the consent-gated API endpoints

2. 🏦 Data Architecture

What is BIAN and why does it matter here

BIAN (Banking Industry Architecture Network) is an international non-profit that publishes a standard reference architecture for banking. Its core output is a catalogue of Service Domains (SDs): precisely defined business capabilities that any bank performs. Each SD has one Control Record (the central business artifact it manages), a set of standard field names, and a defined lifecycle.

Using BIAN means the data model is not invented for the demo. Every collection name, field name, and entity relationship corresponds to a concept that a bank architect, a QSA (Qualified Security Assessor), or an FSI prospect will already recognise.

BIAN compliance status

The demo implements strict BIAN Service Domain separation. All 20 previously identified deviations have been resolved. The table below documents the compliance decisions made and why they matter for an FSI or QSA audience.

BIAN requirement Implementation Status
PII in SD-13 only party collection owns partyEmailAddress (QE:equality), partyMobilePhoneNumber (QE:equality), partyName. customerAgreementProcedure stores only a partyInstanceReference FK. The service layer (buildResponse, getSelfProfile) reads PII from party and maps it to the API response β€” no PII is stored or read from customerAgreementProcedure. βœ… Strict BIAN
Control Record type suffixes All collection names carry the CR suffix: customerAgreementProcedure, cardTransactionLog, paymentCardManagement, partyAuthenticationAssessment, customerCreditRatingState, customerAuthenticationAssessment βœ… Strict BIAN
SD-91 for authentication Credentials and roles live in customerAuthenticationAssessment (SD-91 Customer Authentication). partyAuthenticationAssessment (SD-16) is reserved for identity verification events only. βœ… Strict BIAN
bianServiceDomain with spaces All documents carry bianServiceDomain: 'Customer Agreement' (with spaces), matching the BIAN catalogue exactly. βœ… Strict BIAN
FK naming convention All foreign keys in fraudDiagnosisCase use the *InstanceReference suffix pattern: cardTransactionInstanceReference, customerAgreementInstanceReference. βœ… Strict BIAN
Open Banking foundation consentAgreement and consentAccessLog stub collections created (SD-36 Information Provider Operations). OAuth 2.0 / FAPI token validation is v4+ scope. βœ… v3 stub

How BIAN shaped the final solution

1. Collection names map directly to Service Domains. Each MongoDB collection is named after a BIAN Service Domain, making it unambiguous during a demo or a QSA review which business entity is being discussed.

BIAN Service Domain SD number MongoDB collection QE Protected
Party Data Management SD-13 party Yes (email, phone)
Party Authentication SD-16 partyAuthenticationAssessment No
Customer Agreement SD-53 customerAgreementProcedure Yes (accountRef QE:equality; address, govId, riskNotes QE:none, stored inline)
Payment Card SD-88 paymentCardManagement Yes (expiry)
Card Transaction SD-254 cardTransactionLog Yes (accountRef QE:equality; rawGatewayPayload, processorMetadata QE:none, stored inline)
Fraud Diagnosis SD-83 fraudDiagnosisCase No
Customer Credit Rating SD-60 customerCreditRatingState No
Customer Authentication SD-91 customerAuthenticationAssessment Yes (email)
Merchant Agreement SD-89 merchantAgreementProcedure No
Information Provider Operations SD-36 consentAgreement No (v3 stub)
Information Provider Operations SD-36 consentAccessLog No (v3 stub)

2. Field names follow BIAN naming conventions. BIAN defines a compound naming pattern: <ControlRecord><Qualifier><AttributeType>. The Control Record name mirrors the SD name (the Card Transaction SD manages a Control Record also named cardTransaction). cardTransactionAccountReference decomposes as: cardTransaction (Control Record) + Account (qualifier) + Reference (attribute type). This verbosity is intentional: cardTransactionAccountReference is unambiguous across any team, system, or regulator, while accountRef is not.

3. Extended Reference Pattern β€” cost/benefit decisions. BIAN allows embedding stable fields from a referenced SD to avoid multi-collection queries. Each use of this pattern was evaluated for staleness risk and BIAN alignment:

Denormalized field Source Keep? Reason
customerAuthenticationAssessment.customerAuthenticationUserName party.partyName Yes JWT name claim β€” avoids a DB lookup per authenticated request. Stale until next login (acceptable for a display label). Synced when party.partyName changes via PATCH /auth/me.
fraudDiagnosisCase.transactionSnapshot cardTransactionLog (display fields) Yes Single-query fraud investigation view. cardTransactionStatus is mutable but has a controlled update path in the service layer. BIAN SD-83 naturally references the card transaction event.
cardTransactionLog.cardTransactionMaskedPanDisplay paymentCardManagement Yes Immutable field (masked PAN never changes). Zero staleness risk. PCI DSS permits last-4 storage.

4. Sensitive fields live inline. The QE tier enforces the access boundary. BIAN's Control Record model keeps all attributes of a Service Domain in one collection. In v2 the demo follows this principle: sensitive QE:none fields (customerAgreementResidentialAddress, governmentIdentificationReference, rawGatewayPayload, etc.) are stored inline in customerAgreementProcedure and cardTransactionLog. Access control is enforced cryptographically: the Level 1 QE client uses an encryptedFieldsMap that omits QE:none fields, so the driver returns those fields as Binary ciphertext. The Level 2 QE client includes all fields and auto-decrypts them. No collection split is needed because the DEK tier (not a separate collection) is the compliance boundary.

4. The fraud investigation workflow follows the BIAN Fraud Diagnosis SD lifecycle. BIAN defines Fraud Diagnosis as a distinct business service with its own case lifecycle: open, under review, escalated, resolved. The fraudDiagnosisCase collection and its fraudDiagnosisCaseStatus field follow that lifecycle directly.

5. The recurring payment design (v4) fits into existing BIAN SDs without a new collection. A customer's authorisation to charge a card for future payments is a behavioral feature of the Customer Agreement (SD-53) and the Payment Card (SD-88), not a new entity. This kept the data model flat and avoided non-standard collections.

Collections overview

Collection BIAN Service Domain Purpose QE Protected
party Party Data Management (SD-13) Canonical identity record: email, phone, name for all party types Yes (email QE:equality, phone QE:equality)
customerAuthenticationAssessment Customer Authentication (SD-91) Login credentials, roles, and access state. Linked to party via partyInstanceReference Yes (email QE:equality)
partyAuthenticationAssessment Party Authentication (SD-16) Identity verification event log only. Credentials live in SD-91. No
authenticationDomain Party Authentication (SD-16) Enabled authentication domains (local, msentra). Not a BIAN CR; support collection. No
customerAgreementProcedure Customer Agreement (SD-53) Business contract + inline sensitive PII. PII identity fields separated to party (SD-13). Yes (accountRef QE:equality; address, govId, riskNotes QE:none)
paymentCardManagement Payment Card (SD-88) Tokenized card data and cardholder consent Yes (expiry QE:none)
cardTransactionLog Card Transaction (SD-254) Payment transaction records with searchable account reference and inline gateway data Yes (accountRef QE:equality; rawGatewayPayload, processorMetadata QE:none)
fraudDiagnosisCase Fraud Diagnosis (SD-83) Fraud investigation cases with embedded transaction snapshot No (operational data only)
fraudDiagnosisCaseEvents Fraud Diagnosis (SD-83) Append-only audit trail events per case (separated from case document) No
customerCreditRatingState Customer Credit Rating (SD-60) HRPC risk classification flags per customer account reference No (compliance metadata, no PII or CHD)
merchantAgreementProcedure Merchant Agreement (SD-89) Merchant onboarding contract, KYB check (BQ:Step), and agreement lifecycle No
consentAgreement Information Provider Operations (SD-36) Open Banking consent grants (v3 stub, OAuth 2.0 / FAPI in v4) No
consentAccessLog Information Provider Operations (SD-36) Append-only TPP field access log (PCI DSS Req 10 + PSD2) No

QE:none fields are stored inline in their parent collections. The Level 1 QE client's encryptedFieldsMap deliberately omits them, so the driver returns those fields as BSON Binary ciphertext unreadable without the DEK-sensitive key tier. Access to the decrypted values requires a Level 2 role with a valid escalation token, which triggers the L2 QE client pool.

MongoDB schema patterns applied: All collections include a schemaVersion field (Schema Versioning Pattern) to enable zero-downtime migrations. fraudDiagnosisCaseEvents and consentAccessLog separate audit trails from their parent documents (Unbounded Array anti-pattern fix). fraudDiagnosisCase embeds a transactionSnapshot (Extended Reference Pattern) for single-query fraud investigation display. customerCreditRatingState is a static lookup collection seeded once and queried read-only at investigation time (Lookup Table Pattern). PII separation between party and customerAgreementProcedure implements the BIAN cross-SD reference pattern: agreement searches resolve via a two-step QE lookup (party by email/phone, then agreement by partyInstanceReference).

Additional collections (v4 onward)

The table above covers the original v1–v3 core. Subsequent iterations added the following collections (all BIAN-named, sourced from backend/src/vendors/setup/createCollections.ts):

Collection BIAN Service Domain Purpose Iteration
paymentOrderProcedure Payment Order (SD-64) Payment order lifecycle for the gateway v4
cardEtokenProcedure Payment Card token vault (SD-57) Deterministic card token vault v4
checkoutSessionLog Payment Order (SD-64) Hosted-checkout sessions v4
paymentLinkRecord Payment Order (SD-64) Payment links v4
cardAuthorizationRecord Card Authorization (SD-15) Authorization request/response records v4
paymentCardRegistry Payment Card (SD-88) Physical-card registry + deterministic token / shared-card signal later
payoutAccountArrangement Payment Initiation (SD-66) Payout accounts; IBAN/routing encrypted (QE:none, GDPR/PSD2, not PCI) v17.1
paymentExecutionProcedure Payment Execution (SD-65) Bank-transfer executions; destinationIban QE:none v17.1
counterpartyArrangement Payment Initiation (SD-66) Beneficiary / counterparty registry (opaque tokens) v17.1
recurringMandateProcedure Payment Initiation (SD-66) ACH SDD / SEPA SDD recurring mandates v17.1
balanceCreditLog Account Information (SD-36) Balance / credit movement log v17
partyAuthorizationCode Customer Authentication (SD-91) OAuth authorization codes v18
partyIssuedToken Customer Authentication (SD-91) Issued access/refresh tokens v18
partyAuthenticationKey Customer Authentication (SD-91) OAuth/OIDC RS256 signing keys v18
partyAuthConsent Customer Authentication (SD-91) OAuth consent grants (granular scope) v18
partyEnrolledCredential Party Authentication (SD-91/SD-16) Passwordless enrolled public keys (WebAuthn/FIDO2) v24
partyBackchannelAuthentication Party Authentication (SD-91) CIBA backchannel auth requests v24
externalProviderArrangement External Provider Arrangements (SD-193) Integration Hub provider registry v6
externalProviderArrangementActionLog External Provider Arrangements (SD-193) Per-dispatch action log v6
capabilityModuleConfiguration External Provider Arrangements (SD-193) Internal Module engine configuration v8
businessProcessEvent (audit, timeseries) Business event stream (TTL ~90 days) v7
complianceProcessEvent (audit, timeseries) Compliance event stream (TTL ~365 days) v7
role (ACL) Data-driven role→permission definitions (ADR-030) v6
fraudDiagnosisCustomerQuestion Fraud Diagnosis (SD-83) Analyst→customer questions on a case (ADR-031) later
merchantWebhookDeliveryLog Merchant Agreement (SD-89) Outbound merchant webhook delivery log v18
notification (support) User notifications later

Of these, only payoutAccountArrangement and paymentExecutionProcedure add QE-encrypted fields (bank data, QE:none, GDPR/PSD2 not PCI). The rest are plaintext (no CHD/PII) or timeseries audit. See tmp/wiki/Encryption.md and tmp/wiki/Dataset.md.


3. πŸ” Sensitive Fields and Encryption by Collection

Legend

Mode Meaning
QE:equality Encrypted client-side, searchable by exact match. Atlas stores ciphertext.
QE:none Encrypted client-side, not searchable. Retrieved only after decryption with the correct key.
Plaintext Stored unencrypted. Not sensitive or already de-identified (e.g., a token surrogate).
bcrypt hash One-way hash. Plaintext is never stored or transmitted.
Never stored The field is prohibited at all API endpoints.

party

BIAN origin: Party Data Management (SD-13)

Party Data Management is the canonical identity store for every person, organisation, or system that has a relationship with the bank. In BIAN, "Party" means any entity in a banking relationship. This collection is the single source of truth for PII (Personally Identifiable Information) across all other Service Domains.

Why this collection is needed: Strict BIAN requires PII to live exclusively in SD-13. All other SDs reference parties via partyInstanceReference (FK) rather than storing duplicate copies of email or phone. This means a PII update propagates from one place, and a data-subject erasure request (GDPR Article 17) targets one document.

QE design note: partyEmailAddress and partyMobilePhoneNumber use QE:equality so fraud investigators can search by email or phone without the plaintext ever reaching Atlas. The two-step lookup pattern (search party first, then resolve customerAgreementProcedure by partyInstanceReference) adds one network round-trip (+30-80ms) in exchange for strict BIAN compliance. See tmp/wiki/tradeoffs.md.

Field Classification Mode Searchable Notes
partyInstanceReference PK Plaintext Yes (unique index) UUID; referenced as FK by all other SDs
partyEmailAddress PII QE:equality Yes Primary investigation search key
partyMobilePhoneNumber PII QE:equality Yes Secondary investigation search key
partyName PII (low sensitivity) Plaintext No Display name
partyType Operational Plaintext No customer, employee, service_account
partyDateOfBirth PII Plaintext No ISO 8601 date; optional
partyNationality Operational Plaintext No ISO 3166-1 alpha-2

customerAuthenticationAssessment

BIAN origin: Customer Authentication (SD-91)

Customer Authentication (SD-91) owns the login credential lifecycle: credential storage, role assignment, and account access state. It is explicitly separate from Party Authentication (SD-16), which covers identity verification events.

Why this collection is needed: BIAN SD-16 (Party Authentication) is an identity verification SD, not a credential store. Placing credentials in SD-16 mixes two concerns. SD-91 is the correct home for login state, bcrypt hashes, and application roles.

Field naming convention: Every field carries the full customerAuthentication prefix:

customerAuthenticationEmailAddress
|                    |    |
|                    |    AttributeType: EmailAddress
|                    Qualifier: (none - direct attribute of the CR)
ControlRecord: customerAuthentication
Field Classification Mode Searchable Notes
customerAuthenticationInstanceReference PK Plaintext Yes (unique index) UUID; used as JWT sub
partyInstanceReference FK Plaintext Yes (index) Links to party (SD-13)
customerAuthenticationEmailAddress PII QE:equality Yes Login lookup; never stored plaintext in Atlas
customerAuthenticationCredentialHash Security credential bcrypt hash No 12-round bcrypt; plaintext never persisted
customerAuthenticationUserRole Operational Plaintext Yes (index) customer, level1_analyst, level2_investigator, security_auditor, merchant_officer
customerAuthenticationUserName Display Plaintext No Denormalized from party for JWT name claim
customerAuthenticationLoginDomain Operational Plaintext No local or msentra (v2)
customerAuthenticationAccountStatus Operational Plaintext No active, suspended

partyAuthenticationAssessment

BIAN origin: Party Authentication (SD-16)

Party Authentication (SD-16) covers the formal assessment of a party's identity during a verification event. In this architecture it is a lightweight plaintext collection for identity verification events. Credentials and roles live in SD-91 (customerAuthenticationAssessment).

This collection is structurally present but not exercised in v1-v3. It holds the SD-16 boundary as a clean extension point for formal identity verification flows (e.g., document verification, biometric check) in v4+.

Field Classification Mode Searchable Notes
partyAuthenticationInstanceReference PK Plaintext Yes (unique index) UUID
partyInstanceReference FK Plaintext Yes (index) Links to party (SD-13)
partyAuthenticationLoginDomain Operational Plaintext No local or msentra
partyAuthenticationAccountStatus Operational Plaintext No active, suspended

authenticationDomain

BIAN origin: Party Authentication (SD-16), support collection

Stores the enabled authentication domains for the system (local credentials, Microsoft Entra, etc.). This is not a BIAN Control Record; it is a support collection that holds the configuration for each login domain referenced by partyAuthenticationAssessment and customerAuthenticationAssessment.

Field Classification Mode Searchable Notes
partyAuthenticationDomainInstanceReference PK Plaintext Yes (unique index) UUID
partyAuthenticationDomainName Operational Plaintext Yes (unique index) local, msentra, bigid
partyAuthenticationDomainDisplayName Operational Plaintext No Human-readable name shown in the login UI
partyAuthenticationDomainType Operational Plaintext No local, oidc, saml
partyAuthenticationDomainFlowType Operational Plaintext No client_credentials, authorization_code, saml, oidc
partyAuthenticationDomainEnabled Operational Plaintext No Boolean; disabled domains reject login attempts
partyAuthenticationDomainAlertMessage Operational Plaintext No Optional message shown when a domain is unavailable
partyAuthenticationDomainConfiguration Operational Plaintext No Provider config: tenant ID, client ID, OIDC/SAML endpoints

customerAgreementProcedure

BIAN origin: Customer Agreement (SD-53)

Customer Agreement maintains the product and service contracts between the bank and its customers. In strict BIAN, PII is not stored here; the agreement holds a reference to the party and the business terms of the relationship.

Two-step lookup pattern: Fraud investigators search for a customer by email or phone via the party collection (SD-13) first. The partyInstanceReference returned is then used to look up the agreement. customerAgreementReference (account reference) uses QE:equality for direct account-reference searches.

Base fields (all QE tiers):

Field Classification Mode Searchable Notes
customerAgreementInstanceReference PK Plaintext Yes (unique index) UUID
partyInstanceReference FK Plaintext Yes (index) Links to party (SD-13); enables two-step lookup
customerAgreementReference Account reference QE:equality Yes Bank account number equivalent; investigator search key
customerSegment Operational Plaintext No retail, premium, corporate, sme
customerAgreementStatus Operational Plaintext No initiated, agreed, active, amended, suspended, dormant, closed
customerAgreementEnrollmentDate Operational Plaintext No Date the customer enrolled
customerAgreementPreferredLanguage Operational Plaintext No ISO 639-1 language code
customerAgreementPreferredPaymentCardReference FK Plaintext No Link to saved card UUID (v4 recurring payment)

Sensitive fields (Level 2 only, QE:none inline):

The following high-sensitivity fields are stored inline in customerAgreementProcedure. Access control is cryptographic: the Level 1 QE client's encryptedFieldsMap omits these fields, so the driver returns them as Binary ciphertext. Only the Level 2 QE client (activated by an escalation token) auto-decrypts them.

Field Classification Mode Searchable Notes
customerAgreementResidentialAddress PII (high) QE:none No Full address object: street, city, postal code, country
governmentIdentificationReference PII (high) QE:none No National ID or passport reference
customerAgreementRiskNotes Internal sensitive QE:none No Analyst notes; never exposed to Level 1

BQ:Step sub-document β€” KYC Check (ch-06, schemaVersion 3):

BIAN SD-53 allows BQ:Step (Behavior Qualifier: Step) sub-documents to record discrete procedural outcomes within the agreement lifecycle. The KYC Check is one such step: it records the outcome and reference of the identity verification performed during onboarding. All fields carry the customerAgreementKycCheck prefix to comply with the BIAN BQ naming convention.

Field Classification Mode Notes
customerAgreementKycCheck.customerAgreementKycCheckStatus Compliance Plaintext initiated, verified, rejected, expired (BIAN status vocabulary)
customerAgreementKycCheck.customerAgreementKycCheckCompletedDate Compliance Plaintext ISO 8601 date of verification completion
customerAgreementKycCheck.customerAgreementKycCheckReference Compliance Plaintext External verification service reference ID
customerAgreementKycCheck.customerAgreementKycCheckNotes Internal Plaintext Optional compliance notes

PCI DSS alignment: Req 8.1 (identity verification prior to account access) and Req 12.8.3 (third-party verification documentation).


paymentCardManagement

BIAN origin: Payment Card (SD-88)

Payment Card manages the lifecycle of payment instruments issued to customers: issuance, activation, blocking, and the link between a card and the customer's agreement.

Why this collection is needed: A transaction references a card; a card references a customer agreement. This collection is that link. For the recurring payment mandate (v4), it holds the consent timestamp and mandate status required by PCI DSS Req 3.1 and 3.7.

Field Classification Mode Searchable Notes
paymentCardInstanceReference PK Plaintext Yes (unique index) UUID
customerAgreementInstanceReference FK Plaintext Yes (index) Links to customerAgreementProcedure (SD-53)
paymentCardReference Card token (not CHD) Plaintext Yes (standard index) Surrogate for the PAN (Primary Account Number); not CHD (Cardholder Data) under PCI DSS v4.0
paymentCardExpirationDate CHD QE:none No MM/YY format; CHD when stored alongside a card reference
paymentCardMaskedPanDisplay Display only Plaintext No ****-****-****-1234; last 4 digits only, permitted by PCI DSS
paymentCardNetwork Operational Plaintext No VISA, MASTERCARD, AMEX, ELO
paymentCardStatus Operational Plaintext No issued, active, pending_activation, blocked, suspended, revoked, expired
paymentCardIssuanceDateTime Operational Plaintext No Date the card was issued
paymentCardIsPreferred Operational Plaintext No true when saved as preferred payment method
paymentCardConsentDateTime Legal record Plaintext No Recorded at save-card time; required by PCI DSS Req 3.1
paymentCardMandateStatus Operational Plaintext No active, cancelled, expired; drives Req 3.7 purge logic
paymentCardMandateExpiryDate Operational Plaintext No Auto-purge trigger for Req 3.7
CVV / PIN SAD (Sensitive Authentication Data) Never stored n/a Prohibited at all API endpoints

cardTransactionLog

BIAN origin: Card Transaction (SD-254)

Card Transaction records and manages individual card payment events. Each document represents one payment attempt: amount, channel, merchant, status, and the encrypted account reference linking it to the customer.

Why this collection is needed: This is the primary event log of the payment flow. The account reference uses QE:equality so investigators can find all transactions for a given customer without that value leaving the application in plaintext.

Base fields (all QE tiers):

Field Classification Mode Searchable Notes
cardTransactionInstanceReference PK Plaintext Yes (unique index) UUID
cardTransactionExternalReference External ID (optional) Plaintext No Reference from the payment processor or gateway
cardTransactionAccountReference Account reference / PII QE:equality Yes Fraud investigation search key
paymentCardReference Card token (not CHD) Plaintext Yes (standard index) Same token as in paymentCardManagement; plaintext by PCI DSS design
cardTransactionAmount Operational Plaintext No Object: { amount: number, currency: string } (ISO 4217)
cardTransactionDateTime Operational Plaintext No Indexed for time-range queries
cardTransactionStatus Operational Plaintext No authorized, declined, pending, settled, disputed
cardTransactionChannel Operational Plaintext No online, pos, contactless, atm
cardTransactionMerchantName Operational Plaintext No Merchant display name
cardTransactionMerchantCategoryCode Operational Plaintext No MCC (Merchant Category Code); used for fraud auto-trigger logic
cardTransactionInitiationType Operational Plaintext No customerInitiated or merchantInitiated (v4 recurring payment)
cardTransactionMaskedPanDisplay Display only Plaintext No Display copy; same value as paymentCardMaskedPanDisplay

Sensitive fields (Level 2 only, QE:none inline):

The following fields are stored inline in cardTransactionLog. Raw gateway payloads and processor metadata contain authorisation codes and network identifiers that are not accessible to Level 1 roles. The same QE tier pattern applies: Level 1 client omits them from its encryptedFieldsMap; Level 2 client auto-decrypts.

Field Classification Mode Searchable Notes
rawGatewayPayload Sensitive operational QE:none No Full JSON payload from the payment gateway
processorTransactionMetadata Sensitive operational QE:none No Internal processor response metadata

fraudDiagnosisCase

BIAN origin: Fraud Diagnosis (SD-83)

Fraud Diagnosis manages the lifecycle of fraud investigation cases: creation, assignment, escalation, resolution, and the audit trail. It is a distinct SD from Card Transaction: a transaction records what happened in the payment network; a fraud case records what analysts do in response.

Why no QE: This collection contains no PII or CHD directly. The cardTransactionInstanceReference and customerAgreementInstanceReference fields are UUIDs; without the source collection and the corresponding DEK, they reveal nothing.

MongoDB patterns applied:

  • Extended Reference Pattern: transactionSnapshot embeds the stable display fields from cardTransactionLog so the fraud investigation view requires only one collection query instead of three.
  • Unbounded Array fix: The audit trail is stored in fraudDiagnosisCaseEvents (separate collection), not as an embedded array. This eliminates the risk of document growth without bound as cases accumulate events over weeks.
Field Classification Mode Notes
fraudDiagnosisInstanceReference PK Plaintext UUID; primary key
fraudDiagnosisCaseReference Human-readable ID Plaintext FD-2026-001234; displayed in the investigation UI
cardTransactionInstanceReference FK Plaintext UUID pointing to cardTransactionLog (SD-254)
customerAgreementInstanceReference FK Plaintext UUID pointing to customerAgreementProcedure (SD-53)
transactionSnapshot Extended Reference Plaintext Embedded display fields from cardTransactionLog: amount, merchant, datetime, status, masked PAN
fraudDiagnosisCaseStatus Operational Plaintext open, under_review, escalated, resolved_cleared, resolved_fraud, closed
fraudDiagnosisCaseSeverity Operational Plaintext low, medium, high, critical
fraudDiagnosisRequestDateTime Operational Plaintext Timestamp when the case was opened
fraudDiagnosisCaseClosingDateTime Operational (optional) Plaintext Timestamp when the case was resolved or closed
fraudDiagnosisAnalystInstanceReference FK (optional) Plaintext FK to customerAuthenticationAssessment; L1 analyst assigned to the case
fraudDiagnosisInvestigatorInstanceReference FK (optional) Plaintext FK to customerAuthenticationAssessment; L2 investigator who approved escalation
fraudDiagnosisAssessment Operational Plaintext Embedded object: riskIndicators[], fraudDiagnosisScore (0-100), fraudDiagnosisConclusion
fraudDiagnosisEscalationRecord Operational (optional) Plaintext Populated when status becomes escalated: escalation datetime, reason, actor FKs
fraudDiagnosisCaseNotes Operational (optional) Plaintext Analyst notes; visible to L1/L2/Auditor
fraudDiagnosisCustomerSubjectNotes Operational (optional) Plaintext Customer-facing notes visible in the transaction detail view
fraudDiagnosisResolutionRecord Operational (optional) Plaintext Populated on close: datetime, outcome (cleared, confirmed_fraud, referred), notes, resolver FK
agentDraftDiagnosis AI output (v3) Plaintext Draft risk summary: summary, recommended action, confidence score (0-100), supporting evidence
schemaVersion Operational Plaintext Schema Versioning Pattern: 1 in v1, incremented per iteration

fraudDiagnosisCaseEvents

BIAN origin: Fraud Diagnosis (SD-83), audit trail partition

Append-only audit events for each fraud case. Separated from fraudDiagnosisCase to avoid an unbounded array growing inside the case document as investigators work the case over days or weeks.

Why compliance requires this collection to be always readable: The audit trail must survive DEK rotation or revocation. Since this collection has no QE fields, it remains readable regardless of key lifecycle events.

Field Classification Mode Notes
fraudDiagnosisInstanceReference FK Plaintext Links event to its case; part of the compound index
actionDateTime Operational Plaintext Event timestamp; part of the compound index for ordered retrieval
actionType Operational Plaintext case_opened, assigned, note_added, field_accessed, escalated, ai_review, resolved, closed
performedByInstanceReference Operational Plaintext FK to customerAuthenticationAssessment; system for automated events
performedByRole Operational Plaintext Role at time of action
actionDetails Audit detail Plaintext Free-form JSON payload specific to the action type
schemaVersion Operational Plaintext Schema Versioning Pattern

customerCreditRatingState

BIAN origin: Customer Credit Rating (SD-60)

Customer Credit Rating manages the creditworthiness and compliance risk classification state for a customer. In this demo it is used to store High-Risk Person and Counterparty (HRPC) classification flags, which indicate whether a customer account requires enhanced scrutiny under fraud, AML, KYC, sanctions, or EDD policies.

Why this collection is needed: HRPC flags are a structured risk indicator used during L1 triage and L2 investigation. Storing them in a dedicated collection keeps risk classification data separate from operational PII, supports lookup by customerAgreementReference, and makes the data visible in Atlas Data Explorer without exposing sensitive customer records.

No QE: The collection contains no PII, no CHD, and no sensitive authentication data. Classification labels, category codes, and source references are compliance metadata only.

Relationship to HRPC framework: The HRPC (High-Risk Person and Counterparty) framework defines nine risk categories: PEP (Politically Exposed Person), SIP (Special Interest Person), HNWI (High Net Worth Individual), UBO (Ultimate Beneficial Owner), terrorism-linked, high-risk jurisdiction, sanctioned, prior fraud history, and suspicious transaction patterns. Each record in customerCreditRatingState maps one customer account to one or more of these categories. See tmp/wiki/HRPC.md for the full framework definition.

Field Classification Mode Notes
customerCreditRatingInstanceReference PK Plaintext UUID, primary key
customerAgreementReference FK Plaintext Links to customerAgreementProcedure.customerAgreementReference (QE:equality field in parent collection)
customerCreditRatingClassificationFlags Compliance Plaintext Array of HRPC flag objects (see sub-fields below)
bianServiceDomain Operational Plaintext Always 'Customer Credit Rating'
bianControlRecordType Operational Plaintext Always 'CustomerCreditRatingState'
recordCreatedDateTime Operational Plaintext ISO 8601 timestamp
recordUpdatedDateTime Operational Plaintext ISO 8601 timestamp
schemaVersion Operational Plaintext Schema Versioning Pattern

Flag sub-fields (customerCreditRatingClassificationFlags[]):

Sub-field Description
customerCreditRatingClassificationCategory HRPC category code: pep, sip, hnwi, ubo, terrorism_linked, high_risk_jurisdiction, sanctioned, financial_fraud_history, suspicious_transaction_patterns
customerCreditRatingClassificationLevel Risk level: low, medium, high
customerCreditRatingClassificationLabel Human-readable label for UI display
customerCreditRatingClassificationDescription Narrative explanation of why the flag was raised
customerCreditRatingClassificationDetectedDateTime Date when the flag was first detected
customerCreditRatingClassificationSource Detection source: kyc_periodic_review, transaction_monitoring, correspondent_screening, aml_due_diligence, internal_case_history
customerCreditRatingReviewRequiredIndicator Boolean: whether active EDD or review is pending

consentAgreement

BIAN origin: Information Provider Operations (SD-36)

Stores customer consent grants that authorise a Third-Party Provider (TPP) to access scoped data via the Open Banking API. Each consent grant specifies which data scopes the customer authorised, for which TPP, under which regulatory framework (PSD2 (Payment Services Directive 2), CDR (Consumer Data Right), FAPI (Financial-grade API)).

v3 stub: The schema and indexes are provisioned. OAuth 2.0 / FAPI token validation is v4+ scope. In v3 the table is empty; Leafy Bank Open Finance integration populates it in v4.

Field Classification Mode Notes
consentAgreementInstanceReference PK Plaintext UUID
partyInstanceReference FK Plaintext Links to party (SD-13)
customerAgreementInstanceReference FK Plaintext Links to customerAgreementProcedure (SD-53)
consentRecipientIdentifier Operational Plaintext TPP identifier (e.g., 'leafy-bank-open-finance')
consentScopeGrants Legal Plaintext Array of granted scopes: read:profile, read:transactions, read:card_metadata, read:fraud_status, write:fraud_case
consentStatus Operational Plaintext active, expired, revoked, pending
consentGrantDateTime Legal Plaintext Timestamp of customer consent
consentExpiryDateTime Legal Plaintext Mandatory consent expiry (PSD2: max 90 days)
consentRegulationFramework Legal Plaintext PSD2, CDR, FAPI, internal

consentAccessLog

BIAN origin: Information Provider Operations (SD-36)

Append-only log of every TPP field access made under a consent grant. Follows the same pattern as fraudDiagnosisCaseEvents. Satisfies PCI DSS Req 10 and PSD2 access-log obligations simultaneously.

Field Classification Mode Notes
consentAccessLogInstanceReference PK Plaintext UUID
consentAgreementInstanceReference FK Plaintext Links to the consent grant
accessDateTime Operational Plaintext Indexed descending for time-range queries
accessorIdentifier Operational Plaintext TPP system identifier
accessedScopes Operational Plaintext Which scopes were exercised in this call
accessedResourceType Operational Plaintext e.g., 'cardTransactionLog'
accessOutcome Operational Plaintext granted, denied, partial

merchantAgreementProcedure

BIAN origin: Merchant Agreement (SD-89)

Merchant Agreement manages the lifecycle of agreements between the bank (as acquirer) and merchants who accept card payments. In BIAN, SD-89 is the acquiring-side counterpart to SD-53 (Customer Agreement). Each merchant onboarding flow creates a merchantAgreementProcedure record that goes through KYB (Know Your Business) review before activation.

Why this collection is needed: PCI DSS Req 12.8 requires documented agreements with all service providers and merchants that can affect card data security. The merchantAgreementProcedure collection is the canonical record of that agreement β€” reviewed by a merchant_officer before any merchant can process payments.

KYB Check as BQ:Step: The KYB outcome is stored as a merchantAgreementKybCheck BQ:Step sub-document within the agreement, following the same pattern as customerAgreementKycCheck in SD-53. See ADR-009 in docs/engineering-proposal.md.

Base fields:

Field Classification Mode Searchable Notes
merchantAgreementInstanceReference PK Plaintext Yes (unique index) UUID
partyInstanceReference FK Plaintext Yes (index) Links to party (SD-13); merchant as a party
merchantAgreementStatus Operational Plaintext Yes (index) initiated, under_review, agreed, active, amended, suspended, rejected, closed
merchantAgreementName Operational Plaintext No Business name of the merchant
merchantAgreementLegalEntityReference Compliance Plaintext No Tax ID / Company registration reference
merchantAgreementMerchantCategoryCode Operational Plaintext No MCC code (ISO 18245); used for fraud risk tier
merchantAgreementCountry Operational Plaintext No ISO 3166-1 alpha-2 country code
merchantAgreementInitiatedDateTime Operational Plaintext No ISO 8601 timestamp of application submission
merchantReviewedDateTime Operational Plaintext No ISO 8601 timestamp of officer review
merchantReviewNote Operational Plaintext No Optional note written by the reviewing officer
merchantAgreementReviewedByReference FK Plaintext No FK to customerAuthenticationAssessment of the reviewing merchant_officer
bianServiceDomain Operational Plaintext No Always 'Merchant Agreement'
bianControlRecordType Operational Plaintext No Always 'MerchantAgreementControlRecord'
schemaVersion Operational Plaintext No Schema Versioning Pattern; current: 2

BQ:Step sub-document β€” KYB Check (ch-06, schemaVersion 2):

BIAN BQ:Step documents the KYB verification step within the merchant agreement lifecycle. All fields carry the merchantAgreementKybCheck prefix to comply with the BIAN BQ naming convention.

Field Classification Mode Notes
merchantAgreementKybCheck.merchantAgreementKybCheckStatus Compliance Plaintext initiated, verified, rejected, expired (BIAN status vocabulary)
merchantAgreementKybCheck.merchantAgreementKybCheckCompletedDate Compliance Plaintext ISO 8601 date of KYB verification completion
merchantAgreementKybCheck.merchantAgreementKybCheckReference Compliance Plaintext External verification service reference ID
merchantAgreementKybCheck.merchantAgreementKybCheckNotes Internal Plaintext Optional compliance notes

PCI DSS alignment: Req 12.8.2 (written agreements with third parties), Req 12.8.3 (due diligence before engagement), Req 12.8.5 (monitoring and periodic review).


4. πŸ”— Collection Relationships

erDiagram
    party {
        string partyInstanceReference PK
        string partyEmailAddress "QE:equality"
        string partyMobilePhoneNumber "QE:equality"
        string partyMobilePhoneNumberDigest "blind index"
        string partyName
        string partyType
        object partyPostalAddress "QE:none"
        string partyDateOfBirth "QE:none"
    }
    customerAuthenticationAssessment {
        string customerAuthenticationInstanceReference PK
        string partyInstanceReference FK
        string customerAuthenticationEmailAddress "QE:equality"
        string customerAuthenticationCredentialHash "bcrypt"
        string customerAuthenticationUserRole
        string customerAuthenticationLoginDomain
    }
    partyAuthenticationAssessment {
        string partyAuthenticationInstanceReference PK
        string partyInstanceReference FK
        string partyAuthenticationLoginDomain
        string partyAuthenticationAccountStatus
    }
    authenticationDomain {
        string partyAuthenticationDomainInstanceReference PK
        string partyAuthenticationDomainName
        string partyAuthenticationDomainType
        boolean partyAuthenticationDomainEnabled
    }
    customerAgreementProcedure {
        string customerAgreementInstanceReference PK
        string partyInstanceReference FK
        string customerAgreementReference "QE:equality"
        string customerSegment
        string customerAgreementStatus
        object customerAgreementResidentialAddress "QE:none inline"
        string governmentIdentificationReference "QE:none inline"
        string customerAgreementRiskNotes "QE:none inline"
    }
    paymentCardManagement {
        string paymentCardInstanceReference PK
        string customerAgreementInstanceReference FK
        string paymentCardReference
        string paymentCardExpirationDate "QE:none"
        string paymentCardBin "first 6, non-CHD"
        string paymentCardLast4 "last 4, non-CHD"
    }
    cardIssuerVault {
        string issuedCardInstanceReference PK
        string paymentCardReference FK "join via port"
        string paymentCardInstanceReference FK
        string paymentCardNumber "full PAN, QE:equality"
        string cardServiceCode "QE:equality"
        string cardIssuerCvkKeyId "CVK/DEK ref"
    }
    cardTransactionLog {
        string cardTransactionInstanceReference PK
        string paymentCardReference FK
        string merchantAgreementInstanceReference FK
        string cardTransactionAccountReference "QE:equality"
        string cardTransactionType
        number cardTransactionAmount
        object fee "v18 commission"
        string cardTransactionStatus
        object rawGatewayPayload "QE:none inline"
        object processorTransactionMetadata "QE:none inline"
    }
    fraudDiagnosisCase {
        string fraudDiagnosisInstanceReference PK
        string cardTransactionInstanceReference FK
        string customerAgreementInstanceReference FK
        object transactionSnapshot
        string fraudDiagnosisAnalystInstanceReference FK
        string fraudDiagnosisInvestigatorInstanceReference FK
        string fraudDiagnosisCaseStatus
    }
    fraudDiagnosisCaseEvents {
        string fraudDiagnosisInstanceReference FK
        date actionDateTime
        string actionType
        string performedByInstanceReference
    }
    customerCreditRatingState {
        string customerCreditRatingInstanceReference PK
        string customerAgreementReference FK
        array customerCreditRatingClassificationFlags
        string bianServiceDomain
        string bianControlRecordType
    }
    consentAgreement {
        string consentAgreementInstanceReference PK
        string partyInstanceReference FK
        string customerAgreementInstanceReference FK
        string consentRecipientIdentifier
        array consentScopeGrants
        string consentStatus
    }
    consentAccessLog {
        string consentAccessLogInstanceReference PK
        string consentAgreementInstanceReference FK
        date accessDateTime
        string accessorIdentifier
        array accessedScopes
    }
    merchantAgreementProcedure {
        string merchantAgreementInstanceReference PK
        string partyInstanceReference FK
        string merchantAgreementStatus
        string merchantAgreementName
        string merchantAgreementLegalEntityReference
        string merchantAgreementMerchantCategoryCode
        object merchantAgreementKybCheck "BQ:Step"
    }

    party ||--o{ customerAuthenticationAssessment : "authenticates as"
    party ||--o{ partyAuthenticationAssessment : "identity verified via"
    party ||--o{ customerAgreementProcedure : "party of"
    party ||--o{ merchantAgreementProcedure : "merchant party"
    partyAuthenticationAssessment }o--|| authenticationDomain : "uses domain"
    customerAuthenticationAssessment }o--|| authenticationDomain : "uses domain"
    customerAgreementProcedure ||--o{ paymentCardManagement : "owns"
    paymentCardManagement ||--o{ cardTransactionLog : "used in"
    paymentCardManagement ||--o| cardIssuerVault : "issuer PAN vault (module-owned, via port)"
    cardTransactionLog ||--o{ fraudDiagnosisCase : "triggers"
    customerAgreementProcedure ||--o{ fraudDiagnosisCase : "subject of"
    fraudDiagnosisCase ||--o{ fraudDiagnosisCaseEvents : "audit trail"
    customerAuthenticationAssessment ||--o{ fraudDiagnosisCase : "assigned as analyst or investigator"
    customerAuthenticationAssessment ||--o{ fraudDiagnosisCaseEvents : "performed action"
    customerAuthenticationAssessment ||--o{ merchantAgreementProcedure : "reviewed by officer"
    customerAgreementProcedure ||--o| customerCreditRatingState : "HRPC risk profile"
    party ||--o{ consentAgreement : "grants consent"
    customerAgreementProcedure ||--o{ consentAgreement : "consent scope"
    consentAgreement ||--o{ consentAccessLog : "access log"

    partyEnrolledCredential {
        string credentialId PK
        string customerAuthenticationInstanceReference FK
        string publicKeyPem "public only"
        string alg "ES256 / RS256"
        number signCount
        string status
    }
    partyBackchannelAuthentication {
        string authReqId PK
        string clientId
        string customerAuthenticationInstanceReference FK
        string challenge
        string status
        date expiresAt
    }
    payoutAccountArrangement {
        string payoutAccountInstanceReference PK
        string payoutAccountIban "QE:none"
        string payoutAccountRoutingNumber "QE:none"
        string payoutAccountCurrency
        object payoutAccountBalance
    }
    paymentExecutionProcedure {
        string paymentExecutionInstanceReference PK
        string payoutAccountInstanceReference FK
        string counterpartyArrangementReference FK
        string destinationIban "QE:none"
        string rail "ach / sepa / swift"
        string paymentExecutionStatus
    }
    counterpartyArrangement {
        string counterpartyArrangementReference PK
        string partyInstanceReference FK
        string counterpartyDisplayHint "masked"
    }
    externalProviderArrangement {
        string externalProviderArrangementInstanceReference PK
        string externalProviderArrangementType "capability"
        string externalProviderArrangementStatus
    }

    customerAuthenticationAssessment ||--o{ partyEnrolledCredential : "enrolls"
    customerAuthenticationAssessment ||--o{ partyBackchannelAuthentication : "authenticates via CIBA"
    partyEnrolledCredential ||--o{ partyBackchannelAuthentication : "signs challenge"
    party ||--o{ payoutAccountArrangement : "owns payout account"
    payoutAccountArrangement ||--o{ paymentExecutionProcedure : "debited by"
    counterpartyArrangement ||--o{ paymentExecutionProcedure : "credited to"
    party ||--o{ counterpartyArrangement : "registers beneficiary"
Loading

Reading the diagram:

  • ||--||: one-to-one relationship
  • ||--o{: one-to-many relationship
  • ||--o|: one-to-zero-or-one relationship (not every customer has HRPC flags)
  • }o--||: zero-or-many to exactly-one relationship
  • party is the root identity entity; all other SDs reference it via partyInstanceReference (BIAN SD-13 compliance).
  • customerCreditRatingState links via customerAgreementReference (the QE:equality field), not the UUID, because HRPC lookups happen by account reference during investigation search.
  • partyAuthenticationAssessment and authenticationDomain are SD-16 collections: the former records identity verification events per party, the latter holds the enabled login domain configurations.
  • customerAuthenticationAssessment links to fraudDiagnosisCase via two separate FK fields (fraudDiagnosisAnalystInstanceReference, fraudDiagnosisInvestigatorInstanceReference); the single relationship line represents both.
  • merchantAgreementProcedure (SD-89) links to party for the merchant entity and to customerAuthenticationAssessment for the reviewing merchant_officer. It carries a merchantAgreementKybCheck BQ:Step sub-document for the KYB outcome.
  • consentAgreement and consentAccessLog are v3 stubs for the Open Banking / Open Finance extension (SD-36).
  • cardIssuerVault (BIAN Card Administration) is module-owned by the built-in card-issuer: it holds the full PAN and cardServiceCode (both QE:equality) as the issuer CDE. The core paymentCardManagement stays descoped (token + BIN + last4); the masked PAN is derived on the fly, no longer persisted. A card links to its funding account through payoutAccountArrangement (Funding Account port) and to the vault only through the Card Reference port (the core never reads the vault). The issuer key (CVK) lives wrapped in the key vault (KMS β†’ DEK β†’ CVK); the CVV is derived per card and never stored (SAD).

5. πŸ”‘ Encryption Architecture

Key hierarchy

AWS KMS
  |
  |  CMK (Customer Master Key)
  |  -- held by the customer in their AWS account
  |  -- MongoDB has zero access
  |
  v
Fastify API (application process)
  |
  |  DEKs (Data Encryption Keys) -- unwrapped from CMK at request time
  |  Lookup tier   : QE:equality fields (searchable PII) -- available to any authenticated request
  |  Sensitive tier: QE:none fields (high-sensitivity PII, CHD, bank data) -- Level 2 only
  |  (multi-DEK: one key alias per protected field group; see table below)
  |
  v
MongoDB Atlas
  -- stores encrypted fields as binary ciphertext (BSON subtype 06)
  -- never receives a DEK or CMK
  -- cannot decrypt any QE-protected field

BSON (Binary JSON) subtype 06 is the wire format MongoDB uses for QE-encrypted fields.

Two trust tiers, a multi-DEK key set

The key vault holds 16 DEKs total, split into two trust tiers. Each protected field group has its own DEK alias (envelope encryption: every DEK is individually wrapped by the CMK), so a single field group can be rotated or revoked without touching the others. The tier, not a separate collection, is the compliance boundary.

Lookup tier (5 DEKs, available to any authenticated request) protect the QE:equality search keys:

Key alias Protects
DEK-party-email party.partyEmailAddress
DEK-party-phone party.partyMobilePhoneNumber
DEK-auth-email customerAuthenticationAssessment.customerAuthenticationEmailAddress (login lookup)
DEK-customer-account-ref customerAgreementProcedure.customerAgreementReference
DEK-tx-account-ref cardTransactionLog.cardTransactionAccountReference

Sensitive tier (11 DEKs, Level 2 only) protect QE:none fields (high-sensitivity PII, CHD, and GDPR/PSD2 bank data):

Key alias Protects
DEK-customer-address customerAgreementProcedure.customerAgreementResidentialAddress
DEK-customer-gov-id customerAgreementProcedure.governmentIdentificationReference
DEK-customer-risk-notes customerAgreementProcedure.customerAgreementRiskNotes
DEK-tx-raw-payload cardTransactionLog.rawGatewayPayload
DEK-tx-processor-meta cardTransactionLog.processorTransactionMetadata
DEK-card-expiry paymentCardManagement.paymentCardExpirationDate
DEK-party-address party.partyPostalAddress (GDPR)
DEK-party-dob party.partyDateOfBirth (GDPR)
DEK-payout-iban payoutAccountArrangement.payoutAccountIban (GDPR/PSD2, not PCI)
DEK-payout-routing payoutAccountArrangement.payoutAccountRoutingNumber (GDPR/PSD2)
DEK-exec-dest-iban paymentExecutionProcedure.destinationIban (GDPR/PSD2)

A Level 1 Analyst can search by email/phone/account reference (lookup tier) and retrieve transaction records, but cannot read residential address, government ID, raw gateway payload, card expiry, or bank IBAN/routing. The middleware never passes the sensitive-tier DEKs to the QE client for Level 1 requests. IBAN/routing are bank data under GDPR Art. 32 / PSD2, not PCI-scoped card data. See tmp/wiki/Encryption.md for the authoritative field-by-field map.

🚫 What revoking the CMK does

Revoking or deleting the CMK in AWS KMS immediately renders all QE-protected fields unreadable from every system (Atlas, the API, and any backup). This is a cryptographic guarantee, not a policy control.


6. πŸͺͺ Role-Based Access Model

Access control evolved from a fixed role→permission table into a data-driven, default-deny ACL (ADR-030). Permissions are resource × action pairs (view, viewSensitive, manage, investigate) stored in the role collection and resolved at runtime via GET /api/v1/acl/effective. The role is still carried in the JWT, but the permissions are not: they are looked up server-side per request, so a caller cannot self-elevate. The table below summarises the effective capabilities of each seeded role.

Role Search QE:equality Read QE:none (sensitive) Open fraud cases Escalate View HRPC profile View audit log Scope
customer Own records only No No No No No Own profile, cards, transfers; can apply for a merchant account
level1_analyst Yes No Yes Yes (triggers escalation) Summary flags only Current case only Fraud triage
level2_investigator Yes Yes (with escalation token) Yes Approves escalation Full detail Full Fraud investigation
security_auditor Read-only Read-only No No Read-only Full, all cases Read-only oversight
merchant_officer No No No No No No Reviews and approves/rejects merchant applications (KYB review) via the shared gateway routes
operations_officer No No No No No No Global card (SD-88) and payout-account (SD-66) administration through the built-in modules; reveals full PAN / IBAN on demand (eye icon, ephemeral, audited) and the derived CVV; never sees SAD persisted; 409 when the capability is external
manager No No No No No Configuration audit SD-193 platform / integration-hub administration; separation of duties, no CHD access

A system_admin role also exists for Integration Hub administration (registering providers, routing configuration); it is a platform role rather than a login persona.

Merchant unification (v23, ADR-042): there is no separate /merchant/* surface. A merchant authenticates as an OAuth client and is treated as a first-class caller on the same shared capability modules as first-party users, via a dualAuth middleware that accepts EITHER a first-party session JWT (HS256) OR a merchant OAuth Bearer (RS256). Each route then authorises by RBAC action (session) or scope (merchant).

v2 escalation workflow:

  1. L1 Analyst calls POST /fraud/:id/escalate with an escalation reason. Case status changes to escalated. An escalated event is written to fraudDiagnosisCaseEvents.
  2. L2 Investigator calls POST /fraud/:id/escalate/approve. The backend calls generateToken(caseId, 'level2_investigator') and returns a short-lived UUID token (4-hour TTL). A field_accessed event is written to the audit trail.
  3. L2 Investigator includes the token in X-Escalation-Token header on subsequent requests. The RBAC middleware validates the token and activates the Level 2 QE client pool, which auto-decrypts the QE:none fields stored inline in customerAgreementProcedure and cardTransactionLog.
  4. Every sensitive field access writes an additional field_accessed event, satisfying PCI DSS Req 10.

HRPC check: Available to L1 and L2 via GET /fraud/hrpc/check?accountRef=<ref>. Queries customerCreditRatingState (SD-60) collection. L1 sees flag summary; L2 sees full flag detail including source and review status.


7. πŸ”Œ Provider Groups (Integration Hub, SD-193)

The platform integrates external services through an Integration Hub (BIAN SD-193 External Provider Arrangements). Every outbound capability (fraud scoring, sanctions screening, card authorization, account information, etc.) is modeled as a Provider Group (capability), which has two interchangeable views:

  • an internal Module (a built-in engine that runs against the platform's own data), and
  • zero or more external Providers (vendors registered in the hub).

Calls are routed by dispatchProvider(...) with an internal-first policy: the built-in Module answers on the hot path, and an external provider substitutes it only when configured, without changing the caller. This taxonomy (Provider / Capability / Module) and the canonical registry live in backend/src/modules/provider/config/capabilities.ts. Every dispatch is logged in externalProviderArrangementActionLog and emits an audit event to the timeseries businessProcessEvent / complianceProcessEvent collections (see Β§2).

The table below groups the system by Provider Group, listing the data collections each one relates to and the standards and regulations it is aligned with. The Module engines never receive cardholder data on the bus: card data is decrypted just in time only by the card-issuer path (see Β§5), and public-key material only for identity.

Provider Group (capability) Related collections BIAN Service Domain Standards and regulations
Card Issuer (card-issuer) paymentCardManagement, cardEtokenProcedure (token vault, SD-57), paymentCardRegistry, cardTransactionLog; module-owned: cardIssuerVault (full PAN + service code, QE:equality) + the CVK in the key vault SD-88 Payment Card, SD Card Administration (vault) PCI DSS Req 3.2 / 3.3 (CVV derived per card, never stored; full PAN only in the module-owned vault, core keeps token + BIN + last4), ISO/IEC 7812 (PAN), EMV
Card Authorization (card-authorization) cardAuthorizationRecord, cardTransactionLog SD-15 Card Authorization PCI DSS Req 3.3 / 4, ISO 8583 (authorization messaging)
Fraud Detection (fds) cardTransactionLog, complianceProcessEvent SD-63 Fraud Evaluation PCI DSS Req 10, PSD2 Transaction Risk Analysis (RTS Art. 18)
AML Monitoring (aml) cardTransactionLog, complianceProcessEvent SD-99 Suspicious Activity Analysis EU AMLD 4/5/6, FATF Recommendations, US BSA
HRP / Sanctions (hrp) party, complianceProcessEvent SD-13 Party Data Management OFAC / EU / UN sanctions lists, PEP screening, FATF
KYC / Identity (kyc) customerAgreementProcedure, party SD-53 Customer Agreement KYC / CDD (AMLD), GDPR, eIDAS
KYB / Business (kyb) merchantAgreementProcedure SD-89 Merchant Relations KYB / CDD (AMLD), GDPR, PCI DSS Req 12.8
Credit Bureau (credit-bureau) customerCreditRatingState SD-60 Customer Credit Rating US FCRA, GDPR Art. 22 (automated profiling)
Account Information / AISP (account-information) payoutAccountArrangement, balanceCreditLog SD-36 Account Information PSD2 AISP, GDPR Art. 32, ISO 20022
Payment Initiation / PISP (payment-initiation) paymentExecutionProcedure, payoutAccountArrangement, counterpartyArrangement, recurringMandateProcedure SD-65 / SD-66 Payment Execution / Initiation PSD2 PISP, SCA (dynamic linking deferred), SEPA / ACH / SWIFT scheme rules, ISO 20022
Currency Exchange (currency-exchange) paymentExecutionProcedure (FX amounts) Foreign Exchange ISO 4217 (currency codes)
Merchant Notifications (generic) merchantWebhookDeliveryLog, externalProviderArrangement SD-193 External Provider Arrangements REST, signed webhooks (HMAC), OpenID CIBA ping/push

The Provider Modules are adapters: the "Related collections" column lists the domain-owned collections whose data each capability reads, scores, or writes, since the provider adapters themselves persist only through externalProviderArrangement* and the event audit collections. All groups share the same Integration Hub registry (externalProviderArrangement, capabilityModuleConfiguration) and the v7 event-driven audit trail.


8. 🧩 Collections by Core Module

Section 7 maps collections by Provider Group (the outbound-capability view). This section maps them by core PSP module (the code module that owns the domain and its data), so together they give the complete picture of collection usage. A collection can appear under more than one module and under Β§7: that is expected and intentional, it shows which collections are shared across modules and provider groups. Ownership below follows the backend/src/modules/<module> model definitions; the "Also reads" column notes the main cross-module and provider-group readers.

Core module Owned collections BIAN Service Domains Also read by
identity (auth, OAuth/OIDC, CIBA, ACL) party, customerAuthenticationAssessment, partyAuthenticationAssessment, authenticationDomain, partyAuthorizationCode, partyIssuedToken, partyAuthenticationKey, partyAuthConsent, partyEnrolledCredential, partyBackchannelAuthentication, role, consentAgreement, consentAccessLog, counterpartyArrangement SD-13, SD-16, SD-91, SD-36 party read by customer, fraud, gateway and the HRP/KYC provider groups; counterpartyArrangement used by gateway (PIS) transfers
customer (agreement, cards) customerAgreementProcedure, paymentCardManagement, paymentCardRegistry SD-53, SD-88 read by transaction, fraud, gateway; paymentCardManagement/registry by the Card Issuer provider group; customerAgreementProcedure by KYC
transaction (card transactions) cardTransactionLog SD-254 read by fraud, gateway; scored by the Fraud Detection, AML and Card Authorization provider groups
fraud (diagnosis, HRPC) fraudDiagnosisCase, fraudDiagnosisCaseEvents, fraudDiagnosisCustomerQuestion, customerCreditRatingState SD-83, SD-60 customerCreditRatingState read by the HRP/Sanctions provider group and the HRPC check
gateway (payments, checkout, merchant, bank transfers) merchantAgreementProcedure, paymentOrderProcedure, checkoutSessionLog, paymentLinkRecord, cardAuthorizationRecord, cardEtokenProcedure, payoutAccountArrangement, paymentExecutionProcedure, recurringMandateProcedure, balanceCreditLog, merchantWebhookDeliveryLog SD-89, SD-64, SD-65, SD-66, SD-57, SD-15 merchantAgreementProcedure read by KYB and by resolveOAuthClient (identity); payoutAccountArrangement/balanceCreditLog by the Account Information (AIS) group; paymentExecutionProcedure/counterpartyArrangement by the Payment Initiation (PIS) group; cardAuthorizationRecord by Card Authorization
provider (Integration Hub, event audit) externalProviderArrangement, externalProviderArrangementActionLog, capabilityModuleConfiguration, businessProcessEvent, complianceProcessEvent SD-193, audit (timeseries) every provider group logs dispatches to externalProviderArrangementActionLog; every module emits to businessProcessEvent / complianceProcessEvent
notification notification support read by the frontends
admin / domain / system (no owned domain collections) operational admin operates across collections (setup, webhook inspector, runners); domain manages module config; system exposes health / simulator / raw reads

Collection classification (ADR-043). Beyond core-module ownership, every collection is classified as Core PSP (business domain, never replaced by a provider), Integration / EDA infrastructure (externalProviderArrangement*, capabilityModuleConfiguration, businessProcessEvent, complianceProcessEvent, domainEvent), or module-owned (replaceable / extractable). The built-in card-issuer is the first module with owned data: the CVK (issuer key, in the key vault) and the cardIssuerVault collection (BIAN Card Administration) holding the full PAN and cardServiceCode (both QE:equality). It is the reference example of PCI scope containment: disabling the module or routing the capability to an external provider leaves the PSP core descoped for the PAN (core keeps token + BIN + last4). Cross-frontier reads use the Card Reference, Funding Account, and Card-by-account ports, never direct collection access. The core owns paymentCardManagement / paymentCardRegistry; the module depends on them by port.

Cross-cutting collections (touched by many modules and provider groups): party, cardTransactionLog, customerAgreementProcedure, customerCreditRatingState, externalProviderArrangement/ActionLog, and the businessProcessEvent / complianceProcessEvent audit streams. Seeing the same collection under several modules here and under Β§7 is the point: it visualises where data ownership (module) and data consumption (other modules, provider groups) diverge.


KYC and KYB Built-in Module Administration (Operations Officer)

Two identity/onboarding built-in modules now have a production-grade administration surface, each split into Configuration (built-in engine policy incl. the decision mode) and Administration (the review workbench). Roles:

  • Merchant Officer (merchant_officer, SD-89, merchants:[view,manage]) owns the KYB DECISION (review, approve/reject/suspend) via the merchant review flow.
  • Operations Officer (operations_officer) gains customers:[view,manage] + merchants:[view,manage] for KYC/KYB DATA administration (review + correct KYC/KYB records and beneficial owners). Data correction only, NOT the KYB decision (SoD, PCI Req 7). Both emit the same compliance events; neither replaces the other.

Beneficial owners (UBO)

merchantAgreementProcedure now carries a bounded merchantBeneficialOwners sub-document array (SD-89 + SD-13, FATF/4th AMLD): 1..N owners, numeric ownership participation, exactly one primary/controlling owner (= the legacy merchantOwnerPartyReference derived pointer), sum at most 100, hard cap 25. Owner PII (name, DOB, address, gov ID) lives in the party record (QE tiers), never duplicated in the embed (GDPR Art. 5 minimization). Reverse lookup "which merchants does a party own" is a multikey index; every shareholder sees the merchant. Demo seed adds a 60/40, a 50/30/20 and an 80/15 free-float cap table.

Two-layer KYB risk

KYB screens the BUSINESS (entity layer: merchantAgreementKybCheck gains businessRiskLevel / sanctionsResult / adverseMediaResult / screeningProviderRef) AND every controlling person (owner layer, composed by reference from each UBO KYC verdict). Starting a KYB process fans out on the event bus to kyb_business + hrp_sanctions + aml_monitoring (entity) and kyc_identity per owner; a KybVerificationSaga aggregates, persists the verdict, and resolves per the module decision mode. Every process is reconstructable from its single correlationId (bus milestones + provider wire calls).

Decision mode

Per-module policy in capabilityModuleConfiguration.moduleConfig: manual (officer decides), automated (auto-resolve within thresholds), assisted (rules/AI recommend, human confirms, HITL). Unset defaults to manual (fail-safe). Sanctions/PEP hit never auto-approves. Seeded KYC=automated, KYB=manual.

API (defers to Swagger /doc for the full contract)

  • KYC administration (customer module): GET /customer/kyc, GET/PATCH /customer/:partyRef/kyc, POST /customer/:partyRef/kyc/re-screen, GET /customer/:partyRef/kyc/process.
  • KYB administration (gateway module): GET/PATCH /merchants/:id/kyb, GET/POST/PATCH/DELETE /merchants/:id/kyb/owners[/:partyRef], GET /merchants/:id/kyb/process.

Frontend

Bespoke two-tab pages system/admin/modules/{kyc,kyb} (Configuration + Administration), deep-linkable detail routes with structured verdict, owners/shareholders panel (sum meter, one-primary), field info tooltips, correlated process timeline, responsive mobile-to-wide. An Owners tab is also on the merchant shell (system/merchant/[merchantId]/owners).

Added 2026-07-24. Version 2.5.0. See docs/ for the source of truth.

Clone this wiki locally