trace-verify 0.1.0__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.
- trace_verify-0.1.0/.github/workflows/ci.yml +85 -0
- trace_verify-0.1.0/.github/workflows/publish.yml +47 -0
- trace_verify-0.1.0/.gitignore +1 -0
- trace_verify-0.1.0/CODE_OF_CONDUCT.md +17 -0
- trace_verify-0.1.0/CONTRIBUTING.md +18 -0
- trace_verify-0.1.0/LICENSE +6 -0
- trace_verify-0.1.0/PKG-INFO +127 -0
- trace_verify-0.1.0/README.md +102 -0
- trace_verify-0.1.0/SECURITY.md +7 -0
- trace_verify-0.1.0/docs/anchor-format.md +147 -0
- trace_verify-0.1.0/pyproject.toml +38 -0
- trace_verify-0.1.0/registry/2026/06/12.ndjson +1 -0
- trace_verify-0.1.0/samples/example-trust-record.json +75 -0
- trace_verify-0.1.0/samples/inclusion-proof.json +4 -0
- trace_verify-0.1.0/schema/registry-entry.schema.json +41 -0
- trace_verify-0.1.0/src/trace_verify/__init__.py +12 -0
- trace_verify-0.1.0/src/trace_verify/__main__.py +169 -0
- trace_verify-0.1.0/src/trace_verify/_verify.py +77 -0
- trace_verify-0.1.0/tests/test_anchor.py +153 -0
- trace_verify-0.1.0/tests/test_registry_gates.py +194 -0
- trace_verify-0.1.0/tests/test_trace_verify_pkg.py +240 -0
- trace_verify-0.1.0/tools/anchor.py +152 -0
- trace_verify-0.1.0/tools/check_append_only.py +100 -0
- trace_verify-0.1.0/tools/validate_registry.py +86 -0
- trace_verify-0.1.0/tools/verify_inclusion.py +175 -0
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
validate:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
steps:
|
|
13
|
+
- uses: actions/checkout@v4
|
|
14
|
+
with:
|
|
15
|
+
fetch-depth: 0
|
|
16
|
+
|
|
17
|
+
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
|
|
18
|
+
with:
|
|
19
|
+
python-version: "3.12"
|
|
20
|
+
|
|
21
|
+
- name: Install dependencies
|
|
22
|
+
run: pip install jsonschema==4.26.0 hatchling
|
|
23
|
+
|
|
24
|
+
- name: Install trace-verify package (editable)
|
|
25
|
+
run: pip install -e .
|
|
26
|
+
|
|
27
|
+
- name: Smoke-test trace-verify CLI
|
|
28
|
+
run: |
|
|
29
|
+
set -euo pipefail
|
|
30
|
+
trace-verify --version
|
|
31
|
+
trace-verify \
|
|
32
|
+
--claim samples/example-trust-record.json \
|
|
33
|
+
--proof samples/inclusion-proof.json \
|
|
34
|
+
--entry registry/2026/06/12.ndjson
|
|
35
|
+
trace-verify \
|
|
36
|
+
--claim samples/example-trust-record.json \
|
|
37
|
+
--proof samples/inclusion-proof.json \
|
|
38
|
+
--entry registry/2026/06/12.ndjson \
|
|
39
|
+
--json | python -c "import json,sys; r=json.load(sys.stdin); assert r['verified'], r"
|
|
40
|
+
|
|
41
|
+
- name: Determine base SHA for append-only check
|
|
42
|
+
id: base
|
|
43
|
+
run: |
|
|
44
|
+
if [ "${{ github.event_name }}" = "pull_request" ]; then
|
|
45
|
+
echo "sha=${{ github.event.pull_request.base.sha }}" >> "$GITHUB_OUTPUT"
|
|
46
|
+
elif [ "${{ github.event.before }}" = "0000000000000000000000000000000000000000" ]; then
|
|
47
|
+
echo "sha=" >> "$GITHUB_OUTPUT"
|
|
48
|
+
else
|
|
49
|
+
echo "sha=${{ github.event.before }}" >> "$GITHUB_OUTPUT"
|
|
50
|
+
fi
|
|
51
|
+
|
|
52
|
+
- name: Check registry is append-only
|
|
53
|
+
if: steps.base.outputs.sha != ''
|
|
54
|
+
run: python tools/check_append_only.py "${{ steps.base.outputs.sha }}"
|
|
55
|
+
|
|
56
|
+
- name: Validate registry entries against schema
|
|
57
|
+
run: python tools/validate_registry.py
|
|
58
|
+
|
|
59
|
+
- name: Run tool tests
|
|
60
|
+
run: python -m unittest discover -s tests -v
|
|
61
|
+
|
|
62
|
+
- name: Round-trip the sample anchor
|
|
63
|
+
run: |
|
|
64
|
+
set -euo pipefail
|
|
65
|
+
# Re-anchor the committed sample claim and check the recomputed
|
|
66
|
+
# Merkle root matches the committed registry entry.
|
|
67
|
+
recomputed=$(python tools/anchor.py samples/example-trust-record.json \
|
|
68
|
+
--producer ci --proof-dir "$(mktemp -d)" \
|
|
69
|
+
| python -c "import json,sys; print(json.load(sys.stdin)['merkle_root'])")
|
|
70
|
+
committed=$(python -c "import json; print(json.loads(open('registry/2026/06/12.ndjson').readline())['merkle_root'])")
|
|
71
|
+
echo "recomputed: $recomputed"
|
|
72
|
+
echo "committed: $committed"
|
|
73
|
+
test "$recomputed" = "$committed"
|
|
74
|
+
# Verify the committed inclusion proof against the committed entry.
|
|
75
|
+
python tools/verify_inclusion.py \
|
|
76
|
+
--claim samples/example-trust-record.json \
|
|
77
|
+
--proof samples/inclusion-proof.json \
|
|
78
|
+
--entry registry/2026/06/12.ndjson
|
|
79
|
+
|
|
80
|
+
- name: Check for required files
|
|
81
|
+
run: |
|
|
82
|
+
for f in LICENSE README.md CONTRIBUTING.md CODE_OF_CONDUCT.md SECURITY.md; do
|
|
83
|
+
[ -f "$f" ] || { echo "Missing required file: $f"; exit 1; }
|
|
84
|
+
done
|
|
85
|
+
echo "All required community health files present"
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
name: Publish trace-verify to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
workflow_dispatch:
|
|
7
|
+
inputs:
|
|
8
|
+
dry_run:
|
|
9
|
+
description: "Build and attest without publishing to PyPI"
|
|
10
|
+
required: false
|
|
11
|
+
type: boolean
|
|
12
|
+
default: true
|
|
13
|
+
|
|
14
|
+
permissions:
|
|
15
|
+
contents: read
|
|
16
|
+
|
|
17
|
+
jobs:
|
|
18
|
+
build:
|
|
19
|
+
runs-on: ubuntu-latest
|
|
20
|
+
environment: pypi
|
|
21
|
+
permissions:
|
|
22
|
+
contents: read
|
|
23
|
+
id-token: write
|
|
24
|
+
steps:
|
|
25
|
+
- uses: actions/checkout@v4
|
|
26
|
+
|
|
27
|
+
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
|
|
28
|
+
with:
|
|
29
|
+
python-version: "3.12"
|
|
30
|
+
|
|
31
|
+
- name: Install build tools
|
|
32
|
+
run: pip install build
|
|
33
|
+
|
|
34
|
+
- name: Build wheel and sdist
|
|
35
|
+
run: python -m build
|
|
36
|
+
|
|
37
|
+
- name: Validate artifacts
|
|
38
|
+
run: |
|
|
39
|
+
pip install twine
|
|
40
|
+
twine check dist/*
|
|
41
|
+
ls -lh dist/
|
|
42
|
+
|
|
43
|
+
- name: Publish to PyPI
|
|
44
|
+
if: github.event_name == 'release' || (github.event_name == 'workflow_dispatch' && github.event.inputs.dry_run == 'false')
|
|
45
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
46
|
+
with:
|
|
47
|
+
skip-existing: true
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__pycache__/
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Contributor Covenant Code of Conduct
|
|
2
|
+
|
|
3
|
+
## Our Pledge
|
|
4
|
+
|
|
5
|
+
We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, caste, color, religion, or sexual identity and orientation.
|
|
6
|
+
|
|
7
|
+
## Our Standards
|
|
8
|
+
|
|
9
|
+
Positive behavior includes: using welcoming and inclusive language, being respectful of differing viewpoints, gracefully accepting constructive criticism, and focusing on what is best for the community.
|
|
10
|
+
|
|
11
|
+
Unacceptable behavior includes: the use of sexualized language or imagery, trolling, insulting or derogatory comments, personal or political attacks, public or private harassment, and publishing others' private information without permission.
|
|
12
|
+
|
|
13
|
+
## Enforcement
|
|
14
|
+
|
|
15
|
+
Instances of abusive or unacceptable behavior may be reported to the project team via [GitHub Security Advisories](https://github.com/agentrust-io/trace-registry/security/advisories/new).
|
|
16
|
+
|
|
17
|
+
Adapted from the [Contributor Covenant](https://www.contributor-covenant.org), version 2.1.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
Thank you for your interest in contributing to the TRACE Registry.
|
|
4
|
+
|
|
5
|
+
## How to Contribute
|
|
6
|
+
|
|
7
|
+
1. Fork the repository
|
|
8
|
+
2. Create a feature branch
|
|
9
|
+
3. Commit your changes using [Conventional Commits](https://www.conventionalcommits.org)
|
|
10
|
+
4. Open a pull request against `main`
|
|
11
|
+
|
|
12
|
+
## Reporting Security Issues
|
|
13
|
+
|
|
14
|
+
Use [GitHub Security Advisories](https://github.com/agentrust-io/trace-registry/security/advisories/new) rather than opening a public issue.
|
|
15
|
+
|
|
16
|
+
## Code of Conduct
|
|
17
|
+
|
|
18
|
+
See [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md).
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
Creative Commons Attribution 4.0 International (CC BY 4.0)
|
|
2
|
+
|
|
3
|
+
Copyright 2026 AgentTrust Contributors
|
|
4
|
+
|
|
5
|
+
This work is licensed under the Creative Commons Attribution 4.0 International License.
|
|
6
|
+
To view a copy of this license, visit https://creativecommons.org/licenses/by/4.0/
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: trace-verify
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Verify TRACE claim inclusion proofs against the public registry
|
|
5
|
+
Project-URL: Homepage, https://github.com/agentrust-io/trace-registry
|
|
6
|
+
Project-URL: Documentation, https://github.com/agentrust-io/trace-registry/blob/main/docs/anchor-format.md
|
|
7
|
+
Project-URL: Bug Tracker, https://github.com/agentrust-io/trace-registry/issues
|
|
8
|
+
Project-URL: Changelog, https://github.com/agentrust-io/trace-registry/blob/main/CHANGELOG.md
|
|
9
|
+
License: CC-BY-4.0
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: ai-governance,audit,inclusion-proof,merkle,trace
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Topic :: Security :: Cryptography
|
|
22
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
23
|
+
Requires-Python: >=3.9
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
|
|
26
|
+
[](LICENSE)
|
|
27
|
+
[](https://github.com/agentrust-io/trace-spec)
|
|
28
|
+
[](https://discord.gg/9JWNpH7E)
|
|
29
|
+
|
|
30
|
+
# TRACE Registry
|
|
31
|
+
|
|
32
|
+
The public accountability layer for TRACE claim anchors. Each entry records the
|
|
33
|
+
Merkle root of a batch of signed TRACE Trust Records, committed to this
|
|
34
|
+
repository as an append-only record. Git's commit history is the
|
|
35
|
+
tamper-evidence layer: any rewrite of a published entry diverges the commit
|
|
36
|
+
hashes that auditors and mirrors have already observed.
|
|
37
|
+
|
|
38
|
+
## Current Registry State
|
|
39
|
+
|
|
40
|
+
The registry currently contains one development entry (registry/2026/06/12.ndjson).
|
|
41
|
+
This is a software-only example anchor with advisory enforcement and a zeroed
|
|
42
|
+
measurement, committed as a launch-day example. It does not represent a production
|
|
43
|
+
Trust Record. The first production entries will be added after Confidential Computing
|
|
44
|
+
Summit launch on June 23, 2026.
|
|
45
|
+
|
|
46
|
+
The anchor construction (canonical claim bytes, leaf hashing, RFC 6962 Merkle
|
|
47
|
+
tree, inclusion proofs) is specified in
|
|
48
|
+
[docs/anchor-format.md](docs/anchor-format.md). A third party can implement a
|
|
49
|
+
verifier from that document alone; the reference tools in [tools/](tools/) are
|
|
50
|
+
one implementation.
|
|
51
|
+
|
|
52
|
+
> **Status.** The format, reference tooling, schema validation, and a first
|
|
53
|
+
> real entry ([registry/2026/06/12.ndjson](registry/2026/06/12.ndjson)) are
|
|
54
|
+
> live. Anchoring is currently manual and low volume; a continuous anchoring
|
|
55
|
+
> cadence and a packaged `trace-verify` CLI on PyPI are planned but not yet
|
|
56
|
+
> operational.
|
|
57
|
+
|
|
58
|
+
## Why this exists
|
|
59
|
+
|
|
60
|
+
Anyone holding a TRACE trust record and its inclusion proof can verify that the
|
|
61
|
+
record was anchored in this registry without trusting the operator who issued
|
|
62
|
+
it, using only this public git history and the verifier below. No single
|
|
63
|
+
operator controls the audit trail.
|
|
64
|
+
|
|
65
|
+
## Registry Format
|
|
66
|
+
|
|
67
|
+
Each daily file in `registry/YYYY/MM/` is newline-delimited JSON, one anchor
|
|
68
|
+
entry per line, validated by CI against
|
|
69
|
+
[schema/registry-entry.schema.json](schema/registry-entry.schema.json):
|
|
70
|
+
|
|
71
|
+
```json
|
|
72
|
+
{"ts": "2026-06-12T18:09:41Z", "merkle_root": "sha256:9279...bada", "leaf_count": 1, "producer": "cmcp-gateway/0.1.0", "batch_id": "2026-06-12-001"}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Entries are append-only. See [docs/anchor-format.md](docs/anchor-format.md)
|
|
76
|
+
for field semantics.
|
|
77
|
+
|
|
78
|
+
## Verifying a claim
|
|
79
|
+
|
|
80
|
+
You need three things: your signed claim (Trust Record), the inclusion proof
|
|
81
|
+
your producer gave you, and the registry entry for the batch. Then:
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
git clone https://github.com/agentrust-io/trace-registry.git
|
|
85
|
+
cd trace-registry
|
|
86
|
+
python tools/verify_inclusion.py \
|
|
87
|
+
--claim samples/example-trust-record.json \
|
|
88
|
+
--proof samples/inclusion-proof.json \
|
|
89
|
+
--entry registry/2026/06/12.ndjson
|
|
90
|
+
# OK: claim is included in batch '2026-06-12-001' (root sha256:9279..., ts 2026-06-12T18:09:41Z)
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Exit code 0 means the claim is proven included; 1 means it is not. The
|
|
94
|
+
verifier is a single standard-library Python file, so you can audit it (or
|
|
95
|
+
reimplement it from the spec) rather than trust it. The `samples/` files above
|
|
96
|
+
are a real anchored example you can use to exercise the tooling.
|
|
97
|
+
|
|
98
|
+
Inclusion verification proves the signed claim bytes were anchored at the
|
|
99
|
+
entry's timestamp. Validating the claim's signature against the producer key
|
|
100
|
+
is a separate TRACE step.
|
|
101
|
+
|
|
102
|
+
## Anchoring claims
|
|
103
|
+
|
|
104
|
+
Producers batch signed claims and anchor them with:
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
python tools/anchor.py claim1.json claim2.json \
|
|
108
|
+
--producer my-gateway/1.0 --proof-dir proofs/ \
|
|
109
|
+
>> registry/2026/06/12.ndjson
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
This emits the registry entry line and writes one inclusion proof per claim to
|
|
113
|
+
hand back to claim holders.
|
|
114
|
+
|
|
115
|
+
## Canonical Registry
|
|
116
|
+
|
|
117
|
+
This repository exists for independence: TRACE claim anchors can be checked
|
|
118
|
+
without trusting any single operator's infrastructure, and the git history is
|
|
119
|
+
auditable by anyone.
|
|
120
|
+
|
|
121
|
+
## Community
|
|
122
|
+
|
|
123
|
+
Questions, feedback, integration help: [Discord](https://discord.gg/9JWNpH7E).
|
|
124
|
+
|
|
125
|
+
## License
|
|
126
|
+
|
|
127
|
+
Creative Commons Attribution 4.0 International (CC BY 4.0). See [LICENSE](LICENSE).
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
[](LICENSE)
|
|
2
|
+
[](https://github.com/agentrust-io/trace-spec)
|
|
3
|
+
[](https://discord.gg/9JWNpH7E)
|
|
4
|
+
|
|
5
|
+
# TRACE Registry
|
|
6
|
+
|
|
7
|
+
The public accountability layer for TRACE claim anchors. Each entry records the
|
|
8
|
+
Merkle root of a batch of signed TRACE Trust Records, committed to this
|
|
9
|
+
repository as an append-only record. Git's commit history is the
|
|
10
|
+
tamper-evidence layer: any rewrite of a published entry diverges the commit
|
|
11
|
+
hashes that auditors and mirrors have already observed.
|
|
12
|
+
|
|
13
|
+
## Current Registry State
|
|
14
|
+
|
|
15
|
+
The registry currently contains one development entry (registry/2026/06/12.ndjson).
|
|
16
|
+
This is a software-only example anchor with advisory enforcement and a zeroed
|
|
17
|
+
measurement, committed as a launch-day example. It does not represent a production
|
|
18
|
+
Trust Record. The first production entries will be added after Confidential Computing
|
|
19
|
+
Summit launch on June 23, 2026.
|
|
20
|
+
|
|
21
|
+
The anchor construction (canonical claim bytes, leaf hashing, RFC 6962 Merkle
|
|
22
|
+
tree, inclusion proofs) is specified in
|
|
23
|
+
[docs/anchor-format.md](docs/anchor-format.md). A third party can implement a
|
|
24
|
+
verifier from that document alone; the reference tools in [tools/](tools/) are
|
|
25
|
+
one implementation.
|
|
26
|
+
|
|
27
|
+
> **Status.** The format, reference tooling, schema validation, and a first
|
|
28
|
+
> real entry ([registry/2026/06/12.ndjson](registry/2026/06/12.ndjson)) are
|
|
29
|
+
> live. Anchoring is currently manual and low volume; a continuous anchoring
|
|
30
|
+
> cadence and a packaged `trace-verify` CLI on PyPI are planned but not yet
|
|
31
|
+
> operational.
|
|
32
|
+
|
|
33
|
+
## Why this exists
|
|
34
|
+
|
|
35
|
+
Anyone holding a TRACE trust record and its inclusion proof can verify that the
|
|
36
|
+
record was anchored in this registry without trusting the operator who issued
|
|
37
|
+
it, using only this public git history and the verifier below. No single
|
|
38
|
+
operator controls the audit trail.
|
|
39
|
+
|
|
40
|
+
## Registry Format
|
|
41
|
+
|
|
42
|
+
Each daily file in `registry/YYYY/MM/` is newline-delimited JSON, one anchor
|
|
43
|
+
entry per line, validated by CI against
|
|
44
|
+
[schema/registry-entry.schema.json](schema/registry-entry.schema.json):
|
|
45
|
+
|
|
46
|
+
```json
|
|
47
|
+
{"ts": "2026-06-12T18:09:41Z", "merkle_root": "sha256:9279...bada", "leaf_count": 1, "producer": "cmcp-gateway/0.1.0", "batch_id": "2026-06-12-001"}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Entries are append-only. See [docs/anchor-format.md](docs/anchor-format.md)
|
|
51
|
+
for field semantics.
|
|
52
|
+
|
|
53
|
+
## Verifying a claim
|
|
54
|
+
|
|
55
|
+
You need three things: your signed claim (Trust Record), the inclusion proof
|
|
56
|
+
your producer gave you, and the registry entry for the batch. Then:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
git clone https://github.com/agentrust-io/trace-registry.git
|
|
60
|
+
cd trace-registry
|
|
61
|
+
python tools/verify_inclusion.py \
|
|
62
|
+
--claim samples/example-trust-record.json \
|
|
63
|
+
--proof samples/inclusion-proof.json \
|
|
64
|
+
--entry registry/2026/06/12.ndjson
|
|
65
|
+
# OK: claim is included in batch '2026-06-12-001' (root sha256:9279..., ts 2026-06-12T18:09:41Z)
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Exit code 0 means the claim is proven included; 1 means it is not. The
|
|
69
|
+
verifier is a single standard-library Python file, so you can audit it (or
|
|
70
|
+
reimplement it from the spec) rather than trust it. The `samples/` files above
|
|
71
|
+
are a real anchored example you can use to exercise the tooling.
|
|
72
|
+
|
|
73
|
+
Inclusion verification proves the signed claim bytes were anchored at the
|
|
74
|
+
entry's timestamp. Validating the claim's signature against the producer key
|
|
75
|
+
is a separate TRACE step.
|
|
76
|
+
|
|
77
|
+
## Anchoring claims
|
|
78
|
+
|
|
79
|
+
Producers batch signed claims and anchor them with:
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
python tools/anchor.py claim1.json claim2.json \
|
|
83
|
+
--producer my-gateway/1.0 --proof-dir proofs/ \
|
|
84
|
+
>> registry/2026/06/12.ndjson
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
This emits the registry entry line and writes one inclusion proof per claim to
|
|
88
|
+
hand back to claim holders.
|
|
89
|
+
|
|
90
|
+
## Canonical Registry
|
|
91
|
+
|
|
92
|
+
This repository exists for independence: TRACE claim anchors can be checked
|
|
93
|
+
without trusting any single operator's infrastructure, and the git history is
|
|
94
|
+
auditable by anyone.
|
|
95
|
+
|
|
96
|
+
## Community
|
|
97
|
+
|
|
98
|
+
Questions, feedback, integration help: [Discord](https://discord.gg/9JWNpH7E).
|
|
99
|
+
|
|
100
|
+
## License
|
|
101
|
+
|
|
102
|
+
Creative Commons Attribution 4.0 International (CC BY 4.0). See [LICENSE](LICENSE).
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# Security Policy
|
|
2
|
+
|
|
3
|
+
## Reporting a Vulnerability
|
|
4
|
+
|
|
5
|
+
Please do not report security vulnerabilities through public GitHub issues.
|
|
6
|
+
|
|
7
|
+
Use [GitHub Security Advisories](https://github.com/agentrust-io/trace-registry/security/advisories/new) to report vulnerabilities privately. We respond within 72 hours.
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
# TRACE Registry Anchor Format v1
|
|
2
|
+
|
|
3
|
+
This document is the normative specification for how TRACE Trust Records (signed
|
|
4
|
+
claims) are anchored into this registry and how a third party verifies, without
|
|
5
|
+
trusting the registry operator, that a given claim was included in an anchor.
|
|
6
|
+
|
|
7
|
+
The construction follows [RFC 6962](https://www.rfc-editor.org/rfc/rfc6962)
|
|
8
|
+
(Certificate Transparency) Merkle trees. A conforming verifier can be written
|
|
9
|
+
from this document alone; the reference tools in [`tools/`](../tools/) are one
|
|
10
|
+
implementation, not the definition.
|
|
11
|
+
|
|
12
|
+
## 1. Canonical claim bytes
|
|
13
|
+
|
|
14
|
+
The unit of anchoring is the COMPLETE signed claim object, signature included.
|
|
15
|
+
Anchoring binds the signed artifact, not a pre-signature payload: if either the
|
|
16
|
+
claim body or its signature changes, the anchor no longer matches.
|
|
17
|
+
|
|
18
|
+
`canonical_claim_bytes` is the canonical JSON serialization of the claim object:
|
|
19
|
+
|
|
20
|
+
- Object keys sorted lexicographically (by Unicode code point), recursively.
|
|
21
|
+
- Separators `","` and `":"` with no whitespace.
|
|
22
|
+
- Non-ASCII characters escaped (`ensure_ascii`); output encoded as ASCII bytes.
|
|
23
|
+
- No trailing newline.
|
|
24
|
+
|
|
25
|
+
In Python this is exactly:
|
|
26
|
+
|
|
27
|
+
```python
|
|
28
|
+
json.dumps(claim, sort_keys=True, separators=(",", ":"), ensure_ascii=True).encode("ascii")
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Claims MUST be JSON objects. TRACE Trust Records contain only strings,
|
|
32
|
+
integers, booleans, nulls, arrays, and objects; claims containing non-integer
|
|
33
|
+
numbers are outside this profile because cross-language float serialization is
|
|
34
|
+
not canonical.
|
|
35
|
+
|
|
36
|
+
## 2. Leaf hash
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
leaf = SHA-256(0x00 || canonical_claim_bytes)
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
The `0x00` domain-separation prefix is the RFC 6962 leaf prefix. It prevents an
|
|
43
|
+
interior node from being presented as a leaf (second-preimage attack).
|
|
44
|
+
|
|
45
|
+
## 3. Merkle tree
|
|
46
|
+
|
|
47
|
+
The tree over the ordered list of leaves is the RFC 6962 Merkle Tree Hash:
|
|
48
|
+
|
|
49
|
+
- A tree of one leaf has root equal to that leaf hash.
|
|
50
|
+
- Interior nodes: `node = SHA-256(0x01 || left || right)` where `left` and
|
|
51
|
+
`right` are the 32-byte child hashes and `0x01` is the interior prefix.
|
|
52
|
+
- Construction proceeds level by level over the ordered leaves: adjacent pairs
|
|
53
|
+
are hashed together; when a level has an odd number of nodes, the final node
|
|
54
|
+
is promoted unchanged to the next level (it is NOT duplicated). This yields
|
|
55
|
+
the same tree as the RFC 6962 recursive split at the largest power of two.
|
|
56
|
+
- The empty batch (zero leaves) is invalid and MUST be rejected. This registry
|
|
57
|
+
never anchors an empty tree.
|
|
58
|
+
|
|
59
|
+
The root is published hex-encoded, lowercase, prefixed with the algorithm:
|
|
60
|
+
|
|
61
|
+
```
|
|
62
|
+
sha256:<64 lowercase hex characters>
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## 4. Registry entry format
|
|
66
|
+
|
|
67
|
+
Anchors are recorded in newline-delimited JSON files at
|
|
68
|
+
`registry/YYYY/MM/DD.ndjson` (UTC date of anchoring). Each line is one JSON
|
|
69
|
+
object with exactly these fields:
|
|
70
|
+
|
|
71
|
+
| field | type | meaning |
|
|
72
|
+
|---------------|---------|---------------------------------------------------------------|
|
|
73
|
+
| `ts` | string | Anchoring time, ISO-8601 UTC with `Z` suffix |
|
|
74
|
+
| `merkle_root` | string | `sha256:<hex>` root of the batch tree (section 3) |
|
|
75
|
+
| `leaf_count` | integer | Number of leaves in the batch, >= 1 |
|
|
76
|
+
| `producer` | string | Identifier of the party that produced and submitted the batch |
|
|
77
|
+
| `batch_id` | string | Producer-scoped unique identifier for the batch |
|
|
78
|
+
|
|
79
|
+
Example:
|
|
80
|
+
|
|
81
|
+
```json
|
|
82
|
+
{"ts": "2026-06-12T18:30:00Z", "merkle_root": "sha256:9c4f...", "leaf_count": 1, "producer": "cmcp-gateway/0.1.0", "batch_id": "2026-06-12-001"}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Entries are append-only: lines are only ever added to the end of a day file,
|
|
86
|
+
and existing files are never rewritten. Git history is the tamper-evidence
|
|
87
|
+
layer; any rewrite of a published entry diverges the commit hashes that
|
|
88
|
+
auditors and mirrors have already observed. The machine-readable schema is
|
|
89
|
+
[`schema/registry-entry.schema.json`](../schema/registry-entry.schema.json)
|
|
90
|
+
and is enforced by CI on every line of every `registry/**/*.ndjson` file.
|
|
91
|
+
|
|
92
|
+
## 5. Inclusion proof
|
|
93
|
+
|
|
94
|
+
A producer gives each claim holder an inclusion proof:
|
|
95
|
+
|
|
96
|
+
```json
|
|
97
|
+
{"leaf_index": 0, "audit_path": ["sha256:<hex>", "sha256:<hex>"]}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
- `leaf_index`: 0-based position of the claim's leaf in the batch.
|
|
101
|
+
- `audit_path`: the RFC 6962 audit path, ordered leaf-to-root: at each tree
|
|
102
|
+
level the sibling hash needed to recompute the parent. A level where the
|
|
103
|
+
node was promoted (no sibling) contributes no path element. A single-leaf
|
|
104
|
+
batch has an empty `audit_path`.
|
|
105
|
+
|
|
106
|
+
### Verification algorithm
|
|
107
|
+
|
|
108
|
+
Inputs: the claim object, the proof (`leaf_index`, `audit_path`), and the
|
|
109
|
+
registry entry (`merkle_root`, `leaf_count`). All `sha256:<hex>` strings are
|
|
110
|
+
decoded to 32 raw bytes. The algorithm is the RFC 9162 inclusion-proof check:
|
|
111
|
+
|
|
112
|
+
```
|
|
113
|
+
1. If leaf_index >= leaf_count, reject.
|
|
114
|
+
2. r = SHA-256(0x00 || canonical_claim_bytes) # section 2
|
|
115
|
+
fn = leaf_index
|
|
116
|
+
sn = leaf_count - 1
|
|
117
|
+
3. For each p in audit_path, in order:
|
|
118
|
+
a. If sn == 0, reject (path too long).
|
|
119
|
+
b. If fn is odd, or fn == sn:
|
|
120
|
+
r = SHA-256(0x01 || p || r)
|
|
121
|
+
If fn is even: # fn == sn, right-edge promotion
|
|
122
|
+
until fn is odd or fn == 0: fn = fn >> 1; sn = sn >> 1
|
|
123
|
+
else:
|
|
124
|
+
r = SHA-256(0x01 || r || p)
|
|
125
|
+
c. fn = fn >> 1; sn = sn >> 1
|
|
126
|
+
4. Accept iff sn == 0 and r equals the entry's merkle_root.
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Any failure (wrong root, leftover `sn`, malformed hash, out-of-range index)
|
|
130
|
+
means the claim is NOT proven included in that entry.
|
|
131
|
+
|
|
132
|
+
### What verification proves
|
|
133
|
+
|
|
134
|
+
A successful verification proves the exact signed claim bytes were part of the
|
|
135
|
+
batch whose root is committed in the public, append-only git history at the
|
|
136
|
+
entry's timestamp. It does not validate the claim's signature or semantics;
|
|
137
|
+
signature verification against the producer key is a separate TRACE step.
|
|
138
|
+
|
|
139
|
+
## 6. Reference tooling
|
|
140
|
+
|
|
141
|
+
- [`tools/anchor.py`](../tools/anchor.py): builds the tree over one or more
|
|
142
|
+
claim files, emits the registry entry line and one inclusion proof per claim.
|
|
143
|
+
- [`tools/verify_inclusion.py`](../tools/verify_inclusion.py): standalone
|
|
144
|
+
verifier (claim + proof + entry, exit 0/1). Deliberately self-contained so it
|
|
145
|
+
can be copied and audited in isolation.
|
|
146
|
+
|
|
147
|
+
Both are Python 3 standard library only (`hashlib`, `json`).
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "trace-verify"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Verify TRACE claim inclusion proofs against the public registry"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = { text = "CC-BY-4.0" }
|
|
11
|
+
requires-python = ">=3.9"
|
|
12
|
+
dependencies = []
|
|
13
|
+
keywords = ["trace", "merkle", "inclusion-proof", "audit", "ai-governance"]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Development Status :: 4 - Beta",
|
|
16
|
+
"Intended Audience :: Developers",
|
|
17
|
+
"License :: OSI Approved",
|
|
18
|
+
"Programming Language :: Python :: 3",
|
|
19
|
+
"Programming Language :: Python :: 3.9",
|
|
20
|
+
"Programming Language :: Python :: 3.10",
|
|
21
|
+
"Programming Language :: Python :: 3.11",
|
|
22
|
+
"Programming Language :: Python :: 3.12",
|
|
23
|
+
"Programming Language :: Python :: 3.13",
|
|
24
|
+
"Topic :: Security :: Cryptography",
|
|
25
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
[project.scripts]
|
|
29
|
+
trace-verify = "trace_verify.__main__:main"
|
|
30
|
+
|
|
31
|
+
[project.urls]
|
|
32
|
+
Homepage = "https://github.com/agentrust-io/trace-registry"
|
|
33
|
+
Documentation = "https://github.com/agentrust-io/trace-registry/blob/main/docs/anchor-format.md"
|
|
34
|
+
"Bug Tracker" = "https://github.com/agentrust-io/trace-registry/issues"
|
|
35
|
+
Changelog = "https://github.com/agentrust-io/trace-registry/blob/main/CHANGELOG.md"
|
|
36
|
+
|
|
37
|
+
[tool.hatch.build.targets.wheel]
|
|
38
|
+
packages = ["src/trace_verify"]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"ts": "2026-06-12T18:09:41Z", "merkle_root": "sha256:9279111218532d83279a0ce4c8d114168e2558b8f85054878c97ee5f428abada", "leaf_count": 1, "producer": "cmcp-gateway/0.1.0", "batch_id": "2026-06-12-001"}
|