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.
Files changed (39) hide show
  1. {agentrust_trace-0.1.0 → agentrust_trace-0.1.1}/.github/workflows/ci.yml +4 -1
  2. agentrust_trace-0.1.1/.github/workflows/contributor-check.yml +42 -0
  3. {agentrust_trace-0.1.0 → agentrust_trace-0.1.1}/.github/workflows/publish.yml +4 -1
  4. agentrust_trace-0.1.1/.github/workflows/require-maintainer-approval.yml +84 -0
  5. agentrust_trace-0.1.1/.github/workflows/sbom.yml +30 -0
  6. agentrust_trace-0.1.1/ADOPTERS.md +11 -0
  7. {agentrust_trace-0.1.0 → agentrust_trace-0.1.1}/CHARTER.md +3 -1
  8. {agentrust_trace-0.1.0 → agentrust_trace-0.1.1}/PKG-INFO +33 -9
  9. {agentrust_trace-0.1.0 → agentrust_trace-0.1.1}/README.md +32 -8
  10. {agentrust_trace-0.1.0 → agentrust_trace-0.1.1}/SECURITY.md +1 -1
  11. agentrust_trace-0.1.1/examples/README.md +11 -0
  12. {agentrust_trace-0.1.0 → agentrust_trace-0.1.1}/examples/amd-sev-snp.json +7 -8
  13. {agentrust_trace-0.1.0 → agentrust_trace-0.1.1}/examples/intel-tdx.json +8 -9
  14. {agentrust_trace-0.1.0 → agentrust_trace-0.1.1}/examples/nvidia-h100.json +9 -10
  15. {agentrust_trace-0.1.0 → agentrust_trace-0.1.1}/pyproject.toml +1 -1
  16. {agentrust_trace-0.1.0 → agentrust_trace-0.1.1}/schema/trace-claim.json +31 -9
  17. {agentrust_trace-0.1.0 → agentrust_trace-0.1.1}/spec/trace-v0.1.md +24 -8
  18. {agentrust_trace-0.1.0 → agentrust_trace-0.1.1}/src/agentrust_trace/models.py +23 -2
  19. {agentrust_trace-0.1.0 → agentrust_trace-0.1.1}/src/agentrust_trace/schema/trace-v0.1.json +31 -9
  20. agentrust_trace-0.1.1/tests/test_models.py +131 -0
  21. agentrust_trace-0.1.1/tests/test_validate.py +77 -0
  22. agentrust_trace-0.1.0/tests/test_models.py +0 -61
  23. agentrust_trace-0.1.0/tests/test_validate.py +0 -44
  24. {agentrust_trace-0.1.0 → agentrust_trace-0.1.1}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  25. {agentrust_trace-0.1.0 → agentrust_trace-0.1.1}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  26. {agentrust_trace-0.1.0 → agentrust_trace-0.1.1}/.github/ISSUE_TEMPLATE/spec_change.md +0 -0
  27. {agentrust_trace-0.1.0 → agentrust_trace-0.1.1}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  28. {agentrust_trace-0.1.0 → agentrust_trace-0.1.1}/CHANGELOG.md +0 -0
  29. {agentrust_trace-0.1.0 → agentrust_trace-0.1.1}/CODE_OF_CONDUCT.md +0 -0
  30. {agentrust_trace-0.1.0 → agentrust_trace-0.1.1}/CONTRIBUTING.md +0 -0
  31. {agentrust_trace-0.1.0 → agentrust_trace-0.1.1}/GOVERNANCE.md +0 -0
  32. {agentrust_trace-0.1.0 → agentrust_trace-0.1.1}/LICENSE +0 -0
  33. {agentrust_trace-0.1.0 → agentrust_trace-0.1.1}/MAINTAINERS.md +0 -0
  34. {agentrust_trace-0.1.0 → agentrust_trace-0.1.1}/ROADMAP.md +0 -0
  35. {agentrust_trace-0.1.0 → agentrust_trace-0.1.1}/docs/assets/icon.svg +0 -0
  36. {agentrust_trace-0.1.0 → agentrust_trace-0.1.1}/src/agentrust_trace/__init__.py +0 -0
  37. {agentrust_trace-0.1.0 → agentrust_trace-0.1.1}/src/agentrust_trace/py.typed +0 -0
  38. {agentrust_trace-0.1.0 → agentrust_trace-0.1.1}/src/agentrust_trace/validate.py +0 -0
  39. {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@v5
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@v5
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.0
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 Trust Runtime Attestation and Compliance Evidence
36
+ # TRACE: Trust Runtime Attestation and Compliance Evidence
37
+
38
+ [![License: CC BY 4.0](https://img.shields.io/badge/License-CC_BY_4.0-lightgrey.svg)](LICENSE)
39
+ [![AAIF](https://img.shields.io/badge/Targeting-AAIF_%2F_Linux_Foundation-6366f1)](https://agenticai.foundation)
40
+ [![Spec](https://img.shields.io/badge/Spec-v0.1-0ea5e9)](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> &nbsp;|&nbsp;
46
+ <a href="schema/trace-claim.json">Schema</a> &nbsp;|&nbsp;
47
+ <a href="examples/">Examples</a> &nbsp;|&nbsp;
48
+ <a href="https://github.com/agentrust-io/trace-registry">Registry</a> &nbsp;|&nbsp;
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 bound into a single signed artifact rooted in silicon attestation.
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) full specification
90
- - [`schema/trace-claim.json`](schema/trace-claim.json) JSON Schema
91
- - [`examples/`](examples/) example Trust Records for Intel TDX, AMD SEV-SNP, and NVIDIA H100
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) Confidential MCP Gateway. Hardware-attested policy enforcement at the MCP tool-call boundary on Intel TDX, AMD SEV-SNP, and NVIDIA H100/Blackwell.
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 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.
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 Trust Runtime Attestation and Compliance Evidence
5
+ # TRACE: Trust Runtime Attestation and Compliance Evidence
6
+
7
+ [![License: CC BY 4.0](https://img.shields.io/badge/License-CC_BY_4.0-lightgrey.svg)](LICENSE)
8
+ [![AAIF](https://img.shields.io/badge/Targeting-AAIF_%2F_Linux_Foundation-6366f1)](https://agenticai.foundation)
9
+ [![Spec](https://img.shields.io/badge/Spec-v0.1-0ea5e9)](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> &nbsp;|&nbsp;
15
+ <a href="schema/trace-claim.json">Schema</a> &nbsp;|&nbsp;
16
+ <a href="examples/">Examples</a> &nbsp;|&nbsp;
17
+ <a href="https://github.com/agentrust-io/trace-registry">Registry</a> &nbsp;|&nbsp;
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 bound into a single signed artifact rooted in silicon attestation.
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) full specification
59
- - [`schema/trace-claim.json`](schema/trace-claim.json) JSON Schema
60
- - [`examples/`](examples/) example Trust Records for Intel TDX, AMD SEV-SNP, and NVIDIA H100
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) Confidential MCP Gateway. Hardware-attested policy enforcement at the MCP tool-call boundary on Intel TDX, AMD SEV-SNP, and NVIDIA H100/Blackwell.
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 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.
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 by email to: **security@opaque.co**
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:c9e4b1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4...",
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:b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2...",
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:d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4...",
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:e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5...",
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:a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5...",
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:c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3...",
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:f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6...",
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:b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8...",
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:d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4...",
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:b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6...",
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:e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5...",
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:a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7...",
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:c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9...",
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
  }
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "agentrust-trace"
7
- version = "0.1.0"
7
+ version = "0.1.1"
8
8
  description = "TRACE v0.1 — hardware-attested governance records for AI agents"
9
9
  readme = "README.md"
10
10
  license = { text = "Apache-2.0" }
@@ -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):[0-9a-f]+"
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):[0-9a-f]+"
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):[0-9a-f]+"
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):[0-9a-f]+"
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):[0-9a-f]+"
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: aaron@opaque.co or open an issue on this repository.
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 enforces tamper-evident logging for high-risk AI in August 2026. Autonomous agents inside critical infrastructure are landing before the standard exists to govern them.
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. Signature chain resolves to a known silicon root (NVIDIA, Intel, AMD, or equivalent).
191
- 2. Runtime measurements match published Reference Integrity Manifests (RIMs).
192
- 3. Policy hash matches the policy bundle the verifier expects.
193
- 4. SCITT receipt resolves on the named transparency log.
194
- 5. SLSA provenance resolves to a trusted builder.
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
- OPAQUE's reference build at the MCP tool-call boundary.
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):[0-9a-f]+"
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):[0-9a-f]+"
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):[0-9a-f]+"
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):[0-9a-f]+"
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):[0-9a-f]+"
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):[0-9a-f]+"
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