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.
- attest_protocol-0.2.0/.github/workflows/ci.yml +33 -0
- attest_protocol-0.2.0/.github/workflows/publish.yml +26 -0
- attest_protocol-0.2.0/.gitignore +9 -0
- attest_protocol-0.2.0/CLAUDE.md +31 -0
- attest_protocol-0.2.0/CONTRIBUTING.md +62 -0
- attest_protocol-0.2.0/LICENSE +21 -0
- attest_protocol-0.2.0/PKG-INFO +257 -0
- attest_protocol-0.2.0/README.md +228 -0
- attest_protocol-0.2.0/SECURITY.md +41 -0
- attest_protocol-0.2.0/pyproject.toml +67 -0
- attest_protocol-0.2.0/src/attest_protocol/__init__.py +139 -0
- attest_protocol-0.2.0/src/attest_protocol/_version.py +1 -0
- attest_protocol-0.2.0/src/attest_protocol/py.typed +0 -0
- attest_protocol-0.2.0/src/attest_protocol/receipt/__init__.py +70 -0
- attest_protocol-0.2.0/src/attest_protocol/receipt/chain.py +95 -0
- attest_protocol-0.2.0/src/attest_protocol/receipt/create.py +101 -0
- attest_protocol-0.2.0/src/attest_protocol/receipt/hash.py +113 -0
- attest_protocol-0.2.0/src/attest_protocol/receipt/signing.py +146 -0
- attest_protocol-0.2.0/src/attest_protocol/receipt/types.py +157 -0
- attest_protocol-0.2.0/src/attest_protocol/store/__init__.py +17 -0
- attest_protocol-0.2.0/src/attest_protocol/store/store.py +214 -0
- attest_protocol-0.2.0/src/attest_protocol/store/verify.py +21 -0
- attest_protocol-0.2.0/src/attest_protocol/taxonomy/__init__.py +29 -0
- attest_protocol-0.2.0/src/attest_protocol/taxonomy/actions.py +105 -0
- attest_protocol-0.2.0/src/attest_protocol/taxonomy/classify.py +35 -0
- attest_protocol-0.2.0/src/attest_protocol/taxonomy/config.py +54 -0
- attest_protocol-0.2.0/src/attest_protocol/taxonomy/types.py +26 -0
- attest_protocol-0.2.0/tests/__init__.py +0 -0
- attest_protocol-0.2.0/tests/conftest.py +100 -0
- attest_protocol-0.2.0/tests/fixtures/ts_vectors.json +139 -0
- attest_protocol-0.2.0/tests/receipt/__init__.py +0 -0
- attest_protocol-0.2.0/tests/receipt/test_chain.py +107 -0
- attest_protocol-0.2.0/tests/receipt/test_create.py +109 -0
- attest_protocol-0.2.0/tests/receipt/test_hash.py +109 -0
- attest_protocol-0.2.0/tests/receipt/test_signing.py +81 -0
- attest_protocol-0.2.0/tests/receipt/test_types.py +61 -0
- attest_protocol-0.2.0/tests/store/__init__.py +0 -0
- attest_protocol-0.2.0/tests/store/test_store.py +223 -0
- attest_protocol-0.2.0/tests/store/test_verify.py +57 -0
- attest_protocol-0.2.0/tests/taxonomy/__init__.py +0 -0
- attest_protocol-0.2.0/tests/taxonomy/test_actions.py +52 -0
- attest_protocol-0.2.0/tests/taxonomy/test_classify.py +40 -0
- attest_protocol-0.2.0/tests/taxonomy/test_config.py +80 -0
- attest_protocol-0.2.0/tests/test_cross_language.py +84 -0
- 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,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
|
+
[](https://pypi.org/project/attest-protocol/)
|
|
37
|
+
[](LICENSE)
|
|
38
|
+
[](https://www.python.org/)
|
|
39
|
+
[](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) • [TypeScript SDK](https://github.com/attest-protocol/attest-ts) • [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
|
+
[](https://pypi.org/project/attest-protocol/)
|
|
8
|
+
[](LICENSE)
|
|
9
|
+
[](https://www.python.org/)
|
|
10
|
+
[](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) • [TypeScript SDK](https://github.com/attest-protocol/attest-ts) • [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).
|