Audit Log Reference
jitsudo maintains an append-only audit log in PostgreSQL. Every significant action produces an AuditEvent record with a SHA-256 hash of the previous entry, forming a tamper-evident hash chain.
AuditEvent Schema
Section titled “AuditEvent Schema”{ "id": 1042, "timestamp": "2026-03-20T16:00:00Z", "actor_identity": "alice@example.com", "action": "request.created", "request_id": "req_01J8KZ4F2EMNQZ3V7XKQYBD4W", "provider": "aws", "resource_scope": "123456789012", "outcome": "success", "details_json": "{\"duration_seconds\":7200,\"role\":\"prod-infra-admin\"}", "prev_hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "hash": "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"}Field Reference
Section titled “Field Reference”| Field | Type | Description |
|---|---|---|
id | int64 | Sequential event ID. Monotonically increasing, never reused. |
timestamp | RFC3339 (UTC) | When the event occurred. |
actor_identity | string | The user who performed the action — the IdP email. system for automated actions (expiry sweeper, etc.). |
action | string | The action that occurred. See Action Types below. |
request_id | string | The associated elevation request ID, or empty if not applicable. |
provider | string | The cloud provider involved (aws, gcp, azure, kubernetes), or empty. |
resource_scope | string | The provider-specific scope (account ID, project ID, namespace), or empty. |
outcome | string | success or failure. |
details_json | string | JSON object with additional context. Schema varies by action type. |
prev_hash | string | SHA-256 of the previous audit entry. "" for the first entry. |
hash | string | SHA-256 of this entry (see Hash Chain below). |
Action Types
Section titled “Action Types”Request lifecycle
Section titled “Request lifecycle”| Action | Actor | Description |
|---|---|---|
request.created | User | A new elevation request was submitted |
request.approved | User | A request was approved by an approver |
request.denied | User | A request was denied by an approver |
request.revoked | User | An active request was manually revoked |
Grant lifecycle
Section titled “Grant lifecycle”| Action | Actor | Description |
|---|---|---|
grant.issued | system | Credentials were issued after approval |
grant.expired | system | A grant reached its natural expiry time |
grant.revoked | system | A grant was revoked (triggered by request.revoked) |
Policy management
Section titled “Policy management”| Action | Actor | Description |
|---|---|---|
policy.created | User | A new OPA policy was applied |
policy.updated | User | An existing OPA policy was updated |
policy.deleted | User | An OPA policy was deleted |
details_json Schemas
Section titled “details_json Schemas”request.created
Section titled “request.created”{ "provider": "aws", "role": "prod-infra-admin", "resource_scope": "123456789012", "duration_seconds": 7200, "reason": "Investigating P1 ECS crash", "break_glass": false}request.approved / request.denied
Section titled “request.approved / request.denied”{ "comment": "Approved for INC-4421 response"}grant.issued
Section titled “grant.issued”{ "expires_at": "2026-03-20T18:00:00Z"}policy.created / policy.updated
Section titled “policy.created / policy.updated”{ "policy_name": "sre-eligibility", "policy_type": "eligibility"}Hash Chain
Section titled “Hash Chain”The audit log uses a SHA-256 hash chain to detect tampering. Each event’s hash field covers the event’s content and links to the previous event.
Hash computation
Section titled “Hash computation”The hash of each event is computed as:
SHA-256(prev_hash + "|" + id + "|" + timestamp + "|" + actor_identity + "|" + action + "|" + request_id + "|" + outcome + "|" + details_json)Where prev_hash is the hash of the immediately preceding event (empty string for the first event).
Verification
Section titled “Verification”To verify the hash chain is intact, compute the expected hash for each event and compare it to the stored hash field. Any modification to a historical event — including the prev_hash field — will cause all subsequent hashes to mismatch.
Example verification script (Python):
import hashlib, json, sys
def compute_hash(event): parts = "|".join([ event.get("prev_hash", ""), str(event["id"]), event["timestamp"], event["actor_identity"], event["action"], event.get("request_id", ""), event["outcome"], event.get("details_json", ""), ]) return hashlib.sha256(parts.encode()).hexdigest()
events = json.load(sys.stdin) # list of AuditEvent objectsfor event in events: expected = compute_hash(event) if expected != event["hash"]: print(f"TAMPERED: event id={event['id']} hash mismatch") print(f" expected: {expected}") print(f" stored: {event['hash']}") sys.exit(1)print(f"Chain intact: {len(events)} events verified")Usage:
# Export events as JSONjitsudo audit --output json > audit-export.json
# Verify the chainpython3 verify-chain.py < audit-export.jsonQuerying the Audit Log
Section titled “Querying the Audit Log”# All events for the last 24 hoursjitsudo audit --since 24h
# All events for a specific requestjitsudo audit --request req_01J8KZ4F2EMNQZ3V7XKQYBD4W
# All events by a specific user in JSONjitsudo audit --user alice@example.com --output json
# Export a date range as CSVjitsudo audit \ --since 2026-01-01T00:00:00Z \ --until 2026-02-01T00:00:00Z \ --output csv > january-2026-audit.csvSee jitsudo audit for the full CLI reference.
REST API
Section titled “REST API”curl "https://jitsudo.example.com/api/v1alpha1/audit?\actor_identity=alice@example.com&\since=2026-03-01T00:00:00Z" \ -H "Authorization: Bearer $TOKEN"See the REST API reference for query parameters and response schema.
Append-Only Guarantee
Section titled “Append-Only Guarantee”The audit log is append-only at the database layer. jitsudo uses serializable transactions to:
- Read the latest event’s
hashfield. - Compute the new event’s
hashusing the latest asprev_hash. - Insert the new event atomically.
No UPDATE or DELETE operations are ever performed on the audit table. Database-level permissions on the jitsudo role should enforce this (REVOKE UPDATE, DELETE ON audit_events FROM jitsudo).