2026 · CUSTOMS SAAS

TariffRefunded

A live service whose prewritten broker-match launch gate held when the required partner was absent.

LIVE Built the product workflow, privacy posture, and compliance checks.
On this page

CONTEXT

The work started here.

A refund-recovery service had to respond to a time-limited tariff refund opportunity while handling importer information carefully.

BUILD

What was built.

A refund-recovery service for small importers. In February 2026 the Supreme Court ruled that a class of tariffs imposed under the IEEPA emergency-powers statute had no legal authority, which opened a refund path on the duties already collected. Upload your customs entries, see what you're owed, and get connected to a licensed customs broker to file. All against protest deadlines that expire on a fixed legal schedule. 729 commits and 427 merged PRs in 29 active days.

Upload-to-broker flow
Customs entry dataRefund analysisProtest-deadline flagsBroker match

PROJECT DESIGN

How it was shaped.

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 customs regulation shaped the product. A compliance audit of the site's own wording found 18 violations, so the forbidden phrasings got wired into the build checks, where a violation can't ship past the pages the check covers. And mid-build, the evidence forced a pivot: from $79 self-serve filing to free analysis plus a broker connection.

Anonymous-first analysis funnel — React SPA, Netlify Functions, Supabase RLS
Frontend
React 19 + ViteReact Router 7Tailwind 4Anonymous-first upload/analysis
API / edge
~20 Netlify FunctionsES-003 parser + entry validatorCAPE CSV generatorStripe + webhooksRAG chatbot (OpenAI)
Data
Supabase Postgres + RLS (28 migrations)Anon-session merge-on-authIOR privacy scrub (suffix key only)
External
Clerk (JWT ↔ RLS bridge)StripeResend emailPostHog + Sentry (entry-data denylist)

Key modules

Data

ES-003 parser

Parses ACE entry exports by header name and classifies IEEPA vs. Section 301/232 tariff lines.

Pipeline

Entry classifier

Runs an 8-category eligibility triage with CBP CAPE Stage-2 edge-case flags.

Data

CAPE CSV generator

Produces CBP-compliant declaration CSVs — deduped, check-digit validated, grouped by importer.

Auth

Clerk ↔ RLS bridge

Bridges non-UUID Clerk IDs into row-level security and merges anonymous sessions on sign-in.

Security

PII scrubbing

A four-layer denylist keeps entry numbers, HTS codes, and duty amounts out of analytics and error reports.

Pipeline

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.

MessageValidateInjection scan → 400Rate limitTopic gate (RAG)Model
The first four checks resolve before a token is spent — the perimeter is in the request path, not the prompt

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.

Code changeCI greps the forbidden-phrase denylistMatch in a covered page?Blocked — fix or justify inlineMerge
The rule lives in the pipeline, not in a style guide — a violation can't ship past a covered page

SECURITY & OPS

The operating 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.
Importer-data protection
Importer customs data
Not stored: full importer-of-record numbers dropped from the schema — a suffix key and an opaque group ID stand in
Analytics autocapture off from day one — manual instrumentation only
Four-layer PII scrubber: client, server, error reports, breadcrumbs
Anonymous sessions: header-bound row access; the merge function raises rather than letting an unauthenticated caller hijack an account

BUILDER NOTES

What the build taught.

  • 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

What changed afterward.

  • A market can have an expiration date. The addressable refund pool shrinks an estimated 8–10% a month as protest windows close, and the strategy priced that decay in from day one.
  • Regulatory compliance works best as an automated gate, not a style guide: the wording audit became a denylist in the build pipeline. But a gate is only as good as its scope — one forbidden phrase survived in a page the check didn't cover.
  • 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 CHANGED

Evidence changed the commercial path.

The product pivoted from a self-serve filing offer to free analysis plus a broker connection when the evidence did not support the original path.

CONTROL

The constraint that held.

Privacy scrubbing shipped from day one, and wording violations were turned into build checks for the pages they cover.

EVIDENCE

Where to inspect it next.

  • 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.
Featured field noteRead The regulation in my build pipeline

CARRIED FORWARD

What survived the project.

StackBadger was born here, as the internal pentest harness. So did three habits that moved into everything after: the PII-scrubbing patterns, the Clerk-to-RLS auth fix, and a decision-hygiene routine — a versioned strategy doc with an open-decisions table, plus dated audits that diffed the live site against it.

WRITING

Field notes from this work.

Trust belongs in the schema, not the application.
The operating principle behind every project here. A rule the database enforces can't be forgotten in a hurry. Constraints, denied-by-default access, append-only logs — the controls that hold are the ones the system won't run without.