Security architecture
The controls are the product, not the paperwork.
Most EHR security is described in a policy document and implemented as a filter somewhere in application code. Here it is the other way round: authorization, isolation, and audit are load-bearing architecture, and a change that weakens one of them fails the build.
Tenant isolation
Enforced twice, so one bug is not a breach.
Every row and every event carries a tenant id. Application code scopes each query to the caller's tenant, and PostgreSQL row-level security enforces the same boundary independently — a mistake in one layer is caught by the other.
| Tier | Data isolation | Encryption boundary | Blast radius | Typical practice |
|---|---|---|---|---|
| Shared, RLS-enforcedshared_rls | Logical — RLS on tenant_id | Shared customer-managed key, rotation enabled | The shared cluster, held by RLS and the isolation suite | Solo practices and small clinics (default) |
| Dedicated databasededicated_db | Physical — your own database | Your own customer-managed key | Your database only | Practices with strict separation or blast-radius requirements (every tier is US-hosted) |
| Dedicated environmentdedicated_env | Physical — your own database, bucket and keys | Your own KMS keys | No database, bucket or key is shared | High-assurance and contractual-isolation tenants |
Crossing a tenant boundary is not a configuration flag. It requires an explicit platform-scope grant and emits a security.cross_tenant_access event — so support access is visible in your own audit trail, not just in ours.
Tamper-evident audit
A log you can hand to an auditor without a caveat.
Events are append-only and hash-chained: each one commits to the one before it. Removing or editing an entry breaks the chain, and a broken chain is detectable. Every event carries the correlation id of the request that produced it, so everything one request did — the read, the policy decision, the AI call — comes back together.
Always audited
AI actions, break-glass access, exports, and privilege changes. No exceptions.
Redaction at the edge
PHI is minimized on the way into the log, not scrubbed afterwards.
Detections on top
Bulk-access spikes, after-hours reads on the practice’s clock, charts opened outside the care team.
Forwardable
Findings forwarded to a SIEM as OCSF or ECS, credited only once delivered.
Beyond a tamper-evident log
A log only its owner can check is a log its owner can rewrite.
A hash chain proves nobody edited the middle of the log. It proves nothing about a log that was rebuilt from scratch and re-signed — and that is the failure an auditor actually has to rule out. So the log does not stop at being tamper-evident; it can be published outward to a witness the practice arranges, somebody other than us who countersigns it. None is configured by default, and the security console says when nobody is countersigning.
Every consequential act is entered
A signed note, a prescription, a filed claim, an export, a break-glass access. The entry commits to the content without containing it, so the log proves what happened without becoming a second copy of the record.
Entries build a Merkle tree
An RFC 6962 append-only log — the same construction the public web uses to keep certificate authorities honest. Any entry can be proven to be in it, and any two states of it can be proven consistent.
The tree root is signed into a checkpoint
An Ed25519 signature over the root, at a point in time. This is where an ordinary tamper-evident log stops.
Checkpoints go to the witnesses a practice configures
Independent parties countersign what they saw — once a practice has arranged one. None is configured by default; until one is, a checkpoint is stored here only, and the security console says nobody is countersigning. This is the step that matters: a log only its owner can check is a log its owner can rewrite, and no amount of hashing fixes that.
Disagreement is detectable
If a practice were ever shown one history and an auditor another, the two would carry different witness cosignatures for the same tree size. That is a split view, and it cannot be hidden from anybody holding a checkpoint.
Proofs are served on demand
Inclusion and consistency proofs and a record integrity report a practice can hand to an auditor, each on the security console’s Record integrity tab, and a runnable reference witness that ships with the platform so nobody has to take our word for the protocol.
Patient receipts
Proof a patient holds, that does not depend on trusting us.
Every other assurance on this page is one a practice verifies. This is the one a patient can verify without an account, without our cooperation, and without taking anybody's word for it.
Proof they hold themselves
A patient can be handed a cryptographic inclusion proof that the history of their own record has not been rewritten — verifiable against the verification key the receipt carries, with no privileged access to the platform and no account with us. No public route serves that key; staff the practice authorises read it from the API. The receipt is issued, and a returned one checked, on the security console’s Record integrity tab.
It carries no clinical content
The receipt proves history, not diagnosis. Handing one to somebody discloses nothing about the care itself, which is what makes it safe to hand over at all.
It does not depend on trusting us
That is the entire point. A receipt that only verifies if the issuer is honest is a promise, not a proof.
The compliance programme
Proving a control works, and proving nobody reached past it.
Two halves of the same job. The GRC programme runs inside the record system it governs — frameworks, controls, evidence and the crosswalk between them. Department segregation is the other end: the records a practice holds that most of the practice has no business opening.
Frameworks, controls, and the crosswalk between them
Requirements map to canonical controls, controls carry tests and results, and evidence carries a freshness clock. The crosswalk shows one control satisfying equivalent requirements across frameworks, so answering a second auditor is not a second project.
Blast radius, and what to build next
For any control: what stops being satisfied if it fails. For any gap: what it would close if you built it. Both are read off the mapping rather than assembled by hand the week before an assessment.
Every mapping that counts was accepted by a human
Each mapping carries a written rationale, and none is inferred by similarity scoring — a crosswalk generated that way is one an assessor is right to reject. The HIPAA Privacy Rule and NIST CSF 2.0 mappings were drafted with a model: they ship marked as proposed and contribute nothing to coverage until a person accepts each one.
A service line carries a sensitivity
Behavioural health and substance-use records sit in departments the rest of the practice does not open by default. A standard line audits a crossing; a sensitive one challenges for a step-up; a 42 CFR Part 2 line refuses everyone but a member unless the practice raised break-glass.
Seniority is not membership
The practice owner is challenged on a behavioural health record exactly like anybody else. Part 2 has no exception for being senior, and the owner is often the clinician’s employer and sometimes their neighbour.
A vendor support grant does not open a sensitive department
Break-glass and support elevation look identical on the request, so a check for “is there an elevation” would admit both. Only a grant the practice raised, for a patient, inside its own walls, opens one.
HIPAA Security Rule
Technical safeguards, mapped to mechanisms.
45 CFR § 164.312 asks for specific things. This is what implements each of them — a named control in the codebase, exercised by the security test suite.
- Access control§ 164.312(a)(1)
- Deny-by-default AccessRequest to PolicyDecision (RBAC + ABAC), plus row-level security keyed on tenant id.
- Audit controls§ 164.312(b)
- Append-only, hash-chained audit events with correlation ids.
- Integrity§ 164.312(c)(1)
- A note or encounter is signed with a passkey over a hash of its exact content and cannot change afterwards — a correction is a new, signed note; the audit chain detects retroactive edits.
- Person or entity authentication§ 164.312(d)
- Passkeys scoped to your practice’s own address, enforced on every session and again before sensitive actions. Bring your own single sign-on if you already have one.
- Transmission security§ 164.312(e)(1)
- TLS in transit, HSTS preload, and a deny-by-default content security policy.
- Encryption at rest§ 164.312(a)(2)(iv)
- Encrypted at rest under a customer-managed KMS key, with rotation. Point-in-time recovery is specified in infrastructure code and has not been applied or rehearsed.
Control inventory
Every control, and the test that would fail without it.
24 controls, each naming a mechanism and the spec file that breaks if somebody weakens it. The security suite is a required merge gate, so these are claims with a build behind them rather than sentences somebody wrote.
Authorization
A route that declares no permission is denied
PolicyGuard refuses any handler with no @RequirePermission, so forgetting to declare one fails closed rather than open.
test/security/deny-by-default.spec.ts
Every decision carries a field-level scope
The policy decision returns the minimum-necessary shape of the response, so hiding a tab is never the control.
packages/policy-engine/src/__tests__/security/
Tenant isolation
One practice cannot read another’s rows
Every query is bound to the request’s tenant in application code and again by PostgreSQL row-level security.
test/security/tenant-isolation.spec.ts
Identity
A passkey works at one practice and nowhere else
The WebAuthn relying-party id is the practice’s own host, so a browser will not even offer a credential elsewhere.
test/security/passkey-step-up.spec.ts
Forged Med staff cannot enter a practice without an approved, scoped, expiring grant
A guard between authentication and authorization refuses a platform principal inside a customer tenant with no active grant.
test/security/support-access.spec.ts
Evidence
No request past the paywall completes without a record
The audit interceptor files an event for every authenticated request and every sign-in attempt, successful or not.
test/security/audit-coverage.spec.ts
An audit event cannot be altered or removed undetected
HIPAA 164.312(b)Per-practice HMAC hash chain persisted to an append-only table: each event commits to the one before it, the chain stops verifying if one changes, and it continues across a restart rather than beginning again.
packages/audit/test/hash-chain.test.ts, test/security/audit-durability.spec.ts
The record’s history is provable to somebody who does not trust us
A Merkle transparency log with Ed25519-signed tree heads. A consistency proof shows nothing was removed, reordered, or rewritten — checkable with the public key (served to a practice’s authorized auditors and carried in every patient receipt) and no access to anything of ours.
packages/audit/test/transparency.test.ts, test/security/transparency-ledger.spec.ts
A second history would be refused, not merely unnoticed
A witness, once a practice configures one, holds the last checkpoint it saw and countersigns only an extension of it. A vendor keeping two logs cannot get both signed — the second is refused, and the refusal is the evidence. None operates by default; run one yourself, or have somebody independent run it: scripts/witness/ is 200 dependency-free lines.
packages/audit/test/witness.test.ts, test/security/checkpoint-publishing.spec.ts
A checkpoint is owed to your destinations until it arrives
A durable outbox with backoff that never gives up, so a destination down for an hour costs minutes of evidence rather than that checkpoint forever. Coverage is reported as the newest tree size that actually reached you — not the number of checkpoints issued.
test/security/checkpoint-publishing.spec.ts
Evidence tables cannot be updated or deleted
21 CFR 1311.120(b)UPDATE and DELETE are revoked from the application role on the audit chain and the transparency log, with a trigger behind it.
prisma/rls/enable-rls.sql
A signature covers what the signer was shown, not a moment in time
The WebAuthn challenge IS the hash of the displayed content, so an edit between review and signature produces one that does not verify.
test/security/attestation.spec.ts
A signed note, encounter, or prescription cannot be edited
21 CFR 1311.140(a)(5)Enforced in the one place every mutation goes through. Corrections are amendments that name the original, which keeps its signature.
test/security/attestation.spec.ts, test/security/controlled-substances.spec.ts
PHI never reaches the audit trail
A redaction guard runs on every event’s context before it is chained, and the ledger leaf carries a content hash rather than content.
packages/audit/test/redaction.test.ts, test/security/attestation.spec.ts
Prescribing
Nobody can grant themselves the authority to sign controlled substances
21 CFR 1311.125Held by no role. Conferred per practitioner and per schedule by an entry one person proposes and a DEA registrant approves with their own credential.
test/security/controlled-substances.spec.ts
Two-factor authentication at each signing, not once per session
21 CFR 1311.140(a)(1)One ceremony signs one prescription: the challenge is consumed when claimed, and a sign-in step-up cannot be redeemed as a signature.
test/security/controlled-substances.spec.ts
An uncertified deployment cannot sign a controlled substance
21 CFR 1311.300A runtime gate, with no override flag. No certification, a lapsed one, or one naming a different build all refuse.
test/security/controlled-substances.spec.ts
Schedule rules are enforced, not displayed
21 CFR 1306.12, 1306.22Schedule I refuses outright; Schedule II carries no refills; III–V cap at five within six months. One definition, enforced by the API and rendered by the UI.
test/security/controlled-substances.spec.ts
Clinical safety
A severe interaction cannot be dismissed, only signed for
The prescriber’s passkey signs a hash of the alert text and their stated reason. Signing is blocked while one is outstanding.
Not yet, in full: No drug knowledge base is bound, so screening answers “not configured” and no interaction is raised until a deployment licenses and connects one.
test/security/interaction-override.spec.ts
Separation of duties
One account cannot quietly stop your receipts
Adding a destination takes one person; turning one off takes two. Anyone intending to rewrite history would begin by narrowing the evidence, so that is the direction the ceremony guards.
test/security/checkpoint-publishing.spec.ts
High-consequence actions need two people
Granting practice ownership, a bulk export, turning off a checkpoint destination, and accepting third-party API access: one proposes, a second approves with their own credential, the approval is signed over the exact terms, and an ownership approval grants once. Granting a prescriber controlled-substance authority takes two people too, in its own ceremony.
test/security/user-administration.spec.ts, test/security/bulk-export-scope.spec.ts, test/security/checkpoint-publishing.spec.ts, test/security/third-party-api-enabled.spec.ts, test/security/controlled-substances.spec.ts
Nobody can grant a permission they do not hold
The escalation check runs before any wildcard, so an account administrator cannot mint an owner and a practice cannot mint platform staff.
test/security/user-administration.spec.ts
AI governance
A high-risk AI action needs a human signature
Autonomy is a per-domain setting the practice owns, and the clinical default is human sign-off enforced by the obligation interceptor.
test/security/ai-approval.spec.ts
A model with no BAA never sees PHI
The gateway checks PHI eligibility per provider before routing, and a payload carrying PHI cannot reach an ineligible model.
packages/ai-gateway/src/__tests__/security/
What “tested” means here, precisely: a test in this repository asserts it, and a change that weakens it fails the build. It does not mean an auditor confirmed it — nothing below has been independently verified, and the next section says so.
Compliance status
What is true today, stated plainly.
Security marketing is usually written to imply more than it says. Here is the actual position, so you can evaluate it rather than decode it.
Implemented and tested
- Deny-by-default authorization with RBAC and ABAC
- Tenant isolation in application code and in PostgreSQL RLS
- Append-only, hash-chained audit with mandatory sensitive-action coverage
- MFA, step-up re-authentication, and audited break-glass
- ePHI encrypted at rest under a customer-managed KMS key with rotation — shared across practices on the default tier, your own on the dedicated tiers
- Controlled-substance prescribing controls: per-signature two-factor over the prescription itself from a separately enrolled hardware key — attested model, device-bound, synced passkeys refused, issued only after identity proofing and two-person approval — plus immutability after signing and the daily auditable-event report
- A security test suite that is a required merge gate
Not yet claimed
- No SOC 2 report or HIPAA attestation has been issued
- Point-in-time recovery and cross-region backup copies are specified in infrastructure code that has not been applied from this repository, and no restore drill has been run
- No independent third-party penetration test has been published
- Business Associate Agreements are executed per deployment, not implied by this page
- No DEA third-party EPCS certification has been issued — until one is recorded, the platform refuses every controlled-substance signature rather than pretending otherwise
- Identity proofing at IAL2 is performed by an approved provider, not by us; we record who did it
- EPCS signing keys must be a model the practice has approved; we verify the key is that model and not synced, but not that the model is FIPS 140-2 validated — no models are approved by default
- No drug interaction or allergy checking, and no automated PDMP lookup
- No certification of any kind is under way: we have mapped the ONC criteria against the platform one by one, and no certification body or auditor has been engaged
Before Forged Med holds real PHI in your practice, expect a HIPAA risk analysis, executed BAAs with every subprocessor that touches PHI, and verification of these controls in your own environment. We would rather say that here than in a footnote after you have signed.
Security is engineered by Forged Technology. Disclosures reach a human, not a queue.
