Audit Trail
SureCentric implements a two-layer audit trail designed for regulated health science environments. Every schema change, data operation, configuration update, and AI agent action is recorded in an indelible, tamper-evident audit trail.
Design Principles
The audit trail is modeled after the production audit trail in the SureClinical network-service backend (com.sureclinical.network.audit.trail), adapted for MikroORM + Dolt/MySQL:
| SC Pattern | SureCentric Equivalent |
|---|---|
AuditTrailEntry (permanent) | audit_trail table — append-only, never deleted |
AuditTrailLog (5-day TTL) | audit_trail_log table — operational, supports rollback |
AuditTrailLogCleanUpScheduler | Node.js cron job in CARD API, 24h interval |
AuditedEntityChangedEvent (Spring) | MikroORM @Subscriber() EventSubscriber |
AuditEventType enum | SurecentricEventType TypeScript enum |
Two-Layer Architecture
Layer 1 — audit_trail (Permanent Record)
The permanent regulatory record. Append-only — no UPDATE or DELETE is ever issued.
| Column | Type | Description |
|---|---|---|
event_id | VARCHAR(36) | UUID v4 — unique per event |
occurred_at | DATETIME(3) | UTC, millisecond precision |
event_type | VARCHAR(64) | See Event Types below |
resource_type | VARCHAR(64) | schema, card_profile, project, agent, … |
resource_id | VARCHAR(128) | Entity primary key affected |
actor_type | ENUM | user, system, or agent |
actor_id | VARCHAR(128) | User ID, system, or agent_run_id |
actor_email | VARCHAR(320) | Email or system@surecentric |
agent_run_id | VARCHAR(36) | UUID of AI Agent execution (null for users) |
agent_name | VARCHAR(128) | SureAgent, DataAgent, etc. (null for users) |
before_state | JSON | Entity state before the change |
after_state | JSON | Entity state after the change |
dolt_commit_hash | VARCHAR(64) | Links to dolt_log — Layer 1 cross-reference |
outcome | ENUM | success, failure, pending |
correlation_id | VARCHAR(36) | Groups related events across a workflow |
parent_event_id | VARCHAR(36) | Parent event in a chain |
Dolt's content-addressed storage makes any post-write modification to audit_trail cryptographically detectable in dolt_log.
Layer 2 — audit_trail_log (5-day TTL)
A transient operational log used during active schema deployments to support rollback. Entries expire after 5 days (matching AuditTrailLog.AUDIT_TRAIL_LOG_ENTRY_TTL = 5 in SC). This is not a regulatory record — it is a deployment safety net.
Key additional column: rollback_target VARCHAR(64) — the Dolt commit hash to revert to if a deployment must be rolled back.
Actor Types
enum ActorType {
USER = 'user', // Authenticated human user (JWT session)
SYSTEM = 'system', // Scheduled jobs, migrations, cleanup
AGENT = 'agent', // AI Agent with UUID execution envelope
}
Event Types
| Category | Event Types |
|---|---|
| Generic CRUD | ADD, MOD, DEL |
| Schema | SCHEMA_CREATE, SCHEMA_UPDATE, SCHEMA_COMPILE, SCHEMA_DEPLOY, SCHEMA_PROMOTE, SCHEMA_ROLLBACK, SCHEMA_DELETE |
| CARD | CARD_PROFILE_CREATE, CARD_PROFILE_UPDATE, CARD_PROFILE_PROVISION, CARD_PROFILE_DELETE |
| Project | PROJECT_CREATE, PROJECT_UPDATE, PROJECT_DELETE |
| Analytics | SUPERSET_DASHBOARD_EMBED, SUPERSET_GUEST_TOKEN_ISSUE, DUCKDB_QUERY, DUCKDB_DATASET_CREATE |
| AI Agents | AGENT_RUN_START, AGENT_TOOL_CALL, AGENT_SANDBOX_EXEC, AGENT_ACTION, AGENT_RUN_FINISH |
| Session | SESSION_LOGIN, SESSION_LOGOUT, SESSION_TOKEN_REFRESH |
| Config | CONFIG_UPDATE, API_KEY_CREATE, API_KEY_REVOKE |
AI Agent Attribution
AI Agents are identified using the same pattern SC applies to Nuxeo workflow processes: each agent execution gets a UUID and emits a START / FINISH event pair that acts as a traceable envelope around all actions the agent takes.
AGENT_RUN_START actor_type=agent agent_run_id=uuid-abc spawned_by=jane@clinic.org
├── AGENT_TOOL_CALL tool=dolt_commit args={message: "schema: deploy edc.v1.1"}
├── AGENT_ACTION event_type=SCHEMA_DEPLOY resource_id=edc-v1.1
├── AGENT_TOOL_CALL tool=superset_provision args={profileId: "prof-001"}
AGENT_RUN_FINISH outcome=success duration_ms=4231 tokens_used=1840
Agent Dolt commits are attributed to the agent, not the spawning user, making them distinguishable in dolt_log:
SET @@dolt_head_committer_email = 'agent:SureAgent@surecentric';
SET @@dolt_head_committer_name = 'SureAgent (run: uuid-abc)';
CALL dolt_commit('schema: deploy edc.v1.1 [agent:SureAgent spawned-by:jane@clinic.org]');
Nested agents (an orchestrator spawning sub-agents) are linked via parent_run_id.
Schema Deployment Audit Flow
POST /api/sb/card-profiles/:id/provision
│
├─ 1. Resolve actor context (user JWT | agent_run_id | system)
├─ 2. Layer 2: log CARD_PROFILE_PROVISION → audit_trail_log (pending)
├─ 3. Dolt: checkout new branch schema/deploy-<timestamp>
├─ 4. Compile JSON-LD → DDL → apply to DuckDB
├─ 5. Dolt: SET committer identity → CALL dolt_commit() → capture hash
├─ 6. Layer 2: write SCHEMA_DEPLOY to audit_trail_log (rollback_target = prev hash)
├─ 7. Layer 1: write permanent CARD_PROFILE_PROVISION to audit_trail (with dolt_commit_hash)
└─ 8. Layer 2: mark outcome = success
On failure:
CALL dolt_reset('--hard') ← schema rolled back
audit_trail outcome = 'failure' ← permanent failure record
Regulatory Compliance
| Standard | Clause | Implementation |
|---|---|---|
| FDA 21 CFR Part 11 | §11.10(e) | audit_trail — every change recorded with actor, timestamp, before/after state |
| FDA 21 CFR Part 11 | §11.10(k) | dolt_commit_hash links electronic record to Dolt's tamper-evident commit graph |
| FDA 21 CFR Part 11 | §11.50 | actor_id, actor_email, session_id present on every entry |
| EU Annex 11 | §9 | Append-only audit_trail; Dolt hash-chain provides integrity evidence |
| EU Annex 11 | §10 | Audit trail cannot be disabled — AuditSubscriber is always active |
| EU Annex 11 | §12.4 | dolt push backup to S3 provides disaster recovery |
REST API
| Endpoint | Description |
|---|---|
GET /api/audit-trail | Paginated audit entries (filter by actor, event_type, resource, date) |
GET /api/audit-trail/schema-history | Dolt dolt_log system table proxy |
GET /api/audit-trail/diff/:from/:to | dolt_diff() between two commits or branches |
GET /api/audit-trail/at/:commit | Time-travel: resource state at a Dolt commit |
GET /api/audit-trail/agents | List all agent_registry entries |
GET /api/audit-trail/agents/:runId | Full agent execution log for a run |
GET /api/audit-trail/export | Signed export for regulatory submission |