Pain001 Architecture#
A map of the codebase for new contributors and maintainers. The goal is that anyone can navigate, extend, and reason about Pain001 without prior context — reducing reliance on any single person.
The pipeline#
Generation flows left to right; each stage is a separate, testable unit:
input file / list[dict]
│ data/loader.py (dispatch by extension)
▼
csv/ · db/ · json/ · parquet/ (per-format loaders + streaming)
│ → list[dict] payment rows
▼
validation/ (schema_validator, csv/db validators,
│ iban/bic, charset, schemes)
▼
xml/message_registry.py (per-version field preparation)
│
▼
xml/generate_xml.py (Jinja2 render of templates/<type>/)
│
▼
xml/validate_via_xsd.py (mandatory XSD validation, defusedxml)
│
▼
xml/write_xml_to_file.py ──or── generate_xml_string() (in-memory)
Module map#
Area |
Module(s) |
Responsibility |
|---|---|---|
Entry points |
|
CLI command group ( |
Input |
|
Unified extension-dispatch loader and per-format readers (batch + streaming) |
Validation |
|
|
Generation |
|
|
Templates |
|
|
Migration |
|
Map payment data between pain.001 versions via YAML mappings |
Parsers / builders |
|
Read bank responses (status reports, statements) into dicts; |
API |
|
FastAPI app (routes mounted under |
MCP |
|
FastMCP server (stdio) exposing generation/validation as tools, the XSD set as resources, and a guided prompt — thin adapters over the core, taking inline rows |
LSP |
|
A dependency-free CSV diagnostic engine (IBAN/BIC/currency/charset, required columns) and a |
Config |
|
Layered configuration (CLI args, file, profiles) |
Observability |
|
Structured JSON logging with PII redaction; metric callbacks + OpenTelemetry trace context |
Security |
|
Path-traversal-safe path validation (CWE-22) |
Async |
|
|
Shared |
|
Version, valid message types, paths; the exception hierarchy |
Key design decisions#
Registry-driven generation. Each message type has a
MessageDefinitioninxml/message_registry.pyand a bundledtemplates/<type>/directory. There is no per-version code duplication.Money is
Decimalend to end.NbOfTxsandCtrlSumare always computed from the rows, never trusted from input.XSD validation is mandatory and routed through
defusedxml(XXE / entity-expansion safe). Output that does not validate is never written as a success.Scheme validation is a pluggable layer on top of XSD: a
ValidationProfilereturns structured, per-rowSchemeViolations.Coverage is enforced at 98%; only entry-point guards and genuinely defensive barriers are excluded (
# pragma: no cover), never padded with fake tests.
Extension points#
Add a message type: drop a
templates/<type>/bundle (template, XSD, metadata) and register aMessageDefinitioninxml/message_registry.py. Add a version entry toconstants.py.Add a scheme profile: subclass
ValidationProfileinvalidation/schemes.py, register it inPROFILES, and add remediation text toREMEDIATIONS. (SeeSCHEMES.md.)Add an input format: add a loader under the appropriate package and register its extension in
data/loader.py.
Where to look first#
Runnable, per-feature examples:
examples/.Editor extension client:
editors/vscode/.Scheme rule catalogue:
SCHEMES.md.Release process:
RELEASING.md.Contributing:
CONTRIBUTING.md.