{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://thehiveryiq.com/.well-known/schemas/stage-replay-v1.json",
  "title": "Staged Preprocessing Chain v1",
  "description": "Staged Preprocessing Chain receipt. A document preprocessing chain is recorded stage by stage with contiguous order values, the parameters each stage applied, the digest of the raster it emitted, and the key of the party that operated it, so a disagreement about the output can be narrowed to one stage and one operator instead of the whole pipeline. When the caller supplies observed stage output digests, the service reports the lowest ordered stage where the observed digest first differs from the digest that was committed. A stage carrying no operator signature is reported as incomplete stage custody, which is a different finding from a difference between digests. It does not re-execute any stage; stages_reexecuted_by_service is fixed to false because the service never receives the rasters.",
  "type": "object",
  "required": [
    "receipt_id",
    "payload_sha256",
    "sig_b64u",
    "key_id",
    "algorithm",
    "ts",
    "signed_body"
  ],
  "additionalProperties": false,
  "properties": {
    "receipt_id": {
      "type": "string",
      "pattern": "^r_stage\\.replay_[0-9]{10,}_[0-9a-f]{12}$"
    },
    "payload_sha256": {
      "type": "string",
      "pattern": "^[0-9a-f]{64}$"
    },
    "sig_b64u": {
      "type": "string",
      "pattern": "^[A-Za-z0-9_-]{86}$"
    },
    "key_id": {
      "type": "string",
      "pattern": "^did:(hive|web|key):[A-Za-z0-9._:%-]+$"
    },
    "algorithm": {
      "type": "string",
      "const": "Ed25519"
    },
    "ts": {
      "type": "integer",
      "minimum": 1000000000
    },
    "signed_body": {
      "type": "object",
      "required": [
        "receipt_type",
        "schema",
        "chain_id",
        "evidence_digest_sha256",
        "recorded_at",
        "verdict",
        "boundary",
        "render_profile_digest_sha256",
        "determinism_class_ref",
        "input_raster_digest_sha256",
        "output_raster_digest_sha256",
        "stages"
      ],
      "additionalProperties": false,
      "properties": {
        "receipt_type": {
          "type": "string",
          "const": "stage.replay"
        },
        "schema": {
          "type": "string",
          "const": "r1.0.0"
        },
        "chain_id": {
          "type": "string",
          "minLength": 1,
          "maxLength": 256,
          "pattern": "^[A-Za-z0-9._:-]{1,256}$"
        },
        "evidence_digest_sha256": {
          "type": "string",
          "pattern": "^[0-9a-f]{64}$",
          "description": "SHA-256 of the caller-held evidence bundle this receipt attests was recorded. The service never receives or inspects the underlying evidence, only its digest."
        },
        "recorded_at": {
          "type": "string",
          "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}([.][0-9]{1,9})?Z$"
        },
        "verdict": {
          "type": "object",
          "description": "Service-computed honesty boundary fields. Never caller overridable; recomputed and checked at verify time.",
          "required": [
            "stages_reexecuted_by_service",
            "stage_count",
            "stage_order_contiguous",
            "incomplete_stage_custody",
            "observed_stage_count",
            "first_divergent_stage_order",
            "divergence_localized_to_one_stage",
            "chain_terminates_at_declared_output"
          ],
          "additionalProperties": false,
          "properties": {
            "stages_reexecuted_by_service": {
              "type": "boolean",
              "const": false
            },
            "stage_count": {
              "type": "integer",
              "minimum": 0
            },
            "stage_order_contiguous": {
              "type": "boolean"
            },
            "incomplete_stage_custody": {
              "type": "boolean"
            },
            "observed_stage_count": {
              "type": "integer",
              "minimum": 0
            },
            "first_divergent_stage_order": {
              "type": "integer",
              "minimum": -1
            },
            "divergence_localized_to_one_stage": {
              "type": "boolean"
            },
            "chain_terminates_at_declared_output": {
              "type": "boolean"
            }
          }
        },
        "boundary": {
          "type": "string",
          "const": "Staged Preprocessing Chain receipt. A document preprocessing chain is recorded stage by stage with contiguous order values, the parameters each stage applied, the digest of the raster it emitted, and the key of the party that operated it, so a disagreement about the output can be narrowed to one stage and one operator instead of the whole pipeline. When the caller supplies observed stage output digests, the service reports the lowest ordered stage where the observed digest first differs from the digest that was committed. A stage carrying no operator signature is reported as incomplete stage custody, which is a different finding from a difference between digests. It does not re-execute any stage; stages_reexecuted_by_service is fixed to false because the service never receives the rasters."
        },
        "render_profile_digest_sha256": {
          "type": "string",
          "pattern": "^[0-9a-f]{64}$",
          "description": "Profile the chain rasters are expressed against."
        },
        "determinism_class_ref": {
          "type": "string",
          "minLength": 1,
          "maxLength": 256,
          "description": "Reference to the determinism class declaration that says whether replay is even offered."
        },
        "input_raster_digest_sha256": {
          "type": "string",
          "pattern": "^[0-9a-f]{64}$",
          "description": "Digest of the raster entering the chain."
        },
        "output_raster_digest_sha256": {
          "type": "string",
          "pattern": "^[0-9a-f]{64}$",
          "description": "Digest of the raster leaving the chain."
        },
        "stages": {
          "type": "array",
          "minItems": 1,
          "maxItems": 64,
          "items": {
            "type": "object",
            "required": [
              "order_value",
              "stage_name",
              "implementation_ref",
              "implementation_version",
              "parameters_digest_sha256",
              "output_digest_sha256"
            ],
            "additionalProperties": false,
            "properties": {
              "order_value": {
                "type": "integer",
                "minimum": 0,
                "maximum": 63
              },
              "stage_name": {
                "type": "string",
                "minLength": 1,
                "maxLength": 256,
                "description": "Name of the stage, for example deskew, denoise, binarize, or dewarp."
              },
              "implementation_ref": {
                "type": "string",
                "minLength": 1,
                "maxLength": 256,
                "description": "Implementation that ran."
              },
              "implementation_version": {
                "type": "string",
                "minLength": 1,
                "maxLength": 256,
                "description": "Version of that implementation."
              },
              "parameters_digest_sha256": {
                "type": "string",
                "pattern": "^[0-9a-f]{64}$",
                "description": "Digest over the canonical parameters this stage applied, every numeric one included."
              },
              "output_digest_sha256": {
                "type": "string",
                "pattern": "^[0-9a-f]{64}$",
                "description": "Digest of the raster this stage emitted."
              },
              "operator_key_id": {
                "type": "string",
                "pattern": "^did:(hive|web|key):[A-Za-z0-9._:%-]+$",
                "description": "Optional. Key of the party that operated this stage. Absent means incomplete stage custody."
              },
              "operator_signature_b64u": {
                "type": "string",
                "pattern": "^[A-Za-z0-9_-]{86}$",
                "description": "Optional. Operator signature over the order value, the parameters digest, and the output digest."
              }
            }
          }
        },
        "observed_stage_output_digests": {
          "type": "array",
          "minItems": 0,
          "maxItems": 64,
          "description": "Optional. What a replaying party actually observed, stage by stage.",
          "items": {
            "type": "object",
            "required": [
              "order_value",
              "observed_output_digest_sha256"
            ],
            "additionalProperties": false,
            "properties": {
              "order_value": {
                "type": "integer",
                "minimum": 0,
                "maximum": 63
              },
              "observed_output_digest_sha256": {
                "type": "string",
                "pattern": "^[0-9a-f]{64}$",
                "description": "Digest the replaying party observed for that stage."
              }
            }
          }
        }
      }
    }
  }
}
