Ithamar: bank statements, proven row by row

Drop in an Indian bank statement PDF. Ithamar reads every row and checks it against the statement's own running balance. A row that holds is proven. A row that does not is explained, repaired only when the repair is earned, or handed to you. It all happens in this tab; nothing is uploaded.

The seal of the sample statement below, drawn from its own 23 balances. Violet slices are rows Ithamar repaired. The magenta slip is the one row it refused to invent.

23 rows19 proven3 repaired1 left for youClosing balance 4,04,878.87 matches

How Ithamar proves a statement

Every row obeys one line of arithmetic.

The balance above, plus what came in, minus what went out, equals the balance printed. Ithamar walks that line down the page the way an auditor runs a ruler down a ledger. When it holds, the row is ticked as proven. Nothing is guessed.

Row 4 does not add up.

₹64.27 was read as money coming in. The balances say it went out. Move it to the debit column and this row closes, and so does the next. A missing debit of ₹128.54 would also balance it, but only by coincidence, so that alternative is logged and set aside.

One digit, read wrong.

The debit came out as 42,701.59. The balances on either side need 42,706.59: a single 1 that should be a 6. That is the kind of mistake text extraction actually makes, so it is the only kind of amount Ithamar will correct.

One bad cell, two breaks.

When a balance itself is misread, its row breaks and so does the row below, because the next row starts from the wrong figure. Ithamar recognises the pattern and corrects the one cell. Both rows close. The corrected row is marked repaired, not proven: those are different claims.

A row is missing. Ithamar will not invent it.

Between the 16th and the 18th, ₹1,76,090.10 arrived and no row records it. A missing row can explain any break, which is exactly why it proves nothing. The gap and its amount are handed to you, printed VOID the way a cheque marks a copy.

Carried forward.

The closing balance matches. Nineteen rows are proven as printed, three are repaired with every change logged, and one is left for a person. A tool that quietly smooths that last row over is not auditing anything.

Zero rows marked proven that were wrong.

Across 24 generated statements, 855 rows and 39 injected errors, in three layouts modelled on Indian banks. Every swapped column, misread digit and misread balance was repaired to its true value. Every missing row was flagged and left alone.

These statements come from Ithamar's own generator, so their truth is known exactly. That proves the verifier. Real bank PDFs are the next test, and the result will be published the same way, failures included.

63
automated tests, including property tests that try hundreds of random corruptions on every run
2
engines that agree row for row: Python for the command line, TypeScript in this page
4
kinds of error it can name: a swapped column, a misread amount, a misread balance, a missing row

Audit a statement

Your file is read inside this tab. It is never uploaded; there is no server to upload it to. When it is done, your statement gets its own seal.

No statement to hand? Try one of ours:

Why Ithamar

This is the sum of the tabernacle, even of the tabernacle of testimony, as it was counted, according to the commandment of Moses, for the service of the Levites, by the hand of Ithamar, son to Aaron the priest.

Exodus 38:21, King James Version

The first audit in the record was kept by Ithamar, and its silver can still be checked. Exodus 38 counts 603,550 men giving half a shekel each, and separately reports the total as 100 talents and 1,775 shekels. At 3,000 shekels to the talent, the two records agree.

603,550 men at half a shekel301,775
100 talents at 3,000 shekels300,000
and 1,775 shekels more1,775
Two records, one total301,775

Run it on your machine

The same engine ships as a Python command. It exits 0 when every row is proven, 1 when rows were repaired, and 2 when a person needs to look, so it can guard a script.

Not yet

  • Scanned statements, which need OCR.
  • Statements that print no running balance: those rows stay unchecked rather than pretend.
  • Layouts beyond the three it has been tested on.

Read the source on GitHub

pip install git+https://github.com/victorisaacchinta-eng/ithamar
ithamar audit story.pdf --csv rows.csv

  ITHAMAR 0.2.0  story.pdf
  ASHOKA LEDGER BANK

  ✓   3  2026-04-06  UPI/746814538401/PAYMENT/imran.sharma@                   12,837.61     2,74,589.36
  ↻   4  2026-04-07  UPI/307666193451/PAYMENT/lakshmi.kumar         64.27                   2,74,525.09
  ✓   5  2026-04-07  NEFT CR-SHFL0323205-KONARK LOGISTICS-S                    4,738.18     2,79,263.27
  ...
  ✓  23  2026-04-23  UPI/333123214616/RENT/asha.patel@upi/A     53,094.00                   4,04,878.87

  ↻ row 4: direction swapped. 64.27 is a debit, not a credit
      considered and rejected: a debit of 128.54 may be missing above this row
  ↻ row 9: amount misread. debit reads 42,701.59; the balances need 42,706.59
      considered and rejected: a debit of 5.00 may be missing above this row
  ↻ row 14: balance misread. balance reads 2,84,383.97; the chain gives 2,84,382.97
  ✗ row 19: missing row suspected
      a credit of 1,76,090.10 may be missing above this row

  19/23 proven as printed, 3 repaired, 1 broken, 0 unchecked. Closing balance matches.
  NEEDS REVIEW