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
└─────────────────────┘
- 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.
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 1234make 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 txnsExample 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
===================================================
| 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) |
| 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 |
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.
./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 weightsEnvironment: 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).
- 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.
- 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
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.
- 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). - 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
- Create a topic branch off
main:git checkout -b feat/per-currency-thresholds
- Make your change, with tests. Every package has a
_test.gosibling — new behavior needs new tests. If you touch the COBOL core or its Go port,make characterizemust still pass (behavioral equivalence is the contract). - Run the full gauntlet locally before pushing — it is exactly what
CI runs:
make test && make e2e && make characterize
- 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.
- 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.
MIT © Aravind Balaji
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.
Go (ISO 8583 switch) · COBOL core banking · post-quantum cryptography roadmap · containerized dev environment
- 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