WorkThe April wave
PHIGuard
HIPAA-cited task management and compliance tracking for small clinics with no IT department, with an append-only audit trail behind every write to protected health data.
- Ran on
- Cloudflare
- Ran
- April 2026 to June 2026
- Domain
- Healthcare compliance
That platform choice is part of a longer migration: Azure, then Railway, then Cloudflare, across the whole estate. Read how and why.
Built with
- Cloudflare Workers
- TanStack Start
- React 19
- Astro 5
- PostgreSQL
- Drizzle
- D1
Hosted services
- Cloudflare Workers
- Hyperdrive
- D1
- R2
- Cron Triggers
- Turnstile
- Neon Postgres

The decision
Every schema module that stored or referenced protected health information carried a *.phi.ts
filename, 18 of 48 schema modules, enforced by two review agents so that answering “which tables
can hold PHI?” was a filename search instead of a code review.
Coverage of that convention went past unit tests. packages/integration’s audit-coverage suite
started a real PostgreSQL instance through testcontainers rather than mocking the database, and
asserted that each of 10 named PHI-adjacent mutation paths wrote its audit event, 11 event types
once accepting legal documents is counted as recording the Terms and the BAA separately.
The audit trail sitting behind those tables was append-only behind a Postgres trigger, and that guarantee held against the project’s own tooling: the repository’s demo seed script counted existing audit rows before it ran and refused to delete the workspace it had created, telling the operator to recreate the database instead. Every clinic, patient record, and audit trail shown anywhere in this repository, including the screenshots above, comes from that same seed script: synthetic data generated from a fixed faker seed.
A production smoke check caught what a fully green build had missed. A deploy had removed the
per-request construction of the database client and the auth instance, reasoning it was Hyperdrive
boilerplate; on a multi-tenant PHI database, a Worker isolate’s shared module scope turned that
into a tenant-isolation bug, and the audit trail would have gone on recording the wrong actor.
turbo build, turbo typecheck and turbo test all passed regardless. pnpm smoke:prod running
against the deployed Worker did not: /healthz came back 503 database:error and sign-in returned
intermittent 500s, so a wrangler rollback and three restored files closed the gap the suite
missed. The permanent fix kept the pattern that caused it rather than routing around it:
drizzle-orm/neon-http would have removed the socket, but it also removes interactive
transactions, and this codebase leans on transaction() at roughly 75 call sites, including the
auditedWrite helper that writes a mutation and its audit event inside one transaction so no PHI
write path exists without one. Three regression tests now fail if the scoping is removed again,
which is the difference between a decision and a document.
Architecture
Two independently deployed Cloudflare Workers shared one TypeScript monorepo: a marketing plane that never touched patient data, and a product plane that did.
The marketing site’s leads and rate-limit buckets lived in Cloudflare D1, a physically separate store from the Postgres database holding PHI, which was why a browser analytics SDK was allowed on the public site and forbidden behind auth. Authenticated analytics went through a same-origin proxy with an explicit event allowlist and a scalar-only property sanitizer, so no third-party JavaScript could run on a page that could render PHI.
The health check was split in two for a similar reason on the infrastructure side. A /healthz
liveness check deliberately ran no database query, because a Hyperdrive connection attempt against a
suspended Neon compute wakes it and starts it billing; a separate /readyz readiness check was the
only path allowed to run select 1 against the database.
By the numbers
Each figure carries where it came from and when it was measured. Open one to see the command behind it, where the repository recorded a command.
- Authored code
- 159,092 lines across 835 files
Provenance
Source portfolio/METRICS.md § Code
node scripts/portfolio-metrics.mjsMeasured
- Test-to-source ratio
- 0.66 : 1 (57,202 lines of tests against 87,205 lines of source)
Provenance
Source portfolio/METRICS.md § Code
Measured
- Test cases
- 2,109 across 182 files, 331 suites
Provenance
Source portfolio/METRICS.md § Tests
Measured
- Coverage
- 94.2% across the twelve packages, everything except ui, brand and config
Provenance
Source README § Testing
Measured
- PHI-tagged schema
- 18 of 48 schema modules named *.phi.ts
Provenance
Source portfolio/METRICS.md § Data model
Measured
- Data model
- 49 Postgres tables, 29 enum types, 62 migrations; 3 Cloudflare D1 tables, 5 D1 migrations
Provenance
Source portfolio/METRICS.md § Data model
Measured
- Surfaces
- 41 product routes, 18 API routes, 46 marketing page templates, 869 marketing content entries
Provenance
Source portfolio/METRICS.md § Surfaces
Measured
- History
- 876 commits, 2026-04-14 to 2026-08-11
Provenance
Source README § About this snapshot
Measured
Screens



Its portfolio/ directory holds the architecture, metrics and testing documents these figures were taken from.