{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://authenticmemory.org/schema/v1/gpr.json",
  "title": "GAMI Proof Record",
  "description": "Schema for GAMI Proof Record v1. Covers file integrity, institutional signature, and distributed timestamp anchoring.",
  "type": "object",
  "required": ["@context", "type", "schema", "id", "subject", "institution", "metadata", "parent", "signature", "timestamps"],
  "additionalProperties": false,
  "properties": {
    "@context": {
      "type": "string",
      "const": "https://authenticmemory.org/schema/v1",
      "description": "JSON-LD context URI. Fixed for v1."
    },
    "type": {
      "type": "string",
      "const": "gami-proof"
    },
    "schema": {
      "type": "string",
      "const": "v1",
      "description": "Schema version. Implicitly defines canonicalization (JCS, RFC 8785) and signature algorithm (Ed25519, RFC 8032)."
    },
    "id": {
      "type": "string",
      "pattern": "^sha256:[0-9a-f]{64}$",
      "description": "SHA-256 of the canonical GPR, excluding the id, signature, and timestamps fields. Deterministically derivable by any party — no central assignment required."
    },
    "subject": {
      "type": "object",
      "description": "The file being anchored. Contains only cryptographically stable identifiers.",
      "required": ["hash", "filesize"],
      "additionalProperties": false,
      "properties": {
        "hash": {
          "type": "string",
          "pattern": "^sha256:[0-9a-f]{64}$",
          "description": "SHA-256 of the file content. This is the primary identity of the anchored file."
        },
        "filesize": {
          "type": "integer",
          "minimum": 0,
          "description": "File size in bytes. Convenience field for pre-hash sanity checks."
        }
      }
    },
    "institution": {
      "type": "object",
      "description": "The institution signing this proof.",
      "required": ["name", "key_id"],
      "additionalProperties": false,
      "properties": {
        "name": {
          "type": "string",
          "description": "Human-readable institution name as declared at signing time."
        },
        "key_id": {
          "type": "string",
          "pattern": "^did:web(vh)?:.+#.+$",
          "description": "DID key identifier (did:webvh or did:web). Resolves to the institution's public key via their published DID document; did:webvh additionally binds the key to a self-certifying, hash-chained document history."
        }
      }
    },
    "metadata": {
      "type": "object",
      "description": "Descriptive metadata. All fields are institutionally declared and covered by the signature, not independently proven.",
      "required": ["public"],
      "additionalProperties": false,
      "properties": {
        "public": {
          "type": "object",
          "description": "Publicly visible metadata. Additional institution-specific fields are permitted.",
          "required": ["collection", "classificationCode", "dateSubmitted"],
          "additionalProperties": true,
          "properties": {
            "collection": {
              "type": "string",
              "description": "Collection identifier."
            },
            "classificationCode": {
              "type": "string",
              "description": "Institution-assigned classification or reference code."
            },
            "dateSubmitted": {
              "type": "string",
              "format": "date-time",
              "description": "Claimed submission date (ISO 8601). This is an institutional assertion, not a cryptographic proof. The proven existence date is determined by blockHeight in the confirmed timestamp entry."
            },
            "filename": {
              "type": "string",
              "description": "Filename as declared by the institution at signing time. Mutable — files may be renamed. Treat as a label, not an identifier."
            }
          }
        },
        "private_hash": {
          "type": "string",
          "pattern": "^sha256:[0-9a-f]{64}$",
          "description": "SHA-256 of the institution's private metadata block, retained internally. Commits to private metadata without revealing it."
        },
        "private_hash_canonicalization": {
          "type": "string",
          "enum": ["JCS"],
          "description": "Canonicalization applied to the private metadata before hashing. Required when private_hash is present so the institution can reproduce the hash."
        }
      },
      "dependentRequired": {
        "private_hash": ["private_hash_canonicalization"],
        "private_hash_canonicalization": ["private_hash"]
      }
    },
    "parent": {
      "description": "Reference to a predecessor GPR, enabling append-only updates (e.g. attaching additional metadata without invalidating the original proof). Null for root records.",
      "oneOf": [
        {
          "type": "null"
        },
        {
          "type": "string",
          "pattern": "^sha256:[0-9a-f]{64}$",
          "description": "The id of the predecessor GPR. Since id is content-addressed, this reference is tamper-evident."
        }
      ]
    },
    "signature": {
      "type": "string",
      "pattern": "^ed25519:[A-Za-z0-9+/]{86}==$",
      "description": "Ed25519 signature (RFC 8032) over the canonical GPR bytes, excluding the signature and timestamps fields. The institution's signing key signs the canonical bytes directly — no pre-hashing."
    },
    "timestamps": {
      "type": "array",
      "description": "One or more timestamp anchors. Verification passes if any single entry verifies successfully. Entries are ordered chronologically; index 0 is the original anchor.",
      "minItems": 1,
      "uniqueItems": true,
      "items": {
        "$ref": "#/$defs/timestampEntry"
      }
    }
  },
  "$defs": {
    "timestampEntry": {
      "type": "object",
      "description": "A single timestamp anchor. The proof field is a binary OTS blob (base64-encoded). When status is pending, the proof contains a calendar server URL and no blockHeight is present. When confirmed, blockHeight is required.",
      "required": ["method", "calendar", "status", "proof"],
      "additionalProperties": false,
      "properties": {
        "method": {
          "type": "string",
          "enum": ["opentimestamps"],
          "description": "Timestamping protocol. opentimestamps is the only supported method in v1."
        },
        "calendar": {
          "type": "string",
          "enum": ["bitcoin", "bloxberg"],
          "description": "Target ledger for the anchor."
        },
        "status": {
          "type": "string",
          "enum": ["pending", "confirmed"],
          "description": "pending: Bitcoin confirmation not yet received; proof contains calendar URL reference. confirmed: proof contains full Merkle path to a confirmed block; blockHeight is populated."
        },
        "blockHeight": {
          "type": "integer",
          "minimum": 0,
          "description": "Block height of the confirmed anchor. Human-readable existence proof — verifiable without parsing the proof blob."
        },
        "proof": {
          "type": "string",
          "contentEncoding": "base64",
          "description": "Binary OTS proof blob, base64-encoded. For a pending entry, this is an incomplete proof referencing a calendar server. For a confirmed entry, this is a complete proof containing the full Merkle path to the block header."
        }
      },
      "if": {
        "properties": { "status": { "const": "confirmed" } },
        "required": ["status"]
      },
      "then": {
        "required": ["blockHeight"]
      }
    }
  }
}
