{
  "$id": "https://uape.dev/schemas/audit-event-1.0.json",
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "title": "Audit Event",
  "description": "Single entry in the append-only, hash-chained audit log.",
  "type": "object",
  "additionalProperties": false,
  "required": ["seq", "ts", "module_id", "event_name", "payload", "prev_hash", "hmac"],
  "properties": {
    "seq": {
      "type": "integer",
      "minimum": 0,
      "description": "Monotonic per-instance sequence number."
    },
    "ts": {
      "type": "integer",
      "minimum": 0,
      "description": "Host clock unix-millis at the time of append."
    },
    "module_id": {
      "type": "string",
      "format": "uuid"
    },
    "event_name": {
      "type": "string",
      "pattern": "^[A-Z][A-Za-z0-9]+$",
      "description": "Module-declared name. Must appear in manifest.audit.allowed_events or be one of the kernel reserved events. Reserved kernel events (always permitted, never require manifest declaration): 'AuthzAllow', 'AuthzDenied' (emitted by the authorization PDP — see ADR-0010), 'SecurityViolation' (high-severity event emitted when a non-trusted peer attempts subject injection over the gRPC control plane — see ADR-0021), 'DevBypassUsed', 'KillSwitchTripped', 'BackupCreated', 'RestoreCompleted', 'RestoreAborted' (emitted by the disaster-recovery pipeline — see ADR-0013), 'MigrationApplied', 'ModuleRolledBack', 'ManifestLoadRejected' (emitted by the storage migration runner — see ADR-0007), 'CandidateValidated', 'CandidateRejected' (emitted by the autopoiesis Sandbox Replay engine — see ADR-0017), 'OperatorEntityCreated', 'OperatorEntityUpdated', 'OperatorEntityDeleted' (emitted by the StorageService when an operator mutates a storage record through the Web Dashboard control plane — see CP-001 / ADR-0021), 'StudioDraftGenerated' (emitted by StudioLlmService when an operator generates an unsigned manifest draft from a natural-language prompt), 'ManifestPublished' (emitted by Operator::LoadManifest when a signature-verified manifest is loaded through the Studio publish path — see ADR-0022)."
    },
    "payload": {
      "type": "object",
      "description": "Arbitrary JSON object. MUST NOT contain secrets or PII unless the module manifest declares the event as containing such data and the storage encryption policy applies.",
      "additionalProperties": true
    },
    "actor": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "kind": { "type": "string", "enum": ["operator", "telegram_user", "module", "kernel"] },
        "id":   { "type": "string", "maxLength": 256 }
      }
    },
    "prev_hash": {
      "type": "string",
      "pattern": "^[0-9a-f]{64}$",
      "description": "HMAC of the previous event. First event uses all-zero hash."
    },
    "hmac": {
      "type": "string",
      "pattern": "^[0-9a-f]{64}$",
      "description": "HMAC-SHA256 over canonical-JSON of all fields except hmac itself, using the audit subkey."
    }
  }
}
