Skip to main content
Ferrum records an authoritative audit trail of security-relevant activity performed through the FHIR API, modeled as FHIR AuditEvent resources.

Key properties

What gets audited

Ferrum audits FHIR REST interactions under /fhir including (when enabled):
  • read, vread, history
  • search
  • create, update, patch, delete
  • capabilities (GET /fhir/metadata)
  • $operation and $export
  • batch / transaction
Audit logging currently targets the FHIR API surface. Internal routes like /admin and /health are not audited by the same middleware.

Captured security context (SMART / OAuth2)

When requests are authenticated, Ferrum binds the audit record to the SMART security context:
  • client_id (OAuth2 client)
  • user_id (end-user subject when present)
  • scopes (granted scopes)
  • token_type inferred as user / system / anonymous / unknown
This context is stored both as indexed columns (for fast filtering) and inside the JSON details.

Captured data access semantics

For each interaction, Ferrum captures (best-effort):
  • Target resource: resource_type + resource_id when known
  • Patient subject: patient_id when resolvable
  • Request correlation: request_id (server-generated), client IP, user agent
  • Outcome:
    • success for HTTP < 400
    • authz_failure for 401/403
    • processing_failure for other >= 400 responses

Search events and entity.query

FHIR recommends that servers capture search activity as an Execute event and include the raw HTTP request as base64 in AuditEvent.entity.query. Ferrum can do this for search requests:
  • Stores the full HTTP start line + headers + body as base64binary
  • Also records the “harmonized” request URL string (path + query) in details

OperationOutcome capture

On failures, Ferrum can capture the returned OperationOutcome and include it in the serialized AuditEvent (contained + linked from entity.what).

Patient resolution

When possible, audit records include the patient subject:
  • From SMART context (patient claim on the token, if present)
  • From a compartment path (/fhir/Patient/{id}/...)
  • From direct resource targets (Patient/{id})
  • For successful searches: by inspecting a searchset Bundle and extracting referenced patients
For searches that touch multiple patients, Ferrum can emit one audit record per patient to improve privacy segmentation. This is configurable.

Storage model (audit_log)

Audit records are stored in PostgreSQL in the audit_log table.
  • Structured columns for common filters (action, outcome, patient_id, request_id, …)
  • audit_event column stores the complete FHIR AuditEvent as JSONB
  • details stores additional structured metadata (SMART + HTTP context)
The table is created in the main DB migration (server/migrations/001_init.sql) and is protected with triggers to prevent modification.

Admin API

Audit logs are intentionally not accessible via the normal FHIR resource API. Instead, Ferrum exposes an internal admin interface (behind admin auth middleware).

List audit events

GET /admin/audit/events Query parameters:
  • action: interaction (e.g. read, search, create, transaction)
  • outcome: success | authz_failure | processing_failure
  • resourceType, resourceId, patientId, clientId, userId, requestId
  • limit (default 100, max 1000), offset
Example:

Get a single audit event

GET /admin/audit/events/{id} Returns the full stored row including the serialized audit_event JSON.

Configuration

Audit logging is configured under logging.audit:
Common hardening examples:

Operational notes

  • Audit writes are queued and inserted asynchronously; on DB errors, the server logs warnings and continues serving requests.
  • The audit log table can grow quickly. Use standard Postgres operational strategies (partitioning, retention policies, backups) depending on your compliance needs.