{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://runbooks.directory/spec/v1/schema/attestation.json",
  "title": "Runbook attestation v1",
  "description": "A signed claim about a run of a specific version of a specific document (RUNBOOK.md 8). T3 exists so that trust can rise above what the catalog can check statically without the catalog owning a runner, which means a third party has to be able to issue a claim we can verify. The subject binds to content_hash rather than to a name: a rename cannot move an attestation and an edit cannot inherit one. The signable bytes are defined in spec/v1/attestations.md and must be byte-identical across implementations.",
  "type": "object",
  "additionalProperties": false,
  "required": ["attestation_schema", "predicate_type", "subject", "predicate", "signatures"],
  "properties": {
    "attestation_schema": { "const": "runbook-attestation/v1" },
    "predicate_type": { "const": "https://runbooks.directory/attestation/run/v1" },
    "subject": {
      "type": "object",
      "additionalProperties": false,
      "required": ["runbook", "semver", "content_hash"],
      "properties": {
        "runbook": {
          "type": "string",
          "pattern": "^(std|[a-z][a-z0-9-]{0,30}[a-z0-9]|github:[A-Za-z0-9][A-Za-z0-9-]{0,38}|domain:[a-z0-9.-]+)/[a-z0-9]+(-[a-z0-9]+)*$"
        },
        "semver": { "type": "string", "pattern": "^(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)$" },
        "content_hash": { "type": "string", "pattern": "^sha256:[0-9a-f]{64}$" }
      }
    },
    "predicate": {
      "type": "object",
      "additionalProperties": false,
      "required": ["issuer", "env_class", "outcome", "runtime_profile", "issued_at"],
      "properties": {
        "issuer": {
          "type": "string",
          "format": "uri",
          "description": "An identity a verifier can look up, not a display name. Its origin is where the key set is found."
        },
        "env_class": { "enum": ["local", "ci", "staging", "prod"] },
        "outcome": { "enum": ["success", "failed", "aborted", "partial"] },
        "runtime_profile": { "enum": ["R0", "R1", "R2"] },
        "issued_at": {
          "type": "string",
          "format": "date",
          "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}$",
          "description": "A date, not a timestamp: §14's reasoning applies to anything that leaves a machine."
        },
        "report_hash": { "type": "string", "pattern": "^sha256:[0-9a-f]{64}$" }
      }
    },
    "signatures": {
      "type": "array",
      "minItems": 1,
      "description": "A statement with no signature is a draft, not an attestation.",
      "items": {
        "type": "object",
        "additionalProperties": false,
        "required": ["key_id", "sig"],
        "properties": {
          "key_id": { "type": "string", "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]{0,63}$" },
          "sig": { "type": "string", "pattern": "^[A-Za-z0-9+/]{86}==$", "description": "base64 of 64 Ed25519 bytes." }
        }
      }
    }
  }
}
