{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://aevum.build/spec/aevum-event-v1.json",
  "title": "Aevum AuditEvent v1",
  "description": "Schema for a single Aevum episodic ledger entry as returned by Engine.get_ledger_entries(). The entry shape is dataclasses.asdict(AuditEvent) plus a derived 'audit_id' field added by the engine. Core fields are always present and non-null; nullable fields are typed [T, null]; Phase 1 dual-sig and TSA fields are optional (null when no DualSigner / TSA client is configured). The signing specification (aevum-signing-v1.md) defines how to verify the signature and chain-linkage fields.",
  "type": "object",
  "required": [
    "event_id",
    "episode_id",
    "sequence",
    "event_type",
    "schema_version",
    "valid_from",
    "valid_to",
    "system_time",
    "causation_id",
    "correlation_id",
    "actor",
    "trace_id",
    "span_id",
    "payload",
    "payload_hash",
    "prior_hash",
    "signature",
    "signer_key_id",
    "key_scheme",
    "sig_format_version",
    "hash_alg",
    "audit_id"
  ],
  "additionalProperties": false,
  "properties": {
    "event_id": {
      "type": "string",
      "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$",
      "description": "UUID v7 (time-ordered). Globally unique event identifier. The audit_id field encodes this as a URN."
    },
    "episode_id": {
      "type": "string",
      "description": "Groups related events into a logical episode. Empty string ('') for kernel-generated events not associated with a specific episode (session.start, etc.). Never null."
    },
    "sequence": {
      "type": "integer",
      "minimum": 1,
      "description": "Monotonically increasing sequence number within this chain. session.start is always sequence 1. Included in the signing fields."
    },
    "event_type": {
      "type": "string",
      "pattern": "^[a-z][a-z0-9_]*(\\.[a-z][a-z0-9_]*)+$",
      "description": "Dotted-namespace event type. Reserved prefixes: session., ingest., query., review., commit., replay., consent., capture., transparency., chain., barrier., policy., agent., complication., context., commitment_key."
    },
    "schema_version": {
      "type": "string",
      "const": "1.0",
      "description": "Schema version. Will be incremented for breaking changes to the event format."
    },
    "valid_from": {
      "type": "string",
      "description": "Wall-clock ISO 8601 timestamp of the event (e.g. '2026-05-06T21:54:11.401122+00:00'). For causal ordering use system_time (HLC); valid_from is for human readability."
    },
    "valid_to": {
      "type": ["string", "null"],
      "description": "Wall-clock ISO 8601 timestamp when the record's validity ends. Null for point-in-time events."
    },
    "system_time": {
      "type": "integer",
      "minimum": 0,
      "description": "Hybrid Logical Clock timestamp in nanoseconds. Monotonically non-decreasing within a chain. Provides causal ordering; NOT a substitute for UTC-traceable timestamps in regulated contexts. Note: in the signing field set this value is encoded as a string (str(system_time)) because HLC values may exceed the RFC 8785 safe integer domain (2^53-1). The JSON entry carries the raw integer."
    },
    "causation_id": {
      "type": ["string", "null"],
      "description": "audit_id of the event that directly caused this one. For session.start on a persistent backend: the audit_id of the last event in the previous session. Null otherwise."
    },
    "correlation_id": {
      "type": ["string", "null"],
      "description": "Deployment-wide correlation identifier. Used to group events across sessions or deployments."
    },
    "actor": {
      "type": "string",
      "minLength": 1,
      "description": "The agent, user, or system component that caused this event. For kernel-generated events: 'aevum-core'."
    },
    "trace_id": {
      "type": ["string", "null"],
      "description": "OpenTelemetry W3C trace ID (32 hex chars) for distributed tracing correlation."
    },
    "span_id": {
      "type": ["string", "null"],
      "description": "OpenTelemetry W3C span ID (16 hex chars) for distributed tracing correlation."
    },
    "payload": {
      "type": "object",
      "description": "Event-specific structured data. Schema varies by event_type. The payload_hash field contains its SHA3-256 digest for integrity verification."
    },
    "payload_hash": {
      "type": "string",
      "pattern": "^[0-9a-f]{64}$",
      "description": "SHA3-256 hex digest of the JCS-canonical JSON-serialised payload object."
    },
    "prior_hash": {
      "type": "string",
      "pattern": "^[0-9a-f]{64}$",
      "description": "SHA3-256 hex digest of the previous event's signing representative (DOMAIN_PREFIX + rfc8785.dumps(19 signing fields)). For sequence=1 (session.start): the genesis hash constant sha3_256('aevum:genesis')."
    },
    "signature": {
      "type": "string",
      "description": "Base64url-encoded (no padding) Ed25519 signature over SHA3-256(DOMAIN_PREFIX + rfc8785.dumps(19 signing fields)). See aevum-signing-v1.md for the exact field set, encoding rules, and verification procedure."
    },
    "signer_key_id": {
      "type": "string",
      "minLength": 1,
      "description": "Informational identifier for the signing key. For InProcessSigner: a UUID v4. For VaultTransitSigner: the Vault key URL. Included in the signing field set; part of the signed digest. NOT a key-identity anchor — the verifier's anchor is the published public key supplied out-of-band."
    },
    "key_scheme": {
      "type": "string",
      "description": "Algorithm posture. 'ed25519' for classical entries; 'ed25519+ml-dsa-65' for hybrid post-quantum entries. All entries in a chain must share the same key_scheme (homogeneity requirement). Included in the signing field set."
    },
    "sig_format_version": {
      "type": "integer",
      "enum": [1, 2],
      "description": "Signing field-set version. 1: the 19 base signing fields. 2 (aevum-signing-v2.md): the same 19 fields plus principal_binding, principal_commitment, principal_commitment_key_id. Included in the signing field set; verify_chain rejects any entry where this is not 1 or 2, and rejects a chain where it decreases across entries (downgrade/splice fingerprint, DD4)."
    },
    "hash_alg": {
      "type": "string",
      "const": "sha3-256",
      "description": "Hash algorithm used for the signed digest and chain hash. Always 'sha3-256'. Bound into the signing field set so that changing the algorithm changes the signature."
    },
    "mldsa65_sig": {
      "type": ["string", "null"],
      "description": "Hex-encoded ML-DSA-65 (CRYSTALS-Dilithium, FIPS 204) signature over the message representative (DOMAIN_PREFIX + rfc8785.dumps(19 signing fields)). Approximately 6618 hex characters (~3309 bytes). Present only on hybrid entries (key_scheme='ed25519+ml-dsa-65'); null on classical entries. Absence on a hybrid entry is a verification failure."
    },
    "mldsa65_pub": {
      "type": ["string", "null"],
      "description": "Hex-encoded ML-DSA-65 public key. Approximately 3904 hex characters (~1952 bytes). Present only on hybrid entries. Must equal the pinned ML-DSA public key supplied out-of-band; a mismatch is a verification failure."
    },
    "tsa_url": {
      "type": ["string", "null"],
      "description": "URL of the RFC 3161 Timestamp Authority that issued tsa_token. Null when no TSA client is configured or when the TSA request failed (TSA outages are non-blocking)."
    },
    "tsa_token": {
      "type": ["string", "null"],
      "description": "Hex-encoded RFC 3161 timestamp token (DER bytes) over the message representative. Null when no TSA client is configured. Independent of the self-asserted system_time — provides an externally verifiable timestamp anchor."
    },
    "receipt_cbor": {
      "type": ["string", "null"],
      "description": "Hex-encoded COSE_Sign1 receipt bytes (Phase 1A transparency receipt). Null when no receipt encoder is configured. When present, provides a verifiable log receipt binding this entry to a transparency log."
    },
    "principal_binding": {
      "type": ["string", "null"],
      "description": "P2-IDENTITY-V2 (aevum-signing-v2.md). Base64url-encoded (no padding) RFC 8785-canonical JSON of allow-listed verifiable credential claims (only iss, aud, jti, iat, exp, and cnf.jkt may appear — never sub, never a bearer token). Null when the entry has no external credential binding or sig_format_version != 2. Part of the signing field set only when sig_format_version == 2."
    },
    "principal_commitment": {
      "type": ["string", "null"],
      "description": "P2-IDENTITY-V2 (aevum-signing-v2.md). Base64url-encoded (no padding) HMAC-SHA256 commitment over the bound CREDENTIAL identity (OIDC sub / SPIFFE ID / DID), keyed by a CommitmentKeyStore secret. Opaque to chain verification — only identity-matching (performed with the key, out of band) interprets it. Null when sig_format_version != 2 or no external credential is bound. Part of the signing field set only when sig_format_version == 2."
    },
    "principal_commitment_key_id": {
      "type": ["string", "null"],
      "description": "P2-IDENTITY-V2 (aevum-signing-v2.md). Identifies which CommitmentKeyStore key produced principal_commitment. Informational signed field (like signer_key_id) — not a security boundary by itself. Null when sig_format_version != 2. Part of the signing field set only when sig_format_version == 2."
    },
    "audit_id": {
      "type": "string",
      "pattern": "^urn:aevum:audit:[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$",
      "description": "URN-encoded audit identifier. Format: urn:aevum:audit:{event_id}. Derived field added by Engine.get_ledger_entries(); not part of the signing field set and not present in the raw dataclasses.asdict(AuditEvent) output."
    }
  },
  "allOf": [
    {
      "if": {
        "properties": { "event_type": { "const": "session.start" } }
      },
      "then": {
        "properties": {
          "payload": {
            "required": ["capture_surface", "key_provenance"],
            "properties": {
              "capture_surface": {
                "type": "object",
                "required": ["llm", "mcp"],
                "properties": {
                  "llm": { "type": "boolean" },
                  "mcp": { "type": "boolean" }
                }
              },
              "key_provenance": {
                "type": "string",
                "enum": ["in-process", "external", "vault-transit", "aws-kms", "pkcs11"]
              }
            }
          }
        }
      }
    },
    {
      "if": {
        "properties": { "event_type": { "const": "capture.gap" } }
      },
      "then": {
        "properties": {
          "payload": {
            "required": ["gap_type", "reason"],
            "properties": {
              "gap_type": {
                "type": "string",
                "enum": ["llm", "mcp", "tool", "custom"]
              },
              "reason": { "type": "string" },
              "model_hint": { "type": "string" }
            }
          }
        }
      }
    }
  ]
}
