Hacki
All BUIDLs

About the project

Ledger Witness

Ten invoices reconciled against their purchase orders by a 382 MB model running on my laptop — no cloud, no API key, no internet. The interesting question isn't whether it got them right. It's why you would believe it.

Ledger Witness answers that: every run emits a signed evidence bundle that anyone can re-execute. Same machine, same model, same bytes — or the bundle does not validate.

How it works

image → OCR (@qvac/sdk) → geometric reading order → local LLM → deterministic rules → signed bundle → replay

The LLM only transcribes. Deciding and correcting is the code's job, because code is reproducible and auditable. Reconciliation itself (line matching, PO totals, price and quantity deltas) is pure deterministic logic — no vectors, no fuzzy scoring.

Three levels of verification

LevelWhat it provesResult on the real OCR run
L1 — structureed25519 signature + hash chain of the bundle; runs on any machineVALIDATES
L2 — inference replayevery model call re-executed, response hashes compared31/31 steps bit-exact
L3 — re-derivationfindings recomputed from the replayed outputsMATCHES

L2 covers all three capabilities — OCR, chat and VLM — reproducing byte for byte. That was the make-or-break checkpoint of the project, and it's reached on the full path, not on a shortcut.

The UI re-runs L1 on every page load and includes a live tampering bench: change one digit in the bundle and the seal turns red in front of you.

QVAC capabilities used

CapabilityHowWhere
Chat / completionsqvac serve openai on 127.0.0.1:11434, alias extractor, temperature 0, seed 42recon/qvac.synqvac_chat
OCR (OCR_LATIN)@qvac/sdk loadModel + ocr() behind a local HTTP shimqvac-shim/server.mjsPOST /ocr
VLM (SmolVLM2-500M Q8_0 + mmproj)completion() with an image attachment — second opinion on the invoice totalqvac-shim/server.mjsPOST /vlm
Embeddingsdeliberately dropped — matching is deterministic, so vectors would add 600 MB and non-determinism for nothing

All inference goes through exactly two files and there is no other path to a model. Each task returns {result, step}; the step carries the exact request plus the sha256 of the response — that is the unit of replay. Model sha256s come from the SDK registry and travel inside the bundle.

Why a 0.6B model is enough: nine deterministic defenses

Each one was born from an observed failure, not from a guess:

  1. Geometric reading order — the OCR does not return blocks in reading order; they are

grouped into rows by vertical center and sorted by X.

  1. Skew correction — degraded scans sit 3–7° tilted, which drifts a row ~36 px between

its first word and its last amount. The slope is estimated with the median of pairwise slopes, so mismatched pairs are ignored without magic thresholds.

  1. Unambiguous OCR fixes — an S glued in front of a number is a misread peso sign. The

sibling case (a 5) is genuinely ambiguous and is left alone — arithmetic resolves it.

  1. Per-row arithmetic coherence — if qty × unit_price ≠ amount, the model mixed

columns; it retries with the concrete calculation in the prompt.

  1. Total-driven re-read, 6. row deduplication, 7. PO-number normalization,
  2. description-tolerant matching, and
  3. the VLM's second opinion — it nails the digits of the total in 7 of 7 invoices

(including the handwritten one), and the total is only corrected when the row sum and the VLM agree.

All repairs live in a single task (aplicar_reparaciones) shared by the pipeline and the verifier. If they ever diverge, L3 fails. That's the invariant that keeps the evidence honest.

Measured results

Windows 11, GTX 1050 3 GB, 7.36 GB RAM, Node 24, @qvac/cli 0.11.0, @qvac/sdk 0.17.1, Synsema v0.5.99. Model: Qwen3-0.6B-Q4_0 (382 MB).

Reconciliation vs. ground truth6/10 overall — 5/5 on clean scans
VerificationL1 validates · L2 31/31 bit-exact · L3 matches
Extraction latency~1 s per invoice (OCR ~27 s/page)
RAM with model loaded~1.0 GB text + ~1.8 GB OCR
Tests26 green, all offline

We also measured Qwen3-1.7B: same 6/10, 3× slower. Model size stopped being the lever — the deterministic defenses closed the gap. Swapping models is a config flag; not one line of `.syn` changes.

Where it breaks (honestly)

  1. Cross-machine bit-exact replay is not solved (kernels, batching, floating point).

What exists: deterministic replay on the same machine + structural verification (L1) anywhere.

  1. Reading degraded images is the weak link, not the rest of the system. With clean OCR

the pipeline is 100%. On phone photos the OCR emits TOTAL: $55.500, 0o — no larger LLM recovers information that isn't in the text.

  1. The business counter-argument: cryptographic proof does not replace the human

reviewer's signature. It makes it cheaper — verify in seconds instead of redoing the work.

Stack

Written in Synsema (.syn), a capability-gated language for agents, with a small Node shim for the SDK's native OCR/VLM addons. UI served by synsema serve app.syn.

.\tools\arrancar.ps1            # QVAC server + shim
synsema run recon/main.syn      # invoices -> findings -> signed bundle
synsema run recon/verify.syn    # L1 structure · L2 replay · L3 re-derivation