Audit Log
Every access check, mutation, and key rotation is recorded in an append-only log with per-record HMAC signatures. Verify integrity independently — no trust in the database required.
Event-driven architecture
Audit logging is fully asynchronous. The request path fires an event into SQS and moves on — logging never adds latency to access checks.
Hot path (DynamoDB): real-time queryable for 7–180 days depending on your
subscription tier. Records carry a ttl field — DynamoDB auto-deletes them on
expiry.
Cold path (S3): as records expire from DynamoDB, the stream triggers the
archive Lambda. Records are grouped by (org, day), gzip'd, and written to S3
with GLACIER_IR storage class and Object Lock (COMPLIANCE or GOVERNANCE
per org). Cold retention ranges from 0–360 days.
Failure handling: every stage has a DLQ. Messages that fail 3 processing cycles go to a parking queue — never silently discarded.
Retention by tier
| Tier | Hot (DynamoDB) | Cold (S3 Archive) |
|---|---|---|
| Free | 7 days | None |
| Starter | 30 days | None |
| Growth | 30 days | 60 days |
| Business | 90 days | 90 days |
| Scale | 180 days | 180 days |
| Enterprise | 180 days* | 360 days |
*Enterprise tier can override hot retention per-org via subscription settings.
HMAC signing
Every record is independently signed with HMAC-SHA256 using a per-stage
secret. The signing payload is:
eventType|ISO_8601_timestamp|actor|result|action|scopeId|resourceType|resourceId
The signature is stored on the DynamoDB record. An auditor can recompute it to prove no record was modified. Each record stands alone — there's no hash chain linking records together, so records can be verified individually or in bulk without traversing the full history.
What gets logged
Every mutation, access check, and system event is recorded with: who, what, when, result, and an HMAC signature.
RBAC operations
| Event | Trigger |
|---|---|
permission.created, .updated, .deleted | Permission CRUD |
role.created, .updated, .deleted | Role CRUD |
scope.created, .updated, .deleted | Scope management |
access-policy.created, .updated, .deleted | Access policy assignment |
resource-type.created, .updated, .deleted | Resource type registration |
Authentication & organization
| Event | Trigger |
|---|---|
auth.success, auth.failed | Login attempts |
organization.created | New org registration |
team-member.added, .removed | Team membership changes |
backup-codes.generated, backup-code.consumed, backup-codes.disabled | MFA backup codes |
API keys & SSO
| Event | Trigger |
|---|---|
api-key.created, .deleted, .rotated | Key lifecycle |
sso.configured, .updated, .deleted | SSO configuration |
scim.token-rotated | SCIM token rotation |
scim.user-provisioned, .user-deprovisioned | SCIM user sync |
scim-group.created, .updated, .deleted | SCIM group sync |
Access decisions
| Event | Trigger |
|---|---|
access.granted, .denied | Every access check (via SQS consumer) |
Relationships (ReBAC)
| Event | Trigger |
|---|---|
tuple.written | Relationship tuple created |
tuple.deleted | Relationship tuple removed |
MCP / Agent IAM
| Event | Trigger |
|---|---|
mcp-server.created, .updated, .deleted | MCP server CRUD |
mcp.tool-call | Tool execution via gateway |
mcp.consent-granted, .consent-revoked | User consent decisions |
mcp.approval-decided | HITL approval/denial |
mcp.assignment-set, .assignment-removed | User server assignments |
mcp.threat-detected, .drift-detected | Security detection events |
session-token.minted, .revoked | Downscoped token lifecycle |
Policy & configuration
| Event | Trigger |
|---|---|
tier-policy.updated | Agent tool tier ceiling changes |
ip-allowlist.updated | IP allowlist modifications |
webhook-endpoint.created, .updated, .deleted | Webhook registration |
sod-constraint.created, .deleted | Separation of duty constraints |
approval-request.created, .approved, .denied | HITL approval lifecycle |
Billing & monitoring
| Event | Trigger |
|---|---|
subscription.changed | Plan upgrades/downgrades |
overage.consent-granted | Overage consent acceptance |
anomaly.detected | Automatic detection by the anomaly system |
anomaly.acknowledged | Manual acknowledgment in dashboard |
Verifying integrity
An auditor verifies records independently — no trust in WardenAuth required:
cURL
curl -X POST https://api.wardenauthz.com/v1/scope/workspace-eng/audit/verify \
-H "x-api-key: YOUR_MANAGEMENT_KEY"
{
"valid": true,
"total": 12847,
"matched": 12847,
"mismatchCount": 0
}
Each record's HMAC is recomputed and compared to the stored signature. A mismatch means the record was modified after writing.
Querying
List events filtered by actor, resource, date range, or scope:
curl -H "x-api-key: YOUR_MANAGEMENT_KEY" \
"https://api.wardenauthz.com/v1/audit?scopeId=workspace-eng&actor=subject-alice&limit=50"
Access decisions (access.granted / access.denied) are listed by scope and date
range. The actor and resource filters return the other events (configuration
changes, key and role management, approvals); to review decisions, query the scope
for a time window.
Exporting
Bulk export is disabled while it moves to dedicated infrastructure; the endpoint answers
503 Service Unavailable. Page through GET /v1/audit in the meantime. The dashboard's
"Export CSV" button still exports the page you are viewing.
Download up to 100,000 records as CSV or JSON:
curl -H "x-api-key: YOUR_MANAGEMENT_KEY" \
"https://api.wardenauthz.com/v1/scope/workspace-eng/audit/export?format=csv" \
-o audit-export.csv
Edge cases
- The audit log is append-only. No update or delete operations exist. Accidental entries cannot be removed — they're permanent evidence.
- SQS publish failures drop the event. The request path does not retry audit logging — it swallows errors so the request succeeds. Audit events lost to transient SQS failures are accepted as a trade-off for zero latency.
- Verification works on individual records. There's no chain traversal — you can verify a single record or a date range without recomputing the entire history.
- Cold archive writes are idempotent. The archive Lambda uses conditional
S3 puts (
IfNoneMatch) with content-hash keys — stream redelivery never creates duplicate objects.