Trust
Built to be defensible.
Documented to be auditable.
Server-stamped customer agreements. Append-only financial and assent ledgers. Policy versioning with a pre-commit drift gate. GPC-honored consent. A documented review posture — not a legal certification.
21+ age gate
A 21+ affirmation, on the door and on every order.
Visitors must affirm they are 21 years of age or older and acknowledge the research-use-only nature of USPP products before they can browse or check out. The affirmation carries the 503A/503B statutory position and the operator-locked "will not administer, ingest, inject…" warning.
Acknowledgement persists in local storage with a 30-day TTL and is re-stamped onto every order at checkout (age_ack, age_ack_version) — so the record on each order is the version active when that researcher actually agreed.
A failure-to-affirm routes to a dedicated /restricted-access page that is intentionally bare: no analytics, no shared chrome, locked operator-dictated text.
What gets captured per order
- 21+ affirmation version active at checkout
- Local-storage TTL: 30 days
- Statutory positioning: 503A / 503B
- Fallback page:
/restricted-access(locked copy)
Consent modes
| Mode | Coverage | Banner |
|---|---|---|
| GDPR | EU27, EEA (IS, LI, NO), UK, Switzerland | Full opt-in modal; analytics off until accepted |
| CCPA | CA, CO, CT, VA, UT, TX, OR, MT | Opt-out notice; analytics on by default |
| None | All other regions | Silent |
Jurisdictional consent
Mode is server-derived from the Vercel IP-geo header.
Visitors map into GDPR, CCPA, or None at the edge — before any consent banner renders. A missing IP-geo header defaults to the strictest mode (GDPR), so dev environments and edge failures stay protected.
The Sec-GPC: 1 browser signal is honored regardless of jurisdiction. It forces opt_out_sale_share and opt_out_targeted_ads to true and writes a GPC event to the audit log.
Rejecting analytics purges GA, GA4, GAID, Clarity, MUID, and related cookies on the spot. The consent record lives in uspp_consent_v3 with categories, mode, decision time, expiration, and source.
Privacy Choices opt-out
A standalone CCPA / CPRA opt-out at /privacy-choices.
Three toggles — do not sell or share, limit targeted advertising, limit sensitive personal information — plus a GPC pill that reflects the current signal.
Preferences sit in two tables: privacy_preferences keyed by anonymous anon_id for un-logged-in visitors, and privacy_preferences_customer keyed by customer_id for signed-in researchers. A migration on account creation carries anonymous preferences across, idempotently.
Every toggle change writes one append-only row to privacy_audit_log — field, previous value, new value, source (user / GPC / agent request / migration), consent version, truncated user agent. The audit log carries no IP address (data minimization).
Audit row shape
- Field changed
- Previous value → new value
- Source: user, GPC, agent request, migration
- Consent version at the moment
- Truncated user agent
- No IP captured
order_assent row, per order
- RUO, age, shipping, combined-legal acknowledgement versions
- Snapshots: Terms of Sale, Terms of Use, Privacy Policy, RUO disclaimer
- Age-gate version, consent-banner version, banner mode (gdpr / ccpa / none)
- SHA-256 hash of the exact checkbox text shown at submission
- Customer IP (XFF leftmost via Vercel,
req.ipfallback) - Truncated user agent
assented_attimestamp
Order-time agreement capture
Every order writes one immutable assent row, server-stamped.
At order create, the server stamps the exact policy version active at submission, the customer IP, the user agent, and a SHA-256 hash of every checkbox label the researcher just acknowledged. The row lands in order_assent, which is append-only at the database level.
Corrections never overwrite. A financial_postings_immutable-class trigger raises an exception on UPDATE or DELETE; new rows point back via reverses_id when an event has to be re-stated.
The base orders table additionally carries the RUO acknowledgement text (ruo_ack_text) and version (ruo_ack_version) directly on the order row as a fallback evidence layer, plus an append-only order_status_log capturing every from/to status transition with actor and reason.
The customer IP capture is the chargeback-evidence layer: any later dispute can verify the exact wording the researcher saw and the network they submitted from.
Policy versioning
One registry, canonical text exports, pinned-version permalinks, pre-commit drift gate.
policy-versions.json is the single source of truth: every policy slug carries a current version, source type (canonical text export, versioned HTML, or static HTML), and a version history.
Canonical text exports live in lib/policy-text.js — RUO_AFFIRMATION_TEXT, AGE_AFFIRMATION_TEXT, AGE_GATE_TEXT, and others — so every surface that displays them reads from one place.
Versioned legal HTML lives under legal-content/<slug>/<YYYY-MM-DD-vN>.html as immutable historical artifacts. Pinned URLs (/legal/:slug/:version) serve any historical version on demand — so a researcher (or their counsel) can read the exact terms they agreed to at order time.
A pre-commit hook (tools/policy-check.js, installed per worktree via tools/install-hooks.sh) hashes the current-version files. Any edit to a policy surface that forgets to bump policy-versions.json blocks the commit.
The drift gate at commit time
- Hashes every current-version policy file
- Compares to
policy-versions.json - Mismatch → commit blocks
- Operator must bump the version, then commit
- Historical versions stay immutable and pinned
financial_postings columns
kind: sale, refund, tax_collected, tax_refunded, shipping_collected, discount, adjustmentrail: bankful, venmo, solana, btcpay, zelle, manualjurisdiction: US-TX or US-OTHER (from shipping address)idempotency_key: UNIQUE — safe for live hook + daily backstopreverses_id: reversal-chain pointer- Indexes: period-by-kind, jurisdiction-by-period, rail-by-period, order-level
Immutable financial postings
One row per money event. One reversal chain. One export path.
financial_postings is the canonical money ledger. Every sale, refund, tax collection or refund, shipping collection, discount, and manual adjustment writes exactly one row. Append-only at the database level: UPDATE and DELETE raise an exception. Corrections must point back via reverses_id.
A unique idempotency_key lets the live posting hook (fires on paid + refund) and the daily backstop cron (catches anything missed) both run safely against the same event.
lib/finance-export.js produces a QuickBooks IIF export with configurable account mapping (TRNS / SPL / ENDTRNS blocks) plus a CSV path for per-period journal import.
Audit logs
Ten append-only tables. One per concern.
Every operationally meaningful action writes to an append-only audit table. None of these tables permit UPDATE or DELETE through application code; some are protected by a database trigger.
| Table | What it records |
|---|---|
order_assent |
Customer agreement capture at order time: versions, snapshots, checkbox-hash, IP, UA, timestamp. |
order_status_log |
Every from-status → to-status transition with actor, reason, timestamp. |
financial_postings |
Every money event with reversal chain, idempotency key, jurisdiction, rail. |
customer_audit_log |
CRM mutations (note delete, tag delete, task delete) and authentication events. |
customer_tier_audit |
Per-researcher pricing-tier changes with admin email and reason. |
privacy_audit_log |
Every privacy-preference change with source (user / GPC / agent / migration). |
stock_ledger |
Every inventory movement: receive, allocate, return, reconcile, adjust. |
webhook_outbox |
Every outbound webhook attempt with status, attempt count, error. |
rate_limit_hits |
IP + bucket + timestamp for rate-limit enforcement. |
coa_files |
COA upload metadata. |
Research-Use-Only copy controls
A documented review skill, canonical text exports, locked compliance surfaces.
USPP positions as a chemical supplier to researchers — not a pharmacy, supplement vendor, wellness brand, or therapeutic provider. The project carries a dedicated SEO Regulated Claims Guard skill at .claude/skills/seo-regulated-claims-guard/ with three files: a procedure (SKILL.md), a rule posture (RUO_COPY_RULES.md), and 37 numbered regex rules with category, rationale, and suggested rephrasing (HIGH_RISK_PHRASES.md).
Required vocabulary includes "researcher" (never customer / user / patient), "chemical supplier" (never vendor), "released" or "analytically released" (never approved / verified for use / certified), "lot" (preferred over batch), and "RUO" present at least once per product or policy page.
Hard bans cover therapeutic verbs, human-use guidance, outcome claims, superlatives, supplement framing, comparative-drug claims, and bodybuilding lexicon.
Honest scope. The skill is a documented review process, not a build-time enforcement. The codebase contains two operator-marked review traces (an explicit row-19 exemption comment in the landing and bundle pages) but does not contain an automated log proving every existing storefront string has been scanned. Site-wide application of the guard against every researcher-facing surface remains an operator follow-up.
Surfaces under explicit version control
- RUO affirmation at checkout (
RUO_AFFIRMATION_TEXT) - Age-gate text + 21+ affirmation (
AGE_GATE_TEXT,AGE_AFFIRMATION_TEXT) - Shipping affirmation, combined-legal statement, consent-banner copy
- Every versioned legal HTML body under
legal-content/<slug>/ - Pre-commit hook blocks edits that forget the version bump
Security & reliability
Six controls that anchor the perimeter.
Need to verify a specific control?
Every claim above maps to a documented code path. Email the team if you want to walk through a specific safeguard for compliance review or audit.