Companion to the IT & Security Review Packet and Security White Paper (§3–§6). Everything below is verified against the shipped code; file references are given so your reviewer can spot-check — they refer to the vendor's (HelixWrks) BenchForge source tree, not paths inside your deployment. Where this page and those documents differ, they control.
BenchForge is a Next.js browser application whose deterministic economics engine runs client-side (packages/engine). Persistent state lives in Supabase managed Postgres (US) behind Row-Level Security keyed to tenant membership; the browser talks to it with the public-by-design anon key, which grants nothing on its own — authorization is enforced in the database, not the client. Every workspace write funnels through one SECURITY DEFINER function (save_lab_state) that re-checks role and per-assay scope, enforces optimistic concurrency, checks the billing entitlement, size-limits the payload, is throttled by a save-storm breaker, and writes an audit entry. Two high-sensitivity flows deliberately never leave the browser at all (the engine, and 835/ERA remittance parsing); one deliberate egress path works without a login (Board share links).
The system diagram is given as Mermaid flowchart source — paste it into any Mermaid renderer for the picture; the boundary table beneath it carries the same information in prose.
flowchart TB
subgraph Browser["User's browser"]
ENGINE["Deterministic economics engine<br/>(packages/engine — runs client-side)"]
UI["BenchForge web app (Next.js/React)"]
P835["835/ERA parser (lib/integration/edi835.ts)<br/>raw payer file parsed HERE —<br/>NM1 names / REF member IDs / CLP01 skipped,<br/>never read; file never uploaded"]
CSVSCREEN["CSV actuals import —<br/>parsed + PHI-screened in browser;<br/>only computed figures saved"]
UI --- ENGINE
P835 -- "aggregates only<br/>(paid/billed by CPT, claim counts, payer name)" --> UI
CSVSCREEN --> UI
end
subgraph Vercel["App tier — Vercel (US)"]
EDGE["Hosting / edge (TLS)"]
API["Server API routes (withAuth)<br/>SOP parse · server ingest · support · Stripe"]
SPLASH["Public splash + og-image routes<br/>server-rendered marketing only — no tenant data"]
end
subgraph Supabase["Data tier — Supabase (US)"]
AUTH["Auth (email+password ·<br/>opt-in TOTP, app-layer gate)"]
RLS[("Postgres, RLS per tenant<br/>(app.my_tenant())")]
SAVE{{"save_lab_state<br/>SECURITY DEFINER chokepoint:<br/>role + assay scope · entitlement ·<br/>concurrency · size cap · breaker · audit"}}
AUDIT[("audit_log — no client<br/>update/delete · 180-day sweep")]
STORE[("File storage —<br/>attached SOPs/diagrams")]
RESOLVE{{"resolve_share_link (anon-callable)<br/>frozen snapshot only, no metadata"}}
PRELOGIN{{"record_pre_login_event (anon-callable)<br/>4-event allowlist · no free text ·<br/>session/source/global rate caps"}}
ERR[("error_events — server-stamped<br/>tenant/user id · 500-char message ·<br/>NOT opt-out/DNT gated · 90-day sweep")]
SAVE --> RLS
SAVE --> AUDIT
end
AI["Anthropic (US)<br/>one-shot SOP-text parse"]
EMAIL["Resend (US) — operational email"]
STRIPE["Stripe (US) — billing, when enabled"]
RECIP(["Link recipient — no account"])
UI -- "TLS" --> EDGE
UI -- "TLS (anon key + session JWT)" --> RLS
UI -- "workspace writes" --> SAVE
UI -- "error reports" --> ERR
SPLASH -. "pre-login funnel events (opt-out/DNT gated)" .-> PRELOGIN
API -- "gated single call:<br/>entitlement → tenant AI kill-switch (fails closed) →<br/>server PHI screen (fails closed) →<br/>rate limit 10/min·100/day (fails closed) →<br/>monthly $ budget pre-charge (fails closed)" --> AI
API --> EMAIL
API --> STRIPE
RECIP -- "/s/<token> — 192-bit, expiring ≤180d,<br/>revocable, 50-cap, audited" --> RESOLVE| Boundary | What crosses | What never crosses |
|---|---|---|
| Browser → Supabase | Model state (workspace JSON) via save_lab_state; reads under RLS; error reports (identity stamped server-side); product-usage events (opt-out/DNT) | Raw 835/ERA files; raw CSV actuals files (both parsed in-browser — aggregates only are saved) |
| Browser → Vercel API routes | SOP text for AI parse; ingest rows for server-side mapping; support messages — each PHI-screened server-side, failing closed | SOP PDF/image bytes (attachable to storage, but never accepted by the AI route) |
| API route → Anthropic | De-identified SOP text / import rows, once, at the user's request, after the five gates in the diagram | Attached files; anything when the tenant AI kill-switch is on (fails closed) |
| Supabase → anonymous internet | One share-link snapshot per valid token (frozen Board Summary figures only) | Raw lab state, assay detail, membership, audit data, link metadata; unknown/expired/revoked tokens are indistinguishable |
| Anonymous internet → Supabase | Access-request intake, access-status/admin-probe checks, pre-login telemetry (allowlisted, rate-capped, no free text) | Any table read or write — the anon role has every table privilege revoked |
| API routes → Resend / Stripe | Operational email (owner email, workspace name, reconciliation status, support-message content); payment data when billing is enabled | Model/workspace contents |
- The anon key is public by design. It authenticates the app, not a user; RLS + the RPC chokepoint are the
enforcement. A modified client cannot widen its access (verified by an automated tenant-isolation test).
- MFA is an application-layer gate — precisely scoped in the packet's authentication row, including its
fail-open on a probe failure and the absence of database-layer assurance enforcement (roadmap).
- The entitlement gate fails open only on a transient couldn't-determine; a definitive "no entitlement" is
always refused, and the database-side save gate independently refuses unentitled writes.
- Remit cadence / renewal KPIs (newer features) are pure derivations over already-persisted lab state —
they add no new collection or egress; the monthly "remits not loaded" nudge is in-app only, never emailed.
- Code spot-check map: engine
packages/engine/src· 835 parserlib/integration/edi835.ts(+ its
PHI-exclusion test edi835.test.ts) · save chokepoint supabase/migrations/0015/0016/0053/0064/0072 · share links supabase/migrations/0020_board_share_links.sql · AI gates app/api/sop/parse/route.ts · pre-login telemetry supabase/migrations/0073/0074 · error stream lib/observability.ts.