The receipts.
For your IT, compliance, and security teams. This page documents the architectural commitments behind Praxis — the same posture we'd present to a HIPAA consultant or PE diligence vendor.
Privacy by design
Praxis is built so PHI neverreaches the cloud. De-identification runs at the practice, on the practice's own infrastructure, before anything crosses the wire. What lands in Praxis cloud has already had every HIPAA Safe Harbor identifier removed, dates generalized to month, ZIPs truncated to the first three digits, patient identifiers replaced with non-reversible HMAC hashes, and small-N cells suppressed. The salt that makes those hashes irreversible stays at the practice.
For analytics use cases (Praxis Intelligence), this means we never need a Business Associate Agreement (BAA) to handle data, because we're not handling Protected Health Information. Compliance scope shrinks dramatically; the analytics product ships without the $50K+/year compliance overhead a cloud-PHI architecture demands.
HIPAA Safe Harbor — what gets stripped
HIPAA Safe Harbor enumerates 18 categories of identifiers that must be removed for data to be considered de-identified. Praxis honors all of them at the practice-side tool. A summary of the most consequential transforms:
| Category | Treatment |
|---|---|
| Names, phone, email, address | Removed entirely. Never crosses to Praxis. |
| SSN, MRN, account numbers | Removed entirely. |
| Birth dates / exact age | Replaced with age band (e.g. 31-45). 90+ collapsed to 76+ per Safe Harbor. |
| Geographic identifiers smaller than state | ZIP truncated to first 3 digits. Restricted prefixes (population <20,000) suppressed to "000". |
| Specific dates (admit/discharge/visit) | Generalized to YYYY-MM. Day-of-week is not derivable. |
| Specific procedure codes | Mapped to category strings (e.g. "knee_replacement") per vertical. |
| Per-record dollar amounts | Bucketed into bands ($1,000-5,000, etc.). Aggregate totals across many records may be exact. |
| Patient identifiers | Replaced with HMAC-SHA256(salt, source_id) truncated to 16 hex chars. Stable across runs, irreversible without the salt. |
| Provider names + NPI | Kept (providers are not PHI subjects). |
| Referring provider info | Kept (not PHI subjects). |
The de-identification tool
Praxis ships a small, auditable de-identification tool that runs at each practice. It's open source under MIT license so your IT and compliance teams can read it, audit it, and run it on their own infrastructure. No black boxes. github.com/hurshpatel95-source/praxis-deid-tool ↗
- Standard runtime, deployable via standard schedulers (cron, Task Scheduler, systemd timer)
- Configured via a single YAML file — practice ID, source paths, salt, audit log location
- Append-only local audit log: every run records what was processed and what crossed the wire
- The salt that drives patient ID hashing is held by the practice; Praxis never sees it
- Comprehensive test suite: stable IDs, non-reversibility, no PHI in output, small-N suppression, schema conformance
- Designed for HIPAA consultant validation before any production deployment
Vendor stack
Every vendor in the Praxis stack offers a BAA at higher tiers. We start on standard tiers (no PHI yet); when a customer requirement triggers it, we upgrade tiers and sign BAAs without rewriting code.
| Vendor | Purpose | BAA path |
|---|---|---|
| Supabase | Postgres + Auth + RLS + Storage | In use · Team tier |
| Railway | Application hosting + workers | In use · Pro / Team tier |
| Anthropic | Agent runtime (Claude API) | In use |
| Resend | Outbound email for practice-manager briefings | In use |
Authentication & access control
- MFA (TOTP) enforced from day one — every protected route checks for AAL2; no AAL1 session reaches dashboards
- Row-Level Security at the database layer — application bugs cannot leak Practice A data to Practice B
- Three-layer data model: l1_* (operational PHI, when it ever exists), l2_* (de-identified aggregates), Layer 3 (organizations / practices / providers — non-PHI metadata). RLS gates each layer separately.
- Layer-1 access requires explicit grant beyond practice membership; sponsors get layer_2_only by default and a database trigger forbids them being granted higher
- Cross-org isolation: an org_admin in Org A cannot see Org B even with a misconfigured policy
- Defense in depth: application middleware enforces session AAL, database enforces tenant + layer, BEFORE-UPDATE triggers prevent users from rewriting their own email or role
Audit trail
Every meaningful action lands in an append-only audit log, indexed by user, practice, organization, action type, and timestamp. Designed for BAA-relevant questions:
user.login (provider + auth method)user.logoutauth.signin_failedmfa.enrolled / verified / unenrolledauth.password_reset_requesteduser.created / deleted / role_changed / email_changedaccess.granted / revoked / layer_changed / reinstatedpractice.created / updated / deletedorganization.created / updatedadapter_config.created / updated / deletedingestion.started / succeeded / faileddata.export (when implemented)Audit rows are forensic — they survive deletion of the entity they reference. Salt and other secrets are never logged; the practice-side tool's local audit log enforces the same rule.
How we know it works
Architectural promises that aren't tested are wishes. Praxis ships with a verification harness — every commit runs assertions against a live staging database to prove the invariants still hold.
125 verified invariants — including the load-bearing claim: after a full round-trip ingestion against the live database, the count of rows in the operational-PHI layer (l1_patient) for the practice is unchanged. PHI never crossed the wire.
Status: Praxis is in private staging. HIPAA consultant validation of the de-identification tool is scheduled before first production deployment. BAA tier upgrades are sequenced to compliance triggers (revenue, customer requirement, or sponsor diligence). This page is the technical claim — the validation report and BAA inventory will live alongside it.