-
Notifications
You must be signed in to change notification settings - Fork 0
Architecture
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.
- System Architecture
- Data Architecture
- Sensitive Fields and Encryption by Collection
- Collection Relationships
- Encryption Architecture
- Role-Based Access Model
- Provider Groups (Integration Hub, SD-193)
- Collections by Core Module
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.
+-----------------------------------------+ +-----------------------------+ +-----------------------------+
| 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.
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 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
-
consentAgreementandconsentAccessLogcollections (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
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.
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 |
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.
| 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
schemaVersionfield (Schema Versioning Pattern) to enable zero-downtime migrations.fraudDiagnosisCaseEventsandconsentAccessLogseparate audit trails from their parent documents (Unbounded Array anti-pattern fix).fraudDiagnosisCaseembeds atransactionSnapshot(Extended Reference Pattern) for single-query fraud investigation display.customerCreditRatingStateis a static lookup collection seeded once and queried read-only at investigation time (Lookup Table Pattern). PII separation betweenpartyandcustomerAgreementProcedureimplements the BIAN cross-SD reference pattern: agreement searches resolve via a two-step QE lookup (party by email/phone, then agreement bypartyInstanceReference).
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.
| 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. |
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 |
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
|
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
|
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 |
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).
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 |
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 |
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:
transactionSnapshotembeds the stable display fields fromcardTransactionLogso 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 |
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 |
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
customerCreditRatingStatemaps one customer account to one or more of these categories. Seetmp/wiki/HRPC.mdfor 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 |
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
|
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
|
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).
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"
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 -
partyis the root identity entity; all other SDs reference it viapartyInstanceReference(BIAN SD-13 compliance). -
customerCreditRatingStatelinks viacustomerAgreementReference(the QE:equality field), not the UUID, because HRPC lookups happen by account reference during investigation search. -
partyAuthenticationAssessmentandauthenticationDomainare SD-16 collections: the former records identity verification events per party, the latter holds the enabled login domain configurations. -
customerAuthenticationAssessmentlinks tofraudDiagnosisCasevia two separate FK fields (fraudDiagnosisAnalystInstanceReference,fraudDiagnosisInvestigatorInstanceReference); the single relationship line represents both. -
merchantAgreementProcedure(SD-89) links topartyfor the merchant entity and tocustomerAuthenticationAssessmentfor the reviewingmerchant_officer. It carries amerchantAgreementKybCheckBQ:Step sub-document for the KYB outcome. -
consentAgreementandconsentAccessLogare v3 stubs for the Open Banking / Open Finance extension (SD-36). -
cardIssuerVault(BIAN Card Administration) is module-owned by the built-incard-issuer: it holds the full PAN andcardServiceCode(both QE:equality) as the issuer CDE. The corepaymentCardManagementstays descoped (token + BIN + last4); the masked PAN is derived on the fly, no longer persisted. A card links to its funding account throughpayoutAccountArrangement(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).
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.
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.
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.
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:
- L1 Analyst calls
POST /fraud/:id/escalatewith an escalation reason. Case status changes toescalated. Anescalatedevent is written tofraudDiagnosisCaseEvents. - L2 Investigator calls
POST /fraud/:id/escalate/approve. The backend callsgenerateToken(caseId, 'level2_investigator')and returns a short-lived UUID token (4-hour TTL). Afield_accessedevent is written to the audit trail. - L2 Investigator includes the token in
X-Escalation-Tokenheader 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 incustomerAgreementProcedureandcardTransactionLog. - Every sensitive field access writes an additional
field_accessedevent, 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.
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.
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.
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) gainscustomers:[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.
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.
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).
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.
- 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.
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.