{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "urn:cede:schema:feed-index:v0",
  "title": "CEDE feed registry index, v0",
  "description": "Every pinned data snapshot this build can read, grouped by feed. SPEC.md section 2A makes feeds first-class registry objects: versioned snapshots of hazard, event and domain data, immutable once published, which is what lets a result replay byte for byte. This document is the body of GET /feeds — the entry point for verifying the lineage of a price or a backtest, whose assumption set names the feed id, the version and the sha256 of the exact bytes it read. Each version below carries that same sha256 and the paths at which the manifest and the bytes themselves are readable.",
  "type": "object",
  "additionalProperties": false,
  "required": ["schema_version", "note", "feeds"],
  "properties": {
    "schema_version": { "type": "string", "pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+$" },
    "note": {
      "type": "string",
      "minLength": 1,
      "description": "What this index is for, in the response itself, so a caller reading it in a terminal need not hold the docs."
    },
    "feeds": {
      "type": "array",
      "description": "One entry per feed, sorted by id. Empty is a legitimate answer for a build holding no snapshots; it is never a way of hiding one, because the enumeration is the feeds directory itself.",
      "items": { "$ref": "#/$defs/feed" }
    }
  },
  "$defs": {
    "feed": {
      "type": "object",
      "additionalProperties": false,
      "required": ["id", "name", "publisher", "kind", "versions"],
      "properties": {
        "id": { "type": "string", "minLength": 1 },
        "name": { "type": "string", "minLength": 1 },
        "publisher": {
          "type": "string",
          "minLength": 1,
          "description": "Who published the underlying data. Cede pins a copy of it; it is not the publisher of the data."
        },
        "kind": { "type": "string", "minLength": 1 },
        "versions": {
          "type": "array",
          "minItems": 1,
          "items": { "$ref": "#/$defs/version" }
        }
      }
    },
    "version": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "version",
        "vintage",
        "sha256",
        "media_type",
        "bytes",
        "aggregation_level",
        "content_servable",
        "links"
      ],
      "properties": {
        "version": {
          "type": "string",
          "minLength": 1,
          "description": "The identifier a risk object's trigger.data_sources pins, and the one a price or backtest assumption set reports."
        },
        "vintage": { "type": "string", "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}$" },
        "sha256": {
          "type": "string",
          "pattern": "^[0-9a-f]{64}$",
          "description": "Digest of the exact bytes, in full — never truncated. The same value appears in the assumption set of every result that read this version, and the data document re-derives it from the bytes it serves."
        },
        "media_type": { "type": "string", "minLength": 1 },
        "bytes": {
          "type": "integer",
          "minimum": 0,
          "description": "Length of the snapshot in bytes, so a caller can see the size of the data document before requesting it."
        },
        "record_count": { "type": "integer", "minimum": 0 },
        "licence": {
          "type": "string",
          "minLength": 1,
          "description": "The licence name from the manifest. The full statement, including any required attribution, is on the feed object at links.self."
        },
        "aggregation_level": { "$ref": "#/$defs/aggregation_level" },
        "content_servable": { "$ref": "#/$defs/content_servable" },
        "links": { "$ref": "#/$defs/links" }
      }
    },
    "aggregation_level": {
      "type": "object",
      "required": ["level"],
      "description": "The level the manifest declares, per SPEC.md section 4, P10: feeds are aggregate only — triangles, tables and indices at portfolio level, never individual-level records. `undeclared` means the manifest states no level, and content is not served for it.",
      "properties": {
        "level": { "type": "string", "minLength": 1 },
        "note": { "type": "string" }
      }
    },
    "content_servable": {
      "type": "boolean",
      "description": "Whether links.data serves this snapshot's bytes. False means the declared aggregation level is not one whose content this build serves; the manifest, its coverage and its digest stay readable either way, because naming a feed is not serving its content."
    },
    "links": {
      "type": "object",
      "additionalProperties": false,
      "required": ["self", "data"],
      "description": "API paths, relative to the deployment's base URL.",
      "properties": {
        "self": { "type": "string", "minLength": 1 },
        "data": { "type": "string", "minLength": 1 }
      }
    }
  }
}
