5.5 KiB
5.5 KiB
Audit Trail
Audit event structure and audited operations for compliance and forensics.
Status: Planned (not yet implemented) Source: Architecture Advisory Section 8.5 Related Modules: Evidence Module, Security Overview Sprints: 109_001 Evidence Collector
Overview
The Release Orchestrator maintains a tamper-evident audit trail of all security-relevant operations. Audit events are cryptographically chained to detect tampering.
Audit Event Structure
TypeScript Interface
interface AuditEvent {
id: UUID;
timestamp: DateTime;
tenantId: UUID;
// Actor
actorType: "user" | "agent" | "system" | "plugin";
actorId: UUID;
actorName: string;
actorIp?: string;
// Action
action: string; // "promotion.approved", "deployment.started"
resource: string; // "promotion"
resourceId: UUID;
// Context
environmentId?: UUID;
releaseId?: UUID;
promotionId?: UUID;
// Details
before?: object; // State before (for updates)
after?: object; // State after
metadata?: object; // Additional context
// Integrity
previousEventHash: string; // Hash chain for tamper detection
eventHash: string;
}
Audited Operations
| Category | Operations |
|---|---|
| Authentication | Login, logout, token refresh, failed attempts |
| Authorization | Permission denied events |
| Environments | Create, update, delete, freeze window changes |
| Releases | Create, deprecate, archive |
| Promotions | Request, approve, reject, cancel |
| Deployments | Start, complete, fail, rollback |
| Targets | Register, update, delete, health changes |
| Agents | Register, heartbeat gaps, capability changes |
| Integrations | Create, update, delete, test |
| Plugins | Enable, disable, config changes |
| Evidence | Create (never update/delete) |
Hash Chain
Chain Verification
The audit trail uses SHA-256 hash chaining for tamper detection:
interface HashChainEntry {
eventId: UUID;
eventHash: string;
previousEventHash: string;
}
function computeEventHash(event: AuditEvent): string {
const payload = JSON.stringify({
id: event.id,
timestamp: event.timestamp,
tenantId: event.tenantId,
actorType: event.actorType,
actorId: event.actorId,
action: event.action,
resource: event.resource,
resourceId: event.resourceId,
previousEventHash: event.previousEventHash,
});
return sha256(payload);
}
function verifyChain(events: AuditEvent[]): VerificationResult {
for (let i = 1; i < events.length; i++) {
const current = events[i];
const previous = events[i - 1];
if (current.previousEventHash !== previous.eventHash) {
return {
valid: false,
brokenAt: i,
reason: "Hash chain broken"
};
}
const computed = computeEventHash(current);
if (computed !== current.eventHash) {
return {
valid: false,
brokenAt: i,
reason: "Event hash mismatch"
};
}
}
return { valid: true };
}
Example Audit Events
Promotion Approved
{
"id": "evt-123",
"timestamp": "2026-01-09T14:32:15Z",
"tenantId": "tenant-uuid",
"actorType": "user",
"actorId": "user-uuid",
"actorName": "jane@example.com",
"actorIp": "192.168.1.100",
"action": "promotion.approved",
"resource": "promotion",
"resourceId": "promo-uuid",
"environmentId": "env-uuid",
"releaseId": "rel-uuid",
"promotionId": "promo-uuid",
"before": {
"status": "pending"
},
"after": {
"status": "approved",
"approvals": 2
},
"metadata": {
"comment": "LGTM"
},
"previousEventHash": "sha256:abc...",
"eventHash": "sha256:def..."
}
Deployment Started
{
"id": "evt-124",
"timestamp": "2026-01-09T14:32:20Z",
"tenantId": "tenant-uuid",
"actorType": "system",
"actorId": "system",
"actorName": "deployment-orchestrator",
"action": "deployment.started",
"resource": "deployment",
"resourceId": "deploy-uuid",
"environmentId": "env-uuid",
"releaseId": "rel-uuid",
"promotionId": "promo-uuid",
"after": {
"status": "deploying",
"strategy": "rolling",
"targetCount": 5
},
"previousEventHash": "sha256:def...",
"eventHash": "sha256:ghi..."
}
Retention Policy
| Environment | Retention Period |
|---|---|
| All tenants | 7 years (compliance) |
| After tenant deletion | 7 years (legal hold) |
| Archive format | NDJSON, signed |
Export Format
Audit events can be exported for compliance reporting:
# Export audit trail for a date range
GET /api/v1/audit/export?
start=2026-01-01T00:00:00Z&
end=2026-01-31T23:59:59Z&
format=ndjson
Response includes signed digest for verification:
{
"export": {
"startDate": "2026-01-01T00:00:00Z",
"endDate": "2026-01-31T23:59:59Z",
"eventCount": 15234,
"firstEventHash": "sha256:abc...",
"lastEventHash": "sha256:xyz...",
"downloadUrl": "https://..."
},
"signature": "base64-signature",
"signedAt": "2026-02-01T00:00:00Z"
}