Skip to content

About

The open-source ATM stack — from COBOL core to quantum-safe crypto. ISO 8583 switch (Go) + COBOL core banking + roadmap to ML fraud scoring and post-quantum crypto.

Resources

Stars

2 stars

Watchers

0 watching

Forks

Repository files navigation

QuantumTeller

The open-source ATM stack — from COBOL core to quantum-safe crypto.

QuantumTeller is a working, end-to-end bank payment infrastructure across three eras in one repo: the COBOL batch core that still moves most of the world's money, a modern real-time switch with an ML fraud engine and an AI analyst copilot, and the post-quantum security layer the industry is migrating to right now. One docker compose up, and a real ISO 8583 cash withdrawal flows through all of it.

┌──────────┐  ISO 8583 0200/0210    ┌─────────────────────────────┐   exec    ┌─────────────┐
│   ATM    │ ─────────────────────▶ │           SWITCH            │ ────────▶ │    CORE     │
│simulator │   TLS 1.3 hybrid PQ    │ dedupe → PIN (HSM, 9564-4)  │  RC/BAL   │ COBOL — or  │
│ PIN pad  │ ◀───────────────────── │ → fraud (rules+ML) → route  │ ◀──────── │ its Go port │
└──────────┘   X25519MLKEM768       └──────────┬──────────────────┘           └─────────────┘
                                               │                            accounts.dat
                                    ┌──────────┴──────────┐
                                    │  hash-chained audit │◀── every txn, case, analyst action
                                    │  analyst dashboard  │◀── copilot narratives, human-in-the-loop
                                    └─────────────────────┘

Why this exists

  • 95% of ATM transactions still touch a COBOL system, and ISO 8583 carries virtually every card transaction on earth — yet almost nobody can see a full working example of the pipeline. This is that example.
  • US and India flavors: USD (840) and INR (356), Visa-style and RuPay-style test ranges — the two largest ATM markets.
  • The 2026 problems, not just the 1976 ones: real-time ML fraud decisioning with explainability, LLM-assisted case narratives that are audit-safe, "harvest now, decrypt later"-proof channel crypto, and a provably-equivalent COBOL→Go modernization.

Disclaimer: educational infrastructure simulation. Not PCI-DSS scoped, not for production money. All PANs, PINs, and keys are test values.

Quick start

Docker

docker compose up -d bank        # switch + COBOL core + dashboard
open http://localhost:8080       # fraud-operations dashboard

# Withdraw $250 (PIN verified as an ISO 9564 format-4 AES block)
docker compose run --rm atm -pan 4000001234567890 -op withdraw -amount 250.00 -currency USD -pin 1234

# India RuPay balance inquiry
docker compose run --rm atm -pan 6076620000000021 -op balance -currency INR -pin 4321

# Trip the fraud engine (amount rule -> RC=59, core never sees it)
docker compose run --rm atm -pan 4000001234567890 -op withdraw -amount 1500.00 -currency USD -pin 1234

Local (Go 1.24+, GnuCOBOL 3.x)

make build && make test   # unit tests
make e2e                  # 51-check full-stack suite (COBOL core, Go core, PQ-TLS)
make characterize         # prove COBOL == Go core on 400 randomized txns

Example session:

================ QUANTUMTELLER ATM ================
  TERMINAL : ATM00001
  CHANNEL  : TLS1.3 PQ-HYBRID (X25519MLKEM768)
  CARD     : 400000******7890
  OPERATION: WITHDRAW USD 250.00
  MTI      : 0200 -> 0210
  STATUS   : RC=00 APPROVED
  BALANCE  : USD 1000.00
===================================================

The stack, layer by layer

Layer Component What it proves
Legacy corebank/acctops.cbl — GnuCOBOL core: account master file, debit/credit/inquiry, RC 00/12/13/14/51/62/96 Fluency in what banks actually run
Legacy internal/iso8583 — dependency-free ISO 8583:1987 codec (bitmap, LLVAR/LLLVAR, framing), fully unit-tested Wire-protocol literacy
Modern cmd/switch — TCP switch: STAN idempotency cache, PIN gate, fraud gate, core routing, slog structured logs, graceful shutdown, per-call core timeouts Production switch engineering
Modern internal/fraud — declarative rules (JSON DSL) + logistic risk model trained offline (scripts/train_model.py, holdout precision 1.00 / recall 0.87) exported to portable weights; pure-Go microsecond inference; sliding-window velocity ML in the hot path, done explainably
Trust internal/audit — hash-chained append-only JSONL; tampering breaks the chain; cmd/auditverify checks it; the switch refuses to run unaudited What "immutable audit trail" actually means
Trust internal/copilot + internal/cases — deterministic narratives from rule hits & feature contributions, optionally enriched by Claude (ANTHROPIC_API_KEY); LLM never decides, humans resolve cases on the dashboard AI-assisted ops with governance
Quantum internal/pqtls — TLS 1.3 restricted to X25519MLKEM768 (hybrid ML-KEM-768, FIPS 203); classical-only clients are rejected (tested) Post-quantum migration, the real profile
Quantum internal/hsm — ISO 9564-1:2017 format-4 AES PIN blocks (randomized, PAN-bound), simulated HSM verify Payment crypto beyond TLS
Quantum cmd/cryptoscan — crypto-agility inventory: classifies every algorithm reference by PQ risk (NIST IR 8547 spirit) Where every bank's PQC program starts
Modernization internal/corebank + scripts/characterize.sh — Go port of ACCTOPS proven byte-identical to the COBOL binary over 400 randomized transactions incl. malformed input; switch runs either via -corebank The only modernization story auditors accept (docs/modernization.md)

Test cards

PAN Market PIN Balance Status
4000001234567890 US (Visa-style) 1234 $1,250.00 Active
6076620000000021 India (RuPay-style) 4321 ₹50,000.00 Active
4000009999999999 US 9999 $10.00 Active (low balance)
5100005555444433 US 5555 $7,500.00 Blocked

ISO 8583 dialect

Fields 2 (PAN, LLVAR), 3 (processing code: 01=withdrawal 21=deposit 31=balance), 4 (amount, minor units), 7, 11 (STAN), 39 (response code), 41 (terminal), 49 (currency: 840/356), 52 (format-4 PIN block, hex), 54 (balance). ASCII encoding, 4-digit length framing, primary bitmap only — every byte on the wire is readable in the tests. Response codes: 00 approved · 12 invalid txn · 13 invalid amount · 14 no account · 51 insufficient funds · 55 incorrect PIN · 59 suspected fraud · 62 restricted card · 96 system error.

Operating it

./build/switch \
  -addr :8583 -http :8080 \
  -corebank build/acctops        # or build/acctops-go \
  -datadir corebank/data \
  -rules configs/rules.json -pins configs/pins.json \
  -audit audit.jsonl \
  -tls                            # hybrid PQ channel

./build/auditverify audit.jsonl   # verify the tamper-evident chain
./build/cryptoscan -strict .      # fail the build on quantum-vulnerable crypto
python3 scripts/train_model.py    # retrain + re-export fraud model weights

Environment: QT_PIN_KEY (hex AES-128 PIN key; defaults to the published test key), ANTHROPIC_API_KEY + QT_LLM_MODEL (optional copilot enrichment; degrades gracefully to deterministic narratives).

Design notes

  • Fraud sits in front of the core, not behind it — declined-by-risk transactions never touch the ledger, matching real acquirer topology. Velocity state is in-memory per switch instance; a shared store (Redis) is the documented scale-out path.
  • The LLM never decides. Rules + model + human are the decision chain; the copilot only explains, and every explanation carries its deterministic basis. This is the governance posture regulators require.
  • Idempotent retries (STAN+terminal+timestamp dedupe cache) because ATMs retransmit — double-debiting a cash withdrawal is the unforgivable sin of switching.
  • Amount rule is currency-blind (1,000.00 major units regardless of USD/INR) — a deliberate, documented simplification; per-currency thresholds belong in the rules DSL next.
  • Why exec a COBOL binary per transaction? It reproduces batch-core semantics (exclusive data access, CICS-equivalent serialization via switch-side mutex) with zero mainframe dependencies — and it makes the Phase-5 equivalence demonstration honest.
  • Why hybrid PQ, not pure ML-KEM? Defense in depth during migration: breaking the channel requires breaking both X25519 and ML-KEM-768. Signatures stay Ed25519 until ML-DSA lands in the standard library — signatures aren't harvest-now-decrypt-later exposed; key exchange is.

Roadmap

  • Phase 1 — ISO 8583 switch + COBOL core, US & India flavors
  • Phase 2 — fraud engine: rules DSL, trained risk model, velocity windows
  • Phase 3 — hash-chained audit, AI copilot, human-in-the-loop dashboard
  • Phase 4 — hybrid PQ TLS, format-4 PIN blocks, crypto-agility scanner
  • Phase 5 — COBOL→Go port with characterization-proven equivalence
  • Kafka connector for the transaction stream; shared velocity store
  • EBCDIC/BCD codec variants; secondary bitmap
  • ML-DSA certificates when Go stdlib ships FIPS 204
  • Per-currency rule thresholds; model retraining on IEEE-CIS

Contributing

Contributions are welcome — this project is meant to be a shared reference for payments infrastructure, and there is real work to pick up on the roadmap.

  1. Star and fork the repo. Starring helps other engineers find the project; forking gives you your own copy to work in (gh repo fork AravindB98/quantumteller --clone, or the Fork button).
  2. Clone your fork and set up the toolchain — Go 1.24+ and GnuCOBOL 3.x (apt install gnucobol / brew install gnucobol):
    git clone https://github.com/<your-username>/quantumteller.git
    cd quantumteller && make build && make test
  3. Create a topic branch off main:
    git checkout -b feat/per-currency-thresholds
  4. Make your change, with tests. Every package has a _test.go sibling — new behavior needs new tests. If you touch the COBOL core or its Go port, make characterize must still pass (behavioral equivalence is the contract).
  5. Run the full gauntlet locally before pushing — it is exactly what CI runs:
    make test && make e2e && make characterize
  6. Open a pull request with a clear description: what changed, why, and how you tested it. Reference a roadmap item or issue if one exists. One logical change per PR keeps review fast.
  7. Expect review feedback. Style follows gofmt (enforced by CI) and the design principles in Design notes — in particular: the LLM never decides, PANs are always masked in logs, and nothing bypasses the audit chain.

Good first contributions: per-currency rule thresholds, EBCDIC/BCD codec variants, a Kafka connector for the transaction stream, or additional fraud-rule types with tests.

License

MIT © Aravind Balaji


🧒 Explain Like I'm 5

Everything an ATM needs to work, as open source: the message language ATMs speak (ISO 8583), a real core-banking ledger written in COBOL (the 60-year-old language banks still run on), a modern Go switch connecting them — plus a plan for encryption that even future quantum computers can't crack.

🧰 Tech Stack

Go (ISO 8583 switch) · COBOL core banking · post-quantum cryptography roadmap · containerized dev environment

🌍 Real-Life Applications

  • Fintech engineers learning how real ATM/banking rails actually work
  • Banks prototyping quantum-safe crypto migration paths
  • A sandbox for payments testing without commercial licenses

About

The open-source ATM stack — from COBOL core to quantum-safe crypto. ISO 8583 switch (Go) + COBOL core banking + roadmap to ML fraud scoring and post-quantum crypto.

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages