Skip to content

Latest commit

Β 

History

4 Commits

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

πŸ” Proofline

Evidence-linked financial audit engine.

Auditors can't act on a verdict they can't verify. Proofline ingests a mixed-format company dossier, runs deterministic fraud-detection procedures over it, and produces ranked findings where every claim, every amount, every accusation, carries the exact file, locator, excerpt, and SHA-256 hash it rests on.

Investigation view showing a promoted finding with its evidence, amounts, and severity

The Investigation view: a promoted finding with its sourced amounts, evidence links, and severity.

🧩 Problem

Fraud in a real audit dossier rarely sits in one document. It's split across a ledger entry, a vendor master-data change, a permissions spreadsheet, and a policy PDF, and only becomes visible when those are read together. An AI system that reads a dossier and asserts "this is fraud" isn't useful to an auditor on its own: a verdict nobody can trace back to evidence can't be acted on and can't be defended. The cost of getting this wrong runs both directions. Missing a real scheme is a failure, but flagging a clean, legitimate transaction as fraud is a failure too, and it erodes trust in every other finding the system produces.

🏁 Objective

Proofline sets out to produce financial-fraud findings an auditor can act on the moment they're read, each one traceable back to the exact row, cell, or passage it came from. The standard it holds itself to is simple: a claim without a citation does not ship, no matter how likely it looks. Recall is welcome, but never at the cost of a promoted finding that can't be independently verified.

πŸ’‘ Solution

Every table in a dossier, however it's formatted or named, is mapped onto a small set of canonical tables (general ledger, vendor postings, asset postings, master-data changes) before any detection logic runs. A fixed set of deterministic procedures then tests those canonical tables for specific fraud patterns, and every fact a finding relies on is registered in an evidence book with its source file, locator, and hash. A finding is only promoted once its detector's conditions are fully met with cited evidence, because a missed finding is a smaller failure than a false accusation.

✨ Key features

  • Per-claim source citation. Every amount and every accusation links to its exact file, row or cell, excerpt, and SHA-256 hash.
  • Deterministic detection. Findings come from fixed, reproducible procedures in backend/audit.py, not from a language model's judgment.
  • Counter-evidence and held-back signals. Contradictory or unresolved items stay out of the accusation queue instead of being flagged anyway.
  • Evidence overlay. Click any citation to see the exact source row alongside its neighboring rows for context.
  • Materiality bridge. Traces the profit impact of confirmed findings against a sourced trial balance, when one exists.
  • Printable evidence report. A full audit trail for every run, ready to hand to a reviewer.
  • Bilingual parsing. Reads German and English accounting terms, number formats, and column headers natively.
  • Optional AI layer. Adaptive schema mapping, vision extraction, and narrative review are available, but none of them are required for a result.

βš™οΈ How it works?

Deterministic detectors in backend/audit.py read ledger, vendor, and asset tables directly. No language model decides whether something is fraud. Every fact a finding cites is registered as an evidence object with its source file, row or cell locator, exact excerpt, and SHA-256 digest, and each run's unsupported_claims count is computed by checking that every cited evidence ID actually resolves. It's measured, not asserted. A small invented dossier ships with the repo and the Docker image (demo-dossier/), so /api/demo runs a real analysis on first launch with no API key and no setup.

Evidence overlay showing a cited source row, its hash, and surrounding context

Every citation opens to its exact source row, with its SHA-256 hash and surrounding context.

πŸ—οΈ Architecture

ZIP / folder upload
        |
        v
Safe extraction and format normalization
        |
        +--> Native parsers and deterministic schema recognition
        +--> AI schema mapping for unfamiliar tables (optional)
        +--> Vision extraction for scans and embedded images (optional)
        |
        v
Canonical audit tables and source locators
        |
        v
Deterministic reconciliations and explicit audit tests
        |
        v
Evidence and counter-evidence validation
        |
        +--> Optional AI investigation and grounded Q&A
        |
        v
Ranked findings, materiality bridge, evidence viewer, report

The application is a single deployable Docker service: Vite builds the React frontend, FastAPI serves the API and the compiled frontend, and analysis results live in process memory for the duration of a run.

🧱 Technology stack

Backend

  • Python 3.12, FastAPI
  • openpyxl for spreadsheets, pypdf for PDF text
  • LibreOffice, for legacy DOC/PPT/XLS conversion (inside Docker)
  • Pydantic, validating every request and every AI structured output
  • OpenAI Python SDK, for the optional AI layer

Frontend

  • React, built with Vite
  • No component library, chart library, or CSS framework, just hand-written CSS

Testing

  • pytest, for the backend suite
  • Node's built-in test runner, for the frontend

Deployment

  • One multi-stage Docker image
  • Optional Render blueprint (render.yaml)
  • Exact versions pinned in requirements.txt and frontend/package.json

πŸ“ Repository structure

.
β”œβ”€β”€ backend/
β”‚   β”œβ”€β”€ app.py            FastAPI routes, upload controls, run lifecycle, reports
β”‚   β”œβ”€β”€ ingest.py         document parsing and legacy Office normalization
β”‚   β”œβ”€β”€ adaptive.py       confidence-gated schema discovery and canonical mapping
β”‚   β”œβ”€β”€ audit.py          deterministic audit procedures and evidence construction
β”‚   β”œβ”€β”€ synthesis.py      OpenAI vision, investigation, and grounded answers
β”‚   └── tests/            backend and integration tests
β”œβ”€β”€ frontend/
β”‚   └── src/
β”‚       β”œβ”€β”€ App.jsx           application shell and view routing
β”‚       β”œβ”€β”€ components/       auditor workspace components
β”‚       β”œβ”€β”€ model.js          presentation calculations and data normalization
β”‚       └── styles.css        visual system
β”œβ”€β”€ demo-dossier/         invented demo dossier (ships with the repo and the image)
β”œβ”€β”€ scripts/
β”‚   └── evaluate.py       precision/recall evaluation against known findings
β”œβ”€β”€ Dockerfile             production multi-stage image
β”œβ”€β”€ render.yaml            optional Render deployment blueprint
└── .env.example           supported environment variables

docs/TECHNICAL_DOCUMENTATION.md has a deeper implementation walkthrough.

βœ… Prerequisites

Recommended:

  • Docker Desktop with Docker Engine running
  • At least 2 GB free memory for the container

For local development instead of Docker:

  • Python 3.12
  • Node.js 22 and npm
  • LibreOffice, for legacy DOC/PPT/XLS conversion

πŸš€ Quick start

From the repository root:

docker build -t proofline .
docker run --rm -p 8000:8000 proofline

Open http://localhost:8000. The demo dossier analyzes automatically, no environment variables required. To enable the optional AI layer, copy .env.example to .env, set OPENAI_API_KEY and ENABLE_AI=true, and pass --env-file .env to docker run. DEMO_TOKEN and PORT are also read from the environment; see .env.example for details.

For local development without Docker:

python -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
cd frontend && npm install && npm run build && cd ..
uvicorn backend.app:app --reload --port 8000

For live frontend editing, run npm run dev from frontend/ instead of building; the Docker image remains the authoritative production build.

🧭 Using the application

  1. Open a dossier. Use Open ZIP or Open folder, or just load the app. The committed demo dossier analyzes automatically on first launch.
  2. Read the top finding. The Investigation view leads with the highest-severity promoted finding; click any evidence chip to see the exact source row, its neighbors, and its hash.
  3. Check Materiality and Sources. Materiality traces the profit-bridge impact where a sourced trial balance exists; Sources shows format coverage, hashes, and lets you search the whole dossier.
  4. Record a decision. Confirm or dismiss each finding (a dismissal requires a reason), then open the printable evidence report for the audit trail.

🚧 Constraints

Uploads are capped at 600 MB, checked against raw uploaded bytes, each archive's decompressed size, and the run's total decompressed size alike, with a maximum of 100 files (counted across top-level uploads and everything extracted from ZIPs) and a nested-ZIP depth limit of 3. Path traversal and unsupported extensions are rejected, macOS metadata is ignored, and filenames suggesting answer keys or ground truth are refused outright.

The detectors have a real scope limit worth stating plainly. vendor-control-chain and bank-change-before-payment require a vendor/master-data-change log with a GENEHMIGT_VON column and a goods-receipt table keyed by WARENEINGANG_DATUM; they find nothing if a dossier doesn't provide tables shaped like that under a recognized header, silently rather than with an error. The precision and recall reported above hold for the demo and practice dossiers specifically. They are not a guarantee against an arbitrary company's real accounting export.

🎯 How findings are identified

backend/audit.py runs a fixed set of explicit, reproducible procedures (bank-to-ledger reconciliation, invoice/order/receipt/payment matching, duplicate and overpayment detection, missing goods-receipt checks, vendor master-data changes ahead of cash movement, segregation-of-duties conflicts, approval-threshold splitting, cut-off and premature-revenue tests, asset-versus-expense classification, and trial-balance integrity) rather than asking a language model to declare the dossier fraudulent. A finding is only promoted once its detector's specific conditions are met with cited evidence on every side; anything weaker, contradictory, or merely suspicious-looking is held back as a data-quality signal instead of an accusation, because flagging a clean item is treated as a real failure, not a harmless false positive. Every promoted finding carries a severity, a confidence level, sourced amounts, evidence and counter-evidence IDs, explicit caveats about what still requires professional judgment, and a suggested next audit procedure.

πŸ€– Optional AI layer

Proofline's detection engine is fully deterministic and needs no API key. Every number in a promoted finding is a fact read from the dossier or a calculation over cited inputs, and the demo-dossier results above were produced with ENABLE_AI=false. Setting ENABLE_AI=true with a valid OPENAI_API_KEY turns on four bounded, optional additions: adaptive schema mapping for unfamiliar table headers, vision extraction for scanned PDFs and embedded images that native parsers can't read, an optional narrative review pass that can propose new cross-document hypotheses, and LLM-phrased answers from Ask the Ledger instead of the deterministic keyword-routed ones. A claim firewall keeps AI output honest either way (it rejects unsourced numbers and requires evidence from multiple files before a new AI-proposed finding is promoted), and a broken or missing key falls back to the deterministic engine rather than failing the run; real results do not require a key.

πŸ”Œ API reference

FastAPI also exposes interactive OpenAPI documentation at /docs and the schema at /openapi.json while the service is running.

Method Path Purpose
GET /api/health Service health and sample availability
GET /api/demo?ai=false Load the bundled demo run; optionally request AI enhancement
POST /api/runs?ai=false Upload one ZIP or multiple folder files and create an analysis run
GET /api/runs/{run_id} Return the complete run, findings, evidence, manifest, and metrics
GET /api/runs/{run_id}/findings Return findings for a run
POST /api/runs/{run_id}/ai-review Run optional AI enhancement on an existing run
GET /api/runs/{run_id}/events Stream analysis stage events using server-sent events
GET /api/evidence/{evidence_id}?run_id={run_id} Return one evidence object
GET /api/runs/{run_id}/evidence/{evidence_id}/context Return surrounding source rows or document context
GET /api/runs/{run_id}/source/{source_path} Download or open an original uploaded source
GET /api/runs/{run_id}/search?q={query} Search the parsed dossier corpus
POST /api/runs/{run_id}/ask Ask an evidence-grounded question
PATCH /api/findings/{finding_id}/review Save confirmed, dismissed, or unreviewed status
GET /api/runs/{run_id}/report Render a printable HTML evidence report

Create a run

curl -X POST "http://localhost:8000/api/runs?ai=true" \
  -F "files=@dossier.zip"

Response:

{
  "run_id": "a1b2c3d4e5f6",
  "status": "complete"
}

Folder uploads send each file under the same multipart field, preserving its relative path.

Ask the Ledger

Runnable as-is against the demo run (run_id=sample), no upload required first:

curl -X POST "http://localhost:8000/api/runs/sample/ask" \
  -H "Content-Type: application/json" \
  -d "{\"question\":\"Show transactions split below the freigabe threshold\"}"

The response contains answer, finding_id, evidence_ids, and sourced facts.

Record auditor judgment

Also runnable as-is, against one of the demo run's real findings:

curl -X PATCH "http://localhost:8000/api/findings/capitalized-repairs/review" \
  -H "Content-Type: application/json" \
  -d "{\"run_id\":\"sample\",\"status\":\"confirmed\",\"note\":\"Validated against invoice scope.\"}"

Allowed statuses are confirmed, dismissed, and unreviewed. A dismissal requires a non-empty note.

πŸ“Š Evaluation

Numbers below are generated by scripts/evaluate.py. The demo row reproduces on any checkout with no setup; the practice row additionally requires a local practice-dossier checkout that isn't distributed with this repo, and is skipped automatically if it's absent.

Dossier Findings detected Decoys correctly ignored Precision Recall Unsupported claims Citation coverage
Demo (ships with this repo) 3/3 1/1 100% 100% 0 100%
Practice (local-only, not shipped) 4/4 7/7 100% 100% 0 100%

The demo-dossier row is what a fresh clone or deployed instance actually runs on first launch. The practice-dossier row is a separate, local-only evaluation against a larger seeded dataset used during development; two of its four findings (vendor-control-chain, bank-change-before-payment) depend on source tables that don't exist under any recognized name in the organizers' final evaluation dataset, so that 100% is not a claim about performance there (see Constraints above).

πŸ§ͺ Testing

python -m pytest -q runs the backend suite (API behavior, upload validation, German and English money parsing, adaptive schema handling, the fraud detectors, evidence context, review actions, and the deterministic/AI-mode boundary) against the committed demo dossier, so it passes on a fresh clone with no setup and no key. cd frontend && npm test && npm run build runs the frontend's calculation checks and confirms the production bundle builds.

πŸ“œ Provenance and license

Originally built for a hackathon audit-analytics track; the practice and final evaluation datasets belong to the organizers and are not included in this repository. Only the invented demo-dossier/ is.

About

Deterministic financial fraud detection engine with per-claim source citation. Every finding traces to an exact file, row, excerpt, and SHA-256 hash and no unsupported claims. Ships with an invented demo dossier; runs live with no API key. FastAPI + React.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Contributors

Languages