Skip to main content

Audit, Security & Data Retention


Audit Loggingโ€‹

Files: src/audit/audit-logger.ts, src/audit/audit-routes.ts

Every patient-related action in ChartChat is persisted as a FHIR AuditEvent resource. This is the HIPAA-standard mechanism for tamper-evident, append-only activity logs. Audit events are never deleted by the system (see Data Retention below).

AuditEvent field mappingโ€‹

AuditEvent fieldValue
recordedTimestamp of the event
agent[0].whoUser reference (Patient/abc or Practitioner/xyz)
agent[0].extension[userRole]patient, admin, support, or system
agent[0].extension[source]web, mobile, or api
agent[0].extension[userAgent]Browser/client user-agent string
agent[0].network.addressClient IP address
entity[0] (role=1)Patient reference
entity[1] (role=2)The FHIR resource accessed/modified
entity[1].detailbeforeValue, afterValue, fileName, metadata
subtype[0].codeChartChat action code (see table below)
actionFHIR CRUDE code (C, R, U, D, E)
outcome0 = SUCCESS, 8 = FAILURE

Action codesโ€‹

All action codes use the system https://chartchat.com/audit/action.

Patient Data Accessโ€‹

CodeFHIR actionDescription
VIEW_PATIENT_PROFILERPatient profile page viewed
VIEW_OBSERVATIONRLab result viewed
VIEW_DIAGNOSTIC_REPORTRDiagnostic report viewed
VIEW_ENCOUNTERREncounter/visit viewed
VIEW_APPOINTMENTRAppointment viewed
VIEW_MEDICATIONSRMedications page viewed
VIEW_CONDITIONSRConditions page viewed
VIEW_ALLERGYRAllergy record viewed
VIEW_IMMUNIZATIONSRImmunizations viewed
VIEW_PROCEDURESRProcedures viewed

Documents & Filesโ€‹

CodeFHIR actionDescription
DOWNLOAD_REPORTEReport/document downloaded
VIEW_DOCUMENTRDocument reference viewed
UPLOAD_DOCUMENTCDocument uploaded
GENERATE_PDFEPDF generated

Data Modificationโ€‹

CodeFHIR actionDescription
UPDATE_PATIENT_PROFILEUPatient profile updated
CREATE_RECORDCNew FHIR resource created
DELETE_RECORDDFHIR resource deleted

Authentication & Sessionโ€‹

CodeFHIR actionDescription
LOGIN_SUCCESSESuccessful login
LOGIN_FAILEDEFailed login attempt
LOGOUTEUser logged out
SESSION_EXPIREDESession timed out
TOKEN_REFRESHEOAuth token refreshed
PASSWORD_CHANGEDEPassword changed

EHR Integrationโ€‹

CodeFHIR actionDescription
EHR_CONNECTEEHR account connected
EHR_SYNCEEHR data sync completed
EHR_SYNC_FAILEDEEHR sync failed
EHR_DISCONNECTEEHR account disconnected

Admin & Exportโ€‹

CodeFHIR actionDescription
ADMIN_EXPORT_DATAEAdmin exported patient data
ADMIN_DELETE_PATIENTDAdmin deleted a patient
SUPPORT_ACCESS_GRANTEDESupport access granted
EXPORT_PATIENT_DATAEPatient data exported
GENERATE_REPORTEReport generated

Security & AIโ€‹

CodeFHIR actionDescription
SUSPICIOUS_LOGIN_ATTEMPTEUnusual login detected
UNUSUAL_DATA_ACCESSEAnomalous access flagged
RATE_LIMIT_EXCEEDEDERate limit hit
CHAT_MESSAGE_SENTEAI chat message sent
CHAT_SAFETY_TRIGGEREDEAI safety refusal

System / Admin Configโ€‹

CodeFHIR actionDescription
ROLE_UPDATEDEUser role changed
ACCESS_POLICY_CHANGEDEAccess policy modified
CONFIG_UPDATEDESystem configuration changed

Audit API Endpointsโ€‹

POST /api/audit/log โ€” Client-side event loggingโ€‹

Called by the Member Portal frontend to record client-side events (page views, downloads, AI chat actions). The server automatically extracts the user identity, role, IP, and user-agent from the request.

Auth: Patient or admin bearer token required.

Request body:

{
"action": "VIEW_OBSERVATION",
"resourceType": "Observation",
"resourceId": "abc123",
"status": "SUCCESS",
"metadata": { "category": "lab" }
}
FieldRequiredDescription
actionYesOne of the action codes above
resourceTypeNoFHIR resource type being accessed
resourceIdNoFHIR resource ID
statusNoSUCCESS or FAILURE. Default: SUCCESS
metadataNoFree-form JSON object
beforeValueNoPrevious value (for update/delete events)
afterValueNoNew value (for create/update events)
fileNameNoFile name (for document events)
reasonNoReason for access (admin/support use)

GET /api/audit/export โ€” CSV exportโ€‹

Admin-only. Exports AuditEvent records as a CSV file with optional filters. Returns up to 5,000 records.

Auth: Admin (Practitioner) bearer token required. Patients are blocked.

Query parameters:

ParameterDescription
fromISO date โ€” events on or after this date
toISO date โ€” events on or before this date
patientFilter by patient reference (e.g., Patient/abc123)
actionFilter by action code (e.g., VIEW_OBSERVATION)
statusSUCCESS or FAILURE

Response: text/csv file downloaded as audit-log-YYYY-MM-DD.csv

CSV columns: timestamp, userId, userRole, patientId, action, resourceType, resourceId, status, ipAddress, source, userAgent, outcome

Standard FHIR audit queryโ€‹

For paginated, filtered queries the portals call the standard FHIR endpoint directly:

GET /fhir/R4/AuditEvent?patient=Patient/abc123&date=ge2024-01-01&_sort=-date

Breach Detectionโ€‹

File: src/security/breachDetection.ts

The breach detection system monitors every PHI access and fires an alert if a single user accesses an unusual number of patient records in a short time window.

Thresholdsโ€‹

WindowThresholdDescription
1 hour100 accessesMore than 100 PHI reads in a single hour
24 hours500 accessesMore than 500 PHI reads in a single day

Counters are maintained in Redis using atomic INCR with a TTL matching the window (3,600s for hourly, 86,400s for daily).

Alert behaviorโ€‹

When a threshold is exceeded:

  1. Checks Redis for an alert cooldown key (breach:alert:{userId}) โ€” if set, suppresses the duplicate alert.
  2. Sets the cooldown key with a 1-hour TTL to prevent alert flooding.
  3. Logs a warning to the server log with userId, hourCount, and dayCount.
  4. Sends an email alert to config.supportEmail with the user ID, reason, timestamp, and recommended HIPAA incident response steps.

Redis key patternsโ€‹

KeyTTLValue
breach:h:{userId}:{hourBucket}3,600sCount of PHI accesses this hour
breach:d:{userId}:{dayBucket}86,400sCount of PHI accesses today
breach:alert:{userId}3,600sAlert suppression flag

Where it is triggeredโ€‹

trackPhiAccess(userId) is called from the PHI audit middleware (fhir/phi-audit-middleware.ts) on every FHIR read operation. It is fire-and-forget โ€” errors are silently swallowed to ensure breach detection never disrupts normal request handling.


Data Retentionโ€‹

File: src/workers/dataRetention.ts

A daily background job enforces data retention policies per HIPAA requirements. It runs at 02:00 every day via BullMQ.

Retention rulesโ€‹

Data typePolicyReason
AuditEventKeep for 7 years โ€” never deleted automaticallyHIPAA ยง 164.530(j) requires 6-year minimum; ChartChat keeps 7 for margin
Revoked Login recordsPurge after 90 daysShort-term forensics window; no PHI in login records
Soft-deleted non-PHI resourcesPurge after 30 daysClean up deleted resources
HIPAA note

AuditEvent resources are never automatically deleted by the data retention job. The job only logs a reminder to the server log when events reach the 7-year boundary, so operators know when to archive or review them per their own incident response policy.

If you need to delete audit events, use the Admin Portal โ†’ Super Admin โ†’ Purge tool, which requires an explicit date cutoff and admin confirmation.

Job detailsโ€‹

  • Queue: DataRetentionQueue
  • Schedule: Daily at 02:00 (BullMQ cron: 0 2 * * *)
  • Job ID: data-retention-daily (deduped โ€” only one instance runs at a time)
  • Processes up to 500 revoked login records per run