HIPAA Compliance Gap-Assessment

revique-api security architecture assessed against the HIPAA Security Rule — 45 CFR §§164.308, 164.310, 164.312
revique-api  ·  environment hptprod  ·  account 501****  ·  regions us-e****-1 / us-e****-2  ·  generated 2026-08-03  ·  values masked
Disclaimer. This is an internal engineering HIPAA gap-assessment. It is not a certified HIPAA audit, a compliance attestation, or a legal determination. Formal compliance requires a qualified assessor and an administrative review of policies, procedures, training records and Business Associate Agreements — none of which can be established from source code and infrastructure telemetry. The percentage below measures technically-verifiable posture only and must not be quoted as a compliance score.

Executive summary

revique-api is a TypeScript / Fastify service on Node 20 running as 4 ECS Fargate tasks across 2 services in us-e****-1, backed by an Aurora PostgreSQL Serverless v2 cluster and an S3 patient-files bucket, and still federated to a legacy AWS Lambda / API Gateway / MySQL platform in us-e****-2. Both tiers hold ePHI.

The engineering fundamentals are genuinely good. Tenant isolation is fail-closed and enforced twice over; there is no SQL injection exposure across 386 query sites; transport security at the edge is modern (TLS 1.2 floor with TLS 1.3); every database, volume and bucket in the native estate is encrypted; CloudTrail is multi-region with log-file validation and S3 object-level data events; GuardDuty is on in both regions with zero open findings; and the observability layer deliberately HMAC-fingerprints source IPs and account identifiers rather than storing them raw — a privacy control that goes beyond what HIPAA asks for.

The shortfalls are concentrated and specific. The application does not record who accessed which patient record, which is the heart of §164.312(b) and also what would make a breach assessment possible. Four of five console-capable AWS identities have no MFA. One legacy database is unencrypted at rest. The production API connects to its PHI database over TLS but does not validate the server certificate. And role-level “minimum necessary” permissions, though fully built, are enforced on only 9 of 48 route modules — not on the patient-facing ones.

Estimated technically-verifiable posture
52%
Overall plausible range 38% – 64%, depending entirely on whether the 9 administrative / procedural controls below turn out to be documented.
0%lower bound 38%upper bound 64%100%

Scorecard

5
Compliant
1
AWS-Managed
15
Partial
5
Gap
9
Manual-Review
35
Controls assessed
Safeguard categoryControlsScoredPointsSection scoreWeightContribution
Technical — §164.31216157.550.0%45%22.5
Administrative — §164.308126350.0%35%17.5
Physical — §164.31075360.0%20%12.0
Overall352613.5100%52.0%

Methodology — how the number was produced

Each applicable control is scored Compliant = 1.0, Partial = 0.5, Gap / Critical = 0. AWS-Managed counts as 1.0 because responsibility transfers to AWS under the Shared Responsibility Model — but only if the AWS BAA is signed (control A12). N/A controls are excluded. Manual-Review controls are excluded from the headline percentage rather than guessed at, and are listed in full further down this page.

The three categories are weighted Technical 45% / Administrative 35% / Physical 20%. The rationale: technical safeguards are where an application-layer assessment has the most evidence and where the most exploitable risk sits, so it takes the largest share; administrative safeguards carry more weight than physical because HIPAA enforcement actions overwhelmingly turn on missing risk analyses, policies and BAAs rather than on data-centre security; physical safeguards take the smallest share because they are almost entirely discharged by AWS.

The range is produced by re-running the same arithmetic twice: the lower bound (38%) treats all 9 Manual-Review controls as failing, and the upper bound (64%) treats them all as satisfied. The true figure sits between the two and cannot be narrowed without the documentation. The headline 52% is the score across scored controls only.

Read this honestly: a percentage is a prioritisation aid, not a compliance statement. An organisation can score well here and still be non-compliant for want of a risk analysis or a single BAA; the converse is also true. Treat the findings, not the number, as the deliverable.

What would move the number most

Highest-leverage changes, in order

  1. Emit a PHI-access audit event — closes the single largest technical gap (T8) and unlocks breach-notification readiness.
  2. Enforce MFA on the four AWS console identities that lack it (T13) — hours of work, removes a critical exposure.
  3. Produce the administrative evidence set — 9 unscoreable controls carry 35% of the weight; documenting them is worth more percentage points than any single technical fix.
  4. Set DATABASE_SSL_REJECT_UNAUTHORIZED=true (T15) — a one-line task-definition change; the pattern already exists in the repo.
  5. Encrypt the legacy MySQL secondary (T6) — the only unencrypted PHI store in the estate.

Top risks

  1. No PHI-access audit trail. The application cannot evidence who viewed which patient record. §164.312(b).
  2. Unencrypted legacy database. The 2019 MySQL secondary holds legacy platform data with StorageEncrypted=false. §164.312(a)(2)(iv).
  3. MFA absent on 4 of 5 console identities with production PHI access. §164.312(d).
  4. Database TLS certificate not validated in production. §164.312(e)(1).
  5. Legacy databases publicly accessible — restricted to four static addresses, but reachable across the internet with no VPN or bastion. §164.312(a)(1).
  6. Audit retention 180 days on the native tier against a 6-year expectation. §164.316(b)(2)(i).

Architecture — how PHI moves through revique-api

Provider browser (por**** / adm**** origins, CORS allow-list)
  ↓  HTTPS — Cloudflare DNS → ALB, TLS 1.2 floor / TLS 1.3, :80 redirects to :443
  ↓
ECS Fargate — cluster revique-api-prod-cluster, awsvpc, 4 tasks / 2 services (API + Payments)
  ↓  preValidation guard — strips client identity headers → asserts x-forwarded-proto = https
      → authenticate: legacy-compatible mode in prod — server-side session verification over HTTPS
      → assertTenantContext() — practiceId must be present and must match the principal (fail-closed)
      → feature-level RBAC check — only on 9 of 48 modules
  ↓
Repository layer — every query parameterised and scoped by practice_id; reads in READ ONLY transactions
  ↓
Aurora PostgreSQL Serverless v2 — private subnets, KMS-encrypted, Multi-AZ, IAM auth available
      credentials from Secrets Manager  |  TLS certificate not validated

Patient documents  →  S3 presigned URLs — upload 900s / download 600s TTL, SSE, TLS-only policy, versioned
Legacy platform  →  API Gateway vax**** + Lambda + MySQL in us-e****-2  (public, 1-day backups, one unencrypted)
Audit  →  CloudTrail auditlog-hipaa (multi-region, validated, KMS, S3 data events)  |  no application PHI-access trail

Where the safeguards sit. Authentication and tenant authorization are centralised in a single Fastify preValidation hook, so no route can accidentally opt out of them — and routes that fail to declare a tenant context are rejected rather than allowed through. That is the right shape. The weakness is one layer in: once a request is inside the correct practice, there is usually nothing further restricting which records that user may read.

Safeguard sections

Full control tables with per-control evidence:

SectionControlsCompliantPartialGapManual-ReviewScore
Technical — §164.312 →16 474150.0%
Physical — §164.310 →7 221260.0%
Administrative — §164.308 →12 060650.0%

Prioritised remediation roadmap

High§164.312(b)Emit a PHI-access audit eventAdd a first-class access-audit record (actor, subject/patient id, action, route, correlation id, timestamp) on every read and write of PHI, written to an append-only store. Extend the existing rbac_audit_events pattern — the plumbing already exists — or add a dedicated phi_access_events table. This is the single largest technical gap and it also gates breach-notification readiness.
High§164.312(a)(2)(iv)Encrypt the legacy MySQL secondaryThe 2019 instance runs unencrypted. Encryption cannot be enabled in place: snapshot → copy the snapshot with a KMS key → restore → cut over. Plan a maintenance window; verify the primary’s encryption key management at the same time.
High§164.312(d)Enforce MFA on every AWS console identityFour of five console-capable users have no MFA device. Register TOTP devices and attach an IAM policy that denies all actions when aws:MultiFactorAuthPresent is false. Fastest high-impact fix in this report.
High§164.312(e)(1)Validate the database TLS certificateSet DATABASE_SSL_REJECT_UNAUTHORIZED=true on the ECS task definition and supply the AWS RDS CA bundle already vendored at certs/aws-rds-global-bundle.pem. The sibling capture stack in this repo already does exactly this — mirror it.
High§164.312(a)(1)Enforce feature permissions on PHI modulesExtend requireAnyRbacFeaturePermission() to patients, patient-files, face-charts and appointment-summaries so “minimum necessary” is enforced within a practice, not just between practices.
Medium§164.316(b)(2)(i)Raise audit retention toward 6 yearsNative API log groups retain 180 days. Either raise CloudWatch retention or, more economically, export audit-relevant logs to S3 with a lifecycle policy and Object Lock reaching 2192 days. Confirm the CloudTrail S3 lifecycle also meets the 6-year expectation.
Medium§164.312(a)(1)Remove direct internet access to the legacy databasesBoth legacy instances are PubliclyAccessible=true, reachable on 3306 from four static public addresses. Move DBA access behind a VPN or SSM Session Manager port-forward and set PubliclyAccessible=false.
Medium§164.308(a)(1)(ii)(D)Close the monitoring loopWire CloudTrail to CloudWatch Logs and create metric-filter alarms for the standard security events (root usage, console sign-in without MFA, IAM/policy changes, CloudTrail config changes, SG/NACL changes, CMK disable). Enable AWS Config and Security Hub in both regions.
Medium§164.312(a)(2)(iv)Rotate secrets and keys0 of 6 Secrets Manager secrets have rotation enabled; KMS CMK alias/auditlog-hipaa has rotation disabled; the oldest active IAM access key dates to 2020. Enable KMS annual rotation, configure Secrets Manager rotation for database credentials, and retire stale access keys.
Medium§164.308(a)(1)(ii)(D)Enable VPC flow logs in us-e****-1Flow logs are active for the us-e****-2 platform VPC but absent in us-e****-1 — the VPC that actually processes native PHI traffic. Enable ALL-traffic flow logs for that VPC and its default VPC.
Medium§164.312(e)(2)Enforce TLS-only access on the PHI bucketsOnly 2 of 22 buckets deny non-TLS requests. Add a Deny statement on aws:SecureTransport = false to every bucket, starting with the legacy patient-document and patient-upload stores, and complete the public-access block on the 6 buckets missing it.
Medium§164.312(c)(1)Enable versioning on legacy PHI bucketsThe legacy patient-documents store has versioning off, so an overwrite or delete is unrecoverable and undetectable. Enable versioning, and consider Object Lock for audit-relevant objects.
Low§164.312(a)(1)Harden the container runtimeThe image and task definition run as root with a writable root filesystem. Add a non-root USER to the Dockerfile and set readonlyRootFilesystem: true on the task definition.
Low§164.308(a)(5)(ii)(D)Align the Cognito password policyRaise the Cognito minimum length to 12 and require upper and lower case, matching the AWS account policy. Move Cognito MFA from OPTIONAL to REQUIRED before the native auth cutover.
Low§164.310(d)(1)Define retention and disposalThere is no retention window or purge path for PHI — only soft deletes. Define a retention schedule, implement expiry, and add S3 lifecycle rules.
Process§164.308Produce the administrative evidence setNine controls in this assessment are unscoreable from code and logs: risk analysis, risk management, sanction policy, security official, workforce training, evaluation, emergency access, workstation use and security, and the BAA register. These carry 35% of the weighting — documenting them is what moves the range from its lower bound toward its upper bound.

Requires human / manual review

These 9 controls are excluded from the headline percentage. They cannot be established from source code or AWS telemetry — only from documentation, contracts and organisational records. They are not assumed to be failing; they are simply unknown from here, and they are the difference between the lower and upper bound of the range.

#CitationRequirementWhy it needs a human
T4§164.312(a)(2)(ii)Emergency Access Procedure (Required)No break-glass role, documented emergency-access runbook, or dedicated emergency account was identified in code or AWS configuration. This is a procedural control — it must be evidenced by documentation, not inferred.
P3§164.310(b)Workstation UsePolicy control governing acceptable workstation use for systems that access ePHI. Not determinable from code or AWS configuration.
P4§164.310(c)Workstation SecurityNot determinable from code or AWS configuration. Relevant observation for the reviewer: the legacy database security group admits MySQL/3306 from four static public /32 addresses (values withheld from this report), i.e. named workstations reach the production PHI database directly across the internet. Physical and endpoint security of those four machines becomes an in-scope control.
A1§164.308(a)(1)(ii)(A)Risk Analysis (Required)No documented, organization-wide risk analysis was found in the repository. This assessment is a technical gap-check and does not satisfy §164.308(a)(1)(ii)(A), which requires an accurate and thorough assessment of risks to all ePHI the organization holds.
A2§164.308(a)(1)(ii)(B)Risk Management (Required)Requires evidence that identified risks are tracked to closure under a documented programme. Not determinable from code or logs.
A3§164.308(a)(1)(ii)(C)Sanction Policy (Required)Workforce sanction policy for security-policy violations. Documentation control.
A5§164.308(a)(2)Assigned Security Responsibility (Required)A named Security Official must be designated and documented. Not determinable technically.
A8§164.308(a)(5)Security Awareness and TrainingPeriodic workforce security training and reminders. Documentation control; no technical evidence available.
A12§164.308(b)(1)Business Associate Agreements (Required)Third parties that touch or may touch PHI have been enumerated from code and configuration (see the Business Associate register on the main page). Whether a signed BAA exists for each is a contractual fact that cannot be read from code or AWS APIs — it must be confirmed against the contract file. Note that the AWS BAA is the precondition for the Physical Safeguards position in P1.

Business Associate register

Third parties identified from code and production configuration that touch, or plausibly touch, PHI. Under §164.308(b)(1) each requires a signed Business Associate Agreement before PHI is disclosed to it. The “BAA on file?” column cannot be answered from code — every row needs contractual verification.

Business associateFunctionTouches PHI?BAA on file? — verify
Amazon Web ServicesHosting, compute, database, object storage, KMSYes — all ePHI at rest and in processAWS offers a standard BAA covering HIPAA-eligible services. Verify signed & current.
AWS HealthScribeClinical note / medical transcription (dedicated prod bucket present)Yes — clinical contentHIPAA-eligible service; covered by the AWS BAA. Confirm it is in scope of the signed addendum.
Amazon SESTransactional and patient-facing email; 2 SMTP service identities activeLikely — identifiers in message bodiesCovered by AWS BAA. Verify no clinical detail is placed in email bodies.
Amazon CognitoIdentity provider for the native platform (implemented; not yet active in prod)Identifiers onlyCovered by AWS BAA.
FluidPayPayment processing and practice provisioningPatient identifiers + payment dataIndependent BAA required. Also in PCI DSS scope.
TebraPractice-management import / production onboarding (largest integration in the codebase)Yes — bulk patient recordsIndependent BAA required — bulk PHI transfer path.
Colorescience (LBR)Product ordering integration (API key + partner/ship-to configured in prod)Possibly — patient/shipping detailsVerify what is transmitted; BAA required if PHI leaves the boundary.
SlackDestination for production error alertsShould be none — payloads are sanitizedError records are allow-list built and identifiers are HMAC-fingerprinted. Confirm no PHI in alert text; BAA if any.
SegmentAnalytics instrumentation referenced in the codebaseShould be noneConfirm whether it is active in production and whether identifiers are transmitted.
StripePayment references in the codebasePayment identifiersConfirm active use; BAA / PCI responsibility matrix required if live.
CloudflareDNS for the native API hostname (Route 53 deliberately unused per stack config)Transits ePHI if proxiedConfirm DNS-only vs proxied. If TLS terminates at Cloudflare, a BAA is required.

Privacy Rule and Breach Notification notes

Minimum necessary — 45 CFR §164.502(b)

Tenant-level minimum necessary is enforced well. Role-level is not: because feature permissions are checked on only 9 of 48 modules, a receptionist and a clinician within the same practice have, in most PHI modules, the same technical reach. Closing T3 is what brings this control home.

Breach notification readiness — §§164.400–414

Detection is reasonable: GuardDuty in both regions, CloudTrail with S3 data events, structured application logs, and an error-alerting pipeline. Assessment is the problem. A breach notification must identify the individuals whose PHI was involved. With no PHI-access audit trail (T8), and with 180-day retention on the native tier (T9), the organisation may be unable to determine whose records were touched — which in practice forces over-notification. This is the strongest business argument for fixing T8 first.

Assessment scope and integrity

Sources. The revique-api repository at commit 45d**** (360 TypeScript source files plus CDK infrastructure definitions), and live read-only AWS API responses from account 501**** across us-e****-1 and us-e****-2.

Method. All AWS calls were read-only — describe-*, list-*, get-* and CloudWatch Logs Insights queries. No resource was created, modified or deleted. Secret values were never retrieved: get-secret-value was not called and no --with-decryption flag was used, so only secret names, ARNs and rotation metadata appear here.

PHI handling. No patient data was read or extracted. Log queries were restricted to counts and metadata — status-code distributions, event-type counts and log-level counts over a single 3-hour window — and returned no message bodies. Five Logs Insights queries scanned approximately 8.7 MB in total, a cost well under one cent.

Masking. Every account identifier, ARN, endpoint, bucket name, IP address, instance / VPC / security-group ID, domain and email address in this report is masked to its first three characters. The four public addresses permitted to reach the legacy database were withheld entirely rather than masked. Raw collected JSON is retained locally in _hipaa-data/, which is excluded from publication and never leaves the workstation.

CONFIDENTIAL — INTERNAL USE ONLY
HIPAA Security Rule engineering gap-assessment · revique-api · generated 2026-08-03
All account identifiers, ARNs, endpoints, bucket names, IP addresses, resource IDs and domains are masked (first 3 characters retained, remainder replaced with ****).
Evidence was collected with read-only AWS API calls. No PHI and no secret values were read, extracted or reproduced in this report.