How I built ExhibitKit
ExhibitKit turns a raw message export into a clean, court-ready PDF exhibit - entirely in your browser, with a SHA-256 integrity hash on every page. This page is the technical overview: what it's made of, the decisions behind it, and where it's headed. The whole project is open source on GitHub.
Built with
- HTML & CSS. Plain, semantic markup and a small hand-written stylesheet - no UI framework.
- Vanilla JavaScript (ES modules). No build step, no bundler, no dependencies to install. Each page loads the modules it needs directly. That keeps the code auditable: what you read in the repo is exactly what runs.
- jsPDF. Generates the exhibit PDF - cover page, Bates-numbered messages, and the declaration - on your device. An optional embedded font is fetched only if your messages contain characters the standard PDF fonts can't render.
- Web Crypto API. The browser's built-in
crypto.subtlecomputes the SHA-256 hash of your original file. No crypto library ships with the site. - A service worker precaches the app so it keeps working offline after the first visit.
Security: your files stay local
The core promise is that your messages never leave your device, and it isn't a policy you have to take on faith - it's enforced by how the site is built:
- No backend. There is no server to receive a file, no account, and no database. The site is static files served from a CDN.
- All processing in the browser. Reading, hashing, parsing, redacting, and PDF generation all happen in page memory. A refresh wipes everything.
- A strict Content-Security-Policy. Each page ships its own CSP that whitelists only what it genuinely needs. Because there's no
connect-srcpath back to our own origin, the browser itself would refuse to upload your messages even if the code tried. You can watch the network tab during a full session and confirm it.
Forensics: SHA-256 hash verification
A digital exhibit is only as good as its tamper-evidence. When you load a file, ExhibitKit computes the SHA-256 hash of the original bytes and prints that fingerprint on the cover, in the declaration, and in the footer of every page. Anyone - opposing counsel, a clerk, a judge - can later drop the same source file into the verify page and confirm the hash matches, proving the exhibit was built from exactly that file and nothing was altered after the fact. The hashing is deterministic and runs the same in any modern browser.
AI: on-device machine learning (the Review Lab)
The most technically involved part of the project is the optional AI Review Lab - and it runs real machine learning entirely in your browser, with no server and nothing uploaded.
- A real embedding model. It loads
all-MiniLM-L6-v2, a ~23 MB quantized sentence-embedding model, through Transformers.js on top of the ONNX WebAssembly runtime - so the inference happens on the user's own device, not a backend. - Semantic ranking, not keyword search. You describe a claim ("he agreed to pay half the rent"); the Lab turns your claim and every pasted message into 384-dimensional vectors, ranks the messages by cosine similarity, then groups them (likely related / possibly related / needs context) with deliberately hedged labels and a gap analysis.
- Private by construction. The model files download once from a public CDN, but your message text is never sent anywhere - the page's Content-Security-Policy doesn't include a route back to our own origin, so there is no upload path at all. The math runs in the WebAssembly sandbox on your machine.
And just as deliberately, the exhibit builder itself uses no AI. Court evidence should be deterministic and auditable, so categorizing, hashing, redacting and PDF generation are all plain, inspectable code. The Lab is a separate, optional review aid - it never touches the exhibit PDF and never judges the strength of a case. Knowing where machine learning belongs (helping a person find relevant messages) and where it doesn't (formatting sworn evidence) was one of the core design decisions of the whole project.
UX: simple evidence organization for non-technical people
Most people who need this are not engineers - they're self-represented litigants, fraud victims, or busy paralegals. So the builder is a plain four-step wizard, and a few touches do the heavy lifting:
- One-click demo files for every supported format, so anyone can see the whole flow without exporting anything first.
- A deterministic Evidence Map that groups messages into plain categories (money, custody, agreements, dates, attachments) by keyword and lays out a factual timeline. It's keyword logic, not a model passing judgment - it never tells you your evidence is "strong," it just helps you find things.
- Genuine redaction. Redacted text is removed from the data before the PDF is built, not hidden under a black box that can be copied out - and the declaration logs how much was redacted or excluded.
- A plain-language declaration template with the case details filled in, ready to sign.
What's next
The direction I'm most excited about is growing the on-device AI: larger or multilingual embedding models as browser ML matures, optional local summaries of a conversation, and smarter grouping - always running on the user's device, always a review aid rather than a judge of the case. On the builder side: more export formats and richer redaction tooling. The builder stays deterministic and AI-free by design.
Want the short version of who and why? See the about page.