Audit Logging
ProvenanceOne records every significant action in an immutable audit log. Each event carries a HMAC-SHA256 message authentication code (mac field) computed at write time, providing cryptographic tamper evidence. Events span 45+ types across 11 domains, are classified by risk level, and are retained for 7 years via a platform database TTL. The audit log cannot be cleared, truncated, or modified by any role or API key.
What customers need to know
- Every audit event has a
macfield: a HMAC-SHA256 digest over the canonical event body. Any post-write alteration to the event produces a MAC mismatch. - Events are retained for 7 years. Deletion before TTL expiry is not possible by any role.
- 45+ event types cover runs, workflows, agents, secrets, connections, approvals, auth, bus messaging, DLP gateway, and workspace/member management.
- Every event records actor kind (
user,agent,system,integration), so you can attribute actions to human users, automated agents, or platform subsystems. - The
PersonIDfield on events is erasable under GDPR on request. The event record itself is preserved. - Access to the audit log requires
editororadminrole, or theaudit:readAPI key scope.viewerrole cannot read the audit log.
Tamper-evidence
Every audit event record contains a mac field. The MAC is computed server-side at write time as a HMAC-SHA256 digest over the canonical serialisation of the event body. It is stored alongside the event in platform database.
To verify an event has not been modified after creation:
- Retrieve the event:
GET /audit/{eventId} - Recompute the HMAC-SHA256 over the canonical event body using the same platform key management key
- Compare the computed value to the stored
macfield
A mismatch indicates the record has been altered since it was written.
Needs product confirmation: Exact canonical serialisation format used for MAC computation; whether a verification utility or SDK method is provided; whether the platform key management key used for MAC is customer-managed (CMK) or platform-managed.
This control provides evidence of log integrity — it does not encrypt the event record. Event fields are readable by anyone with audit:read access.
Retention
Every audit event has a platform database TTL field set to 7 years (2,557 days) from the event's occurredAt timestamp. Records past TTL are automatically expired from platform database. There is no mechanism for early deletion by any role, API key, or admin action.
Note: platform database TTL deletion is asynchronous. Records past their TTL timestamp may persist for up to 48 hours before platform database processes the expiry. Expired records continue to be excluded from API query results during this window.
Needs product confirmation: Whether 7 years is a hard minimum floor for all workspaces, or whether the workspace
dataRetentionDayssetting can reduce audit retention below 7 years. The workspace retention field is confirmed to exist; whether it governs audit events is unconfirmed.
Risk classification
Each event is assigned one of four risk levels at write time. Risk level cannot be modified after creation.
| Risk level | Description | Example events |
|---|---|---|
low | Routine operational events | run.started, workflow.draft_saved, connection.listed, agent.listed |
medium | Configuration changes, data access that is expected but should be tracked | agent.updated, secret.rotated, member.invited, approval.granted |
high | Sensitive operations with direct security or data implications | secret.accessed, authz.denied, connection.accessed, auth.failed, approval.sla_breach |
critical | Highest-impact operations with workspace-wide or compliance implications | member.erased, (gateway policy changes may emit at critical depending on configuration) |
Use risk level filters in audit digest configuration and API queries to prioritise security-relevant events. A digest configured for high and critical only surfaces actionable security events without noise from routine operations.
Actor kinds
Every audit event records the kind of actor that caused it.
| Actor kind | Description |
|---|---|
user | A human user authenticated via platform JWT |
agent | An AI agent step executing within a workflow run |
system | A platform subsystem (e.g. TTL expiry handler, scheduled task) |
integration | An API key-authenticated external integration |
This field is essential for distinguishing human actions from agent actions in a mixed-actor environment. When investigating unexpected behaviour, filter by actorKind=agent to isolate agent-driven events.
Full event taxonomy
Events are grouped by domain. Risk level annotated where higher than low.
Run events (5)
| Event | Risk | Description |
|---|---|---|
run.started | low | Workflow run initiated |
run.canceled | medium | Run cancelled before completion |
run.completed | low | Run finished successfully |
run.failed | medium | Run terminated with error |
run.deleted | medium | Run record deleted |
Workflow events (3)
| Event | Risk | Description |
|---|---|---|
workflow.edited | medium | Workflow definition modified |
workflow.published | medium | Workflow version published |
workflow.draft_saved | low | Workflow draft saved |
Agent events (5)
| Event | Risk | Description |
|---|---|---|
agent.updated | medium | Agent configuration changed |
agent.memory_set | medium | Agent persistent memory key written |
agent.memory_deleted | medium | Agent persistent memory key deleted |
agent.listed | low | Agent list enumerated |
agent.read | low | Agent detail read |
Approval events (6)
| Event | Risk | Description |
|---|---|---|
approval.granted | medium | Approval step approved |
approval.rejected | medium | Approval step rejected |
approval.reassigned | medium | Approval step reassigned |
approval.sla_breach | high | Approval SLA exceeded without action |
grant.approved | medium | Delegated grant created |
grant.denied | medium | Delegated grant refused |
Secret events (5)
| Event | Risk | Description |
|---|---|---|
secret.created | low | New secret created |
secret.updated | medium | Secret metadata updated |
secret.rotated | medium | Secret value rotated |
secret.accessed | high | Raw secret value revealed via POST /secrets/{id}/reveal |
secret.listed | low | Secrets enumerated (SOC2 evidence) |
Connection events (4)
| Event | Risk | Description |
|---|---|---|
connection.updated | medium | Connection configuration changed |
connection.accessed | high | Connection credential used by execution engine or test endpoint |
connection.listed | low | Connections enumerated (SOC2 evidence) |
connection.read | low | Connection detail read |
Workspace and member events (11)
| Event | Risk | Description |
|---|---|---|
workspace.updated | medium | Workspace settings changed |
workspace.created | low | New workspace created |
workspace.environment_updated | medium | Workspace environment configuration changed |
workspace.notification_updated | low | Notification settings updated |
member.invited | low | New member invited |
member.updated | medium | Member role or group changed |
member.removed | medium | Member removed from workspace |
member.erased | critical | GDPR erasure executed for a member |
api_key.created | medium | API key issued |
api_key.revoked | medium | API key revoked |
api_key.listed | low | API keys enumerated (SOC2 evidence) |
Auth events (4)
| Event | Risk | Description |
|---|---|---|
user.login | low | Successful user login |
user.workspace_switched | low | User switched active workspace |
auth.failed | high | Authentication failure (wrong credentials, expired token) |
authz.denied | high | API call refused due to insufficient role or scope |
Bus events (10)
| Event | Risk | Description |
|---|---|---|
bus.message_published | low | Message published to the Bus |
bus.message_delivered | low | Message delivered to a subscriber |
bus.message_fanout | low | Message fanned out to multiple subscribers |
bus.message_delivery_failed | medium | Message delivery failed |
bus.cycle_blocked | high | Routing loop detected and blocked |
bus.subscription_created | low | New Bus subscription created |
bus.subscription_updated | low | Bus subscription updated |
bus.subscription_deleted | medium | Bus subscription deleted |
bus.api_key_created | medium | Bus-scoped API key created |
bus.api_key_revoked | medium | Bus-scoped API key revoked |
Gateway and DLP events (4)
| Event | Risk | Description |
|---|---|---|
gateway_policy.created | medium | MCP Gateway policy created |
gateway_policy.updated | medium | MCP Gateway policy updated |
gateway_policy.deleted | medium | MCP Gateway policy deleted |
policy.violation | medium | Tool call blocked or redacted by gateway policy |
Datastore events (4)
| Event | Risk | Description |
|---|---|---|
datastore.created | low | New datastore created |
datastore.updated | medium | Datastore configuration updated |
datastore.deleted | medium | Datastore deleted |
datastore.object_deleted | medium | Object deleted from a datastore |
How to investigate an agent action
The following is a step-by-step procedure for reconstructing what an agent did during a workflow run.
Step 1: Locate the run
Navigate to Runs in the workspace, or query:
GET /runs?workflowId={workflowId}&limit=20
Identify the runId of the run you want to investigate.
Step 2: Retrieve the run's audit trail
Query all audit events associated with the run:
curl "https://api.provenanceone.ai/audit?runId={runId}" \
-H "x-api-key: YOUR_AUDIT_READ_KEY"
This returns all events with runId matching the run, in chronological order. Look for run.started, any run.failed or authz.denied events, and policy.violation events.
Step 3: Identify the agent steps
Filter the run's events to actorKind=agent:
GET /audit?runId={runId}&actorKind=agent
These events show what the agent initiated: tool calls, memory writes, connection accesses.
Step 4: Check tool calls
For each connection.accessed event in the run, the connectionRef field identifies which connection was used. For policy.violation events, the policyMatched field identifies which gateway policy triggered.
Step 5: Verify event integrity
For any event where integrity is material to the investigation, retrieve the event and verify its MAC:
GET /audit/{eventId}— retrieve the event including themacfield- Recompute the HMAC-SHA256 over the canonical event body using the platform key management key
- A match confirms the event has not been altered since it was written
Step 6: Cross-reference approval events
If the run included approval steps, query:
GET /audit?runId={runId}&eventType=approval.granted,approval.rejected
The actorId and actorName fields identify who approved or rejected each step.
Audit digest
The audit digest sends scheduled email summaries of audit events to configured recipients. It is available to admin role in Settings → Audit Digest.
| Field | Type | Description |
|---|---|---|
enabled | boolean | Whether the digest is active |
frequency | enum | daily, weekly, or monthly |
dayOfWeek | integer | 0 (Sunday) to 6 (Saturday) — weekly only |
dayOfMonth | integer | 1 to 28 — monthly only |
hourUTC | integer | Delivery hour (0–23) |
includeRiskLevels | array | Which risk levels to include |
recipients | array | Email addresses to receive the digest |
Digest emails are sent via the platform email service. For security monitoring, configure includeRiskLevels to ["high", "critical"] to receive only events warranting immediate review.
GDPR considerations
PersonID field
Every audit event includes a PersonID field that links the acting user to a data subject identity. Under a GDPR erasure request, POST /workspace/members/{userId}/erase (admin only) nulls the PersonID field across all historical audit events for that user.
The audit event record itself is not deleted. The event type, timestamps, actorId, resource references, and MAC field are all preserved. Only the PersonID identifier is replaced with a tombstone value.
This design maintains audit trail integrity (event records are not destroyed) while satisfying the right to erasure for the personal data field specifically.
The erasure itself produces a member.erased event at risk level critical, logged with the admin actor who initiated the erasure.
Data in event fields
Audit events may contain data from the operations they record (e.g. objectName, reason, extra fields). The scope of GDPR erasure is limited to the PersonID field. Whether additional personal data in event payload fields is in scope for erasure requires confirmation.
Needs product confirmation: Full scope of personal data erasure in audit events beyond the
PersonIDfield; whetherextraor other free-form fields may contain personal data that requires separate erasure procedures.
Querying the audit log
# All high and critical events in the last 7 days
curl "https://api.provenanceone.ai/audit?risk=high,critical&from=2026-04-24T00:00:00Z" \
-H "x-api-key: YOUR_AUDIT_READ_KEY"
# All events for a specific run
curl "https://api.provenanceone.ai/audit?runId=run_abc123" \
-H "x-api-key: YOUR_AUDIT_READ_KEY"
# All secret access events
curl "https://api.provenanceone.ai/audit?eventType=secret.accessed" \
-H "x-api-key: YOUR_AUDIT_READ_KEY"
# All auth failures in the last 24 hours
curl "https://api.provenanceone.ai/audit?eventType=auth.failed&from=2026-05-07T00:00:00Z" \
-H "x-api-key: YOUR_AUDIT_READ_KEY"
Available query parameters: eventType, actorId, actorKind, risk, from, to, workflowId, runId.
Admin controls
| Control | Location | Role required |
|---|---|---|
| Read audit events | Audit log page / GET /audit | editor, admin, or audit:read scope |
| Configure audit digest | Settings → Audit Digest | admin |
| Verify event MAC | Via API | admin or audit:read scope |
| Perform GDPR PersonID erasure | Settings → Members → Erase | admin |
Security implications
- The audit log cannot be suppressed: there is no mechanism for any role or API key to prevent events from being written, or to delete existing events before TTL expiry. This includes the
adminrole. - MAC verification is the primary integrity control: the
macfield on each event enables post-hoc verification that individual records have not been altered. Without periodic MAC verification, tamper-evidence exists only as a theoretical check. - Agent actions are attributed:
actorKind=agentevents identify the specific agent and run that caused an action. This is material for incident investigation in multi-agent deployments. - GDPR erasure preserves event records: audit events survive erasure; only
PersonIDis nulled. This means the actions of erased users remain in the audit record — consider this when responding to data subject access requests. - platform database TTL has up to 48-hour lag: do not rely on same-day deletion of expired records. The effective retention is 7 years plus up to 48 hours.
Limitations and open questions
- No real-time streaming export: there is no confirmed webhook, Kinesis stream, or event bus integration for real-time audit event forwarding to a SIEM. Events must be pulled via
GET /audit. - No bulk export endpoint: there is no one-click export of all audit events. Programmatic access via
GET /auditwith pagination is the confirmed retrieval mechanism. - platform database TTL delivery lag: as noted, expired records may persist up to 48 hours past their TTL timestamp.
- MAC canonical format unconfirmed: the exact serialisation used for MAC computation is not publicly documented. Verification requires platform-provided tooling or documentation.
- Audit retention floor: whether
dataRetentionDayscan reduce audit retention below 7 years is unconfirmed. The 7-year TTL is set on every event at write time; the interaction with workspace-level retention settings is unclear. - GDPR erasure scope in event payloads: erasure is confirmed for the
PersonIDfield. Personal data in other event fields (e.g.objectName,extra) is not confirmed to be in scope.
FAQ
Can audit logs be deleted or modified?▾
No. Audit events cannot be deleted or modified by any role, API key, or admin action. The 7-year TTL is set at write time and cannot be shortened. Every event carries a HMAC-SHA256 MAC field that would produce a mismatch if the record were altered.
What does the MAC field prove?▾
The `mac` field is a HMAC-SHA256 digest computed over the canonical event body at the time the event is written. If the event is retrieved later and the MAC recomputed over the same body, a match proves the record has not changed since it was created. A mismatch indicates alteration. This is a tamper-evidence control — it does not encrypt the event.
How do I find all actions taken by a specific agent?▾
Query `GET /audit?actorKind=agent&actorId={agentId}`. This returns all events where the specified agent was the actor. To narrow to a specific run, add `&runId={runId}`. You can also filter by event type or risk level.
What is logged when a GDPR erasure is performed?▾
The `member.erased` event is logged at risk level `critical`. It records the admin actor who performed the erasure, the target user ID, and the timestamp. The PersonID field on the erased user's historical events is replaced with a tombstone. The audit event records themselves are not deleted.
Can I forward audit logs to my SIEM in real time?▾
There is no confirmed real-time streaming integration (webhook, Kinesis, or event bus) for the audit log. Events are retrievable via `GET /audit` with pagination and filter parameters. For scheduled summaries, the audit digest delivers email reports at configurable intervals. Contact the ProvenanceOne team to discuss SIEM integration requirements.
Which role can read the audit log?▾
`editor` and `admin` roles can read the audit log in the UI and via the API. `viewer` role cannot access the audit log. Programmatic access requires the `audit:read` API key scope, regardless of the role that issued the key.