← Back
Home › Systems › Finance and trading

interbank-messaging

secure financial messaging between banks: store-and-forward network, PKI/HSM, MT-to-MX translation, gpi end-to-end tracking, sanctions pre-screening

SWIFT FIN/ISO 20022, SWIFT gpi

Open the interbank-messaging pageinterbank-messaging Durable job queuedistributed: Persisted tasks that workers lease for a visibility timeout, ack on success and retry with backoff; at-least-once execution that survives worker and queue crashes.›Exactly once processingdistributed: Idempotent producers plus transactional offset+output commits for effectively-once resultsMessage sequence numbersdistributed: A per-stream counter on every message lets the receiver detect loss, duplicates and reordering, ack cheaply and resume after reconnect with "send from N".›Partitioned commit logdistributed: A topic split into replicated append-only partitions: key hash keeps per-key order, offsets give position, and a high watermark limits readers to fully replicated records.›Transactional outboxdistributed: Write the event as a row in the same local transaction as the state change; a relay publishes it later at-least-once, so state and events never diverge.›Event sourcingfinance: Persist state changes as an append-only stream of immutable events per entity; current state is a fold over them, guarded by expected-version appends.›Idempotency keysfinance: Client-chosen unique key per logical operation, stored with a request fingerprint and the final response, so retries replay the first result instead of acting twice.›Iso 20022 messagesfinance: ISO 20022 XML payment and securities messages (pacs, pain, camt, sese): structure and validationTdl message translationnetworking: Translate between bit-packed tactical data link messages (Link 16 J-series, VMF) via field-level mapping, scaling and unit conversionAho-Corasick multi-patternmatchingsearch-sort: Find all occurrences of many fixed patterns in one pass over the text, in time independent of the pattern count, using a trie with failure links.›Fuzzy string matchingsearch-sort: Edit distance, Jaro-Winkler and trigram indexes for approximate name and address matchingName transliterationsearch-sort: Convert personal names between scripts (Cyrillic, Arabic, CJK to Latin) with rule tables and variant generation for matchingPhonetic name matchingsearch-sort: Reduce names to phonetic keys (Soundex, Metaphone, Double Metaphone) so differently spelled but similar-sounding names collideDual control approvalsecurity: Four-eyes / maker-checker approval with quorum policies for sensitive operationsECDSA signaturessecurity: The elliptic-curve signature standardised in FIPS 186-5 (P-256) and used by secp256k1 ledgers; 64-byte signatures, but a repeated or biased nonce leaks the private key.›Hardware security module (HSM)security: A tamper-resistant device or service that generates keys, signs, decrypts and wraps with them internally, and releases a key only as ciphertext.›JSON Schema validationsecurity: Compile a JSON Schema once, then check every untrusted document against it, within hard size and depth limits, and report failures as JSON Pointers.›Message authentication codes(HMAC, CMAC, Poly1305, KMAC)security: A keyed short tag proving a message is unmodified and came from a key holder; pick HMAC, CMAC, KMAC or a one-time polynomial MAC by what primitive you already have.›Mutual TLSsecurity: TLS where the client also proves a certificate key, so every connection carries a verified, rotatable machine identity.›Rsa cryptosystemsecurity: RSA key generation, OAEP encryption and PSS/PKCS#1 v1.5 signatures, key sizes and padding pitfallsTamper-evident audit logsecurity: An append-only log whose Merkle root, signed per batch, lets anyone verify an entry is present and history was never rewritten, with log-size proofs.›X509 certificatessecurity: Certificate chains, path validation, SANs, revocation and short-lived issuanceXML Streaming Parserstorage: Tokenize XML into start, text and end events in constant memory, so a few fields can be extracted from huge, broken or hostile documents.›Distributed tracingsystems: Give each request a 128-bit id, record timed spans linked by parent ids, pass the ids in a header across every hop, and join the spans back into a tree afterwards.›Publish subscribe event bussystems: In-process or on-box topic bus decoupling producers and consumers with bounded queuesDead-letter queuedistributed: A side queue for messages that keep failing: after N delivery attempts or a permanent error, park them with the reason so the main queue keeps moving.›Write-ahead logstorage: Append each change to a checksummed sequential log and fsync it before touching data pages; the basis of crash durability in nearly every database.›Two-phase commitdistributed: Atomic commit across shards: participants durably vote yes, then the coordinator logs and broadcasts one decision; blocks if the coordinator is lost.›Consumer offsetsdistributed: Each consumer group stores one integer per partition, the next position to read, so a restarted consumer resumes where it left off with at-least-once delivery.›Event deduplication windowdistributed: Keep the ids of recently accepted events for a time window and drop repeats; memory stays bounded by rotating time buckets instead of per-entry timers.›Fast hash functionsdata-structures: Non-cryptographic hashes built from 64×64→128-bit multiplies and xor-shifts; pick by key size, trust level and whether the hash is persisted.›Leasesdistributed: A lock that expires on its own: bounded-time ownership that survives crashes and partitions, given bounded clock drift and fencing tokens on writes.›Primary-backup replicationdistributed: One node orders all writes and ships them to standbys; the acknowledgement rule (none, one, all) sets what a crash loses, and an external arbiter decides who takes over.›Zero-copy I/Onetworking: Move bytes between files, sockets and NICs by passing page references instead of copying through user space: sendfile, splice, MSG_ZEROCOPY, kTLS.›Exponential backoff with jittersystems: Retry after delays that grow geometrically and are randomized, so failed clients spread out instead of hammering a recovering service in synchronized waves.›Change data capturestorage: Decode the database's own commit log into row-change events in commit order, with a resume position, so any consumer can mirror the data exactly.›Optimistic concurrency controlstorage: Run transactions without locks, record what they read, and validate at commit that nothing changed; fastest when conflicts are rare.›+ 12 moreCRC32C checksumshardware-os: The Castagnoli CRC-32 with dedicated instructions on x86 (SSE4.2) and ARMv8. It is the standard fast integrity check for storage blocks, logs and network frames.›fsync, O_DSYNC and O_DIRECThardware-os: What fsync, fdatasync, O_DSYNC, O_DIRECT and F_FULLFSYNC really guarantee on Linux and macOS, and the exact call sequences for crash-safe files.›Fencing tokensdistributed: A number that grows with every lock grant and is checked by the protected resource, so a paused ex-holder's late write is rejected.›Health checkingsystems: Decide per backend whether to route to it using probes and real-traffic errors, with consecutive-count hysteresis, capped ejection and a panic fallback.›PRNGs (PCG, xoshiro)numerics: Small, fast, statistically strong non-cryptographic generators: PCG32 and xoshiro256**, with seeding, parallel streams and unbiased ranges.›Incremental sync cursorsdistributed: The client keeps one opaque token; each pull returns only the changes after it plus the next token, so sync cost scales with what changed, not with the dataset.›+ 8 moreSIMD programminghardware-os: Process 4-16 lanes per instruction with AVX2, AVX-512 or NEON via Rust std::arch, with runtime dispatch and a portable fallback.›Cursor paginationnetworking: Keyset/opaque-cursor pagination for stable incremental reads from APIs and tables+ 1 more

How it is built

Architecture overview

Interbank messaging is the postal system of finance: a bank sends a structured, authenticated message (a customer credit transfer, a confirmation, a status report) to another bank, and the network guarantees that it arrives once, unaltered, from the sender it claims to come from, and with a record of what happened. It moves instructions, not money; settlement happens elsewhere. The network is a store-and-forward system: a sender hands a message to the network, the network durably stores it, validates it, and delivers it to the receiver when the receiver is connected, with acknowledgements both ways. Around that core sit a security layer (certificates and keys in hardware modules, signatures on every message), a format layer (the older MT messages and the newer ISO 20022 XML messages, with translation between them), a tracking layer (a unique end-to-end reference so a payment can be followed across the banks that handle it) and a screening layer (names checked against sanctions lists before release). The idea that makes it work is that every message is a durable, ordered, identified event: if you can replay the log and each message has one unchanging identity, you can deliver once, track, audit and recover.

Parts:

 bank A back office ─► gateway (mTLS, HSM signs) ─► validate (syntax, schema) ─► screening ─► ingest log
                                                                   │ NAK                    │ hit: hold
                                                                   ▼                         ▼ release by two people
                                                           reject to sender        durable store (replicated)
                                                                                         │ assign sequence no.
                                                              translate MT ↔ MX ◄────────┤
                                                                                         ▼
                                                  delivery queue per receiver ─► gateway ─► bank B back office
                                                           │  retry/backoff, DLQ              │ ACK / status
                                                           ▼                                  ▼
                                               audit log (signed batches)       tracker: UETR → status events

How the parts fit together

Access and security

A participant does not connect from the open internet. It runs a gateway in its own network that talks to the network over a Mutual TLS channel, so both ends prove an identity with X.509 certificates (no page yet; chains, validity and revocation matter more here than in a web service). Every business message is additionally signed by the sending institution, because the channel proves who connected, not who authored a particular message, and a signature is evidence that survives the connection. Signatures use public-key schemes such as ECDSA or RSA (RSA cryptosystem, no page yet); a shorter, symmetric authenticator (Message authentication codes) protects messages between parties that share a key. Private keys never leave a Hardware security module: the gateway sends a digest (Crypto hash SHA-2 and BLAKE3) and the module returns a signature. Keys are moved between modules wrapped under another key (AES key wrap, no page yet); any copy in process memory is overwritten after use (Secure memory zeroization); keys and nonces come from the operating system's generator (CSPRNG, no page yet). Key ceremonies and the release of a new certificate require two people (Dual-control approval, no page yet).

Message formats and validation

The classic format is a text message in numbered blocks with tagged fields of limited length and a restricted character set; the newer one is ISO 20022, a family of XML schemas for payments, cash management and securities (ISO 20022 messages, no page yet). An MX message is far richer: structured party and address data, remittance information, purpose codes. The network validates before it accepts. For MT that is field syntax against a rule table. For MX it is schema validation, done streaming (XML streaming parser) so a large batch file is checked in constant memory and the few fields the router needs (sender, receiver, type, reference) are read without building a tree. APIs next to the messaging channel (status queries, tracker updates) use JSON Schema validation on untrusted documents. Internal service calls can use a compact binary encoding (Protobuf wire format, no page yet, with Varint / LEB128 integers), but the bytes that get signed are the canonical bytes of the business message, never a re-encoding. A rejected message returns a negative acknowledgement with a reason code, and is never stored as accepted.

Translation

While banks move at different speeds, the network carries both formats and translates between them in flight. MT to MX is mostly mapping: a field becomes one or more elements. MX to MT is lossy, since the old format cannot hold what the new one can (field lengths, a smaller character set, unstructured addresses), so translation rules truncate, fold or move data to narrative fields, and the receiver may get a message with less information than the sender wrote. A sound design keeps the original message with the translated one so the loss can be audited. The same field-level mapping problem appears for bit-packed military data links (TDL message translation, no page yet): a table of source field, destination field and conversion rule, with a defined behaviour for fields that do not fit.

Store-and-forward core

The core is a replicated, partitioned, append-only log, the pattern of a Partitioned commit log: a message is appended to the partition of its receiver (a hash of the receiver key keeps order per receiver), stored in Segmented log files, and acknowledged to the sender only after it is durable on enough replicas (Write-ahead log with the flush rules of fsync and direct I/O). Each receiver is a consumer with its own position (Consumer offsets), so a receiver that was offline for hours resumes where it stopped. Delivery uses a Durable job queue: the delivery worker leases a message, sends it, and acknowledges only when the receiver's gateway confirms. If the receiver stays unreachable, the worker backs off (Exponential backoff with jitter); a message that can never be delivered (invalid receiver, repeated rejection) goes to a Dead-letter queue for an operator, not dropped. Each message carries a session sequence number so gaps and repeats are detectable (Message sequence numbers). On the data path, large files avoid copies with Zero-copy I/O, per-record integrity uses CRC32C checksums, and routing keys are hashed with a Fast hash function. The sender gets a delivery notification only when the message is accepted by the receiver's side.

Exactly-once delivery and state

A network cannot deliver "exactly once"; it can deliver at least once and make duplicates harmless, so the effect happens once. This is Exactly-once processing (no page yet) built from smaller blocks:

  • The sender gives each message a unique reference, used as an Idempotency key: a retry with the same reference returns the earlier result and does not create a second message.
  • The receiver side keeps a window of recent identifiers (Event deduplication) and drops repeats.
  • A state change and its outgoing message are written in one local transaction with a Transactional outbox, and a relay publishes from the table; with Change data capture the relay reads the database's own log instead of polling. This avoids the lost-message and the phantom-message cases of "update, then send".
  • Where two stores must commit together (for example the log and the account of a participant), use Two-phase commit; its cost is why most state is instead updated by idempotent events.
  • Message state (queued, delivered, acknowledged) changes under Optimistic concurrency control with a version check, so two workers cannot both mark a message delivered.

Replication and failover

The network runs in more than one operating centre and must survive the loss of one without losing an accepted message. That is Primary-backup replication: one centre orders the log and ships it to the other, and the acknowledgement rule (the other centre has it) decides how much a failover can lose. A Lease bounds how long a node believes it is the primary, Fencing tokens make a demoted primary's late writes fail at the storage layer, and Health checking with hysteresis decides when to fail over so a slow link does not cause flapping. A participant's own gateway is usually duplicated too and the network treats the pair as one identity.

End-to-end tracking

A cross-border payment passes through several banks, each of which can wait, hold or reject it, and the sender used to learn nothing until it was over. The tracking layer gives every payment one unique end-to-end transaction reference (UETR), created by the originating bank and kept unchanged by every later bank. The prowide-core library documents it as mandatory for gpi payment messages, generated as a random UUID, carried in MT block 3 field 121 (Sources). It works like a trace identifier (Distributed tracing with Distributed ID generation): each bank reports status events (accepted, forwarded, credited, rejected) against the reference, and the tracker joins them into one timeline. Internally the tracker is Event sourcing: the status is a fold over the immutable event stream, so a correction is a new event. Banks and their customers read it with Incremental sync cursors (give me everything since this token), protected by a Token bucket per client; analytics over the history (who is slow, which corridors fail) run on Columnar storage with Vectorized execution. The tracker's own telemetry is sampled deterministically by payment reference (Telemetry sampling, no page yet) so all events of one payment are kept or dropped together.

publish-subscribe-event-bus (no page yet) distributes the status events once they are recorded: each hop's report is published on a topic keyed by the payment reference, and the tracker, the customer-facing notification service, sanctions case management and analytics subscribe independently. A slow subscriber cannot hold up the payment path, because the event store remains the source of truth and a subscriber that falls behind reads on from its own position.

Audit and non-repudiation

A dispute asks: did this bank send this message, at this time, with this content? The audit trail answers it. All messages and their lifecycle events are appended to a Tamper-evident audit log: batches are hashed into a Merkle tree (no page yet) whose root is signed with a key held in the HSM, and any entry is proven by its path to a signed root. An optional Trusted timestamping (no page yet) from a third-party authority shows the root existed at a time. For fast signature verification across many roots, Ed25519 signatures are a compact option.

Screening

Payments must not reach sanctioned parties, so names and free text in the message are matched against watch lists before release. The system must find variants: misspelling, reordering, transliteration from another script, and partial matches. The blocks are Name transliteration (no page yet) to a common script, Phonetic name matching (no page yet) and Fuzzy string matching (no page yet) for variant spellings, and Aho-Corasick to find many list entries at once in a long narrative field in one pass. Matching is data parallel and can use SIMD programming for the distance kernels. A hit does not drop the payment: it puts it on hold, and a trained analyst (with a second approver for release) decides, with the decision recorded in the audit log.

Data and control flow

One payment instruction:

  1. The originating bank's back office creates the message with a new UETR and passes it to its gateway.
  2. The gateway signs the message with a key in the HSM and sends it over mutual TLS; the network checks the certificate, the signature and the sender's authority for that message type.
  3. The network validates syntax or schema; a failure returns a negative acknowledgement with a reason, and nothing is stored.
  4. Screening runs on names and narrative; a hit holds the message, a clean message proceeds.
  5. The message is appended to the replicated log (with a sequence number) and the sender gets a positive acknowledgement: from this moment the network owns delivery.
  6. If the receiver uses the other format, the translator produces the translated message and keeps the original.
  7. The delivery worker leases the message, sends it to the receiver's gateway, and on the receiver's acknowledgement marks it delivered; failures back off and retry.
  8. Status events with the UETR go to the tracker; the lifecycle events go to the audit log. Most of the latency is the durable write and the replication, plus the screening on the slowest messages.

Recovery after a centre failure:

  1. Health checks mark the primary dead; the lease expires.
  2. The standby takes over with a new epoch (fencing token); the storage layer rejects writes with the old one.
  3. Gateways reconnect; each side resends from its last acknowledged sequence number.
  4. Deduplication drops anything that was delivered but not acknowledged; the delivery queue resumes.
  5. Messages that were in flight and not durable are known from the sequence gap and re-sent by the sender.

Scaling

  1. Message rate. The core scales by partitioning the log by receiver; add partitions and consumers. Within a partition, batch appends and flush per batch (fsync and direct I/O).
  2. Screening latency. The slowest stage per message. Levers: normalise and index lists once, exact-match shortcuts for repeat customers, parallel match kernels, risk-based routing to the heavy matcher.
  3. Peak days. Cut-offs and end-of-day windows create bursts. Levers: queue depth as a buffer, per-participant rate limits (Token bucket), priority classes for urgent messages.
  4. Tracker queries. Read-heavy; serve from a materialised view and use cursors instead of polling.
  5. Geography. Latency between centres sets the cost of synchronous replication; the lever is where the standby sits and which acknowledgement rule you pay for.
  6. Format migration. Running two formats doubles test cases; invest in a translation test corpus before volume.

Failure modes

FailureSymptomCauseFix
Duplicate deliveryreceiver processes a payment twiceretry without an idempotency key, or lost acknowledgementunique reference, dedup window at the receiver
Lost messagesender sees ACK, receiver never gets itacknowledged before the durable writeacknowledge only after replicated append
Split braintwo centres both accept messagesno lease or fencinglease with timeout, epoch checked at storage
Silent truncationdata missing after MX to MT translationfield length or charset limitstranslation rules with warnings, keep the original, receiver can request it
Replay of a captured messageold instruction accepted againno sequence or timestamp checkper-session sequence numbers and duplicate detection of references
Key compromiseforged messages or loss of trustkey outside HSM, weak custodykeys only in HSM, dual control, rotation and revocation process
Poison messageone message blocks a queuerepeated failing deliveryattempt limit, DLQ, operator review
Tracker gapa bank's status missing from the timelinestatus event lost or lateoutbox plus idempotent events; mark as unknown, not as failed
Screening false negativessanctioned name passesscript or spelling variant not normalisedtransliteration, phonetic and fuzzy keys, test with known variants
Screening backlogpayments held for hoursmatch latency, analyst queuetuned thresholds, risk-based routing, staffing, never auto-release
Audit gapcannot prove a past messagelog not signed, retention too shortsigned batches, timestamping, retention policy

Real implementations

The large networks (SWIFT FIN and ISO 20022 services, gpi) are proprietary. Open projects cover message models and sessions.

namewhat it isrepolicencenotes
Prowide CoreJava model and parser for SWIFT MT (FIN) messages, builders, MT/JSON/XML conversion, BIC and IBAN validation; includes UETR supportprowide/prowide-coreApache-2.0validation and translation are in the vendor's commercial offerings
Prowide ISO 20022business model and parser for ISO 20022 messagesprowide/prowide-iso20022Apache-2.0companion to Prowide Core
QuickFIX/Jmessaging engine for FIX sessions: sequence numbers, resend and persistencequickfix-j/quickfixj—the same session pattern in securities trading; see FIX protocol

Sources

Similar systems

Share the most building blocks with this one.