ContractPal
Photograph a confusing letter. Get back what it actually says.
- Role
- Author
- Period
- 2026
- Status
- Built; live-API verification pending
- License
- AGPL-3.0
- Latest
- Updated Aug 2026
Overview
Reads insurance policies, bills, contracts and renewal notices and answers three questions: what is this, what do I need to do, and what’s the fine print that could cost me. Nothing you send is stored, and that is enforced in code, not promised in a policy.
- 2
- screens. That is the whole product.
- 0
- databases, for documents or for customers
- 9
- ADRs, including options deliberately not taken
- 17px
- minimum type size, enforced by a check
Problem
Insurance policies, bills and renewal notices are written for the sender, not the reader.
ContractPal is for people who find paperwork stressful rather than interesting: often older, often on a phone, and not interested in learning an app. They also shouldn’t have to trust one with their documents.
What I built
- Upload Hub: one large button. Take a photo or pick a file.
- Breakdown: The Big Picture, What You Actually Need to Do, The Hidden Gotchas. Dates and amounts are emphasised, because those are what people scan for.
- Next.js 16 as a single deployable. No CSS framework, no component library, no database; tests run on Node’s built-in runner.
- One free scan, then a flat subscription through Stripe, with recovery links for subscribers who clear cookies or change device.
Architecture
One Next.js deployable. A document lives only as a buffer inside one request and passes through a provider-agnostic AI seam. There is no database: a signed, expiring token is the entitlement and Stripe holds subscription state.
/api/analyze
a buffer inside one request
The document exists only as an in-memory buffer for the life of the request. It is never written to disk, a database, cloud storage or a log.
Photo or PDF
usually from a phone
The only input. There is no account to create before the first scan.
Upload Hub
validation, rate limit
One large button. Uploads are validated and rate-limited, and both are covered by the Node test suite.
/api/analyze
a buffer inside one request
The document exists only as an in-memory buffer for the life of the request. It is never written to disk, a database, cloud storage or a log.
Provider seam
one environment variable
Vendor adapters behind one interface. Each adapter carries its provider’s retention and jurisdiction facts in code, and the privacy page is written from them.
AI providers
Anthropic, OpenAI, Mistral, OpenRouter
Admitted only if they don’t train on API data, publish a retention window, state a jurisdiction and offer zero retention. OpenRouter is restricted to zero-retention endpoints and has no default model.
Breakdown
three plain sections
The second and last screen. The Content-Security-Policy sets connect-src ’self’, so the page physically cannot send a document to another origin.
Entitlement token
signed, expiring
The signed token is the entitlement. There is no customer table. Recovery tokens use a distinct audience, and the recovery endpoint always answers 202 so it can’t enumerate subscribers.
Stripe
holds subscription state
One free scan, then one flat monthly price. Stripe holds subscription state; the client holds a signed token.
audit:retention
fails the build
A build-time audit that fails if any code path could persist a document or log its content. It is negative-tested against a deliberately violating file.
Decisions
Zero retention is a build failure, not a promise
npm run audit:retentionfails the build if any code path could write a document anywhere, and it is negative-tested against a file that deliberately violates it.No database anywhere
A signed, expiring entitlement token is the entitlement; Stripe holds subscription state.
Privacy criteria gate every AI provider
The vendor is one environment variable, but a provider is only admitted if it meets published retention and training criteria. With a gateway the model slug decides who reads the document, so OpenRouter has no default. Guessing one would be a silent privacy decision.
The honest part, said plainly
The AI provider sees the document transiently and may retain it briefly for abuse monitoring on a standard tier. The privacy page says exactly that rather than implying an absolute.
Accessibility as correctness
Body text is 19px and scales with the browser.
check:contrastenforces WCAG AA for normal text on every colour pair (lowest 6.79:1) and a hard 17px minimum; axe-core reports zero violations on all four pages.
Result
Both screens, the analysis pipeline, the free-scan gate, signed entitlement tokens, Stripe checkout, recovery links, legal pages and security headers are built, with 54 assertions on Node’s test runner and a smoke test over every screen and API path.
The provider and Stripe calls are written and typed but have not yet run against a live key. The repository documents exactly what is verified and what is not.
Known limitations
- Not yet exercised against live third-party APIs.
- Legal copy still carries placeholders;
check:legalfails until they’re replaced.
Stack
Next.js, TypeScript, React, Stripe, Zod, GitHub Actions