{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://travel.agenthaven.dev/contracts/schemas/checkpoints-document.schema.json",
  "title": "The checkpoints document (GET /checkpoints)",
  "description": "What a ledger keeper publishes at GET /checkpoints: every checkpoint it sealed, oldest first, each beside the receipt the witness returned or the error when it did not; the witness it seals at; the cadence of its timer, when the process keeps one; and the signer of every checkpoint_jws with where its key is published. The payload of each checkpoint_jws is checkpoint.schema.json and each receipt_jws is checkpoint-receipt.schema.json. One shape on both sides: the verifier's checkpoints check reads the seller's and the buyer's documents against the chains it fetched (each head must be the chain's head at that height, each receipt must verify under the witness key set and name the checkpoint), and the witness emulator resolves the signer's key from key_url. Status: IMPLEMENTED by the seller (src/app.mjs, since 2026-09-07), by agent-buyer (checkpoints.document, since 2026-09-11) and by agent-authority (GET /checkpoints, since 2026-09-12: sealed after every issuance, denial and revocation and on a timer; signer.key_url is its jwks.json, and the witness resolves the key from there). Written 2026-09-12 from the two live documents, which already agreed; the authority's followed the same day. Since 2026-09-13 a keeper may register each head at a public transparency log it does not operate as well (external-witness.md): the document then names that log as external_witness and each checkpoint carries a second receipt, external, stored as the log returned it and verified by the verifier, never by the keeper.",
  "type": "object",
  "required": ["checkpoints", "witness", "interval_seconds", "signer"],
  "properties": {
    "checkpoints": { "type": "array", "items": { "$ref": "#/$defs/checkpoint" }, "description": "Oldest first; height strictly increasing." },
    "witness": { "type": ["string", "null"], "format": "uri", "description": "The witness base URL this keeper seals at, the iss of its receipts (today https://provedby.dev/witness, the emulator, declared as such). Null when it seals at none: the checkpoints are then published unsealed." },
    "external_witness": { "type": ["string", "null"], "format": "uri", "description": "The public transparency log this keeper also registers each head at (external-witness.md), the base URL of its API. Null, or absent, when it registers at none. Optional since 2026-09-13." },
    "interval_seconds": { "type": ["integer", "null"], "minimum": 1, "description": "The timer half of the sealing policy, when this process keeps one (the seller: 600, an alarm in its Durable Object). Null when the cadence belongs to the host (the buyer: its publish timer), not to the process that signs." },
    "signer": {
      "type": "object",
      "required": ["spiffe_id", "kid", "key_url"],
      "properties": {
        "spiffe_id": { "type": "string", "pattern": "^spiffe://", "description": "The instance that signs the checkpoints; the signer of the chain too." },
        "kid": { "type": "string", "description": "RFC 7638 thumbprint of the instance key, the kid in the header of every checkpoint_jws." },
        "key_url": { "type": "string", "format": "uri", "description": "Where the key is published: the seller's /.well-known/spiffe-svid.json, the buyer's /.well-known/agent-keys.json." }
      }
    }
  },
  "$defs": {
    "checkpoint": {
      "type": "object",
      "required": ["height", "head", "at", "checkpoint_jws", "witness", "receipt_jws", "receipt", "witness_error"],
      "properties": {
        "height": { "type": "integer", "minimum": 1, "description": "Number of entries in the chain at this checkpoint." },
        "head": { "type": "string", "pattern": "^[0-9a-f]{64}$", "description": "The hash of the chain's last entry at that height." },
        "at": { "type": "string", "format": "date-time" },
        "checkpoint_jws": { "type": "string", "description": "Compact checkpoint+jwt over checkpoint.schema.json, signed by signer.kid." },
        "witness": { "type": ["string", "null"], "format": "uri", "description": "The witness this checkpoint was offered to; null when none was configured at the time." },
        "receipt_jws": { "type": ["string", "null"], "description": "Compact checkpoint-receipt+jwt as the witness returned it; null when unsealed." },
        "receipt": {
          "type": ["object", "null"],
          "required": ["witness", "log_index", "logged_at", "kid"],
          "properties": {
            "witness": { "type": "string", "format": "uri", "description": "The receipt's iss." },
            "log_index": { "type": ["integer", "null"], "minimum": 0, "description": "The entry's position in the witness's log, from 0." },
            "logged_at": { "type": ["string", "null"], "format": "date-time" },
            "kid": { "type": "string", "description": "The witness key that signed the receipt, in the witness's /.well-known/jwks.json." }
          },
          "description": "The receipt's claims a reader needs without decoding it; null when unsealed."
        },
        "witness_error": { "type": ["string", "null"], "description": "Why the checkpoint is unsealed, as <code>: <sentence> (witness_unavailable, receipt_invalid); null when sealed, or when no witness was configured." },
        "external": { "$ref": "#/$defs/external" }
      }
    },
    "external": {
      "type": ["object", "null"],
      "description": "The second receipt (external-witness.md): the checkpoint's head registered as a record at a public transparency log the keeper does not operate, and what the log answered, stored as returned. The keeper checks only that entry_hash is the SHA-256 of the head's bytes; the COSE receipt, the inclusion proof and the log's tree head are verified by the verifier. Null when the checkpoint was written with no external log configured; absent in documents from before 2026-09-13.",
      "required": ["log", "registered_at", "receipt_b64", "entry_hash", "entry_hash_scheme", "leaf_index", "tree_size", "error"],
      "properties": {
        "log": { "type": "string", "format": "uri", "description": "The log's base URL, where POST /register was sent and where /anchor/sth, /anchor/consistency-proof and /v1/inclusion/{head} answer." },
        "registered_at": { "type": "string", "format": "date-time", "description": "When the keeper sent the registration, on its own clock." },
        "receipt_b64": { "type": ["string", "null"], "description": "The COSE_Sign1 receipt (CBOR tag 18) as the log returned it, standard base64: EdDSA over the RFC 6962 root at tree_size, the inclusion proof in the unprotected header (external-witness.md). Null when the registration failed." },
        "entry_hash": { "type": ["string", "null"], "pattern": "^[0-9a-f]{64}$", "description": "The log's entry hash: SHA-256 over the 32 bytes of the head. The keeper refuses a receipt whose entry_hash is anything else." },
        "entry_hash_scheme": { "type": ["string", "null"], "description": "The log's name for how entry_hash was derived (legacy for a bare digest)." },
        "leaf_index": { "type": ["integer", "null"], "minimum": 0, "description": "The record's position in the log's tree." },
        "tree_size": { "type": ["integer", "null"], "minimum": 1, "description": "The size of the tree the receipt's root covers." },
        "error": { "type": ["string", "null"], "description": "Why there is no receipt, as <code>: <sentence> (witness_unavailable, receipt_invalid); null when registered." }
      }
    }
  }
}
