About Clarion · live on real CFPB data
The complaint-intelligence layer that shows its work.
Every classification is either backed by a verbatim quote from the original complaint or it isn't made at all — and every human decision that overrides it is permanently, immutably recorded with the reason why.
About this project
What Clarion is
Clarion turns messy, free-text complaints into a controlled taxonomy your team can actually trust — because every classification either points to the exact sentence that justifies it, or it doesn't get made. Built on the public CFPB Consumer Complaint Database as a live, working proof: real ingestion, real classification, real human review, running in production today at signal.prateekmulye.dev.
What we offer
- Evidence-grounded classification
- Every category we assign is backed by a verbatim quote from the complaint. If the evidence doesn't hold up under a strict, deterministic check, we don't guess — we route it to a human instead.
- A human review queue that closes the loop
- Low-confidence and contradictory cases go to a keyboard-driven triage queue. Every accept or correction is transactional, idempotent, and — for corrections — requires a written reason, which also becomes a new training example for our evaluation suite.
- An audit trail you can actually stand behind
- Every classification, every review decision, and every model/prompt version is written to an append-only log with no update or delete path, enforced at the database level.
- Bring your own model, bring your own data source
- Clarion's classification engine is written against swappable adapters — plug in your own LLM provider and your own complaint feed without touching pipeline code. ROADMAP file-drop / SAP QM source adapters are specced, not yet shipped — today's only live source adapter is the CFPB API
- Trend intelligence with honest denominators
- See how failure modes move over time, compare your data against a live national baseline, and know immediately when that comparison isn't representative — when the national baseline is stale, the page says so before it shows you a number.
- Company names, resolved the honest way
- Bank names change — mergers, rebrands, charter conversions. Clarion cross-references FDIC's own free public institution records to link a bank's historical names to its current one, and shows exactly which names came from FDIC versus Clarion's own spelling normalization. We never guess: an ambiguous match is left unmerged rather than silently combined. ROADMAP Exact/normalized matches only today — fuzzy or multi-hop merger-chain matching (e.g. bank A renamed to B, B later absorbed into C) is a deliberately deferred, human-reviewed upgrade, not yet built.
FDIC BankFind institution + name-history data, fetched 2026-08-31. Refreshes daily; used only to link company names already present in Clarion's own corpus, never to add new ones.
How it works
Ingest
A complaint narrative comes in through a source adapter (today: the CFPB public API).
Scrub
Personally identifying details (emails, phone numbers, SSNs, account numbers) are redacted before the narrative is stored or sent to any model.
Classify
An LLM proposes a taxonomy code and the verbatim spans that support it.
Ground or abstain
Every span is checked, in code, against the scrubbed narrative. Any span that doesn't match exactly — no fuzzy matching — downgrades the whole result to "abstained" and sends it to review.
Review
A human confirms or corrects the classification. Corrections require a written reason and become new evaluation examples.
Log
Every step above is written to a permanent, append-only record — nothing is ever silently overwritten.
Learn
Trends and leaderboard views surface how failure modes shift over time, compared against a live national baseline.
For developers
Architecture & API
show
Implementation detail for engineers evaluating the system — collapsed
by default. The product story above stands without it.
Architecture
Clarion's ingestion and classification are written against two swappable behaviours — Clarion.Ingest.Source and Clarion.LLM.Adapter — so a new complaint source or a customer's own LLM provider is a config change, not a pipeline rewrite. That same seam is the data-residency story: a customer can point classification at their own model, keeping regulated text inside their own boundary.
Data quality
Company names are lightly normalized before ranking — CFPB's own company field can carry more than one spelling of the same registered legal entity (a stray comma, "N.A." vs "NA"). Clarion merges those, and additionally cross-references FDIC's free public institution records to link a bank's own prior names and the institutions it directly absorbed. It still does not merge a company's separately-chartered bank subsidiary with its parent holding company — those remain distinct rows, since that would require a fuzzy registry match Clarion deliberately defers to human review rather than guessing. Rows with more than one merged spelling show a "N variants" disclosure on the leaderboard listing exactly which raw strings were combined, and where each came from.
API access
Live — read-onlyClarion's second production surface: a read-only GraphQL API running in-process next to this app, over the same database, with the same evidence-grounding guarantees — callable from your own code instead of a screen.
Browse the schema, run queries, see real results — no setup.
POST a GraphQL query from curl, Python, a notebook, or any client.
Three fields available today
-
taxonomy(version)The controlled failure-mode taxonomy Clarion classifies against — codes, human labels, descriptions. -
trends(dimension, interval, since, until)The same aggregate counts behind the /trends page. -
complaint(id)One classified complaint: its evidence-grounded classification, the verbatim quotes that support it, and its review status.
Guardrails
Max query depth
8
Max query complexity
200
Rate limit
30/min per IP
Query timeout
5s
Example query
{
taxonomy { code label description }
trends(dimension: FAILURE_MODE, interval: MONTH) {
periodStart label count
}
complaint(id: "1") {
receivedOn
classification {
taxonomyLabel
status
evidence { quote }
}
}
}
The complaint(id) example always resolves to the same real, evidence-grounded record, so it keeps working over time. There's no way yet to browse other complaint IDs from GraphQL itself — that's the next field on the list.
Source
Private repository; access on request for evaluation.
Security
What protects your data
Clarion is a server-rendered analytics product. This is the honest posture for an app of that shape — each item below is a property the system holds today, not a marketing aspiration.
- In transit
- TLS 1.3, terminated by Caddy with auto-renewed certificates. Port 80 serves only a redirect to HTTPS and the certificate-renewal handshake; no content is ever served in plaintext.
- At rest, on disk
- The Oracle Cloud volumes holding the database and event log are AES-256 encrypted at rest by default — there is no unencrypted option. Keys are cloud-managed today.
- Passwords
- Never stored. Only a bcrypt hash (per-password salt, work factor) is kept; the plaintext is discarded the moment it's hashed.
- Session & reset tokens
- Opaque random values — only their hash lives in the database, so a database or backup disclosure can't be replayed into a live session or a working reset link. Sessions are revocable and expire on a hard 14-day ceiling; reset links are single-use and expire in 20 minutes.
- Abuse resistance
- Login and password-reset endpoints are rate-limited; authentication responses are constant-time and generic, so they never reveal whether an account exists. The review console sits behind both app auth and a second HTTP-auth layer.
- Accountability
- Every reviewer decision is written to an append-only log, enforced by a database trigger with no update or delete path, bound to the authenticated identity — corrections capture a written reason and nothing is ever silently overwritten.
- Data minimization
- CFPB personal information is scrubbed before storage and before any model call; embeddings derive only from the scrubbed text.
Why we don't say "end-to-end encrypted"
End-to-end encryption means only the endpoints hold the keys and the server can't read the content. Clarion's server must read every narrative in plaintext — to scrub PII, generate embeddings, run semantic search, classify against the taxonomy, and render the console. The server is a designed reader of the data, so end-to-end encryption isn't merely missing here — it's architecturally incompatible with what the product does. Claiming it would be false. What we do provide — TLS in transit, AES-256 at rest, hashed credentials and tokens — is the correct, honest 2026-grade posture for an app of this shape.