attest-protocol 0.2.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.
Files changed (45) hide show
  1. attest_protocol-0.2.0/.github/workflows/ci.yml +33 -0
  2. attest_protocol-0.2.0/.github/workflows/publish.yml +26 -0
  3. attest_protocol-0.2.0/.gitignore +9 -0
  4. attest_protocol-0.2.0/CLAUDE.md +31 -0
  5. attest_protocol-0.2.0/CONTRIBUTING.md +62 -0
  6. attest_protocol-0.2.0/LICENSE +21 -0
  7. attest_protocol-0.2.0/PKG-INFO +257 -0
  8. attest_protocol-0.2.0/README.md +228 -0
  9. attest_protocol-0.2.0/SECURITY.md +41 -0
  10. attest_protocol-0.2.0/pyproject.toml +67 -0
  11. attest_protocol-0.2.0/src/attest_protocol/__init__.py +139 -0
  12. attest_protocol-0.2.0/src/attest_protocol/_version.py +1 -0
  13. attest_protocol-0.2.0/src/attest_protocol/py.typed +0 -0
  14. attest_protocol-0.2.0/src/attest_protocol/receipt/__init__.py +70 -0
  15. attest_protocol-0.2.0/src/attest_protocol/receipt/chain.py +95 -0
  16. attest_protocol-0.2.0/src/attest_protocol/receipt/create.py +101 -0
  17. attest_protocol-0.2.0/src/attest_protocol/receipt/hash.py +113 -0
  18. attest_protocol-0.2.0/src/attest_protocol/receipt/signing.py +146 -0
  19. attest_protocol-0.2.0/src/attest_protocol/receipt/types.py +157 -0
  20. attest_protocol-0.2.0/src/attest_protocol/store/__init__.py +17 -0
  21. attest_protocol-0.2.0/src/attest_protocol/store/store.py +214 -0
  22. attest_protocol-0.2.0/src/attest_protocol/store/verify.py +21 -0
  23. attest_protocol-0.2.0/src/attest_protocol/taxonomy/__init__.py +29 -0
  24. attest_protocol-0.2.0/src/attest_protocol/taxonomy/actions.py +105 -0
  25. attest_protocol-0.2.0/src/attest_protocol/taxonomy/classify.py +35 -0
  26. attest_protocol-0.2.0/src/attest_protocol/taxonomy/config.py +54 -0
  27. attest_protocol-0.2.0/src/attest_protocol/taxonomy/types.py +26 -0
  28. attest_protocol-0.2.0/tests/__init__.py +0 -0
  29. attest_protocol-0.2.0/tests/conftest.py +100 -0
  30. attest_protocol-0.2.0/tests/fixtures/ts_vectors.json +139 -0
  31. attest_protocol-0.2.0/tests/receipt/__init__.py +0 -0
  32. attest_protocol-0.2.0/tests/receipt/test_chain.py +107 -0
  33. attest_protocol-0.2.0/tests/receipt/test_create.py +109 -0
  34. attest_protocol-0.2.0/tests/receipt/test_hash.py +109 -0
  35. attest_protocol-0.2.0/tests/receipt/test_signing.py +81 -0
  36. attest_protocol-0.2.0/tests/receipt/test_types.py +61 -0
  37. attest_protocol-0.2.0/tests/store/__init__.py +0 -0
  38. attest_protocol-0.2.0/tests/store/test_store.py +223 -0
  39. attest_protocol-0.2.0/tests/store/test_verify.py +57 -0
  40. attest_protocol-0.2.0/tests/taxonomy/__init__.py +0 -0
  41. attest_protocol-0.2.0/tests/taxonomy/test_actions.py +52 -0
  42. attest_protocol-0.2.0/tests/taxonomy/test_classify.py +40 -0
  43. attest_protocol-0.2.0/tests/taxonomy/test_config.py +80 -0
  44. attest_protocol-0.2.0/tests/test_cross_language.py +84 -0
  45. attest_protocol-0.2.0/uv.lock +417 -0
@@ -0,0 +1,33 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ permissions:
10
+ contents: read
11
+
12
+ jobs:
13
+ check:
14
+ runs-on: ubuntu-latest
15
+
16
+ strategy:
17
+ matrix:
18
+ python-version: ["3.11", "3.12", "3.13"]
19
+
20
+ steps:
21
+ - uses: actions/checkout@v4
22
+
23
+ - uses: astral-sh/setup-uv@v6
24
+ with:
25
+ python-version: ${{ matrix.python-version }}
26
+
27
+ - run: uv sync --all-extras
28
+
29
+ - run: uv run ruff check .
30
+
31
+ - run: uv run ruff format --check .
32
+
33
+ - run: uv run pytest -v
@@ -0,0 +1,26 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+
7
+ permissions:
8
+ contents: read
9
+ id-token: write
10
+
11
+ jobs:
12
+ publish:
13
+ runs-on: ubuntu-latest
14
+
15
+ steps:
16
+ - uses: actions/checkout@v4
17
+
18
+ - uses: astral-sh/setup-uv@v6
19
+ with:
20
+ python-version: "3.13"
21
+
22
+ - run: uv build
23
+
24
+ - uses: pypa/gh-action-pypi-publish@release/v1
25
+ with:
26
+ packages-dir: dist/
@@ -0,0 +1,9 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *.egg-info/
4
+ dist/
5
+ build/
6
+ .venv/
7
+ .pytest_cache/
8
+ .ruff_cache/
9
+ *.egg
@@ -0,0 +1,31 @@
1
+ # CLAUDE.md
2
+
3
+ ## Project
4
+
5
+ Python SDK for the [Attest Protocol](https://github.com/attest-protocol/spec) — cryptographically signed, hash-chained Action Receipts for AI agent audit trails.
6
+
7
+ ## Commands
8
+
9
+ ```sh
10
+ uv sync --all-extras # install deps
11
+ uv run pytest -v # run tests
12
+ uv run ruff check . # lint
13
+ uv run ruff format . # format
14
+ uv run pyright src # type check (168 pre-existing Pydantic errors are expected)
15
+ ```
16
+
17
+ ## Architecture
18
+
19
+ - `src/attest_protocol/receipt/` — Core: types, create, sign, hash, chain verification
20
+ - `src/attest_protocol/store/` — SQLite persistence: ReceiptStore, query, verify_stored_chain
21
+ - `src/attest_protocol/taxonomy/` — Action type classification, config loading
22
+ - `tests/` — Mirrors src structure. Uses conftest.py fixtures for receipt creation.
23
+
24
+ ## Conventions
25
+
26
+ - `from __future__ import annotations` in every file
27
+ - Pydantic v2 for receipt models, frozen dataclasses for simple types
28
+ - `TYPE_CHECKING` guards for type-only imports
29
+ - Ruff for lint+format (line-length 88), pyright strict mode
30
+ - camelCase aliases exported at package level for TypeScript SDK users
31
+ - Schema must match TypeScript SDK exactly (cross-language tests verify this)
@@ -0,0 +1,62 @@
1
+ # Contributing to attest-protocol
2
+
3
+ Thank you for your interest in contributing to the Action Receipts Python SDK.
4
+
5
+ ## How to contribute
6
+
7
+ ### Reporting issues
8
+
9
+ Open a [GitHub issue](https://github.com/attest-protocol/attest-py/issues) for:
10
+
11
+ - Bugs in receipt creation, signing, hashing, or verification
12
+ - Store (SQLite) issues — data corruption, query problems
13
+ - Taxonomy classification errors
14
+ - Missing or incorrect type annotations
15
+ - Documentation gaps
16
+
17
+ ### Proposing changes
18
+
19
+ 1. Open an issue describing the change and its motivation.
20
+ 2. Fork the repo and create a branch from `main`.
21
+ 3. Make your changes following the conventions below.
22
+ 4. Run `uv run ruff check .` and `uv run pytest -v`.
23
+ 5. Open a pull request referencing the issue.
24
+
25
+ ## Development setup
26
+
27
+ ```sh
28
+ uv sync --all-extras
29
+ uv run pytest -v # run all tests
30
+ uv run ruff check . # lint
31
+ uv run ruff format . # format
32
+ uv run pyright src # type check
33
+ ```
34
+
35
+ ## Code conventions
36
+
37
+ - **Python 3.11+** with `from __future__ import annotations`.
38
+ - **Ruff** for linting and formatting (line length 88).
39
+ - Use `TYPE_CHECKING` guards for type-only imports.
40
+ - **Pydantic v2** for receipt data models, **dataclasses** for simple types.
41
+ - Test files: `tests/` directory mirroring `src/` structure.
42
+ - All changes go through pull requests — never push directly to `main`.
43
+
44
+ ## Testing
45
+
46
+ - Write tests for all new functionality and bug fixes.
47
+ - Tests use [pytest](https://docs.pytest.org/).
48
+ - Cover both happy paths and edge cases (empty inputs, invalid data, error handling).
49
+ - Use the test factories in `tests/conftest.py` for creating receipt fixtures.
50
+ - Cross-language compatibility tests in `tests/test_cross_language.py` verify parity with the TypeScript SDK.
51
+
52
+ ## Spec alignment
53
+
54
+ This SDK implements the [Action Receipt Protocol](https://github.com/attest-protocol/spec). If you find a discrepancy between the SDK behavior and the spec, please open an issue in both repos.
55
+
56
+ ## Code of conduct
57
+
58
+ Be respectful and constructive. We follow the [Contributor Covenant](https://www.contributor-covenant.org/version/2/1/code_of_conduct/).
59
+
60
+ ## License
61
+
62
+ By contributing, you agree that your contributions will be licensed under the [Apache 2.0 License](LICENSE).
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 attest-protocol
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,257 @@
1
+ Metadata-Version: 2.4
2
+ Name: attest-protocol
3
+ Version: 0.2.0
4
+ Summary: Python SDK for the Action Receipts protocol
5
+ Project-URL: Homepage, https://github.com/attest-protocol/attest-py
6
+ Project-URL: Repository, https://github.com/attest-protocol/attest-py
7
+ Project-URL: Issues, https://github.com/attest-protocol/attest-py/issues
8
+ Project-URL: Spec, https://github.com/attest-protocol/spec
9
+ Author: Otto Jongerius
10
+ License-Expression: Apache-2.0
11
+ License-File: LICENSE
12
+ Keywords: agent,ai,audit,receipts,verifiable-credentials
13
+ Classifier: Development Status :: 3 - Alpha
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: License :: OSI Approved :: Apache Software License
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Typing :: Typed
21
+ Requires-Python: >=3.11
22
+ Requires-Dist: cryptography>=41.0
23
+ Requires-Dist: pydantic>=2.0
24
+ Provides-Extra: dev
25
+ Requires-Dist: pyright>=1.1; extra == 'dev'
26
+ Requires-Dist: pytest>=8.0; extra == 'dev'
27
+ Requires-Dist: ruff>=0.9; extra == 'dev'
28
+ Description-Content-Type: text/markdown
29
+
30
+ <div align="center">
31
+
32
+ # attest-protocol
33
+
34
+ ### Python SDK for the Action Receipts protocol
35
+
36
+ [![PyPI](https://img.shields.io/pypi/v/attest-protocol)](https://pypi.org/project/attest-protocol/)
37
+ [![License: Apache 2.0](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](LICENSE)
38
+ [![Python](https://img.shields.io/badge/Python-3.11+-3776AB?logo=python&logoColor=white)](https://www.python.org/)
39
+ [![CI](https://github.com/attest-protocol/attest-py/actions/workflows/ci.yml/badge.svg)](https://github.com/attest-protocol/attest-py/actions/workflows/ci.yml)
40
+
41
+ ---
42
+
43
+ Create, sign, hash-chain, store, and verify cryptographically signed audit trails for AI agent actions.
44
+
45
+ [Spec](https://github.com/attest-protocol/spec) &bull; [TypeScript SDK](https://github.com/attest-protocol/attest-ts) &bull; [Reference Implementation](https://github.com/ojongerius/attest)
46
+
47
+ </div>
48
+
49
+ ---
50
+
51
+ ## Install
52
+
53
+ ```sh
54
+ pip install attest-protocol
55
+ ```
56
+
57
+ ## Quick start
58
+
59
+ ### Create and sign a receipt
60
+
61
+ ```python
62
+ from attest_protocol import (
63
+ create_receipt,
64
+ generate_key_pair,
65
+ hash_receipt,
66
+ sign_receipt,
67
+ CreateReceiptInput,
68
+ Chain,
69
+ Issuer,
70
+ Outcome,
71
+ Principal,
72
+ )
73
+ from attest_protocol.receipt.create import ActionInput
74
+
75
+ # Generate an Ed25519 key pair
76
+ keys = generate_key_pair()
77
+
78
+ # Create an unsigned receipt
79
+ unsigned = create_receipt(CreateReceiptInput(
80
+ issuer=Issuer(id="did:agent:my-agent"),
81
+ principal=Principal(id="did:user:alice"),
82
+ action=ActionInput(
83
+ type="filesystem.file.read",
84
+ risk_level="low",
85
+ ),
86
+ outcome=Outcome(status="success"),
87
+ chain=Chain(
88
+ sequence=1,
89
+ previous_receipt_hash=None,
90
+ chain_id="chain_session-1",
91
+ ),
92
+ ))
93
+
94
+ # Sign and hash
95
+ receipt = sign_receipt(unsigned, keys.private_key, "did:agent:my-agent#key-1")
96
+ receipt_hash = hash_receipt(receipt)
97
+ ```
98
+
99
+ ### Verify a receipt
100
+
101
+ ```python
102
+ from attest_protocol import verify_receipt
103
+
104
+ valid = verify_receipt(receipt, keys.public_key)
105
+ print(f"Signature valid: {valid}") # True
106
+ ```
107
+
108
+ ### Verify a chain
109
+
110
+ ```python
111
+ from attest_protocol import verify_chain
112
+
113
+ # Verify a list of receipts (e.g. [receipt] from the example above)
114
+ result = verify_chain([receipt], keys.public_key)
115
+ print(f"Chain valid: {result.valid}")
116
+ print(f"Receipts verified: {result.length}")
117
+ if not result.valid:
118
+ print(f"Broken at index: {result.broken_at}")
119
+ ```
120
+
121
+ ### Action taxonomy
122
+
123
+ The standardized action taxonomy (action types and risk levels) is defined in the
124
+ [protocol specification](https://github.com/attest-protocol/spec/tree/main/spec/taxonomy).
125
+ Taxonomy classification will be added in a future milestone (M3).
126
+
127
+ ## What is an Action Receipt?
128
+
129
+ A [W3C Verifiable Credential](https://www.w3.org/TR/vc-data-model-2.0/) signed with Ed25519, recording:
130
+
131
+ | Field | What it captures |
132
+ |:---|:---|
133
+ | **Action** | What happened, classified by a [standardized taxonomy](https://github.com/attest-protocol/spec/tree/main/spec/taxonomy) |
134
+ | **Principal** | Who authorized it (human or organization) |
135
+ | **Issuer** | Which agent performed it |
136
+ | **Outcome** | Success/failure, reversibility, undo method |
137
+ | **Chain** | SHA-256 hash link to the previous receipt (tamper-evident) |
138
+ | **Privacy** | Parameters are hashed, never stored in plaintext |
139
+
140
+ ## API reference
141
+
142
+ ### Receipt creation and signing
143
+
144
+ ```python
145
+ from attest_protocol import (
146
+ create_receipt, # Build an unsigned receipt from input fields
147
+ generate_key_pair, # Ed25519 key pair (PEM-encoded)
148
+ sign_receipt, # Sign with Ed25519Signature2020 proof
149
+ verify_receipt, # Verify a receipt's signature
150
+ )
151
+ ```
152
+
153
+ ### Hashing and canonicalization
154
+
155
+ ```python
156
+ from attest_protocol import (
157
+ canonicalize, # RFC 8785 JSON canonicalization
158
+ hash_receipt, # Hash receipt (excluding proof) -> "sha256:<hex>"
159
+ sha256, # Hash arbitrary data -> "sha256:<hex>"
160
+ )
161
+ ```
162
+
163
+ ### Chain verification
164
+
165
+ ```python
166
+ from attest_protocol import (
167
+ verify_chain, # Verify signatures, hash links, and sequence numbering
168
+ )
169
+ ```
170
+
171
+ ### Types (Pydantic v2 models)
172
+
173
+ ```python
174
+ from attest_protocol import (
175
+ ActionReceipt, # Signed receipt with proof
176
+ UnsignedActionReceipt, # Receipt before signing
177
+ Action, ActionTarget, Authorization, Chain,
178
+ CredentialSubject, Intent, Issuer, Operator,
179
+ Outcome, Principal, Proof, StateChange,
180
+ )
181
+ ```
182
+
183
+ ### Subpackage imports
184
+
185
+ ```python
186
+ from attest_protocol.receipt import create_receipt, sign_receipt
187
+ from attest_protocol.receipt.hash import canonicalize
188
+ from attest_protocol.receipt.types import CONTEXT, CREDENTIAL_TYPE
189
+ ```
190
+
191
+ ### TypeScript SDK compatibility
192
+
193
+ camelCase aliases are available for users coming from the TS SDK:
194
+
195
+ ```python
196
+ from attest_protocol import (
197
+ createReceipt, # = create_receipt
198
+ generateKeyPair, # = generate_key_pair
199
+ signReceipt, # = sign_receipt
200
+ verifyReceipt, # = verify_receipt
201
+ hashReceipt, # = hash_receipt
202
+ verifyChain, # = verify_chain
203
+ )
204
+ ```
205
+
206
+ ## Cross-language compatibility
207
+
208
+ This SDK produces **byte-identical** output to [`@attest-protocol/attest-ts`](https://github.com/attest-protocol/attest-ts):
209
+
210
+ - RFC 8785 canonical JSON matches exactly
211
+ - SHA-256 hashes are identical
212
+ - Ed25519 signatures from either SDK verify in the other
213
+
214
+ Cross-language compatibility is verified by test vectors generated from the TypeScript SDK.
215
+
216
+ ## Project structure
217
+
218
+ ```
219
+ src/attest_protocol/
220
+ receipt/
221
+ types.py # Pydantic models for all receipt types
222
+ create.py # Receipt creation with auto-generated IDs
223
+ signing.py # Ed25519 signing and verification
224
+ hash.py # RFC 8785 canonicalization + SHA-256
225
+ chain.py # Chain verification
226
+ ```
227
+
228
+ ## Development
229
+
230
+ ```sh
231
+ uv sync --all-extras
232
+ uv run pytest # run tests
233
+ uv run ruff check . # lint
234
+ uv run ruff format . # format
235
+ uv run pyright # type check
236
+ ```
237
+
238
+ | | |
239
+ |:---|:---|
240
+ | **Language** | Python 3.11+ |
241
+ | **Types** | Pydantic v2, pyright strict mode |
242
+ | **Linting** | ruff |
243
+ | **Testing** | pytest |
244
+ | **Dependencies** | `pydantic>=2.0`, `cryptography>=41.0` |
245
+
246
+ ## Ecosystem
247
+
248
+ | Repository | Description |
249
+ |:---|:---|
250
+ | [attest-protocol/spec](https://github.com/attest-protocol/spec) | Protocol specification, JSON Schemas, canonical taxonomy |
251
+ | [attest-protocol/attest-ts](https://github.com/attest-protocol/attest-ts) | TypeScript SDK ([npm](https://www.npmjs.com/package/@attest-protocol/attest-ts)) |
252
+ | **attest-protocol/attest-py** (this package) | Python SDK |
253
+ | [ojongerius/attest](https://github.com/ojongerius/attest) | MCP proxy + CLI (reference implementation) |
254
+
255
+ ## License
256
+
257
+ Apache 2.0 — see [LICENSE](LICENSE).
@@ -0,0 +1,228 @@
1
+ <div align="center">
2
+
3
+ # attest-protocol
4
+
5
+ ### Python SDK for the Action Receipts protocol
6
+
7
+ [![PyPI](https://img.shields.io/pypi/v/attest-protocol)](https://pypi.org/project/attest-protocol/)
8
+ [![License: Apache 2.0](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](LICENSE)
9
+ [![Python](https://img.shields.io/badge/Python-3.11+-3776AB?logo=python&logoColor=white)](https://www.python.org/)
10
+ [![CI](https://github.com/attest-protocol/attest-py/actions/workflows/ci.yml/badge.svg)](https://github.com/attest-protocol/attest-py/actions/workflows/ci.yml)
11
+
12
+ ---
13
+
14
+ Create, sign, hash-chain, store, and verify cryptographically signed audit trails for AI agent actions.
15
+
16
+ [Spec](https://github.com/attest-protocol/spec) &bull; [TypeScript SDK](https://github.com/attest-protocol/attest-ts) &bull; [Reference Implementation](https://github.com/ojongerius/attest)
17
+
18
+ </div>
19
+
20
+ ---
21
+
22
+ ## Install
23
+
24
+ ```sh
25
+ pip install attest-protocol
26
+ ```
27
+
28
+ ## Quick start
29
+
30
+ ### Create and sign a receipt
31
+
32
+ ```python
33
+ from attest_protocol import (
34
+ create_receipt,
35
+ generate_key_pair,
36
+ hash_receipt,
37
+ sign_receipt,
38
+ CreateReceiptInput,
39
+ Chain,
40
+ Issuer,
41
+ Outcome,
42
+ Principal,
43
+ )
44
+ from attest_protocol.receipt.create import ActionInput
45
+
46
+ # Generate an Ed25519 key pair
47
+ keys = generate_key_pair()
48
+
49
+ # Create an unsigned receipt
50
+ unsigned = create_receipt(CreateReceiptInput(
51
+ issuer=Issuer(id="did:agent:my-agent"),
52
+ principal=Principal(id="did:user:alice"),
53
+ action=ActionInput(
54
+ type="filesystem.file.read",
55
+ risk_level="low",
56
+ ),
57
+ outcome=Outcome(status="success"),
58
+ chain=Chain(
59
+ sequence=1,
60
+ previous_receipt_hash=None,
61
+ chain_id="chain_session-1",
62
+ ),
63
+ ))
64
+
65
+ # Sign and hash
66
+ receipt = sign_receipt(unsigned, keys.private_key, "did:agent:my-agent#key-1")
67
+ receipt_hash = hash_receipt(receipt)
68
+ ```
69
+
70
+ ### Verify a receipt
71
+
72
+ ```python
73
+ from attest_protocol import verify_receipt
74
+
75
+ valid = verify_receipt(receipt, keys.public_key)
76
+ print(f"Signature valid: {valid}") # True
77
+ ```
78
+
79
+ ### Verify a chain
80
+
81
+ ```python
82
+ from attest_protocol import verify_chain
83
+
84
+ # Verify a list of receipts (e.g. [receipt] from the example above)
85
+ result = verify_chain([receipt], keys.public_key)
86
+ print(f"Chain valid: {result.valid}")
87
+ print(f"Receipts verified: {result.length}")
88
+ if not result.valid:
89
+ print(f"Broken at index: {result.broken_at}")
90
+ ```
91
+
92
+ ### Action taxonomy
93
+
94
+ The standardized action taxonomy (action types and risk levels) is defined in the
95
+ [protocol specification](https://github.com/attest-protocol/spec/tree/main/spec/taxonomy).
96
+ Taxonomy classification will be added in a future milestone (M3).
97
+
98
+ ## What is an Action Receipt?
99
+
100
+ A [W3C Verifiable Credential](https://www.w3.org/TR/vc-data-model-2.0/) signed with Ed25519, recording:
101
+
102
+ | Field | What it captures |
103
+ |:---|:---|
104
+ | **Action** | What happened, classified by a [standardized taxonomy](https://github.com/attest-protocol/spec/tree/main/spec/taxonomy) |
105
+ | **Principal** | Who authorized it (human or organization) |
106
+ | **Issuer** | Which agent performed it |
107
+ | **Outcome** | Success/failure, reversibility, undo method |
108
+ | **Chain** | SHA-256 hash link to the previous receipt (tamper-evident) |
109
+ | **Privacy** | Parameters are hashed, never stored in plaintext |
110
+
111
+ ## API reference
112
+
113
+ ### Receipt creation and signing
114
+
115
+ ```python
116
+ from attest_protocol import (
117
+ create_receipt, # Build an unsigned receipt from input fields
118
+ generate_key_pair, # Ed25519 key pair (PEM-encoded)
119
+ sign_receipt, # Sign with Ed25519Signature2020 proof
120
+ verify_receipt, # Verify a receipt's signature
121
+ )
122
+ ```
123
+
124
+ ### Hashing and canonicalization
125
+
126
+ ```python
127
+ from attest_protocol import (
128
+ canonicalize, # RFC 8785 JSON canonicalization
129
+ hash_receipt, # Hash receipt (excluding proof) -> "sha256:<hex>"
130
+ sha256, # Hash arbitrary data -> "sha256:<hex>"
131
+ )
132
+ ```
133
+
134
+ ### Chain verification
135
+
136
+ ```python
137
+ from attest_protocol import (
138
+ verify_chain, # Verify signatures, hash links, and sequence numbering
139
+ )
140
+ ```
141
+
142
+ ### Types (Pydantic v2 models)
143
+
144
+ ```python
145
+ from attest_protocol import (
146
+ ActionReceipt, # Signed receipt with proof
147
+ UnsignedActionReceipt, # Receipt before signing
148
+ Action, ActionTarget, Authorization, Chain,
149
+ CredentialSubject, Intent, Issuer, Operator,
150
+ Outcome, Principal, Proof, StateChange,
151
+ )
152
+ ```
153
+
154
+ ### Subpackage imports
155
+
156
+ ```python
157
+ from attest_protocol.receipt import create_receipt, sign_receipt
158
+ from attest_protocol.receipt.hash import canonicalize
159
+ from attest_protocol.receipt.types import CONTEXT, CREDENTIAL_TYPE
160
+ ```
161
+
162
+ ### TypeScript SDK compatibility
163
+
164
+ camelCase aliases are available for users coming from the TS SDK:
165
+
166
+ ```python
167
+ from attest_protocol import (
168
+ createReceipt, # = create_receipt
169
+ generateKeyPair, # = generate_key_pair
170
+ signReceipt, # = sign_receipt
171
+ verifyReceipt, # = verify_receipt
172
+ hashReceipt, # = hash_receipt
173
+ verifyChain, # = verify_chain
174
+ )
175
+ ```
176
+
177
+ ## Cross-language compatibility
178
+
179
+ This SDK produces **byte-identical** output to [`@attest-protocol/attest-ts`](https://github.com/attest-protocol/attest-ts):
180
+
181
+ - RFC 8785 canonical JSON matches exactly
182
+ - SHA-256 hashes are identical
183
+ - Ed25519 signatures from either SDK verify in the other
184
+
185
+ Cross-language compatibility is verified by test vectors generated from the TypeScript SDK.
186
+
187
+ ## Project structure
188
+
189
+ ```
190
+ src/attest_protocol/
191
+ receipt/
192
+ types.py # Pydantic models for all receipt types
193
+ create.py # Receipt creation with auto-generated IDs
194
+ signing.py # Ed25519 signing and verification
195
+ hash.py # RFC 8785 canonicalization + SHA-256
196
+ chain.py # Chain verification
197
+ ```
198
+
199
+ ## Development
200
+
201
+ ```sh
202
+ uv sync --all-extras
203
+ uv run pytest # run tests
204
+ uv run ruff check . # lint
205
+ uv run ruff format . # format
206
+ uv run pyright # type check
207
+ ```
208
+
209
+ | | |
210
+ |:---|:---|
211
+ | **Language** | Python 3.11+ |
212
+ | **Types** | Pydantic v2, pyright strict mode |
213
+ | **Linting** | ruff |
214
+ | **Testing** | pytest |
215
+ | **Dependencies** | `pydantic>=2.0`, `cryptography>=41.0` |
216
+
217
+ ## Ecosystem
218
+
219
+ | Repository | Description |
220
+ |:---|:---|
221
+ | [attest-protocol/spec](https://github.com/attest-protocol/spec) | Protocol specification, JSON Schemas, canonical taxonomy |
222
+ | [attest-protocol/attest-ts](https://github.com/attest-protocol/attest-ts) | TypeScript SDK ([npm](https://www.npmjs.com/package/@attest-protocol/attest-ts)) |
223
+ | **attest-protocol/attest-py** (this package) | Python SDK |
224
+ | [ojongerius/attest](https://github.com/ojongerius/attest) | MCP proxy + CLI (reference implementation) |
225
+
226
+ ## License
227
+
228
+ Apache 2.0 — see [LICENSE](LICENSE).