agentrust-trace 0.1.0__tar.gz → 0.1.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {agentrust_trace-0.1.0 → agentrust_trace-0.1.1}/.github/workflows/ci.yml +4 -1
- agentrust_trace-0.1.1/.github/workflows/contributor-check.yml +42 -0
- {agentrust_trace-0.1.0 → agentrust_trace-0.1.1}/.github/workflows/publish.yml +4 -1
- agentrust_trace-0.1.1/.github/workflows/require-maintainer-approval.yml +84 -0
- agentrust_trace-0.1.1/.github/workflows/sbom.yml +30 -0
- agentrust_trace-0.1.1/ADOPTERS.md +11 -0
- {agentrust_trace-0.1.0 → agentrust_trace-0.1.1}/CHARTER.md +3 -1
- {agentrust_trace-0.1.0 → agentrust_trace-0.1.1}/PKG-INFO +33 -9
- {agentrust_trace-0.1.0 → agentrust_trace-0.1.1}/README.md +32 -8
- {agentrust_trace-0.1.0 → agentrust_trace-0.1.1}/SECURITY.md +1 -1
- agentrust_trace-0.1.1/examples/README.md +11 -0
- {agentrust_trace-0.1.0 → agentrust_trace-0.1.1}/examples/amd-sev-snp.json +7 -8
- {agentrust_trace-0.1.0 → agentrust_trace-0.1.1}/examples/intel-tdx.json +8 -9
- {agentrust_trace-0.1.0 → agentrust_trace-0.1.1}/examples/nvidia-h100.json +9 -10
- {agentrust_trace-0.1.0 → agentrust_trace-0.1.1}/pyproject.toml +1 -1
- {agentrust_trace-0.1.0 → agentrust_trace-0.1.1}/schema/trace-claim.json +31 -9
- {agentrust_trace-0.1.0 → agentrust_trace-0.1.1}/spec/trace-v0.1.md +24 -8
- {agentrust_trace-0.1.0 → agentrust_trace-0.1.1}/src/agentrust_trace/models.py +23 -2
- {agentrust_trace-0.1.0 → agentrust_trace-0.1.1}/src/agentrust_trace/schema/trace-v0.1.json +31 -9
- agentrust_trace-0.1.1/tests/test_models.py +131 -0
- agentrust_trace-0.1.1/tests/test_validate.py +77 -0
- agentrust_trace-0.1.0/tests/test_models.py +0 -61
- agentrust_trace-0.1.0/tests/test_validate.py +0 -44
- {agentrust_trace-0.1.0 → agentrust_trace-0.1.1}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {agentrust_trace-0.1.0 → agentrust_trace-0.1.1}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {agentrust_trace-0.1.0 → agentrust_trace-0.1.1}/.github/ISSUE_TEMPLATE/spec_change.md +0 -0
- {agentrust_trace-0.1.0 → agentrust_trace-0.1.1}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {agentrust_trace-0.1.0 → agentrust_trace-0.1.1}/CHANGELOG.md +0 -0
- {agentrust_trace-0.1.0 → agentrust_trace-0.1.1}/CODE_OF_CONDUCT.md +0 -0
- {agentrust_trace-0.1.0 → agentrust_trace-0.1.1}/CONTRIBUTING.md +0 -0
- {agentrust_trace-0.1.0 → agentrust_trace-0.1.1}/GOVERNANCE.md +0 -0
- {agentrust_trace-0.1.0 → agentrust_trace-0.1.1}/LICENSE +0 -0
- {agentrust_trace-0.1.0 → agentrust_trace-0.1.1}/MAINTAINERS.md +0 -0
- {agentrust_trace-0.1.0 → agentrust_trace-0.1.1}/ROADMAP.md +0 -0
- {agentrust_trace-0.1.0 → agentrust_trace-0.1.1}/docs/assets/icon.svg +0 -0
- {agentrust_trace-0.1.0 → agentrust_trace-0.1.1}/src/agentrust_trace/__init__.py +0 -0
- {agentrust_trace-0.1.0 → agentrust_trace-0.1.1}/src/agentrust_trace/py.typed +0 -0
- {agentrust_trace-0.1.0 → agentrust_trace-0.1.1}/src/agentrust_trace/validate.py +0 -0
- {agentrust_trace-0.1.0 → agentrust_trace-0.1.1}/tests/__init__.py +0 -0
|
@@ -5,6 +5,9 @@ on:
|
|
|
5
5
|
branches: ["main", "feat/**", "fix/**"]
|
|
6
6
|
pull_request:
|
|
7
7
|
|
|
8
|
+
env:
|
|
9
|
+
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
|
|
10
|
+
|
|
8
11
|
jobs:
|
|
9
12
|
test:
|
|
10
13
|
runs-on: ubuntu-latest
|
|
@@ -15,7 +18,7 @@ jobs:
|
|
|
15
18
|
steps:
|
|
16
19
|
- uses: actions/checkout@v4
|
|
17
20
|
|
|
18
|
-
- uses: actions/setup-python@
|
|
21
|
+
- uses: actions/setup-python@v4
|
|
19
22
|
with:
|
|
20
23
|
python-version: ${{ matrix.python-version }}
|
|
21
24
|
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
name: Contributor reputation check
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request_target:
|
|
5
|
+
types: [opened]
|
|
6
|
+
issues:
|
|
7
|
+
types: [opened]
|
|
8
|
+
workflow_dispatch:
|
|
9
|
+
inputs:
|
|
10
|
+
username:
|
|
11
|
+
description: GitHub username to check
|
|
12
|
+
required: true
|
|
13
|
+
|
|
14
|
+
permissions:
|
|
15
|
+
contents: read
|
|
16
|
+
issues: write
|
|
17
|
+
pull-requests: write
|
|
18
|
+
|
|
19
|
+
jobs:
|
|
20
|
+
check:
|
|
21
|
+
runs-on: ubuntu-latest
|
|
22
|
+
if: |
|
|
23
|
+
github.actor != 'dependabot[bot]' &&
|
|
24
|
+
github.actor != 'github-actions[bot]' &&
|
|
25
|
+
github.actor != 'copilot-swe-agent[bot]' &&
|
|
26
|
+
github.actor != 'claude-code[bot]' &&
|
|
27
|
+
github.actor != 'imran-siddique'
|
|
28
|
+
steps:
|
|
29
|
+
- name: Checkout org action
|
|
30
|
+
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
|
31
|
+
with:
|
|
32
|
+
repository: agentrust-io/.github
|
|
33
|
+
ref: main
|
|
34
|
+
persist-credentials: false
|
|
35
|
+
sparse-checkout: |
|
|
36
|
+
scripts
|
|
37
|
+
.github/actions/contributor-check
|
|
38
|
+
|
|
39
|
+
- name: Run contributor check
|
|
40
|
+
uses: ./.github/actions/contributor-check
|
|
41
|
+
with:
|
|
42
|
+
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
@@ -9,13 +9,16 @@ permissions:
|
|
|
9
9
|
contents: read
|
|
10
10
|
id-token: write # OIDC trusted publishing — no stored API token needed
|
|
11
11
|
|
|
12
|
+
env:
|
|
13
|
+
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
|
|
14
|
+
|
|
12
15
|
jobs:
|
|
13
16
|
build:
|
|
14
17
|
runs-on: ubuntu-latest
|
|
15
18
|
steps:
|
|
16
19
|
- uses: actions/checkout@v4
|
|
17
20
|
|
|
18
|
-
- uses: actions/setup-python@
|
|
21
|
+
- uses: actions/setup-python@v4
|
|
19
22
|
with:
|
|
20
23
|
python-version: "3.11"
|
|
21
24
|
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
name: Require maintainer approval
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request_target:
|
|
5
|
+
types: [opened, synchronize, reopened]
|
|
6
|
+
branches: [main]
|
|
7
|
+
# Re-evaluate the gate when a review is submitted or dismissed so an
|
|
8
|
+
# approval clears the failing status without waiting for a new push.
|
|
9
|
+
# Note: pull_request_review does not support a branches filter, so the
|
|
10
|
+
# base-branch check lives in the job-level `if` below.
|
|
11
|
+
pull_request_review:
|
|
12
|
+
types: [submitted, dismissed]
|
|
13
|
+
|
|
14
|
+
permissions:
|
|
15
|
+
contents: read
|
|
16
|
+
pull-requests: read
|
|
17
|
+
statuses: write
|
|
18
|
+
|
|
19
|
+
jobs:
|
|
20
|
+
gate:
|
|
21
|
+
runs-on: ubuntu-latest
|
|
22
|
+
if: github.event.pull_request.base.ref == 'main'
|
|
23
|
+
steps:
|
|
24
|
+
- name: Check for maintainer approval
|
|
25
|
+
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
|
|
26
|
+
with:
|
|
27
|
+
script: |
|
|
28
|
+
const MAINTAINERS = ['imran-siddique'];
|
|
29
|
+
|
|
30
|
+
const author = context.payload.pull_request.user.login;
|
|
31
|
+
if (MAINTAINERS.includes(author)) {
|
|
32
|
+
core.info(`Author ${author} is a maintainer, skipping gate`);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const association = context.payload.pull_request.author_association;
|
|
37
|
+
if (association === 'MEMBER' || association === 'OWNER') {
|
|
38
|
+
core.info(`Author is ${association}, skipping gate`);
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Both pull_request_target and pull_request_review payloads
|
|
43
|
+
// carry the PR at context.payload.pull_request.
|
|
44
|
+
const prNumber = context.payload.pull_request.number;
|
|
45
|
+
|
|
46
|
+
// Re-fetch the PR so we compare against the head SHA at
|
|
47
|
+
// evaluation time, not a possibly stale SHA from the payload.
|
|
48
|
+
const { data: pr } = await github.rest.pulls.get({
|
|
49
|
+
owner: context.repo.owner,
|
|
50
|
+
repo: context.repo.repo,
|
|
51
|
+
pull_number: prNumber,
|
|
52
|
+
});
|
|
53
|
+
const headSha = pr.head.sha;
|
|
54
|
+
|
|
55
|
+
// Paginate: listReviews otherwise returns only the first 30.
|
|
56
|
+
const reviews = await github.paginate(github.rest.pulls.listReviews, {
|
|
57
|
+
owner: context.repo.owner,
|
|
58
|
+
repo: context.repo.repo,
|
|
59
|
+
pull_number: prNumber,
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
// Only a maintainer's most recent non-comment review counts,
|
|
63
|
+
// and it must approve the current head commit. An approval of
|
|
64
|
+
// an older commit does not clear the gate after a later push
|
|
65
|
+
// (prevents approve-then-swap).
|
|
66
|
+
const latestByMaintainer = new Map();
|
|
67
|
+
for (const review of reviews) {
|
|
68
|
+
if (
|
|
69
|
+
review.user &&
|
|
70
|
+
review.user.type === 'User' &&
|
|
71
|
+
MAINTAINERS.includes(review.user.login) &&
|
|
72
|
+
review.state !== 'COMMENTED'
|
|
73
|
+
) {
|
|
74
|
+
latestByMaintainer.set(review.user.login, review);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const approved = [...latestByMaintainer.values()].some(
|
|
79
|
+
(r) => r.state === 'APPROVED' && r.commit_id === headSha
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
if (!approved) {
|
|
83
|
+
core.setFailed('Waiting for maintainer approval of the current head commit before merging.');
|
|
84
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
name: SBOM
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
release:
|
|
7
|
+
types: [published]
|
|
8
|
+
|
|
9
|
+
env:
|
|
10
|
+
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
|
|
11
|
+
|
|
12
|
+
permissions:
|
|
13
|
+
contents: write
|
|
14
|
+
id-token: write
|
|
15
|
+
|
|
16
|
+
jobs:
|
|
17
|
+
sbom:
|
|
18
|
+
runs-on: ubuntu-latest
|
|
19
|
+
steps:
|
|
20
|
+
- uses: actions/checkout@v4
|
|
21
|
+
- name: Generate SBOM
|
|
22
|
+
uses: anchore/sbom-action@v0
|
|
23
|
+
with:
|
|
24
|
+
format: spdx-json
|
|
25
|
+
output-file: sbom.spdx.json
|
|
26
|
+
- name: Upload SBOM artifact
|
|
27
|
+
uses: actions/upload-artifact@v4
|
|
28
|
+
with:
|
|
29
|
+
name: sbom
|
|
30
|
+
path: sbom.spdx.json
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# ADOPTERS
|
|
2
|
+
|
|
3
|
+
Organizations using or evaluating the TRACE specification.
|
|
4
|
+
|
|
5
|
+
To add your organization, open a pull request editing this file.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
| Organization | Usage |
|
|
10
|
+
|---|---|
|
|
11
|
+
| Opaque Systems | Founding contributor — developed the initial specification, reference implementation, and TRACE registry infrastructure |
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
# Technical Charter — TRACE
|
|
2
2
|
|
|
3
3
|
**Proposed hosting**: CoSAI (Coalition for Secure AI) for the technical workstream; Linux Foundation entity hosting the Model Context Protocol for specification, IP, trademark, and conformance mark.
|
|
4
|
-
**Status**: Pre-acceptance draft — effective upon host organization acceptance.
|
|
4
|
+
**Status**: Pre-acceptance draft — effective upon host organization acceptance.
|
|
5
|
+
|
|
6
|
+
> **Note for external contributors:** This charter is a working draft and has not yet been accepted by a host organization. Governance terms, IP policy, and conformance mark ownership described here are proposed, not final. Do not implement production systems based on governance commitments in this document until v1.0 ratification.
|
|
5
7
|
**Version**: 0.1 (aligned with spec v0.1)
|
|
6
8
|
|
|
7
9
|
---
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agentrust-trace
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.1
|
|
4
4
|
Summary: TRACE v0.1 — hardware-attested governance records for AI agents
|
|
5
5
|
Project-URL: Homepage, https://github.com/agentrust-io/trace-spec
|
|
6
6
|
Project-URL: Repository, https://github.com/agentrust-io/trace-spec
|
|
@@ -29,13 +29,27 @@ Requires-Dist: ruff>=0.4; extra == 'dev'
|
|
|
29
29
|
Requires-Dist: types-jsonschema; extra == 'dev'
|
|
30
30
|
Description-Content-Type: text/markdown
|
|
31
31
|
|
|
32
|
-
<p align="center">
|
|
32
|
+
<p align="center">
|
|
33
33
|
<img src="docs/assets/icon.svg" width="96" height="96" alt="TRACE"/>
|
|
34
34
|
</p>
|
|
35
35
|
|
|
36
|
-
# TRACE
|
|
36
|
+
# TRACE: Trust Runtime Attestation and Compliance Evidence
|
|
37
|
+
|
|
38
|
+
[](LICENSE)
|
|
39
|
+
[](https://agenticai.foundation)
|
|
40
|
+
[](spec/trace-v0.1.md)
|
|
41
|
+
|
|
42
|
+
> **Developer Preview.** Launching at Confidential Computing Summit, June 23 2026. May have breaking changes before v1.0.
|
|
43
|
+
|
|
44
|
+
<p align="center">
|
|
45
|
+
<a href="spec/trace-v0.1.md">Specification</a> |
|
|
46
|
+
<a href="schema/trace-claim.json">Schema</a> |
|
|
47
|
+
<a href="examples/">Examples</a> |
|
|
48
|
+
<a href="https://github.com/agentrust-io/trace-registry">Registry</a> |
|
|
49
|
+
<a href="https://github.com/agentrust-io/cmcp">Reference Impl</a>
|
|
50
|
+
</p>
|
|
37
51
|
|
|
38
|
-
An open specification for hardware-attested AI agent governance records. TRACE defines the format, anchoring protocol, and verification rules for cryptographically provable evidence that an AI agent ran under a specific policy, in a verified hardware environment, on classified data, invoking identified tools
|
|
52
|
+
An open specification for hardware-attested AI agent governance records. TRACE defines the format, anchoring protocol, and verification rules for cryptographically provable evidence that an AI agent ran under a specific policy, in a verified hardware environment, on classified data, invoking identified tools, bound into a single signed artifact rooted in silicon attestation.
|
|
39
53
|
|
|
40
54
|
## What a TRACE Trust Record Is
|
|
41
55
|
|
|
@@ -84,11 +98,21 @@ An open specification for hardware-attested AI agent governance records. TRACE d
|
|
|
84
98
|
|
|
85
99
|
The record is a single EAT envelope (RFC 9711). Each field is independently verifiable. No callback to the issuer is required.
|
|
86
100
|
|
|
101
|
+
### Supported platforms
|
|
102
|
+
|
|
103
|
+
| Platform | v0.1 | v0.2 (planned) | Notes |
|
|
104
|
+
|---|---|---|---|
|
|
105
|
+
| `intel-tdx` | Yes | Yes | |
|
|
106
|
+
| `amd-sev-snp` | Yes | Yes | |
|
|
107
|
+
| `nvidia-h100` | Yes | Yes | |
|
|
108
|
+
| `gpu-cc` | No | Planned | Generic GPU confidential compute |
|
|
109
|
+
| `opaque` | Yes | Yes | Explicit opt-in; contact maintainers |
|
|
110
|
+
|
|
87
111
|
## Specification
|
|
88
112
|
|
|
89
|
-
- [`spec/trace-v0.1.md`](spec/trace-v0.1.md)
|
|
90
|
-
- [`schema/trace-claim.json`](schema/trace-claim.json)
|
|
91
|
-
- [`examples/`](examples/)
|
|
113
|
+
- [`spec/trace-v0.1.md`](spec/trace-v0.1.md) -- full specification
|
|
114
|
+
- [`schema/trace-claim.json`](schema/trace-claim.json) -- JSON Schema
|
|
115
|
+
- [`examples/`](examples/) -- example Trust Records for Intel TDX, AMD SEV-SNP, and NVIDIA H100
|
|
92
116
|
|
|
93
117
|
## Standards composition
|
|
94
118
|
|
|
@@ -106,7 +130,7 @@ TRACE profiles existing standards rather than replacing them:
|
|
|
106
130
|
|
|
107
131
|
## Reference implementation
|
|
108
132
|
|
|
109
|
-
[agentrust-io/cmcp](https://github.com/agentrust-io/cmcp)
|
|
133
|
+
[agentrust-io/cmcp](https://github.com/agentrust-io/cmcp) -- Confidential MCP Runtime. Hardware-attested policy enforcement at the MCP tool-call boundary on Intel TDX, AMD SEV-SNP, and NVIDIA H100/Blackwell.
|
|
110
134
|
|
|
111
135
|
## Registry
|
|
112
136
|
|
|
@@ -114,7 +138,7 @@ A public append-only Merkle registry of TRACE Trust Record anchors: [agentrust-i
|
|
|
114
138
|
|
|
115
139
|
## Status
|
|
116
140
|
|
|
117
|
-
Draft v0.1
|
|
141
|
+
Draft v0.1. Publishing at Confidential Computing Summit, San Francisco, June 23 2026. Targeting submission to the [Agentic AI Foundation (AAIF)](https://agenticai.foundation) under the Linux Foundation.
|
|
118
142
|
|
|
119
143
|
## License
|
|
120
144
|
|
|
@@ -1,10 +1,24 @@
|
|
|
1
|
-
<p align="center">
|
|
1
|
+
<p align="center">
|
|
2
2
|
<img src="docs/assets/icon.svg" width="96" height="96" alt="TRACE"/>
|
|
3
3
|
</p>
|
|
4
4
|
|
|
5
|
-
# TRACE
|
|
5
|
+
# TRACE: Trust Runtime Attestation and Compliance Evidence
|
|
6
|
+
|
|
7
|
+
[](LICENSE)
|
|
8
|
+
[](https://agenticai.foundation)
|
|
9
|
+
[](spec/trace-v0.1.md)
|
|
10
|
+
|
|
11
|
+
> **Developer Preview.** Launching at Confidential Computing Summit, June 23 2026. May have breaking changes before v1.0.
|
|
12
|
+
|
|
13
|
+
<p align="center">
|
|
14
|
+
<a href="spec/trace-v0.1.md">Specification</a> |
|
|
15
|
+
<a href="schema/trace-claim.json">Schema</a> |
|
|
16
|
+
<a href="examples/">Examples</a> |
|
|
17
|
+
<a href="https://github.com/agentrust-io/trace-registry">Registry</a> |
|
|
18
|
+
<a href="https://github.com/agentrust-io/cmcp">Reference Impl</a>
|
|
19
|
+
</p>
|
|
6
20
|
|
|
7
|
-
An open specification for hardware-attested AI agent governance records. TRACE defines the format, anchoring protocol, and verification rules for cryptographically provable evidence that an AI agent ran under a specific policy, in a verified hardware environment, on classified data, invoking identified tools
|
|
21
|
+
An open specification for hardware-attested AI agent governance records. TRACE defines the format, anchoring protocol, and verification rules for cryptographically provable evidence that an AI agent ran under a specific policy, in a verified hardware environment, on classified data, invoking identified tools, bound into a single signed artifact rooted in silicon attestation.
|
|
8
22
|
|
|
9
23
|
## What a TRACE Trust Record Is
|
|
10
24
|
|
|
@@ -53,11 +67,21 @@ An open specification for hardware-attested AI agent governance records. TRACE d
|
|
|
53
67
|
|
|
54
68
|
The record is a single EAT envelope (RFC 9711). Each field is independently verifiable. No callback to the issuer is required.
|
|
55
69
|
|
|
70
|
+
### Supported platforms
|
|
71
|
+
|
|
72
|
+
| Platform | v0.1 | v0.2 (planned) | Notes |
|
|
73
|
+
|---|---|---|---|
|
|
74
|
+
| `intel-tdx` | Yes | Yes | |
|
|
75
|
+
| `amd-sev-snp` | Yes | Yes | |
|
|
76
|
+
| `nvidia-h100` | Yes | Yes | |
|
|
77
|
+
| `gpu-cc` | No | Planned | Generic GPU confidential compute |
|
|
78
|
+
| `opaque` | Yes | Yes | Explicit opt-in; contact maintainers |
|
|
79
|
+
|
|
56
80
|
## Specification
|
|
57
81
|
|
|
58
|
-
- [`spec/trace-v0.1.md`](spec/trace-v0.1.md)
|
|
59
|
-
- [`schema/trace-claim.json`](schema/trace-claim.json)
|
|
60
|
-
- [`examples/`](examples/)
|
|
82
|
+
- [`spec/trace-v0.1.md`](spec/trace-v0.1.md) -- full specification
|
|
83
|
+
- [`schema/trace-claim.json`](schema/trace-claim.json) -- JSON Schema
|
|
84
|
+
- [`examples/`](examples/) -- example Trust Records for Intel TDX, AMD SEV-SNP, and NVIDIA H100
|
|
61
85
|
|
|
62
86
|
## Standards composition
|
|
63
87
|
|
|
@@ -75,7 +99,7 @@ TRACE profiles existing standards rather than replacing them:
|
|
|
75
99
|
|
|
76
100
|
## Reference implementation
|
|
77
101
|
|
|
78
|
-
[agentrust-io/cmcp](https://github.com/agentrust-io/cmcp)
|
|
102
|
+
[agentrust-io/cmcp](https://github.com/agentrust-io/cmcp) -- Confidential MCP Runtime. Hardware-attested policy enforcement at the MCP tool-call boundary on Intel TDX, AMD SEV-SNP, and NVIDIA H100/Blackwell.
|
|
79
103
|
|
|
80
104
|
## Registry
|
|
81
105
|
|
|
@@ -83,7 +107,7 @@ A public append-only Merkle registry of TRACE Trust Record anchors: [agentrust-i
|
|
|
83
107
|
|
|
84
108
|
## Status
|
|
85
109
|
|
|
86
|
-
Draft v0.1
|
|
110
|
+
Draft v0.1. Publishing at Confidential Computing Summit, San Francisco, June 23 2026. Targeting submission to the [Agentic AI Foundation (AAIF)](https://agenticai.foundation) under the Linux Foundation.
|
|
87
111
|
|
|
88
112
|
## License
|
|
89
113
|
|
|
@@ -14,7 +14,7 @@ Vulnerabilities in the reference implementation (cMCP) should be reported at [ag
|
|
|
14
14
|
|
|
15
15
|
**Do not open a public GitHub issue for security vulnerabilities.**
|
|
16
16
|
|
|
17
|
-
Report vulnerabilities
|
|
17
|
+
Report vulnerabilities via [GitHub Security Advisories](https://github.com/agentrust-io/trace-spec/security/advisories/new). Do not use public issues.
|
|
18
18
|
|
|
19
19
|
Include:
|
|
20
20
|
- A description of the vulnerability and the affected spec section or schema path
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# TRACE v0.1 example Trust Records
|
|
2
|
+
|
|
3
|
+
Each file is a canonical TRACE v0.1 Trust Record that validates as-is against
|
|
4
|
+
`schema/trace-claim.json` (no preprocessing, no comment stripping).
|
|
5
|
+
|
|
6
|
+
- `intel-tdx.json`: Intel TDX example.
|
|
7
|
+
- `amd-sev-snp.json`: AMD SEV-SNP example.
|
|
8
|
+
- `nvidia-h100.json`: NVIDIA H100 Confidential Computing example.
|
|
9
|
+
|
|
10
|
+
The schema sets `additionalProperties: false`, so examples must not carry
|
|
11
|
+
non-schema keys such as `_comment`. Keep descriptive notes in this file.
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
{
|
|
2
|
-
"_comment": "TRACE v0.1 Trust Record — AMD SEV-SNP example. Truncated digests shown with '...' suffix.",
|
|
3
2
|
"eat_profile": "tag:agentrust.io,2026:trace-v0.1",
|
|
4
3
|
"iat": 1750676142,
|
|
5
4
|
"subject": "spiffe://trust.example.org/agent/payments-processor/prod",
|
|
@@ -11,28 +10,28 @@
|
|
|
11
10
|
},
|
|
12
11
|
"runtime": {
|
|
13
12
|
"platform": "amd-sev-snp",
|
|
14
|
-
"measurement": "sha384:
|
|
13
|
+
"measurement": "sha384:c9e4b1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6",
|
|
15
14
|
"rim_uri": "https://kdsintf.amd.com/vcek/v1/Milan/cert_chain",
|
|
16
15
|
"firmware_version": "1.53.0",
|
|
17
16
|
"nonce": "ZRVkXG1wbVJhY2tSZWNvcmQ"
|
|
18
17
|
},
|
|
19
18
|
"policy": {
|
|
20
|
-
"bundle_hash": "sha256:
|
|
19
|
+
"bundle_hash": "sha256:b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3",
|
|
21
20
|
"enforcement_mode": "enforce",
|
|
22
21
|
"version": "1.2.0",
|
|
23
22
|
"policy_uri": "https://registry.agentrust.io/policy/payments-v1.2.0"
|
|
24
23
|
},
|
|
25
24
|
"data_class": "confidential",
|
|
26
25
|
"tool_transcript": {
|
|
27
|
-
"hash": "sha256:
|
|
26
|
+
"hash": "sha256:d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5",
|
|
28
27
|
"call_count": 3,
|
|
29
28
|
"transcript_uri": "https://registry.agentrust.io/transcript/amd-2026-06-23T09:15:42Z"
|
|
30
29
|
},
|
|
31
30
|
"build_provenance": {
|
|
32
31
|
"slsa_level": 2,
|
|
33
32
|
"builder": "https://github.com/slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml",
|
|
34
|
-
"digest": "sha256:
|
|
35
|
-
"provenance_uri": "https://rekor.sigstore.dev/api/v1/log/entries
|
|
33
|
+
"digest": "sha256:e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6",
|
|
34
|
+
"provenance_uri": "https://rekor.sigstore.dev/api/v1/log/entries/def456"
|
|
36
35
|
},
|
|
37
36
|
"appraisal": {
|
|
38
37
|
"status": "affirming",
|
|
@@ -45,8 +44,8 @@
|
|
|
45
44
|
"jwk": {
|
|
46
45
|
"kty": "EC",
|
|
47
46
|
"crv": "P-256",
|
|
48
|
-
"x": "MEkwEwYHKoZIzj0CAQY
|
|
49
|
-
"y": "GHkVPyQs9bXm2A
|
|
47
|
+
"x": "MEkwEwYHKoZIzj0CAQY",
|
|
48
|
+
"y": "GHkVPyQs9bXm2A",
|
|
50
49
|
"kid": "sev-snp-workload-key-2026-06-23"
|
|
51
50
|
}
|
|
52
51
|
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
{
|
|
2
|
-
"_comment": "TRACE v0.1 Trust Record — Intel TDX example. Truncated digests shown with '...' suffix.",
|
|
3
2
|
"eat_profile": "tag:agentrust.io,2026:trace-v0.1",
|
|
4
3
|
"iat": 1750676200,
|
|
5
4
|
"subject": "spiffe://trust.example.org/agent/document-processor/staging",
|
|
@@ -7,33 +6,33 @@
|
|
|
7
6
|
"provider": "meta",
|
|
8
7
|
"model_id": "llama-3.3-70b-instruct",
|
|
9
8
|
"version": "3.3",
|
|
10
|
-
"weights_digest": "sha256:a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2
|
|
9
|
+
"weights_digest": "sha256:a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2",
|
|
11
10
|
"aibom_uri": "https://registry.agentrust.io/aibom/llama-3.3-70b-20260101"
|
|
12
11
|
},
|
|
13
12
|
"runtime": {
|
|
14
13
|
"platform": "intel-tdx",
|
|
15
|
-
"measurement": "sha384:
|
|
14
|
+
"measurement": "sha384:a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8",
|
|
16
15
|
"rim_uri": "https://api.trustedservices.intel.com/sgx/certification/v4/rootcacrl",
|
|
17
16
|
"firmware_version": "2.0.3",
|
|
18
17
|
"nonce": "dGRYLXdvcmtsb2FkLW5vbmNl"
|
|
19
18
|
},
|
|
20
19
|
"policy": {
|
|
21
|
-
"bundle_hash": "sha256:
|
|
20
|
+
"bundle_hash": "sha256:c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4",
|
|
22
21
|
"enforcement_mode": "enforce",
|
|
23
22
|
"version": "2.0.1",
|
|
24
23
|
"policy_uri": "https://registry.agentrust.io/policy/document-v2.0.1"
|
|
25
24
|
},
|
|
26
25
|
"data_class": "internal",
|
|
27
26
|
"tool_transcript": {
|
|
28
|
-
"hash": "sha256:
|
|
27
|
+
"hash": "sha256:f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7",
|
|
29
28
|
"call_count": 7,
|
|
30
29
|
"transcript_uri": "https://registry.agentrust.io/transcript/tdx-2026-06-23T09:16:40Z"
|
|
31
30
|
},
|
|
32
31
|
"build_provenance": {
|
|
33
32
|
"slsa_level": 3,
|
|
34
33
|
"builder": "https://github.com/slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml",
|
|
35
|
-
"digest": "sha256:
|
|
36
|
-
"provenance_uri": "https://rekor.sigstore.dev/api/v1/log/entries
|
|
34
|
+
"digest": "sha256:b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9",
|
|
35
|
+
"provenance_uri": "https://rekor.sigstore.dev/api/v1/log/entries/abc123"
|
|
37
36
|
},
|
|
38
37
|
"appraisal": {
|
|
39
38
|
"status": "affirming",
|
|
@@ -46,8 +45,8 @@
|
|
|
46
45
|
"jwk": {
|
|
47
46
|
"kty": "EC",
|
|
48
47
|
"crv": "P-384",
|
|
49
|
-
"x": "TDXd2VyaWZpY2F0aW9uS2V5
|
|
50
|
-
"y": "UmVmZXJlbmNlTWVhc3VyZW1l
|
|
48
|
+
"x": "TDXd2VyaWZpY2F0aW9uS2V5",
|
|
49
|
+
"y": "UmVmZXJlbmNlTWVhc3VyZW1l",
|
|
51
50
|
"kid": "tdx-workload-key-2026-06-23"
|
|
52
51
|
}
|
|
53
52
|
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
{
|
|
2
|
-
"_comment": "TRACE v0.1 Trust Record — NVIDIA H100 Confidential Computing example. Truncated digests shown with '...' suffix.",
|
|
3
2
|
"eat_profile": "tag:agentrust.io,2026:trace-v0.1",
|
|
4
3
|
"iat": 1750676300,
|
|
5
4
|
"subject": "spiffe://trust.example.org/agent/image-analysis/prod",
|
|
@@ -7,33 +6,33 @@
|
|
|
7
6
|
"provider": "anthropic",
|
|
8
7
|
"model_id": "claude-opus-4-8",
|
|
9
8
|
"version": "20260601",
|
|
10
|
-
"weights_digest": "sha256:
|
|
9
|
+
"weights_digest": "sha256:d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5",
|
|
11
10
|
"aibom_uri": "https://registry.agentrust.io/aibom/claude-opus-4-8-20260601"
|
|
12
11
|
},
|
|
13
12
|
"runtime": {
|
|
14
13
|
"platform": "nvidia-h100",
|
|
15
|
-
"measurement": "sha384:
|
|
16
|
-
"rim_uri": "https://nras.nvidia.com/v1/attestation/rim
|
|
14
|
+
"measurement": "sha384:b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9",
|
|
15
|
+
"rim_uri": "https://nras.nvidia.com/v1/attestation/rim/h100",
|
|
17
16
|
"firmware_version": "535.183.06",
|
|
18
17
|
"nonce": "bnZpZGlhLWgxMDAtbm9uY2U"
|
|
19
18
|
},
|
|
20
19
|
"policy": {
|
|
21
|
-
"bundle_hash": "sha256:
|
|
20
|
+
"bundle_hash": "sha256:e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6",
|
|
22
21
|
"enforcement_mode": "enforce",
|
|
23
22
|
"version": "1.0.0",
|
|
24
23
|
"policy_uri": "https://registry.agentrust.io/policy/image-analysis-v1.0.0"
|
|
25
24
|
},
|
|
26
25
|
"data_class": "restricted",
|
|
27
26
|
"tool_transcript": {
|
|
28
|
-
"hash": "sha256:
|
|
27
|
+
"hash": "sha256:a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8",
|
|
29
28
|
"call_count": 1,
|
|
30
29
|
"transcript_uri": "https://registry.agentrust.io/transcript/h100-2026-06-23T09:18:20Z"
|
|
31
30
|
},
|
|
32
31
|
"build_provenance": {
|
|
33
32
|
"slsa_level": 2,
|
|
34
33
|
"builder": "https://github.com/slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml",
|
|
35
|
-
"digest": "sha256:
|
|
36
|
-
"provenance_uri": "https://rekor.sigstore.dev/api/v1/log/entries
|
|
34
|
+
"digest": "sha256:c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0",
|
|
35
|
+
"provenance_uri": "https://rekor.sigstore.dev/api/v1/log/entries/ghi789"
|
|
37
36
|
},
|
|
38
37
|
"appraisal": {
|
|
39
38
|
"status": "affirming",
|
|
@@ -46,8 +45,8 @@
|
|
|
46
45
|
"jwk": {
|
|
47
46
|
"kty": "EC",
|
|
48
47
|
"crv": "P-256",
|
|
49
|
-
"x": "SDEwMENvbmZpZGVudGlhbA
|
|
50
|
-
"y": "Q29tcHV0aW5nS2V5Rm9yVA
|
|
48
|
+
"x": "SDEwMENvbmZpZGVudGlhbA",
|
|
49
|
+
"y": "Q29tcHV0aW5nS2V5Rm9yVA",
|
|
51
50
|
"kid": "h100-cc-workload-key-2026-06-23"
|
|
52
51
|
}
|
|
53
52
|
}
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
"weights_digest": {
|
|
54
54
|
"type": "string",
|
|
55
55
|
"description": "SHA-256 or SHA-384 digest of the model weights. Required for local/confidential-inference deployments.",
|
|
56
|
-
"pattern": "^sha(256|384
|
|
56
|
+
"pattern": "^sha(256:[0-9a-f]{64}|384:[0-9a-f]{96})$"
|
|
57
57
|
},
|
|
58
58
|
"aibom_uri": {
|
|
59
59
|
"type": "string",
|
|
@@ -78,14 +78,15 @@
|
|
|
78
78
|
"aws-nitro",
|
|
79
79
|
"arm-cca",
|
|
80
80
|
"google-confidential-space",
|
|
81
|
-
"tpm2"
|
|
81
|
+
"tpm2",
|
|
82
|
+
"software-only"
|
|
82
83
|
],
|
|
83
|
-
"description": "Hardware platform providing the root of trust."
|
|
84
|
+
"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."
|
|
84
85
|
},
|
|
85
86
|
"measurement": {
|
|
86
87
|
"type": "string",
|
|
87
88
|
"description": "Hardware measurement of the workload (e.g. TDX MRTD, SEV measurement, TPM PCR composite).",
|
|
88
|
-
"pattern": "^sha(256|384
|
|
89
|
+
"pattern": "^sha(256:[0-9a-f]{64}|384:[0-9a-f]{96})$"
|
|
89
90
|
},
|
|
90
91
|
"rim_uri": {
|
|
91
92
|
"type": "string",
|
|
@@ -111,7 +112,7 @@
|
|
|
111
112
|
"bundle_hash": {
|
|
112
113
|
"type": "string",
|
|
113
114
|
"description": "SHA-256 or SHA-384 digest of the policy bundle in force at execution time.",
|
|
114
|
-
"pattern": "^sha(256|384
|
|
115
|
+
"pattern": "^sha(256:[0-9a-f]{64}|384:[0-9a-f]{96})$"
|
|
115
116
|
},
|
|
116
117
|
"enforcement_mode": {
|
|
117
118
|
"type": "string",
|
|
@@ -143,7 +144,7 @@
|
|
|
143
144
|
"hash": {
|
|
144
145
|
"type": "string",
|
|
145
146
|
"description": "SHA-256 or SHA-384 digest of the full tool-call transcript, bound into the EAT envelope.",
|
|
146
|
-
"pattern": "^sha(256|384
|
|
147
|
+
"pattern": "^sha(256:[0-9a-f]{64}|384:[0-9a-f]{96})$"
|
|
147
148
|
},
|
|
148
149
|
"call_count": {
|
|
149
150
|
"type": "integer",
|
|
@@ -176,7 +177,7 @@
|
|
|
176
177
|
"digest": {
|
|
177
178
|
"type": "string",
|
|
178
179
|
"description": "SHA-256 or SHA-384 digest of the container image or workload binary.",
|
|
179
|
-
"pattern": "^sha(256|384
|
|
180
|
+
"pattern": "^sha(256:[0-9a-f]{64}|384:[0-9a-f]{96})$"
|
|
180
181
|
},
|
|
181
182
|
"provenance_uri": {
|
|
182
183
|
"type": "string",
|
|
@@ -225,7 +226,7 @@
|
|
|
225
226
|
"properties": {
|
|
226
227
|
"jwk": {
|
|
227
228
|
"type": "object",
|
|
228
|
-
"description": "JWK (RFC 7517) representing the TEE-sealed public key.",
|
|
229
|
+
"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.",
|
|
229
230
|
"required": ["kty"],
|
|
230
231
|
"properties": {
|
|
231
232
|
"kty": {"type": "string"},
|
|
@@ -233,10 +234,31 @@
|
|
|
233
234
|
"x": {"type": "string"},
|
|
234
235
|
"y": {"type": "string"},
|
|
235
236
|
"kid": {"type": "string"}
|
|
236
|
-
}
|
|
237
|
+
},
|
|
238
|
+
"allOf": [
|
|
239
|
+
{
|
|
240
|
+
"if": {
|
|
241
|
+
"required": ["kty"],
|
|
242
|
+
"properties": {"kty": {"const": "OKP"}}
|
|
243
|
+
},
|
|
244
|
+
"then": {"required": ["crv", "x"]}
|
|
245
|
+
},
|
|
246
|
+
{
|
|
247
|
+
"if": {
|
|
248
|
+
"required": ["kty"],
|
|
249
|
+
"properties": {"kty": {"const": "EC"}}
|
|
250
|
+
},
|
|
251
|
+
"then": {"required": ["crv", "x", "y"]}
|
|
252
|
+
}
|
|
253
|
+
]
|
|
237
254
|
}
|
|
238
255
|
},
|
|
239
256
|
"additionalProperties": false
|
|
257
|
+
},
|
|
258
|
+
"signature": {
|
|
259
|
+
"type": "string",
|
|
260
|
+
"description": "OPTIONAL embedded signature: base64url (no padding) signature by the cnf key over the canonical JSON form of the record with 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.",
|
|
261
|
+
"pattern": "^[A-Za-z0-9_-]+$"
|
|
240
262
|
}
|
|
241
263
|
},
|
|
242
264
|
"additionalProperties": false
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
| Reference implementation | [agentrust-io/cmcp](https://github.com/agentrust-io/cmcp) — Confidential MCP |
|
|
10
10
|
| License | CC BY 4.0 |
|
|
11
11
|
|
|
12
|
-
> **Note:** This is a pre-ratification draft. Fields, wire formats, and conformance requirements are subject to change before v1.0. Send feedback to:
|
|
12
|
+
> **Note:** This is a pre-ratification draft. Fields, wire formats, and conformance requirements are subject to change before v1.0. Send feedback to: open an issue on this repository.
|
|
13
13
|
|
|
14
14
|
---
|
|
15
15
|
|
|
@@ -44,7 +44,7 @@ The wall is not technical capability — it is evidence. AI builders today produ
|
|
|
44
44
|
|
|
45
45
|
The result: every regulated AI deployment re-litigates trust at every host boundary. Each cloud, each model provider, each agent framework ships its own evidence shape. Auditors cannot compare. Verifiers cannot federate. Workloads cannot move.
|
|
46
46
|
|
|
47
|
-
The EU AI Act
|
|
47
|
+
The EU AI Act mandates tamper-evident logging for high-risk AI (Article 12); under the current provisional timeline those obligations apply from around December 2027. Frameworks already in force — DORA for financial entities, HIPAA for healthcare — carry equivalent audit-trail requirements today. Autonomous agents inside critical infrastructure are landing before the standard exists to govern them.
|
|
48
48
|
|
|
49
49
|
---
|
|
50
50
|
|
|
@@ -115,6 +115,7 @@ The Trust Record is the unit of evidence. All fields are required unless marked
|
|
|
115
115
|
| `cnf` | Confirmation key — binds record to TEE-held signing key | EAT `cnf` claim (RFC 8747) |
|
|
116
116
|
| `eat_profile` | Profile URI identifying this as a TRACE v0.1 record | EAT profile claim |
|
|
117
117
|
| `iat` | Issued-at timestamp (Unix epoch) | EAT standard claim |
|
|
118
|
+
| `signature` | OPTIONAL as a record field: embedded signature by the `cnf` key over the canonical record (section 3.2.2). Profiles using an enveloping signature (JWS, COSE, cMCP RuntimeClaim) omit this field and carry the signature in the envelope. The signature binding itself is mandatory either way. | JWS / COSE signature over canonical JSON |
|
|
118
119
|
|
|
119
120
|
Each field is independently verifiable. Sub-records (e.g., per-tool-call transcripts) compose under one root envelope.
|
|
120
121
|
|
|
@@ -183,15 +184,30 @@ Each field is independently verifiable. Sub-records (e.g., per-tool-call transcr
|
|
|
183
184
|
- **Revocation:** silicon-root revocation is consumed from existing vendor channels. Workload-level keys SHOULD rotate at TEE-image boundaries. Verifiers MUST consult current revocation status at verification time.
|
|
184
185
|
- **Hash agility:** SHA-256 minimum; SHA-384 required for FIPS-aligned profiles. Algorithm signaled in the EAT envelope per RFC 9711 §6.
|
|
185
186
|
|
|
187
|
+
#### 3.2.2 Mandatory signature and freshness binding
|
|
188
|
+
|
|
189
|
+
**Signature binding.** Every TRACE Trust Record MUST be cryptographically bound by a signature over its canonical JSON form, made by the key in `cnf`. Canonicalization is RFC 8785 (JCS) unless the profile declares a different canonicalization. The signature MAY be either:
|
|
190
|
+
|
|
191
|
+
- **Embedded:** carried in the record's top-level `signature` field (base64url, no padding), computed over the canonical form of the record with the `signature` field absent; or
|
|
192
|
+
- **Enveloping:** carried by a signed wrapper structure, e.g. a JWS (RFC 7515) whose payload is the record, a COSE_Sign1 envelope, or cMCP's RuntimeClaim (signature over the canonical record, key in `trace.cnf.jwk`).
|
|
193
|
+
|
|
194
|
+
Each profile MUST declare which binding form it uses. A record with no verifiable signature binding is not a Trust Record: verifiers MUST reject it. Schema validity alone confers no trust.
|
|
195
|
+
|
|
196
|
+
**Freshness.** Records MUST carry `iat`. Verifiers MUST enforce a maximum record age: a record whose `iat` is older than the maximum age MUST be rejected. The default maximum age is 24 hours; a deployment profile MAY specify a different value. Verifiers SHOULD additionally support challenge-nonce binding for online verification: the verifier supplies a nonce, the issuer echoes it in `runtime.nonce`, and the verifier checks the echo. When a challenge nonce was issued, a record that omits or mismatches it MUST be rejected.
|
|
197
|
+
|
|
198
|
+
**Conformance alignment.** The TRACE conformance suite (trace-tests) already enforces both rules: records without a verifiable signature fail at conformance level 1 and above, and the default 24-hour max-age is enforced.
|
|
199
|
+
|
|
186
200
|
### 3.3 Verification
|
|
187
201
|
|
|
188
202
|
Any party — browser, CLI, in-cluster verifier, third-party auditor — verifies:
|
|
189
203
|
|
|
190
|
-
1.
|
|
191
|
-
2.
|
|
192
|
-
3.
|
|
193
|
-
4.
|
|
194
|
-
5.
|
|
204
|
+
1. The record's signature binding (section 3.2.2) verifies against the key in `cnf`, BEFORE any other field is trusted. A record with no verifiable binding MUST be rejected.
|
|
205
|
+
2. The record is fresh: `iat` is within the maximum age (default 24 hours unless the deployment profile specifies otherwise). If the verifier issued a challenge nonce, `runtime.nonce` echoes it.
|
|
206
|
+
3. Signature chain resolves to a known silicon root (NVIDIA, Intel, AMD, or equivalent).
|
|
207
|
+
4. Runtime measurements match published Reference Integrity Manifests (RIMs).
|
|
208
|
+
5. Policy hash matches the policy bundle the verifier expects.
|
|
209
|
+
6. SCITT receipt resolves on the named transparency log.
|
|
210
|
+
7. SLSA provenance resolves to a trusted builder.
|
|
195
211
|
|
|
196
212
|
No callback to the issuer. No vendor in the trust path beyond silicon root and transparency log operators.
|
|
197
213
|
|
|
@@ -270,7 +286,7 @@ TRACE will publish vendor-co-authored claim-mapping annexes — one per silicon-
|
|
|
270
286
|
|
|
271
287
|
## 5. Reference Implementation: Confidential MCP (cMCP)
|
|
272
288
|
|
|
273
|
-
|
|
289
|
+
the reference implementation at the MCP tool-call boundary.
|
|
274
290
|
|
|
275
291
|
| Phase | What ships | TRACE fields | Timeline |
|
|
276
292
|
|---|---|---|---|
|
|
@@ -2,9 +2,9 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
from typing import Annotated, Literal
|
|
4
4
|
|
|
5
|
-
from pydantic import BaseModel, ConfigDict, Field
|
|
5
|
+
from pydantic import BaseModel, ConfigDict, Field, model_validator
|
|
6
6
|
|
|
7
|
-
_DIGEST_RE = r"^sha(256|384
|
|
7
|
+
_DIGEST_RE = r"^sha(256:[0-9a-f]{64}|384:[0-9a-f]{96})$"
|
|
8
8
|
|
|
9
9
|
DigestStr = Annotated[str, Field(pattern=_DIGEST_RE)]
|
|
10
10
|
|
|
@@ -31,6 +31,10 @@ class RuntimeInfo(BaseModel):
|
|
|
31
31
|
"arm-cca",
|
|
32
32
|
"google-confidential-space",
|
|
33
33
|
"tpm2",
|
|
34
|
+
# Development / non-attested mode. Distinct from tpm2 so a dev-mode
|
|
35
|
+
# record can never be mistaken for hardware-backed evidence by a
|
|
36
|
+
# consumer that only inspects runtime.platform.
|
|
37
|
+
"software-only",
|
|
34
38
|
]
|
|
35
39
|
measurement: DigestStr
|
|
36
40
|
rim_uri: str | None = None
|
|
@@ -83,6 +87,18 @@ class JWK(BaseModel):
|
|
|
83
87
|
y: str | None = None
|
|
84
88
|
kid: str | None = None
|
|
85
89
|
|
|
90
|
+
@model_validator(mode="after")
|
|
91
|
+
def _require_key_material(self) -> JWK:
|
|
92
|
+
"""A confirmation key without key material binds nothing (RFC 7518 §6)."""
|
|
93
|
+
required_by_kty = {"OKP": ("crv", "x"), "EC": ("crv", "x", "y")}
|
|
94
|
+
required = required_by_kty.get(self.kty, ())
|
|
95
|
+
missing = [name for name in required if getattr(self, name) is None]
|
|
96
|
+
if missing:
|
|
97
|
+
raise ValueError(
|
|
98
|
+
f"jwk with kty={self.kty!r} must carry key material: missing {', '.join(missing)}"
|
|
99
|
+
)
|
|
100
|
+
return self
|
|
101
|
+
|
|
86
102
|
|
|
87
103
|
class ConfirmationKey(BaseModel):
|
|
88
104
|
model_config = ConfigDict(extra="forbid")
|
|
@@ -107,3 +123,8 @@ class TrustRecord(BaseModel):
|
|
|
107
123
|
appraisal: Appraisal
|
|
108
124
|
transparency: str
|
|
109
125
|
cnf: ConfirmationKey
|
|
126
|
+
signature: Annotated[str, Field(pattern=r"^[A-Za-z0-9_-]+$")] | None = None
|
|
127
|
+
"""Optional embedded signature (base64url, no padding) by the cnf key over the
|
|
128
|
+
canonical JSON form of the record with this field absent. Every Trust Record must
|
|
129
|
+
be signature-bound per spec section 3.2.2; enveloped profiles carry the signature
|
|
130
|
+
outside the record instead of in this field."""
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
"weights_digest": {
|
|
54
54
|
"type": "string",
|
|
55
55
|
"description": "SHA-256 or SHA-384 digest of the model weights. Required for local/confidential-inference deployments.",
|
|
56
|
-
"pattern": "^sha(256|384
|
|
56
|
+
"pattern": "^sha(256:[0-9a-f]{64}|384:[0-9a-f]{96})$"
|
|
57
57
|
},
|
|
58
58
|
"aibom_uri": {
|
|
59
59
|
"type": "string",
|
|
@@ -78,14 +78,15 @@
|
|
|
78
78
|
"aws-nitro",
|
|
79
79
|
"arm-cca",
|
|
80
80
|
"google-confidential-space",
|
|
81
|
-
"tpm2"
|
|
81
|
+
"tpm2",
|
|
82
|
+
"software-only"
|
|
82
83
|
],
|
|
83
|
-
"description": "Hardware platform providing the root of trust."
|
|
84
|
+
"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."
|
|
84
85
|
},
|
|
85
86
|
"measurement": {
|
|
86
87
|
"type": "string",
|
|
87
88
|
"description": "Hardware measurement of the workload (e.g. TDX MRTD, SEV measurement, TPM PCR composite).",
|
|
88
|
-
"pattern": "^sha(256|384
|
|
89
|
+
"pattern": "^sha(256:[0-9a-f]{64}|384:[0-9a-f]{96})$"
|
|
89
90
|
},
|
|
90
91
|
"rim_uri": {
|
|
91
92
|
"type": "string",
|
|
@@ -111,7 +112,7 @@
|
|
|
111
112
|
"bundle_hash": {
|
|
112
113
|
"type": "string",
|
|
113
114
|
"description": "SHA-256 or SHA-384 digest of the policy bundle in force at execution time.",
|
|
114
|
-
"pattern": "^sha(256|384
|
|
115
|
+
"pattern": "^sha(256:[0-9a-f]{64}|384:[0-9a-f]{96})$"
|
|
115
116
|
},
|
|
116
117
|
"enforcement_mode": {
|
|
117
118
|
"type": "string",
|
|
@@ -143,7 +144,7 @@
|
|
|
143
144
|
"hash": {
|
|
144
145
|
"type": "string",
|
|
145
146
|
"description": "SHA-256 or SHA-384 digest of the full tool-call transcript, bound into the EAT envelope.",
|
|
146
|
-
"pattern": "^sha(256|384
|
|
147
|
+
"pattern": "^sha(256:[0-9a-f]{64}|384:[0-9a-f]{96})$"
|
|
147
148
|
},
|
|
148
149
|
"call_count": {
|
|
149
150
|
"type": "integer",
|
|
@@ -176,7 +177,7 @@
|
|
|
176
177
|
"digest": {
|
|
177
178
|
"type": "string",
|
|
178
179
|
"description": "SHA-256 or SHA-384 digest of the container image or workload binary.",
|
|
179
|
-
"pattern": "^sha(256|384
|
|
180
|
+
"pattern": "^sha(256:[0-9a-f]{64}|384:[0-9a-f]{96})$"
|
|
180
181
|
},
|
|
181
182
|
"provenance_uri": {
|
|
182
183
|
"type": "string",
|
|
@@ -225,7 +226,7 @@
|
|
|
225
226
|
"properties": {
|
|
226
227
|
"jwk": {
|
|
227
228
|
"type": "object",
|
|
228
|
-
"description": "JWK (RFC 7517) representing the TEE-sealed public key.",
|
|
229
|
+
"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.",
|
|
229
230
|
"required": ["kty"],
|
|
230
231
|
"properties": {
|
|
231
232
|
"kty": {"type": "string"},
|
|
@@ -233,10 +234,31 @@
|
|
|
233
234
|
"x": {"type": "string"},
|
|
234
235
|
"y": {"type": "string"},
|
|
235
236
|
"kid": {"type": "string"}
|
|
236
|
-
}
|
|
237
|
+
},
|
|
238
|
+
"allOf": [
|
|
239
|
+
{
|
|
240
|
+
"if": {
|
|
241
|
+
"required": ["kty"],
|
|
242
|
+
"properties": {"kty": {"const": "OKP"}}
|
|
243
|
+
},
|
|
244
|
+
"then": {"required": ["crv", "x"]}
|
|
245
|
+
},
|
|
246
|
+
{
|
|
247
|
+
"if": {
|
|
248
|
+
"required": ["kty"],
|
|
249
|
+
"properties": {"kty": {"const": "EC"}}
|
|
250
|
+
},
|
|
251
|
+
"then": {"required": ["crv", "x", "y"]}
|
|
252
|
+
}
|
|
253
|
+
]
|
|
237
254
|
}
|
|
238
255
|
},
|
|
239
256
|
"additionalProperties": false
|
|
257
|
+
},
|
|
258
|
+
"signature": {
|
|
259
|
+
"type": "string",
|
|
260
|
+
"description": "OPTIONAL embedded signature: base64url (no padding) signature by the cnf key over the canonical JSON form of the record with 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.",
|
|
261
|
+
"pattern": "^[A-Za-z0-9_-]+$"
|
|
240
262
|
}
|
|
241
263
|
},
|
|
242
264
|
"additionalProperties": false
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
"""Parse all three canonical examples through TrustRecord."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
import pytest
|
|
7
|
+
from pydantic import ValidationError
|
|
8
|
+
|
|
9
|
+
from agentrust_trace import TrustRecord
|
|
10
|
+
|
|
11
|
+
EXAMPLES_DIR = Path(__file__).parent.parent / "examples"
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _load(name: str) -> dict:
|
|
15
|
+
# Examples must validate exactly as published: no preprocessing.
|
|
16
|
+
return json.loads((EXAMPLES_DIR / name).read_text())
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@pytest.mark.parametrize("filename", ["intel-tdx.json", "amd-sev-snp.json", "nvidia-h100.json"])
|
|
20
|
+
def test_example_parses(filename: str) -> None:
|
|
21
|
+
record = TrustRecord.model_validate(_load(filename))
|
|
22
|
+
assert record.eat_profile == "tag:agentrust.io,2026:trace-v0.1"
|
|
23
|
+
assert record.subject.startswith("spiffe://")
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def test_intel_tdx_fields() -> None:
|
|
27
|
+
record = TrustRecord.model_validate(_load("intel-tdx.json"))
|
|
28
|
+
assert record.runtime.platform == "intel-tdx"
|
|
29
|
+
assert record.policy.enforcement_mode == "enforce"
|
|
30
|
+
assert record.appraisal.status == "affirming"
|
|
31
|
+
assert record.tool_transcript is not None
|
|
32
|
+
assert record.tool_transcript.call_count == 7
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def test_extra_fields_rejected() -> None:
|
|
36
|
+
data = _load("intel-tdx.json")
|
|
37
|
+
data["unknown_field"] = "should fail"
|
|
38
|
+
with pytest.raises(ValidationError):
|
|
39
|
+
TrustRecord.model_validate(data)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def test_missing_required_field_rejected() -> None:
|
|
43
|
+
data = _load("intel-tdx.json")
|
|
44
|
+
del data["cnf"]
|
|
45
|
+
with pytest.raises(ValidationError):
|
|
46
|
+
TrustRecord.model_validate(data)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def test_bad_digest_rejected() -> None:
|
|
50
|
+
data = _load("intel-tdx.json")
|
|
51
|
+
data["runtime"]["measurement"] = "not-a-digest"
|
|
52
|
+
with pytest.raises(ValidationError):
|
|
53
|
+
TrustRecord.model_validate(data)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def test_bad_platform_rejected() -> None:
|
|
57
|
+
data = _load("intel-tdx.json")
|
|
58
|
+
data["runtime"]["platform"] = "unknown-cloud"
|
|
59
|
+
with pytest.raises(ValidationError):
|
|
60
|
+
TrustRecord.model_validate(data)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
# CRYPTO-008 / CRYPTO-009: DigestStr regex enforcement
|
|
64
|
+
|
|
65
|
+
def test_digest_uppercase_rejected() -> None:
|
|
66
|
+
"""CRYPTO-008: uppercase hex must be rejected (sha256: is lowercase-only)."""
|
|
67
|
+
data = _load("intel-tdx.json")
|
|
68
|
+
data["runtime"]["measurement"] = "sha256:" + "A" * 64
|
|
69
|
+
with pytest.raises(ValidationError):
|
|
70
|
+
TrustRecord.model_validate(data)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def test_digest_sha256_too_short_rejected() -> None:
|
|
74
|
+
"""CRYPTO-008/009: sha256 digest shorter than 64 chars must be rejected."""
|
|
75
|
+
data = _load("intel-tdx.json")
|
|
76
|
+
data["runtime"]["measurement"] = "sha256:" + "a" * 63
|
|
77
|
+
with pytest.raises(ValidationError):
|
|
78
|
+
TrustRecord.model_validate(data)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def test_digest_sha256_exact_length_accepted() -> None:
|
|
82
|
+
"""sha256 digest with exactly 64 lowercase hex chars must be accepted."""
|
|
83
|
+
data = _load("intel-tdx.json")
|
|
84
|
+
data["runtime"]["measurement"] = "sha256:" + "a" * 64
|
|
85
|
+
record = TrustRecord.model_validate(data)
|
|
86
|
+
assert record.runtime.measurement == "sha256:" + "a" * 64
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def test_digest_sha384_exact_length_accepted() -> None:
|
|
90
|
+
"""sha384 digest with exactly 96 lowercase hex chars must be accepted."""
|
|
91
|
+
data = _load("intel-tdx.json")
|
|
92
|
+
data["runtime"]["measurement"] = "sha384:" + "b" * 96
|
|
93
|
+
record = TrustRecord.model_validate(data)
|
|
94
|
+
assert record.runtime.measurement == "sha384:" + "b" * 96
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def test_digest_sha512_rejected() -> None:
|
|
98
|
+
"""CRYPTO-009: unsupported algorithm sha512 must be rejected."""
|
|
99
|
+
data = _load("intel-tdx.json")
|
|
100
|
+
data["runtime"]["measurement"] = "sha512:" + "a" * 128
|
|
101
|
+
with pytest.raises(ValidationError):
|
|
102
|
+
TrustRecord.model_validate(data)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
# cnf.jwk key material enforcement
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def test_okp_jwk_without_key_material_rejected() -> None:
|
|
109
|
+
"""An OKP confirmation key with no crv/x carries no key material and binds nothing."""
|
|
110
|
+
data = _load("intel-tdx.json")
|
|
111
|
+
data["cnf"]["jwk"] = {"kty": "OKP"}
|
|
112
|
+
with pytest.raises(ValidationError):
|
|
113
|
+
TrustRecord.model_validate(data)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def test_ec_jwk_without_y_rejected() -> None:
|
|
117
|
+
data = _load("intel-tdx.json")
|
|
118
|
+
data["cnf"]["jwk"] = {"kty": "EC", "crv": "P-256", "x": "dGVzdA"}
|
|
119
|
+
with pytest.raises(ValidationError):
|
|
120
|
+
TrustRecord.model_validate(data)
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def test_okp_jwk_with_key_material_accepted() -> None:
|
|
124
|
+
data = _load("intel-tdx.json")
|
|
125
|
+
data["cnf"]["jwk"] = {
|
|
126
|
+
"kty": "OKP",
|
|
127
|
+
"crv": "Ed25519",
|
|
128
|
+
"x": "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo",
|
|
129
|
+
}
|
|
130
|
+
record = TrustRecord.model_validate(data)
|
|
131
|
+
assert record.cnf.jwk.x is not None
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"""validate_json and iter_errors against canonical examples."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
import pytest
|
|
7
|
+
|
|
8
|
+
from agentrust_trace import SCHEMA, iter_errors, validate_json
|
|
9
|
+
|
|
10
|
+
EXAMPLES_DIR = Path(__file__).parent.parent / "examples"
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def _load(name: str) -> dict:
|
|
14
|
+
# Examples must validate exactly as published: no preprocessing.
|
|
15
|
+
return json.loads((EXAMPLES_DIR / name).read_text())
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@pytest.mark.parametrize("filename", ["intel-tdx.json", "amd-sev-snp.json", "nvidia-h100.json"])
|
|
19
|
+
def test_examples_pass_json_schema(filename: str) -> None:
|
|
20
|
+
validate_json(_load(filename))
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def test_iter_errors_empty_on_valid() -> None:
|
|
24
|
+
assert iter_errors(_load("intel-tdx.json")) == []
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def test_invalid_eat_profile_fails() -> None:
|
|
28
|
+
data = _load("intel-tdx.json")
|
|
29
|
+
data["eat_profile"] = "wrong-profile"
|
|
30
|
+
errors = iter_errors(data)
|
|
31
|
+
assert errors, "expected at least one schema error"
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def test_missing_required_field_fails() -> None:
|
|
35
|
+
data = _load("intel-tdx.json")
|
|
36
|
+
del data["subject"]
|
|
37
|
+
errors = iter_errors(data)
|
|
38
|
+
assert errors
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def test_schema_is_dict() -> None:
|
|
42
|
+
assert isinstance(SCHEMA, dict)
|
|
43
|
+
assert SCHEMA.get("title") == "TRACE Trust Record"
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def test_comment_key_fails() -> None:
|
|
47
|
+
"""additionalProperties is false: a _comment key must be rejected, including in examples."""
|
|
48
|
+
data = _load("intel-tdx.json")
|
|
49
|
+
data["_comment"] = "human note"
|
|
50
|
+
errors = iter_errors(data)
|
|
51
|
+
assert errors
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def test_okp_jwk_without_key_material_fails() -> None:
|
|
55
|
+
"""cnf.jwk must carry key material: OKP requires crv and x."""
|
|
56
|
+
data = _load("intel-tdx.json")
|
|
57
|
+
data["cnf"]["jwk"] = {"kty": "OKP"}
|
|
58
|
+
errors = iter_errors(data)
|
|
59
|
+
assert errors
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def test_ec_jwk_without_y_fails() -> None:
|
|
63
|
+
"""cnf.jwk must carry key material: EC requires crv, x, and y."""
|
|
64
|
+
data = _load("intel-tdx.json")
|
|
65
|
+
data["cnf"]["jwk"] = {"kty": "EC", "crv": "P-256", "x": "dGVzdA"}
|
|
66
|
+
errors = iter_errors(data)
|
|
67
|
+
assert errors
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def test_okp_jwk_with_key_material_passes() -> None:
|
|
71
|
+
data = _load("intel-tdx.json")
|
|
72
|
+
data["cnf"]["jwk"] = {
|
|
73
|
+
"kty": "OKP",
|
|
74
|
+
"crv": "Ed25519",
|
|
75
|
+
"x": "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo",
|
|
76
|
+
}
|
|
77
|
+
assert iter_errors(data) == []
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
"""Parse all three canonical examples through TrustRecord."""
|
|
2
|
-
|
|
3
|
-
import json
|
|
4
|
-
from pathlib import Path
|
|
5
|
-
|
|
6
|
-
import pytest
|
|
7
|
-
from pydantic import ValidationError
|
|
8
|
-
|
|
9
|
-
from agentrust_trace import TrustRecord
|
|
10
|
-
|
|
11
|
-
EXAMPLES_DIR = Path(__file__).parent.parent / "examples"
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
def _load(name: str) -> dict:
|
|
15
|
-
data = json.loads((EXAMPLES_DIR / name).read_text())
|
|
16
|
-
data.pop("_comment", None) # examples carry a human note; not part of the schema
|
|
17
|
-
return data
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
@pytest.mark.parametrize("filename", ["intel-tdx.json", "amd-sev-snp.json", "nvidia-h100.json"])
|
|
21
|
-
def test_example_parses(filename: str) -> None:
|
|
22
|
-
record = TrustRecord.model_validate(_load(filename))
|
|
23
|
-
assert record.eat_profile == "tag:agentrust.io,2026:trace-v0.1"
|
|
24
|
-
assert record.subject.startswith("spiffe://")
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
def test_intel_tdx_fields() -> None:
|
|
28
|
-
record = TrustRecord.model_validate(_load("intel-tdx.json"))
|
|
29
|
-
assert record.runtime.platform == "intel-tdx"
|
|
30
|
-
assert record.policy.enforcement_mode == "enforce"
|
|
31
|
-
assert record.appraisal.status == "affirming"
|
|
32
|
-
assert record.tool_transcript is not None
|
|
33
|
-
assert record.tool_transcript.call_count == 7
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
def test_extra_fields_rejected() -> None:
|
|
37
|
-
data = _load("intel-tdx.json")
|
|
38
|
-
data["unknown_field"] = "should fail"
|
|
39
|
-
with pytest.raises(ValidationError):
|
|
40
|
-
TrustRecord.model_validate(data)
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
def test_missing_required_field_rejected() -> None:
|
|
44
|
-
data = _load("intel-tdx.json")
|
|
45
|
-
del data["cnf"]
|
|
46
|
-
with pytest.raises(ValidationError):
|
|
47
|
-
TrustRecord.model_validate(data)
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
def test_bad_digest_rejected() -> None:
|
|
51
|
-
data = _load("intel-tdx.json")
|
|
52
|
-
data["runtime"]["measurement"] = "not-a-digest"
|
|
53
|
-
with pytest.raises(ValidationError):
|
|
54
|
-
TrustRecord.model_validate(data)
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
def test_bad_platform_rejected() -> None:
|
|
58
|
-
data = _load("intel-tdx.json")
|
|
59
|
-
data["runtime"]["platform"] = "unknown-cloud"
|
|
60
|
-
with pytest.raises(ValidationError):
|
|
61
|
-
TrustRecord.model_validate(data)
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
"""validate_json and iter_errors against canonical examples."""
|
|
2
|
-
|
|
3
|
-
import json
|
|
4
|
-
from pathlib import Path
|
|
5
|
-
|
|
6
|
-
import pytest
|
|
7
|
-
|
|
8
|
-
from agentrust_trace import SCHEMA, iter_errors, validate_json
|
|
9
|
-
|
|
10
|
-
EXAMPLES_DIR = Path(__file__).parent.parent / "examples"
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
def _load(name: str) -> dict:
|
|
14
|
-
data = json.loads((EXAMPLES_DIR / name).read_text())
|
|
15
|
-
data.pop("_comment", None)
|
|
16
|
-
return data
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
@pytest.mark.parametrize("filename", ["intel-tdx.json", "amd-sev-snp.json", "nvidia-h100.json"])
|
|
20
|
-
def test_examples_pass_json_schema(filename: str) -> None:
|
|
21
|
-
validate_json(_load(filename))
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
def test_iter_errors_empty_on_valid() -> None:
|
|
25
|
-
assert iter_errors(_load("intel-tdx.json")) == []
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
def test_invalid_eat_profile_fails() -> None:
|
|
29
|
-
data = _load("intel-tdx.json")
|
|
30
|
-
data["eat_profile"] = "wrong-profile"
|
|
31
|
-
errors = iter_errors(data)
|
|
32
|
-
assert errors, "expected at least one schema error"
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
def test_missing_required_field_fails() -> None:
|
|
36
|
-
data = _load("intel-tdx.json")
|
|
37
|
-
del data["subject"]
|
|
38
|
-
errors = iter_errors(data)
|
|
39
|
-
assert errors
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
def test_schema_is_dict() -> None:
|
|
43
|
-
assert isinstance(SCHEMA, dict)
|
|
44
|
-
assert SCHEMA.get("title") == "TRACE Trust Record"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|