{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://sabline.dev/receipt/v1/schema.json",
  "title": "https://sabline.dev/receipt/v1",
  "description": "The predicate of an in-toto Statement v1 whose predicateType is https://sabline.dev/receipt/v1: a sabline.receipt/1 document recording one run of the program that is the statement's first subject - the budget it was given, each refusal and declassification, the parameters it ran under, how it ended and how long it took. sabline-spec SPEC.md section 8.7 defines it. No field holds a value the program handled. Within v1 fields may be added and consumers must ignore fields they do not know.",
  "type": "object",
  "required": ["schema", "producer", "budget", "run_parameters", "refusals", "declassifications", "exit", "wall_time_ms", "complete"],
  "properties": {
    "schema": {"enum": ["sabline.receipt/1", "velaris.receipt/1"]},
    "producer": {
      "description": "The implementation that ran the program and wrote the receipt.",
      "type": "object",
      "required": ["name"],
      "properties": {
        "name": {"type": "string", "minLength": 1},
        "uri": {"type": "string", "minLength": 1},
        "version": {"type": "string", "minLength": 1}
      }
    },
    "specification": {
      "description": "The sabline-spec version the producer followed, as 'sabline-spec 0.10.0'. Informative.",
      "type": "string"
    },
    "startedAt": {
      "description": "When the run started, RFC 3339 with timezone Z, by the producer's clock. Informative.",
      "type": "string",
      "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(\\.[0-9]+)?Z$"
    },
    "wall_time_ms": {
      "description": "How long the run took, in milliseconds, by the producer's clock.",
      "type": "number",
      "minimum": 0
    },
    "budget": {
      "description": "The budget the run was given, as the budget grammar writes it (section 4.7). The empty string is a budget that grants nothing.",
      "type": "string"
    },
    "run_parameters": {
      "description": "What the run was given besides its budget.",
      "type": "object",
      "required": ["seed", "freeze_time", "timeout", "max_memory_mb", "confinement"],
      "properties": {
        "seed": {"description": "The seed randomness was fixed to, or null.", "type": ["integer", "null"]},
        "freeze_time": {"description": "The instant the clock was fixed to, RFC 3339 with Z, or null.", "type": ["string", "null"]},
        "timeout": {"description": "The most seconds the run was allowed, or null for no limit.", "type": ["number", "null"], "exclusiveMinimum": 0},
        "max_memory_mb": {"description": "The most memory the run was allowed, in MB, or null for no cap.", "type": ["integer", "null"], "minimum": 1},
        "max_read_bytes": {"description": "The largest file a read accepted, in bytes.", "type": ["integer", "null"], "minimum": 0},
        "confinement": {"description": "What operating-system confinement the run had. From sabline-spec 0.12.0 a level: 'full' when the operating system held every file, network and process limit of the budget, 'partial' when it held some, 'none' when the budget was the only boundary. A receipt written before 0.12.0 says 'none', or under the reference's eval profile names a mechanism: landlock-net, landlock, job-one-process or sandbox-exec (section 8.9).", "type": "string", "minLength": 1},
        "confinement_reason": {"description": "Added in sabline-spec 0.12.0, within version 1: each limit the operating system did not hold and why, or why nothing was asked of it.", "type": "string"},
        "confinement_layers": {"description": "Added in sabline-spec 0.12.0, within version 1: the producer's names for the mechanisms it applied, empty when it applied none.", "type": "array", "items": {"type": "string", "minLength": 1}},
        "os_policy_sha256": {"description": "Added in sabline-spec 0.12.0, within version 1: the sha256 of the policy the producer derived from the budget and asked the operating system to hold, or null.", "type": ["string", "null"], "pattern": "^[0-9a-f]{64}$"},
        "profile": {"description": "Added in sabline-spec 0.11.0, within version 1: the name of a profile the run was held to beyond its budget, as 'eval' (section 8.9). Absent when there was none.", "type": "string", "minLength": 1}
      }
    },
    "effects_used": {
      "description": "Each effect and how many operations of it the budget let through, or null when the producer could not tell (a run stopped from outside).",
      "type": ["object", "null"],
      "additionalProperties": {"type": "integer", "minimum": 0}
    },
    "grants_used": {
      "description": "From sabline-spec 0.13.0. Each grant of the budget that let at least one operation through, as canonical text without a count, with how many. The operator's text: never the path, host, module or argument the program gave.",
      "type": "array",
      "items": {
        "type": "object",
        "required": ["grant", "times"],
        "properties": {
          "grant": {"type": "string", "minLength": 1},
          "times": {"type": "integer", "minimum": 1}
        }
      }
    },
    "tool_calls": {
      "description": "From sabline-spec 0.13.0, for a run given a manifest of tools. Each place a tool was called; never its arguments or its result.",
      "type": "array",
      "items": {
        "type": "object",
        "required": ["tool", "line", "times", "secret", "held_to"],
        "properties": {
          "tool": {"type": "string"},
          "line": {"type": ["integer", "null"], "minimum": 0},
          "times": {"type": "integer", "minimum": 1},
          "secret": {"type": "boolean"},
          "held_to": {"type": "array", "items": {"type": "string"}}
        }
      }
    },
    "tool_ceiling": {
      "description": "From sabline-spec 0.13.0, with tool_calls: the manifest's ceiling, what the run spent of it, and the sha256 of the manifest.",
      "type": "object",
      "required": ["calls", "cost", "unit", "calls_used", "cost_used", "manifest_sha256"],
      "properties": {
        "calls": {"type": ["integer", "null"], "minimum": 0},
        "cost": {"type": ["number", "null"], "minimum": 0},
        "unit": {"type": "string"},
        "calls_used": {"type": "integer", "minimum": 0},
        "cost_used": {"type": "number", "minimum": 0},
        "manifest_sha256": {"type": "string", "pattern": "^[0-9a-f]{64}$"}
      }
    },
    "refusals": {
      "description": "Each place the budget refused the run, once per code, effect, line and whether it stopped the run, with how many times. The path, host or module the program named is not recorded.",
      "type": "array",
      "items": {
        "type": "object",
        "required": ["code", "effect", "line", "stopped", "times"],
        "properties": {
          "code": {"type": ["string", "null"], "pattern": "^E[0-9]{3}$"},
          "effect": {"enum": ["io", "env", "fs", "net", "clock", "rand", "ffi", "declassify", "tool", null]},
          "line": {"type": ["integer", "null"], "minimum": 0},
          "stopped": {"type": "boolean"},
          "times": {"type": "integer", "minimum": 1}
        }
      }
    },
    "declassifications": {
      "description": "Each place the run declassified a value, with the reason written in the program and how many times. The value is not recorded.",
      "type": "array",
      "items": {
        "type": "object",
        "required": ["reason", "line", "times"],
        "properties": {
          "key_fingerprint": {"description": "From sabline-spec 0.13.0, on a MAC under a secret key (reason \"hmac signature\"): twelve lowercase hexadecimal digits that tell one key from another, or \"many\". Never the key or the MAC.", "type": "string", "pattern": "^([0-9a-f]{12}|many)$"},
          "reason": {"type": "string"},
          "line": {"type": ["integer", "null"], "minimum": 0},
          "times": {"type": "integer", "minimum": 1}
        }
      }
    },
    "exit": {
      "description": "How the run ended.",
      "type": "object",
      "required": ["status", "outcome", "code"],
      "properties": {
        "status": {"description": "The exit status the producer reported.", "type": ["integer", "null"]},
        "outcome": {"enum": ["ok", "refused", "failed", "did_not_compile", "timeout", "out_of_memory"]},
        "code": {"description": "The code of the error that ended the run, or null.", "type": ["string", "null"], "pattern": "^E[0-9]{3}$"}
      }
    },
    "stop": {
      "description": "Added in sabline-spec 0.11.0, within version 1: present when a stop was asked for from outside the run. What asked (a signal, or a stop file appearing), how long after the run began, and how the stop was honoured - at a call or loop turn, by killing the worker after the grace period, or not at all because the run had already ended.",
      "type": "object",
      "required": ["asked", "honoured"],
      "properties": {
        "asked": {"type": "string"},
        "honoured": {"type": "string"},
        "after_ms": {"type": "number", "minimum": 0},
        "grace_seconds": {"type": "number", "exclusiveMinimum": 0}
      }
    },
    "complete": {
      "description": "False when the run was stopped from outside before it could report: what the receipt lists happened, and a count is at least the number given.",
      "type": "boolean"
    }
  }
}
