{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://schemas.helloorigo.com/chronicle/witness-receipt-bundle-v1.schema.json",
  "title": "Chronicle Witness Receipt Bundle v1",
  "description": "Product-independent evidence bundle delivered to the owner of a Chronicle chain and included in every export. Together with an export it is the complete input to verification: a conforming verifier needs nothing else and makes no call to the runtime. Structural validity is not verification -- a bundle can satisfy this schema and still fail every step in contracts/integrity-evidence-v1.md section 5.",
  "type": "object",
  "additionalProperties": false,
  "required": [
    "evidenceFormatVersion",
    "chainId",
    "witnessSalt",
    "headEntryHash",
    "headChainSeq",
    "inclusionProof",
    "signedRootStatement",
    "signature",
    "timestampTokens",
    "publisherKeys",
    "keyTransitions",
    "statement"
  ],
  "$defs": {
    "sha256Hex": {
      "type": "string",
      "pattern": "^[0-9a-f]{64}$",
      "description": "Lowercase hexadecimal SHA-256 value."
    }
  },
  "properties": {
    "evidenceFormatVersion": {
      "const": 1,
      "description": "Chronicle integrity evidence contract version. Not a runtime migration, storage, or export-envelope version."
    },
    "chainId": {
      "type": "string",
      "format": "uuid",
      "description": "Immutable chain identity. Reconstructs genesisPrevHash; without it a chain segment cannot be distinguished from a segment of another chain."
    },
    "witnessSalt": {
      "type": "string",
      "pattern": "^[0-9a-f]{64}$",
      "description": "32 octets, hex. Blinds this chain's leaf so a published root discloses nothing even when the publication set is small. Delivered to the owner and published nowhere."
    },
    "headEntryHash": { "$ref": "#/$defs/sha256Hex" },
    "headChainSeq": {
      "type": "integer",
      "minimum": 1,
      "description": "Position of the head entry. Chain positions begin at 1."
    },
    "inclusionProof": {
      "type": "object",
      "additionalProperties": false,
      "required": ["vds", "leafIndex", "treeSize", "auditPath"],
      "description": "Proof that this chain's leaf is in the witnessed root, per RFC 9162 section 2.1.3. Carried as an RFC 9942 COSE Receipt in a delivered bundle; represented here in its decoded form.",
      "properties": {
        "vds": {
          "const": "RFC9162_SHA256",
          "description": "Verifiable data structure identifier registered by RFC 9942. Chronicle does not define, extend, or vary this structure."
        },
        "leafIndex": { "type": "integer", "minimum": 0 },
        "treeSize": { "type": "integer", "minimum": 1 },
        "auditPath": {
          "type": "array",
          "items": { "$ref": "#/$defs/sha256Hex" }
        }
      }
    },
    "signedRootStatement": {
      "type": "object",
      "additionalProperties": false,
      "required": ["evidenceFormatVersion", "vds", "root", "treeSize", "publishedAt", "keyId"],
      "description": "The statement the publisher signs and submits for timestamping.",
      "properties": {
        "evidenceFormatVersion": { "const": 1 },
        "vds": { "const": "RFC9162_SHA256" },
        "root": { "$ref": "#/$defs/sha256Hex" },
        "treeSize": {
          "type": "integer",
          "minimum": 1,
          "description": "Number of leaves. Discloses how many chains are in the publication set; a disclosed property of the format, not a defect."
        },
        "publishedAt": {
          "type": "string",
          "format": "date-time",
          "description": "The publisher's own claim of publication time. A verifier reports the time from the timestamp token instead, never this value."
        },
        "keyId": { "type": "string", "minLength": 1 }
      }
    },
    "signature": {
      "type": ["string", "null"],
      "pattern": "^([0-9a-f]{2})+$",
      "description": "The complete COSE_Sign1 envelope over the signed root statement, lowercase hex, per contract section 4.1 -- the whole tagged envelope, not the 64 signature octets alone. A delivered bundle must carry one; portable fixtures set null because they cannot carry a key. Null is a step-2 failure, never a step not performed."
    },
    "timestampTokens": {
      "type": "array",
      "description": "RFC 3161 tokens over the signed root statement, each the DER encoding in lowercase hex. A delivered bundle must carry at least one, and two independent authorities are recommended. Empty only in a fixture that deliberately does not exercise step 3.",
      "items": { "type": "string", "pattern": "^([0-9a-f]{2})+$" }
    },
    "publisherKeys": {
      "type": "array",
      "description": "Public keys able to verify the signed root statement. Also published in the repository, so that a key withdrawn later remains visible in version history. Shape per contract section 4.1; the envelope's protected alg governs verification, never the advisory algorithm token.",
      "items": {
        "type": "object",
        "additionalProperties": false,
        "required": ["keyId", "algorithm", "publicKey"],
        "properties": {
          "keyId": {
            "type": "string",
            "minLength": 1,
            "description": "Matches the keyId of a signed root statement and its protected kid."
          },
          "algorithm": {
            "const": "Ed25519",
            "description": "Advisory label. A verifier finding it in disagreement with the envelope's protected alg fails step 2 rather than choosing between them."
          },
          "publicKey": {
            "type": "string",
            "pattern": "^[0-9a-f]{64}$",
            "description": "The 32 public octets of RFC 8032, lowercase hex."
          }
        }
      }
    },
    "keyTransitions": {
      "type": "array",
      "description": "Key-transition statements, each naming a retiring and an incoming key and signed by both. Lets a verifier follow a root signed by a retired key back to a key it accepts.",
      "items": { "type": "object" }
    },
    "statement": {
      "type": "string",
      "minLength": 1,
      "description": "Plain-language statement of what the bundle proves and does not prove. Required, not documentation: evidence delivered without a stated boundary is read as proving more than it does."
    }
  }
}
