diff --git a/schemas/trace-claim.json b/schemas/trace-claim.json index 73f8805..97954c2 100644 --- a/schemas/trace-claim.json +++ b/schemas/trace-claim.json @@ -1,141 +1,446 @@ -{ - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://agentrust-io.com/schema/trace-v0.1.json", - "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"}, - "iat": {"type": "integer", "minimum": 1700000000}, - "subject": {"type": "string", "pattern": "^(spiffe://|did:)"}, - "model": { - "type": "object", - "required": ["provider", "model_id"], - "properties": { - "provider": {"type": "string"}, - "model_id": {"type": "string"}, - "version": {"type": "string"}, - "weights_digest": {"type": "string", "pattern": "^sha(256:[0-9a-f]{64}|384:[0-9a-f]{96})$"}, - "aibom_uri": {"type": "string", "format": "uri"} - }, - "additionalProperties": false - }, - "runtime": { - "type": "object", - "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"]}, - "measurement": {"type": "string", "pattern": "^sha(256:[0-9a-f]{64}|384:[0-9a-f]{96})$"}, - "rim_uri": {"type": "string", "format": "uri"}, - "nonce": {"type": "string"}, - "firmware_version": {"type": "string"} - }, - "additionalProperties": false - }, - "policy": { - "type": "object", - "required": ["bundle_hash", "enforcement_mode"], - "properties": { - "bundle_hash": {"type": "string", "pattern": "^sha(256:[0-9a-f]{64}|384:[0-9a-f]{96})$"}, - "enforcement_mode": {"type": "string", "enum": ["enforce", "advisory", "silent"]}, - "version": {"type": "string"}, - "policy_uri": {"type": "string", "format": "uri"} - }, - "additionalProperties": false - }, - "data_class": {"type": "string"}, - "tool_transcript": { - "type": "object", - "required": ["hash"], - "properties": { - "hash": {"type": "string", "pattern": "^sha(256:[0-9a-f]{64}|384:[0-9a-f]{96})$"}, - "call_count": {"type": "integer", "minimum": 0}, - "transcript_uri": {"type": "string", "format": "uri"} - }, - "additionalProperties": false - }, - "origin": { - "type": "object", - "description": "Where imported evidence came from. A non-self origin cannot carry a hardware runtime platform.", - "required": ["kind", "producer"], - "properties": { - "kind": { - "type": "string", - "enum": ["self", "third-party-control-plane", "log-import"] - }, - "producer": {"type": "string", "minLength": 1}, - "source_event_id": {"type": "string", "minLength": 1}, - "ingested_at": {"type": "integer", "minimum": 1700000000} - }, - "additionalProperties": false - }, - "build_provenance": { - "type": "object", - "required": ["slsa_level", "digest"], - "properties": { - "slsa_level": {"type": "integer", "minimum": 0, "maximum": 3}, - "builder": {"type": "string"}, - "digest": {"type": "string", "pattern": "^sha(256:[0-9a-f]{64}|384:[0-9a-f]{96})$"}, - "provenance_uri": {"type": "string", "format": "uri"} - }, - "additionalProperties": false - }, - "appraisal": { - "type": "object", - "required": ["status", "verifier"], - "properties": { - "status": {"type": "string", "enum": ["affirming", "warning", "contraindicated", "none"]}, - "verifier": {"type": "string", "format": "uri"}, - "policy_ref": {"type": "string", "format": "uri"}, - "timestamp": {"type": "integer"} - }, - "additionalProperties": false - }, - "transparency": {"type": "string", "format": "uri"}, - "cnf": { - "type": "object", - "required": ["jwk"], - "properties": { - "jwk": { - "type": "object", - "required": ["kty"], - "properties": { - "kty": {"type": "string"}, - "crv": {"type": "string"}, - "x": {"type": "string"}, - "y": {"type": "string"}, - "kid": {"type": "string"} - } - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false, - "allOf": [ - { - "if": { - "required": ["origin"], - "properties": { - "origin": { - "required": ["kind"], - "properties": { - "kind": {"enum": ["third-party-control-plane", "log-import"]} - } - } - } - }, - "then": { - "properties": { - "runtime": { - "properties": {"platform": {"const": "software-only"}} - } - } - } - } - ] -} +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://agentrust-io.com/schema/trace-v0.2.json", + "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 + }, + "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, + "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, + "description": "Unix time at which the source evidence was ingested. Distinct from iat, which is when this record was issued." + } + }, + "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", + "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) — 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": 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" + } + } + } + } + } + } + ] +} diff --git a/src/trace_tests/modules/tr_pol.py b/src/trace_tests/modules/tr_pol.py index 077150a..7848505 100644 --- a/src/trace_tests/modules/tr_pol.py +++ b/src/trace_tests/modules/tr_pol.py @@ -10,7 +10,7 @@ _DIGEST_RE = re.compile(r"^sha(256:[0-9a-f]{64}|384:[0-9a-f]{96})$") #: Mirrors `policy.enforcement_mode` in the packaged schema; `test_enum_parity` fails if #: it drifts from that copy. -_VALID_ENFORCEMENT = frozenset({"enforce", "advisory", "silent"}) +_VALID_ENFORCEMENT = frozenset({"enforce", "advisory", "silent", "declared"}) def check(trace: dict[str, Any]) -> list[Finding]: diff --git a/tests/conftest.py b/tests/conftest.py index c2ed74c..6e3371e 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -98,6 +98,20 @@ def invalid_missing_runtime(): return load_vector("invalid_missing_runtime.json") +@pytest.fixture +def signed_root(): + """A signed Trust Record with no delegation block, from trace-spec's + `examples/delegation-link/01-valid-single-hop.json`.""" + return load_vector("signed_root.json") + + +@pytest.fixture +def signed_delegated_hop(): + """The delegated hop from the same chain: signed, and carrying a + `delegation` block.""" + return load_vector("signed_delegated_hop.json") + + @pytest.fixture def invalid_wrong_profile(): return load_vector("invalid_wrong_profile.json") diff --git a/tests/test_level0.py b/tests/test_level0.py index fcb9db9..53521b3 100644 --- a/tests/test_level0.py +++ b/tests/test_level0.py @@ -9,7 +9,7 @@ "intel-tdx", "amd-sev-snp", "azure-cvm-sev-snp", "nvidia-h100", "nvidia-blackwell", "aws-nitro", "arm-cca", "google-confidential-space", "tpm2", "software-only", } -VALID_ENFORCEMENT = {"enforce", "advisory", "silent"} +VALID_ENFORCEMENT = {"enforce", "advisory", "silent", "declared"} VALID_APPRAISAL = {"affirming", "warning", "contraindicated", "none"} diff --git a/tests/test_packaged_schema_accepts_real_records.py b/tests/test_packaged_schema_accepts_real_records.py new file mode 100644 index 0000000..55c8a9e --- /dev/null +++ b/tests/test_packaged_schema_accepts_real_records.py @@ -0,0 +1,61 @@ +"""The packaged schema against records that carry the fields real records carry. + +The packaged copy sat at `$id` `trace-v0.1.json` while the normative schema moved +to v0.2, and the two diverged on two top-level properties: `signature` and +`delegation`. With `additionalProperties: false` — which +`test_unknown_fields_rejected` shows is deliberate — the packaged copy therefore +rejected **every signed record**, which is every real one, and every delegated +record twice over. + +Nothing here failed, because not one of the six vectors this suite carried had a +`signature` at all. The schema forbade a field the corpus never used. + +That is the same shape as the canonicalization defect in `tr_sig`: the corpus +avoided the case the defect lived in, so the suite stayed green while the thing it +scores was wrong. Two of these in one repository is a pattern rather than an +accident, and the answer to both is a vector that uses the field. + +The two vectors are copied from trace-spec's `examples/delegation-link/`, which is +generated from a published seed and reproducible by anyone. Nothing here compares +them to the originals — a guard that needs another repository checked out is a +guard that gets skipped. They are held instead to the property that makes them +worth having: they carry the fields whose absence hid the defect. A vector edited +into something unsigned stops being a regression test loudly rather than quietly. +""" + +from __future__ import annotations + +import jsonschema +import pytest + + +@pytest.mark.level0 +class TestPackagedSchemaAcceptsRealRecords: + def test_the_vectors_still_carry_the_fields_that_matter( + self, signed_root, signed_delegated_hop + ): + """Half of what makes these regression material. Without it, both records + could be edited down to the shape the old schema accepted and the two + tests below would keep passing against a schema that had drifted back.""" + assert "signature" in signed_root, "signed_root lost its signature" + assert "delegation" not in signed_root, "signed_root is no longer the root case" + assert "signature" in signed_delegated_hop, "the hop lost its signature" + assert "delegation" in signed_delegated_hop, "the hop lost its delegation block" + + def test_a_signed_record_validates(self, schema, signed_root): + """Rejected by the packaged schema before the resync: `'signature' was + unexpected`.""" + jsonschema.validate(signed_root, schema) + + def test_a_signed_delegated_record_validates(self, schema, signed_delegated_hop): + """Rejected twice before the resync: `'delegation', 'signature' were + unexpected`.""" + jsonschema.validate(signed_delegated_hop, schema) + + def test_the_packaged_schema_declares_the_version_it_is(self, schema): + """The drift was visible in one line the whole time. `$id` named v0.1 + while every record the suite scores declares the v0.2 profile.""" + assert schema["$id"].endswith("trace-v0.2.json"), ( + f"packaged schema declares {schema['$id']}, which is not the version " + "the records it validates say they are" + ) diff --git a/tests/vectors/signed_delegated_hop.json b/tests/vectors/signed_delegated_hop.json new file mode 100644 index 0000000..0e3ea0a --- /dev/null +++ b/tests/vectors/signed_delegated_hop.json @@ -0,0 +1,38 @@ +{ + "eat_profile": "tag:agentrust-io.com,2026:trace-v0.2", + "iat": 1785000000, + "subject": "spiffe://acme.example/agent/planner", + "model": { + "provider": "anthropic", + "model_id": "claude-sonnet-4-6" + }, + "runtime": { + "platform": "software-only", + "measurement": "sha256:0000000000000000000000000000000000000000000000000000000000000000" + }, + "policy": { + "bundle_hash": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "enforcement_mode": "enforce" + }, + "data_class": "confidential", + "build_provenance": { + "slsa_level": 0, + "digest": "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" + }, + "appraisal": { + "status": "affirming", + "verifier": "https://verifier.example/v1" + }, + "delegation": { + "parent_record_hash": "sha256:b1d65c65d7191e1c6f57f8902548475671edad51035f1542f76a4e45337052b6", + "credential_id": "cred:orchestrator-to-planner" + }, + "cnf": { + "jwk": { + "kty": "OKP", + "crv": "Ed25519", + "x": "W2BsRtHZW8BVdiCsCxrNxvv4LOfgBhuS_2v7TU7TyHU" + } + }, + "signature": "w1-yxCO_nNRY2pj2rGO065DN25Myk9xLJ13DzL0d9tTTwb3Wq0tnwe2H5U5oEXctynLDJN8xP0CLhLjVcBUmDg" +} diff --git a/tests/vectors/signed_root.json b/tests/vectors/signed_root.json new file mode 100644 index 0000000..d0c7321 --- /dev/null +++ b/tests/vectors/signed_root.json @@ -0,0 +1,34 @@ +{ + "eat_profile": "tag:agentrust-io.com,2026:trace-v0.2", + "iat": 1785000000, + "subject": "spiffe://acme.example/agent/orchestrator", + "model": { + "provider": "anthropic", + "model_id": "claude-sonnet-4-6" + }, + "runtime": { + "platform": "software-only", + "measurement": "sha256:0000000000000000000000000000000000000000000000000000000000000000" + }, + "policy": { + "bundle_hash": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "enforcement_mode": "enforce" + }, + "data_class": "restricted", + "build_provenance": { + "slsa_level": 0, + "digest": "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" + }, + "appraisal": { + "status": "affirming", + "verifier": "https://verifier.example/v1" + }, + "cnf": { + "jwk": { + "kty": "OKP", + "crv": "Ed25519", + "x": "pNTZUXlAITEWbtVbHk6zGRwVD73s0BEakqkKKaFQyZ4" + } + }, + "signature": "m3TAE_Z_chc0gs8HMh1KAN4NECFLP09kiqyQPBDz9w9G0_iYoQ5-I94gpptC1a4xiRloSFLyTdprEmr9CFD_DA" +}