{
  "$defs": {
    "observation": {
      "additionalProperties": false,
      "properties": {
        "causes": {
          "additionalProperties": false,
          "description": "What the observation can mean, benign and adversarial, unweighted. Absent where the observation is a plain structural fact with nothing to weigh. The engine never states which reading applies.",
          "properties": {
            "adversarial": {
              "type": "string"
            },
            "benign": {
              "type": "string"
            }
          },
          "type": "object"
        },
        "evidence": {
          "description": "Byte offsets, object numbers, and extracted values — enough to confirm or refute the statement independently. Keys are documented per id in OBSERVATIONS.md.",
          "type": "object"
        },
        "id": {
          "description": "Match on this, never on `statement` prose.",
          "enum": [
            "ACTIVE.EMBEDDED_FILE",
            "ACTIVE.EXTERNAL_REFERENCE",
            "ACTIVE.JAVASCRIPT",
            "ACTIVE.LAUNCH_OR_URI",
            "ACTIVE.OPEN_ACTION",
            "FONT.LOCAL_MISMATCH",
            "FONT.NON_SUBSET_AMONG_SUBSET",
            "FONT.NOT_EMBEDDED",
            "FONT.SAME_NAME_DIFFERENT_PROGRAM",
            "IMG.MIXED_QUANTIZATION",
            "IMG.RESOLUTION_DISCONTINUITY",
            "META.DOCID_INSTANCEID_DIFFER",
            "META.MODDATE_BEFORE_CREATEDATE",
            "META.PRODUCER_CREATOR_MISMATCH",
            "META.STRIPPED",
            "META.XMP_HISTORY",
            "REV.ANNOTATION_ONLY",
            "REV.OBJECTS_CHANGED",
            "REV.PAGE_CONTENT_CHANGED",
            "REV.PRESENT",
            "SIG.ABSENT",
            "SIG.BYTERANGE_GAP",
            "SIG.BYTERANGE_MALFORMED",
            "SIG.CERT_EXPIRED_AT_SIGNING",
            "SIG.CHAIN_UNTRUSTED",
            "SIG.COVERAGE_SUPERSEDED",
            "SIG.DIGEST_MISMATCH",
            "SIG.DOCMDP_VIOLATION",
            "SIG.NO_TIMESTAMP",
            "SIG.REVOKED",
            "SIG.TIMESTAMP_VERIFIED",
            "STRUCT.LINEARIZED",
            "STRUCT.MULTIPLE_CATALOGS",
            "STRUCT.ORPHANED_OBJECTS",
            "STRUCT.XREF_MISMATCH",
            "TEXT.ABSENT",
            "TEXT.INVISIBLE_OVER_RASTER",
            "TEXT.VISIBLE_OVER_RASTER",
            "TEXT.WHITE_ON_WHITE"
          ]
        },
        "statement": {
          "description": "Generated from a catalog template. Identical inputs produce identical text.",
          "type": "string"
        }
      },
      "required": [
        "id",
        "statement",
        "evidence"
      ],
      "type": "object"
    },
    "undetermined": {
      "additionalProperties": false,
      "properties": {
        "evidence": {
          "description": "Byte offsets, object numbers, and extracted values — enough to confirm or refute the statement independently. Keys are documented per id in OBSERVATIONS.md.",
          "type": "object"
        },
        "id": {
          "description": "Match on this, never on `statement` prose.",
          "enum": [
            "BUS.FRAME_REJECTED",
            "DETECTOR.FAILED",
            "IMG.ANALYSIS_SKIPPED",
            "LIMIT.SIZE",
            "PARSE.PARTIAL",
            "READER.FAILED",
            "REV.HISTORY_UNAVAILABLE",
            "REV.XREF_UNREADABLE",
            "SIG.CHAIN_UNRESOLVED",
            "SIG.DIGEST_UNVERIFIED",
            "SIG.REVOCATION_UNKNOWN",
            "SIG.SIGNING_TIME_UNKNOWN"
          ]
        },
        "statement": {
          "description": "Generated from a catalog template. Identical inputs produce identical text.",
          "type": "string"
        }
      },
      "required": [
        "id",
        "statement",
        "evidence"
      ],
      "type": "object"
    }
  },
  "$id": "https://trustpdf.io/schema/report/1",
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "additionalProperties": false,
  "description": "Facts about a PDF, each cited to the bytes it came from. Carries no score, no risk level, and no judgement of authenticity — those are the caller's to derive. `observations` are what the engine found; `undetermined` is what it could not check, and is never empty merely because a check passed.",
  "properties": {
    "engine": {
      "additionalProperties": false,
      "description": "Pin both to reproduce this report byte-for-byte.",
      "properties": {
        "ruleset": {
          "type": "string"
        },
        "version": {
          "type": "string"
        }
      },
      "required": [
        "version",
        "ruleset"
      ],
      "type": "object"
    },
    "input": {
      "additionalProperties": false,
      "description": "The document is identified, never echoed. Nothing here reveals its content.",
      "properties": {
        "bytes": {
          "minimum": 0,
          "type": "integer"
        },
        "sha256": {
          "pattern": "^[0-9a-f]{64}$",
          "type": "string"
        }
      },
      "required": [
        "sha256",
        "bytes"
      ],
      "type": "object"
    },
    "observations": {
      "description": "Sorted by id, then by evidence. Ordering never varies between runs.",
      "items": {
        "$ref": "#/$defs/observation"
      },
      "type": "array"
    },
    "schema": {
      "const": 1,
      "description": "Envelope contract version. Increments only on a breaking change to this structure."
    },
    "undetermined": {
      "description": "Checks that could not run, each saying why. An empty list means every defined check ran — it never means the document is clean.",
      "items": {
        "$ref": "#/$defs/undetermined"
      },
      "type": "array"
    }
  },
  "required": [
    "schema",
    "engine",
    "input",
    "observations",
    "undetermined"
  ],
  "title": "TrustPDF report",
  "type": "object"
}