Repository navigation
Releases
Verity v1.1.0
Verity v1.1.0
Verity checks invoices and payments against eighteen written accounts-payable controls and tells you which are fine and which need a person. This release moves the verdict engine to Rust and ships it as one 1.7 MB binary: a CLI, an offline HTML report and an MCP server your agent can call. No Python, no database and no model are needed to get a verdict.
It never pays and never approves. Pass means no control objected.
Measured on real public data
Every figure comes from docs/BENCHMARK.md. One command regenerates it from a hashed, offline snapshot of UK government payments (DCMS, over £25,000), US federal contract transactions (USAspending) and labelled receipts (CORD).
- 70.4% of injected anomalies of the types the controls are built for are blocked with no human involved, and 99.7% are raised for review.
- Across every injected type, including three no control is built to catch, the figures are 48.8% and 69.1% (2,931 cases).
- Around 150,000 payments a second. The MCP cold start is 28 ms.
- A default MCP audit response is 79 to 164 bytes, 90 to 95% smaller than v1.0's.
- The Rust and Python engines agree with 0 differences: 17,647 documents one at a time, including every real row, and 3,528 real payments in batch.
Out of the box it does not catch an inflated single-line payment or a supplier it has never seen, because nothing on the page can be checked against. The benchmark reports that by type, next to the false-alarm rate on untouched data.
What's new
- Native binary.
verity audittakes a document or a whole CSV/JSON file.verity explainprints any control's policy.verity verifychecks a data snapshot offline.verity mcpruns the MCP server over stdio, or over--httpfor private, token-gated self-hosting. - Four forensic controls.
- P-015 Benford first-digit test. It is aware of truncated lists like "spend over £25k".
- P-016 near-duplicate payment. The same supplier billing the same amount again within a week is blocked.
- P-017 reused invoice number.
- P-018 sequential invoice numbering.
- Provenance. Every finding carries its source file, row and sha256, plus links to the documents it relates to. The offline HTML report lets you click through. The MCP
tracetool gives an agent the same thing. - Currency awareness for round amounts and rounding tolerance.
- Honest gaps. A duplicate check with no history reports not evaluated, never a silent pass.
- Token-lean MCP.
audit,audit_batch,explainandtracegive compact answers by default. Full evidence only when asked. - New identity. The bracket-V dot-matrix mark in green, amber and watermelon, with shape-coded verdicts. See
DESIGN.md.
Security
Verity treats every document as hostile.
- Verdicts come from code, so text in a document can't argue a finding away.
- The default agent output contains no document text at all.
- Unknown fields, oversized input and ambiguous amounts are refused.
- No tool touches files, processes or the network.
- HTTP mode requires a token and checks
Origin. - The HTML report is escaped under a strict CSP. CSV exports are formula-safe.
Install
Download the archive for your platform below. Check it against SHA256SUMS, unpack, and put verity on your PATH. Or:
cargo install --git https://github.com/uncoalesced/Verity --tag v1.1.0 veritydocker run -i --rm ghcr.io/uncoalesced/verity:1.1.0Then add it to your agent. For Claude Code:
claude mcp add verity -- verity mcpThe README has the config for Claude Desktop, Cursor, Windsurf, VS Code, Gemini CLI, Antigravity, Codex and ChatGPT.
| Platform | Archive |
|---|---|
| Windows x64 | verity-v1.1.0-x86_64-pc-windows-msvc.zip |
| macOS Apple Silicon | verity-v1.1.0-aarch64-apple-darwin.tar.gz |
| macOS Intel | verity-v1.1.0-x86_64-apple-darwin.tar.gz |
| Linux x64 | verity-v1.1.0-x86_64-unknown-linux-gnu.tar.gz |
| Linux arm64 | verity-v1.1.0-aarch64-unknown-linux-gnu.tar.gz |
Upgrading from v1.0
- The Python console script is now
verity-py.verityis the native binary.verity-mcp, the v1.0 HTTP-client server, still works. - If you use the Postgres ledger, run
uv run alembic upgrade headto apply migration 0004 (invoice_number,currency,source_sha256). - Python dependencies with published advisories (pypdf, Pillow, PyJWT, aiohttp, filelock, urllib3) are upgraded.
Full list: CHANGELOG.md.
Benchmark data contains public sector information licensed under the Open Government Licence v3.0. USAspending.gov data is public domain. CORD v2 labels are CC BY 4.0.
Engineered by uncoalesced.
Verity v1.0
Verity v1.0
First stable release of Verity. It reads invoices and receipts, checks each one against a written set of accounts-payable controls and tells you which ones are fine and which ones need a person. It does not pay anything, and it does not approve anything. It reads, it checks and it holds up its hand.
Every finding carries the exact wording of the policy it fired under, quoted in full rather than paraphrased. Nothing is held because a model "seemed unsure." Every person-decision is recorded against a name, overrides are flagged explicitly and the settings in force at audit time are stored permanently with each record.
The fourteen controls
| ID | Control | What it looks for | Outcome |
|---|---|---|---|
| P-001 | Supporting documentation required | No amount could be read at all, or a sizeable amount with neither a supplier name nor a date anywhere on the document | Blocked |
| P-002 | Duplicate payment prevention | Same supplier, same amount, same date as a document already on file | Blocked |
| P-003 | Supplier identification | No legible supplier or merchant name on the document | Review |
| P-004 | Transaction date required | No date for when the goods or services were supplied | Review |
| P-005 | Late submission | Document is older than the submission window (90 days by default) | Review |
| P-006 | Future-dated documents | Document is dated after today, usually a transposed or fabricated date | Blocked |
| P-007 | Arithmetic integrity | The line items don't reconcile to the stated total or subtotal, beyond a small rounding tolerance | Blocked |
| P-008 | Authorised signature | No visible signature or stamp on a document above the signature threshold | Review |
| P-009 | Delegated approval limit | Amount exceeds what can be approved without escalation. Verity routes this upward; it does not approve it | Blocked |
| P-010 | Round-amount anomaly | Amount is suspiciously exact (a round hundred or more). Not proof of anything on its own, just worth a second look | Review |
| P-011 | Threshold avoidance by splitting | Two or more invoices from the same supplier, close together in time, each sitting just under the approval limit | Blocked |
| P-012 | Approved supplier list | Supplier is not on your approved list. Only active once your team has actually configured a list | Blocked |
| P-013 | Low-confidence extraction | Verity could not read the document with enough confidence to trust its own answer | Review |
| P-014 | Intake rejection | The file itself couldn't be opened: corrupt, blank, password-protected, or an unsupported format | Blocked |
The rule: anything serious fires, the document is blocked and the payment is held until a named person decides. Something less serious with nothing serious means review. Nothing flagged means it passes automatically.
The defaults it ships with
All of these are settings your finance team owns; no code changes needed to adjust them.
| Setting | Default | What it governs |
|---|---|---|
| Receipt required above | 75 | Below this, a missing receipt isn't flagged. Above it, a document with no supplier and no date trips P-001 |
| Signature required above | 2,500 | Above this amount, a document needs a visible signature or stamp (P-008) |
| Approval limit | 10,000 | Above this, an expense needs sign-off at the next level (P-009) and it's the reference point for the splitting check (P-011) |
| Arithmetic tolerance | 0.05 | How far the line items can miss the stated total before it counts as a mismatch (P-007) |
| Maximum explainable mark-up | 35% | How far above the line items a total can sit before the gap stops looking like tax or a service charge (P-007) |
| Submission window | 90 days | Documents older than this are flagged as late (P-005) |
| Splitting window | 14 days | How close together two near-limit invoices from the same supplier need to be before they're reviewed together (P-011) |
| Splitting proximity | 90% of the approval limit | How close to the limit an invoice has to sit to count as "just under" for the splitting check |
| Minimum reading confidence | 60% | Below this, the document goes to a person regardless of what else it shows (P-013) |
| Approved supplier list | Off (empty by default) | Switches on only once your team supplies a list of approved suppliers (P-012) |
Working at scale and getting answers out
- Folder audit: point Verity at a folder of invoices and receipts for a period; it works through every document and one broken file does not stop the batch. Outputs a breakdown of outcomes by control plus a CSV exception report ready for Excel.
- Review screen: a simple queue with three buttons (approve, reject, ask for more information). A decision with no name typed against it is refused outright.
- Exception report: a CSV of everything currently held, for people who will never open Verity itself.
- Audit pack: one document's full record assembled in the order an external auditor would read it: verdict, every firing control with its quoted policy text, the fields read, the settings in force and the complete reviewer history.
Calling Verity from another agent
Two integration paths and neither moves any policy logic off the server. Both go through POST /audit (content_base64 for the file itself, fields for a reading the caller already did). A pass means no control objected; it is never an instruction to pay anything.
- MCP: one stdio server (
uv run verity-mcp) covers Claude, Gemini CLI, Cursor and Codex, exposing a single tool,audit_document. - OpenAPI:
uv run scripts/export_openapi.py --out openapi.json --server https://verity.example.comproduces a schema for ChatGPT Custom GPT Actions. Hosting and authentication are yours.
What the testing shows
- 19 hand-written policy cases: perfect match on all of them. This checks the logic does what was designed, no more.
- 500 invoices with planted problems, labelled by someone other than the control author: Verity caught all 30 of the 30 planted problems its controls cover. One clean document was flagged by mistake (an exactly round amount, which is P-010 doing its job). None of the other 460 clean invoices tripped a fraud control.
- 10 of the 40 planted problems Verity cannot catch at all: three quoting nonexistent purchase orders and seven submitted at odd hours. There is no purchase-order data and no submission-time signal, so no control covers either. If PO matching is central to your spend control, Verity does not do it in this release.
- Reading is the weak step and the numbers say so plainly. On 25 rendered pages the controls scored near-perfectly when handed correct numbers, but reading those pages live, Verity flagged all 25 including all 22 legitimate ones and the confidence score proved unreliable (average reported confidence 95% against 8% of pages read correctly). Do not treat a high-confidence read as a checked one and treat the 60% P-013 threshold as an untuned default.
- Extraction against a public benchmark of 25 scanned receipts: the total was right on 100% of claimed reads and found on 96% of those present. Line items: 67% correct of claimed, 71% found. Supplier name and transaction date come from a weaker step and have no measured accuracy figure; expect to correct them.
- Policy search: the right policy comes back first about 4 times in 10 (hit@1 0.433) and in the top three about 7 times in 10 (hit@3 0.733, MRR 0.561). Search never decides anything; it only affects how easily a reviewer can look up why something was flagged. No RAGAS figures exist; those metrics have never been run.
- Optional citation check (off by default): asks Claude whether the quoted policy passage genuinely supports a finding. It cannot change a verdict or release a payment. Tested against fixed replies only; no accuracy figure at volume.
Nobody has run this against a real company's ledger yet. Everything above comes from test documents and planted examples. Before relying on Verity for an actual period close, run it alongside your existing process for at least one full period and compare the two.
Quickstart
uv sync
cp .env.example .env
docker compose up -d db
uv run alembic upgrade head
uv run uvicorn verity.api.main:app --reloadAudit a folder:
uv run scripts/audit_folder.py ./inbox --out exceptions.csvAdd --save to persist runs; preview mode writes only the CSV and the duplicate-payment (P-002) and splitting (P-011) controls cannot fire without saved history.
Key endpoints: POST /documents, POST /audit, GET /runs, GET /runs/{run_id}, GET /runs/{run_id}/pack, POST /runs/{run_id}/review, GET /reports/exceptions.csv, GET /policies.
Known limitations in v1.0
- Scanned documents only. Born-digital PDFs with no picture of a page inside them are reported as unreadable rather than guessed at.
- Reading accuracy is the bottleneck; line-item extraction in particular is rough (roughly a third of reported lines wrong or spurious).
- The confidence score is not currently a reliable signal of correctness.
- The round-amount check (P-010) is not currency-aware; treat it as advisory if you transact in more than one currency.
- The duplicate and splitting checks look back six months and need saved history to fire.
- No purchase-order matching and no behavioural signals such as submission time.
- The MCP and OpenAPI integrations are verified against the standards but have never been run inside Claude, Gemini CLI, Cursor, Codex, or ChatGPT.
- Supplier name and date accuracy are unmeasured; they come from a weaker reading step.
Verification
Deterministic evaluations, no key and no GPU, run in CI on every push and pull request:
uv run -m verity.eval.policy_eval # 14 controls, 19 hand-written cases
uv run -m verity.eval.fraud_eval --corpus sample/verity_sample_invoices_m_500.csv
uv run -m ve...