Overview
A refund-recovery service for small importers after a Supreme Court ruling invalidated IEEPA tariffs: upload customs data, see what you're owed, get connected to licensed professionals to file — racing protest deadlines that expire on a fixed legal schedule. 728 commits and 426 merged pull requests in 28 active days.
Project Design
Scaffolded in a single ~40-commit day. Privacy was a day-one decision, not a retrofit: analytics and error tracking shipped with importer-data scrubbing already wired in. A federal regulation shaped the product — a compliance audit of the site's own wording found 18 violations, and the forbidden phrasings were then wired into the build checks so a violation can't ship. Mid-build, the evidence forced a pivot from $79 self-serve filing to free analysis plus broker connection.
Key modules
ES-003 parser
Parses ACE entry exports by header name and classifies IEEPA vs. Section 301/232 tariff lines.
Entry classifier
Runs an 8-category eligibility triage with CBP CAPE Stage-2 edge-case flags.
CAPE CSV generator
Produces CBP-compliant declaration CSVs — deduped, check-digit validated, grouped by importer.
Clerk ↔ RLS bridge
Bridges non-UUID Clerk IDs into row-level security and merges anonymous sessions on sign-in.
PII scrubbing
A four-layer denylist keeps entry numbers, HTS codes, and duty amounts out of analytics and error reports.
Lead scoring
Scores broker-match leads on refund size, deadline urgency, and complexity; disqualifies sub-threshold entries.
Key features
RAG chatbot — security checks in the code, not the prompt
The site's assistant is a retrieval-augmented chatbot — it answers from a fixed knowledge base of CBP guidance rather than the model's memory — and its defenses live in the request path, not just the system prompt. Every message runs a gauntlet before the model is called: input validation, a twelve-pattern prompt-injection scan that returns a 400, IP rate limiting, and a relevance gate where the retrieval step itself refuses anything that doesn't resemble the customs knowledge base. Underneath, row-level security and database grants revoked from the public roles mean a prompt talked into misbehaving still can't reach data it shouldn't. The signed-in version answers questions about a user's own analysis from aggregates, never raw entry rows.
Read: A prompt is not a perimeter →
Compliance-as-CI — honesty enforced in the build
A federal customs-broker regulation limits what an unlicensed tool may claim. Rather than rely on careful writing, TariffRefunded turned the rule into a build check: a denylist of forbidden phrasings ("we produce your CBP declaration," "these are good to go") is grepped on every change to the public marketing pages, and a match blocks the merge unless a reviewer justifies it inline. The first audit found 18 violations across the site; wiring the denylist into CI kept them from creeping back as the product pivoted. The honest caveat: the gate only covers the pages it's pointed at — a phrase outside that scope can still slip through, so the boundary definition is the real control.
Security & ops decisions
- Analytics autocapture was off from day one — manual instrumentation only — backed by a four-layer PII scrubber covering client, server, error reports, and breadcrumbs.
- The most reliable scrubber is not storing the data: full importer-of-record numbers were dropped from the schema entirely, replaced by a suffix key and an opaque group ID.
- A federal regulation's forbidden phrasings live in a pre-commit denylist gate, so a compliance violation can't ship.
- Anonymous sessions got real security design: header-bound row access, and a merge function that raises rather than letting an unauthenticated caller hijack an account merge.
Builder notes
- Scaffolded in a single ~40-commit day: auth, hardened analytics, error tracking with scrubbing wired in, and a tariff rate table covering 12 regimes.
- The in-repo pentest harness grew to 13 attack-category test modules plus scanner automation — and was later extracted and open-sourced as StackBadger.
- A versioned strategy doc with an open-decisions table, audited against the live site on dated passes, produced numbered P0–P2 findings traceable to fixing PRs.
- Clerk user IDs aren't UUIDs: replacing auth.uid() with the JWT subject claim in eight places became a hard project rule after every authenticated query failed.
Lessons learned
- A market can have an expiration date: the addressable refund pool shrinks an estimated 8–10% per month as protest windows close — the strategy priced the decay in from day one.
- Regulatory compliance works best as an automated gate: the wording audit became a denylist in the build pipeline.
- A launch gate written in advance — no broker-match promise without a signed partner — is only worth what you'll sacrifice to honor it. It held.
- The pivot itself created new compliance violations: features kept promising a partner that didn't exist yet. Copy drifts toward whatever the feature wants to promise.
What carried forward
StackBadger was born here as the internal pentest harness. The PII-scrubbing patterns, the auth/data-isolation fix, and the decision-hygiene habit — a versioned strategy doc with an open-decisions table, plus dated site-vs-strategy audits — moved into everything after.
Posts from this project
A prompt is not a perimeter
My customs chatbot's system prompt politely asks attackers not to misbehave. That's the weakest control it has — the ones that actually hold are in the request path and the database.
The regulation in my build pipeline
A federal statute constrains what my product is allowed to say. An audit found 18 violations in my own copy — so I wired the forbidden phrasings into the build and made shipping one impossible.
Building for a market with an expiration date
A Supreme Court ruling created a multi-billion-dollar refund pool — one that shrinks every month on a fixed legal schedule. A decaying market changes every product decision.