Data handling#

Pain001 is a library and a set of commands that run on your own infrastructure. There is no hosted Pain001 service, no account and no telemetry. This page says exactly when the software opens a network connection, so a reviewer can check each statement against the code.

What Pain001 does not do#

  • No hosted service. The project runs no server that receives your payment files. Generating, validating and checking files happen in your process.

  • No telemetry. Nothing reports usage, errors or file contents to the project. The only tracing support is the opt-in OpenTelemetry integration described below, which sends spans only to an endpoint you configure.

  • No schema downloads. XSD validation uses the schemas bundled in the package. The lxml parser runs with entity resolution, DTD loading and network access switched off (pain001/xml/validate_via_xsd.py).

When Pain001 opens a network connection#

Each case needs an optional extra, an explicit command or explicit configuration. None is on by default.

Case

What connects, and to where

Needs

pain001 upload

An SFTP connection to the host you name, after its host key is checked and before any credential is sent (pain001/transport/sftp.py)

the upload extra and the command

pain001 download

The same SFTP session, to fetch replies from the host you name (ADR 0013)

the upload extra and the command

pain001 serve

Listens for HTTP requests (it does not connect out). Without PAIN001_API_KEY it refuses to bind anything but loopback unless you pass --insecure (pain001/cli/_serve.py)

the api extra and the command

REST job store or rate limiter on Redis

Connects to the Redis URL you set in PAIN001_JOB_STORE_URL, or with PAIN001_RATE_LIMIT_BACKEND=redis to PAIN001_RATE_LIMIT_REDIS_URL (job_store.py, ratelimit.py)

the redis extra and that setting

OpenTelemetry tracing

Exports spans over OTLP/HTTP to the endpoint in the standard OTEL_EXPORTER_OTLP_* variables (pain001/observability/otel.py)

the otel extra, OTEL_ENABLED and an exporter endpoint

The MCP server (pain001 mcp) talks to its client over standard input and output; it opens no network port and makes no outbound connection (pain001/mcp/server.py). MCP and REST never fetch or send files over SFTP: download is a command-line and library feature only (ADR 0013).

Data at rest#

  • Pain001 writes only what you ask for: generated XML, reports, and files download saves to the directory you name.

  • pain001 download keeps a local SQLite ledger (file mode 0600) of the remote paths it fetched and where it saved them. The ledger holds no file contents and no credentials.

  • The REST API keeps job state in memory by default, or on disk or in Redis when you configure it to.

  • Reading a .gpg or .asc file (the gpg extra) decrypts it in memory, then writes the plaintext to a temporary file created with mode 0600, in /dev/shm on Linux when it is writable and in the system temporary directory otherwise, so the inner loader can read it. The file is unlinked as soon as that read ends (with --streaming, when the last chunk has been read), including on error; it is not overwritten first. With PAIN001_GPG_KEYFILE and no PAIN001_GPG_HOMEDIR, the key is imported into a throwaway keyring directory under /tmp that is removed afterwards (pain001/plugins/builtins_gpg.py).

  • Structured logs redact IBAN, BIC and name fields by default (pain001/logging_schema/redaction.py). In the library and CLI, schema and XSD validation errors carry the rule and the field path, not the value; scheme-rule findings quote the rejected code, currency or amount, and the structural BIC check quotes the BIC it rejects.

  • Row errors in REST responses mask IBAN, BIC, account and name values (first and last four characters, or [REDACTED] for names), with the same rules as the log redactor (pain001/api/_redact.py).

How to check this yourself#

# Network clients appear only in the opt-in transport, API and tracing code
grep -rlE 'import (socket|paramiko|redis)|from (paramiko|redis)|urlopen|httpx|aiohttp|OTLPSpanExporter' pain001/

The expected result is files under pain001/transport/, pain001/api/ and pain001/observability/ only.