{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://agentrust-io.com/schema/trace-v0.2.json",
  "$defs": {
    "canonicalizableValue": {
      "description": "Any JSON value section 3.2.2 can canonicalize without ambiguity. RFC 7517 lets a JWK carry members this schema does not name, those members are covered by the signature, and they therefore need the constraint the named fields carry: `number` is excluded, and an integer is held to the safe-integer domain. Recursive, because such a member may itself be an array or an object.",
      "anyOf": [
        {
          "type": [
            "string",
            "boolean",
            "null"
          ]
        },
        {
          "type": "integer",
          "minimum": -9007199254740991,
          "maximum": 9007199254740991
        },
        {
          "type": "array",
          "items": {
            "$ref": "#/$defs/canonicalizableValue"
          }
        },
        {
          "type": "object",
          "additionalProperties": {
            "$ref": "#/$defs/canonicalizableValue"
          }
        }
      ]
    }
  },
  "title": "TRACE Trust Record",
  "description": "A TRACE v0.2 Trust Record: hardware-attested governance evidence for an AI agent execution.",
  "type": "object",
  "required": [
    "eat_profile",
    "iat",
    "subject",
    "model",
    "runtime",
    "policy",
    "data_class",
    "build_provenance",
    "appraisal",
    "cnf"
  ],
  "properties": {
    "eat_profile": {
      "type": "string",
      "const": "tag:agentrust-io.com,2026:trace-v0.2",
      "description": "EAT profile URI identifying this as a TRACE v0.2 Trust Record."
    },
    "iat": {
      "type": "integer",
      "description": "Issued-at time as Unix epoch seconds.",
      "minimum": 1700000000,
      "maximum": 9007199254740991
    },
    "subject": {
      "type": "string",
      "description": "Workload identity as a SPIFFE SVID URI or DID URI.",
      "pattern": "^(spiffe://|did:)"
    },
    "model": {
      "type": "object",
      "description": "Model identity and provenance.",
      "required": [
        "provider",
        "model_id"
      ],
      "properties": {
        "provider": {
          "type": "string",
          "description": "Model provider (e.g. 'anthropic', 'openai', 'meta')."
        },
        "model_id": {
          "type": "string",
          "description": "Model identifier as used by the provider."
        },
        "version": {
          "type": "string",
          "description": "Model version or snapshot identifier."
        },
        "weights_digest": {
          "type": "string",
          "description": "SHA-256 or SHA-384 digest of the model weights. Required for local/confidential-inference deployments.",
          "pattern": "^sha(256:[0-9a-f]{64}|384:[0-9a-f]{96})$"
        },
        "aibom_uri": {
          "type": "string",
          "format": "uri",
          "description": "URI to SPDX 3.0 AI Profile or CycloneDX 1.7 ML-BOM for this model."
        }
      },
      "additionalProperties": false
    },
    "runtime": {
      "type": "object",
      "description": "TEE measurement chain binding the workload to hardware.",
      "required": [
        "platform",
        "measurement"
      ],
      "properties": {
        "platform": {
          "type": "string",
          "enum": [
            "intel-tdx",
            "amd-sev-snp",
            "azure-cvm-sev-snp",
            "nvidia-h100",
            "nvidia-blackwell",
            "aws-nitro",
            "arm-cca",
            "google-confidential-space",
            "tpm2",
            "software-only"
          ],
          "description": "Hardware platform providing the root of trust. software-only marks development-mode records with no hardware backing; they must never be treated as attested evidence."
        },
        "measurement": {
          "type": "string",
          "description": "Hardware measurement of the workload (e.g. TDX MRTD, SEV measurement, TPM PCR composite).",
          "pattern": "^sha(256:[0-9a-f]{64}|384:[0-9a-f]{96})$"
        },
        "rim_uri": {
          "type": "string",
          "format": "uri",
          "description": "URI to the vendor-published Reference Integrity Manifest for this measurement."
        },
        "nonce": {
          "type": "string",
          "description": "Freshness nonce binding the attestation report to this record (base64url, no padding)."
        },
        "firmware_version": {
          "type": "string",
          "description": "Firmware or microcode version included in the measurement."
        }
      },
      "additionalProperties": false
    },
    "policy": {
      "type": "object",
      "description": "Policy bundle sealed to the TEE measurement.",
      "required": [
        "bundle_hash",
        "enforcement_mode"
      ],
      "properties": {
        "bundle_hash": {
          "type": "string",
          "description": "SHA-256 or SHA-384 digest of the policy bundle in force at execution time.",
          "pattern": "^sha(256:[0-9a-f]{64}|384:[0-9a-f]{96})$"
        },
        "enforcement_mode": {
          "type": "string",
          "enum": [
            "enforce",
            "advisory",
            "silent",
            "declared"
          ],
          "default": "enforce",
          "description": "How the policy named by bundle_hash related to this execution. enforce: evaluated and blocked on deny. advisory: evaluated, logged, allowed. silent: evaluated and enforced with operational logs suppressed; the audit chain still records every would-have-denied decision. declared: the policy is named and bound into the signed record and NOTHING evaluated it, which is the honest value for a producer with no policy engine, such as an agent framework. Gateways MUST default to enforce. A deployment MUST explicitly configure silent. declared is never a default, a producer that evaluates policy MUST NOT use it, and a consumer MUST NOT read it as evidence that any rule was checked."
        },
        "version": {
          "type": "string",
          "description": "Policy bundle version (semantic versioning recommended)."
        },
        "policy_uri": {
          "type": "string",
          "format": "uri",
          "description": "URI to the policy bundle for verification."
        }
      },
      "additionalProperties": false
    },
    "data_class": {
      "type": "string",
      "description": "Highest-sensitivity data classification of inputs and outputs processed during this execution.",
      "examples": [
        "public",
        "internal",
        "confidential",
        "restricted",
        "top-secret"
      ]
    },
    "tool_transcript": {
      "type": "object",
      "description": "Bound hash of the MCP/A2A tool-call transcript. OPTIONAL for Phase 1 records; REQUIRED for Phase 2+.",
      "required": [
        "hash"
      ],
      "properties": {
        "hash": {
          "type": "string",
          "description": "SHA-256 or SHA-384 digest of the full tool-call transcript, bound into the EAT envelope.",
          "pattern": "^sha(256:[0-9a-f]{64}|384:[0-9a-f]{96})$"
        },
        "call_count": {
          "type": "integer",
          "minimum": 0,
          "maximum": 9007199254740991,
          "description": "Total number of tool calls in this session."
        },
        "transcript_uri": {
          "type": "string",
          "format": "uri",
          "description": "URI to the full transcript on the transparency log."
        }
      },
      "additionalProperties": false
    },
    "delegation": {
      "type": "object",
      "description": "A2A profile: links this record to the delegating hop's Trust Record. Present when this execution acted on delegated authority; absent on a root (non-delegated) execution. A chain of these forms an offline-verifiable delegation DAG.",
      "required": [
        "parent_record_hash",
        "credential_id"
      ],
      "properties": {
        "parent_record_hash": {
          "type": "string",
          "description": "SHA-256 or SHA-384 digest of the parent hop's Trust Record.",
          "pattern": "^sha(256:[0-9a-f]{64}|384:[0-9a-f]{96})$"
        },
        "credential_id": {
          "type": "string",
          "minLength": 1,
          "description": "Identifier of the delegation credential this hop acted under."
        }
      },
      "additionalProperties": false
    },
    "origin": {
      "type": "object",
      "description": "Where the evidence in this record came from, when that is not this runtime. Absent means the runtime produced its own record, which is what a consumer assumes and what every hardware profile is. Present means something else assembled the record from evidence it did not measure. A non-self kind cannot carry a hardware runtime.platform.",
      "required": [
        "kind",
        "producer"
      ],
      "properties": {
        "kind": {
          "type": "string",
          "enum": [
            "self",
            "third-party-control-plane",
            "log-import"
          ],
          "description": "self: the runtime produced its own record. third-party-control-plane: assembled from another vendor's runtime governance output, asserted by the system that produced it with no root outside that system. log-import: assembled from a log or export whose producer is not a control plane."
        },
        "producer": {
          "type": "string",
          "minLength": 1,
          "description": "Identifier of the system that produced the source evidence."
        },
        "source_event_id": {
          "type": "string",
          "minLength": 1,
          "description": "Identifier of the source event in the producing system, so a record can be traced back to it."
        },
        "ingested_at": {
          "type": "integer",
          "minimum": 1700000000,
          "maximum": 9007199254740991,
          "description": "Unix time at which the source evidence was ingested. Distinct from iat, which is when this record was issued."
        }
      },
      "additionalProperties": false
    },
    "references": {
      "type": "array",
      "description": "Facts outside this record that it points at. Spec section 3.1.2. An entry is a pointer, not evidence: the signature attests that this record points there, not the truth of what it points at. The block is assurance-neutral and does not affect runtime.platform. Two further rules in 3.1.2 bind verifiers rather than records, so this schema cannot express them: a verifier MUST NOT reject a record because an entry cannot be resolved, and MUST NOT treat a resolved entry as attested evidence.",
      "items": {
        "type": "object",
        "required": [
          "rel",
          "id",
          "resolver"
        ],
        "properties": {
          "rel": {
            "type": "string",
            "minLength": 1,
            "description": "Relationship type. The registered values are a registry that grows, so this is not a closed set. authorized-intent: an authorization decided before execution, held in another system. approval-outcome: an attributable human approval attached to a step-up or defer decision. behavior-trace: a behavioural record of what the agent did, of which this record is the environment evidence."
          },
          "id": {
            "type": "string",
            "minLength": 1,
            "description": "Identifier of the referenced fact within the resolver's system."
          },
          "resolver": {
            "type": "string",
            "minLength": 1,
            "description": "Identifier of the party obliged to resolve id. A producer that cannot name one omits the entry. Which identifiers are self-asserted is not decidable from the record, so this constrains the field's presence and not its value."
          },
          "retention": {
            "type": "string",
            "pattern": "^P(\\d+W|(\\d+Y(\\d+M)?(\\d+D)?|\\d+M(\\d+D)?|\\d+D)(T(\\d+H(\\d+M)?(\\d+S)?|\\d+M(\\d+S)?|\\d+S))?|T(\\d+H(\\d+M)?(\\d+S)?|\\d+M(\\d+S)?|\\d+S))$",
            "description": "Period for which resolver undertakes to keep id resolvable, as an ISO 8601 duration. An undertaking only: nothing in this specification enforces it."
          },
          "digest": {
            "type": "string",
            "pattern": "^sha(256:[0-9a-f]{64}|384:[0-9a-f]{96})$",
            "description": "SHA-256 or SHA-384 digest of the referenced object, when the producer holds it at issue time."
          }
        },
        "additionalProperties": false
      }
    },
    "build_provenance": {
      "type": "object",
      "description": "SLSA provenance for the workload (agent code + container image).",
      "required": [
        "slsa_level",
        "digest"
      ],
      "properties": {
        "slsa_level": {
          "type": "integer",
          "minimum": 0,
          "maximum": 3,
          "description": "SLSA Build Level achieved. Level 0 = software-only (development/staging); Level 2 minimum for TRACE conformance; Level 3 for production mark."
        },
        "builder": {
          "type": "string",
          "description": "SLSA builder URI."
        },
        "digest": {
          "type": "string",
          "description": "SHA-256 or SHA-384 digest of the container image or workload binary.",
          "pattern": "^sha(256:[0-9a-f]{64}|384:[0-9a-f]{96})$"
        },
        "provenance_uri": {
          "type": "string",
          "format": "uri",
          "description": "URI to the SLSA provenance attestation on a Sigstore/Rekor or compatible log."
        },
        "provenance_depth": {
          "type": "string",
          "enum": [
            "surface",
            "builder",
            "transitive"
          ],
          "description": "Depth of supply-chain verification the issuer claims for this record. surface: digest matches the workload artifact and builder is in a trusted set. builder: all of surface, plus provenance_uri resolves, the SLSA attestation signature verifies, its subject matches digest, and its builder.id matches builder. transitive: all of builder, plus every entry in the SLSA materials / resolvedDependencies has a verifiable publisher attestation. Records omitting this field are treated as surface by verifiers, so existing records keep their meaning."
        }
      },
      "additionalProperties": false
    },
    "appraisal": {
      "type": "object",
      "description": "Verifier's EAR appraisal of the evidence (draft-ietf-rats-ar4si).",
      "required": [
        "status",
        "verifier"
      ],
      "properties": {
        "status": {
          "type": "string",
          "enum": [
            "affirming",
            "warning",
            "contraindicated",
            "none"
          ],
          "description": "EAR appraisal status."
        },
        "verifier": {
          "type": "string",
          "format": "uri",
          "description": "URI identifying the verifier that produced this appraisal."
        },
        "policy_ref": {
          "type": "string",
          "format": "uri",
          "description": "URI to the appraisal policy used."
        },
        "timestamp": {
          "type": "integer",
          "minimum": -9007199254740991,
          "maximum": 9007199254740991,
          "description": "Unix epoch seconds when the appraisal was produced."
        },
        "provenance_depth_verified": {
          "type": "string",
          "enum": [
            "surface",
            "builder",
            "transitive"
          ],
          "description": "Depth at which this verifier actually checked build_provenance, which is not necessarily the depth the issuer claimed. A verifier may downgrade when deeper evidence does not resolve, and records the lower depth rather than a failure of the higher one. A verifier does not record a depth higher than it executed."
        }
      },
      "additionalProperties": false
    },
    "transparency": {
      "type": "string",
      "format": "uri",
      "description": "SCITT receipt URI. The Trust Record is the Signed Statement; this URI resolves to the inclusion proof (Receipt) on the transparency log."
    },
    "cnf": {
      "type": "object",
      "description": "Confirmation key (RFC 8747) that binds the Trust Record to the TEE-held signing key.",
      "required": [
        "jwk"
      ],
      "properties": {
        "jwk": {
          "type": "object",
          "description": "JWK (RFC 7517) representing the TEE-sealed public key. Keys must carry actual key material: OKP keys require crv and x; EC keys require crv, x, and y.",
          "required": [
            "kty"
          ],
          "properties": {
            "kty": {
              "type": "string"
            },
            "crv": {
              "type": "string"
            },
            "x": {
              "type": "string"
            },
            "y": {
              "type": "string"
            },
            "kid": {
              "type": "string"
            }
          },
          "allOf": [
            {
              "if": {
                "required": [
                  "kty"
                ],
                "properties": {
                  "kty": {
                    "const": "OKP"
                  }
                }
              },
              "then": {
                "required": [
                  "crv",
                  "x"
                ]
              }
            },
            {
              "if": {
                "required": [
                  "kty"
                ],
                "properties": {
                  "kty": {
                    "const": "EC"
                  }
                }
              },
              "then": {
                "required": [
                  "crv",
                  "x",
                  "y"
                ]
              }
            }
          ],
          "additionalProperties": {
            "$ref": "#/$defs/canonicalizableValue"
          }
        }
      },
      "additionalProperties": false
    },
    "signature": {
      "type": "string",
      "description": "OPTIONAL embedded signature: base64url (no padding) signature by the cnf key over the canonical JSON form of the record with only this field absent. Every Trust Record MUST be signature-bound per spec section 3.2.2, but enveloped profiles (e.g. JWS, cMCP RuntimeClaim) carry the signature outside the record, so this field is not required by the schema.",
      "pattern": "^[A-Za-z0-9_-]+$"
    }
  },
  "additionalProperties": false,
  "allOf": [
    {
      "$comment": "A record assembled from another party's evidence has no hardware root. Mirrors the cross-field check in the reference model, so a validator that only reads this schema enforces it too.",
      "if": {
        "required": [
          "origin"
        ],
        "properties": {
          "origin": {
            "required": [
              "kind"
            ],
            "properties": {
              "kind": {
                "enum": [
                  "third-party-control-plane",
                  "log-import"
                ]
              }
            }
          }
        }
      },
      "then": {
        "properties": {
          "runtime": {
            "properties": {
              "platform": {
                "const": "software-only"
              }
            }
          }
        }
      }
    }
  ]
}
