agent-receipts 0.2.1__py3-none-any.whl
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.
- agent_receipts-0.2.1.dist-info/METADATA +279 -0
- agent_receipts-0.2.1.dist-info/RECORD +21 -0
- agent_receipts-0.2.1.dist-info/WHEEL +4 -0
- agent_receipts-0.2.1.dist-info/licenses/LICENSE +21 -0
- attest_protocol/__init__.py +139 -0
- attest_protocol/_version.py +1 -0
- attest_protocol/py.typed +0 -0
- attest_protocol/receipt/__init__.py +70 -0
- attest_protocol/receipt/chain.py +95 -0
- attest_protocol/receipt/create.py +101 -0
- attest_protocol/receipt/hash.py +113 -0
- attest_protocol/receipt/signing.py +146 -0
- attest_protocol/receipt/types.py +157 -0
- attest_protocol/store/__init__.py +17 -0
- attest_protocol/store/store.py +214 -0
- attest_protocol/store/verify.py +21 -0
- attest_protocol/taxonomy/__init__.py +29 -0
- attest_protocol/taxonomy/actions.py +105 -0
- attest_protocol/taxonomy/classify.py +35 -0
- attest_protocol/taxonomy/config.py +54 -0
- attest_protocol/taxonomy/types.py +26 -0
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: agent-receipts
|
|
3
|
+
Version: 0.2.1
|
|
4
|
+
Summary: Python SDK for the Action Receipts protocol
|
|
5
|
+
Project-URL: Homepage, https://github.com/agnt-rcpt/sdk-py
|
|
6
|
+
Project-URL: Repository, https://github.com/agnt-rcpt/sdk-py
|
|
7
|
+
Project-URL: Issues, https://github.com/agnt-rcpt/sdk-py/issues
|
|
8
|
+
Project-URL: Spec, https://github.com/agnt-rcpt/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
|
+
## Why receipts?
|
|
52
|
+
|
|
53
|
+
If you're building with AI agents, you're probably already logging what they do. Receipts go further: they're **cryptographically signed, hash-chained records** that can't be quietly altered after the fact — and they follow a standard format that works across languages, agents, and systems.
|
|
54
|
+
|
|
55
|
+
Here's where that matters in practice:
|
|
56
|
+
|
|
57
|
+
- **Post-incident review** — An agent ran overnight and something broke. The receipt chain shows exactly which actions it took, in what order, and whether each succeeded or failed — with cryptographic proof the log hasn't been tampered with after the fact.
|
|
58
|
+
|
|
59
|
+
- **Compliance and audit** — Regulated environments require evidence of what systems did and why. Receipts are W3C Verifiable Credentials with Ed25519 signatures, giving auditors a tamper-evident trail they can independently verify.
|
|
60
|
+
|
|
61
|
+
- **Safer autonomous agents** — Agents can query their own audit trail mid-session. Before taking a high-risk action, an agent can check what it has already done and whether previous steps succeeded, enabling self-correcting workflows.
|
|
62
|
+
|
|
63
|
+
- **Multi-agent trust** — When agents collaborate, receipts serve as proof of prior actions. Agent B can verify that Agent A actually completed step 1 before proceeding to step 2, without trusting a shared log.
|
|
64
|
+
|
|
65
|
+
- **Usage tracking** — Every action is classified by type and risk level, giving you a structured breakdown of what agents spent their time on.
|
|
66
|
+
|
|
67
|
+
### Beyond local storage
|
|
68
|
+
|
|
69
|
+
The protocol is designed for receipts to travel — publishing to a shared ledger, forwarding to a compliance system, or exchanging between agents as proof of prior actions. Receipts are portable W3C Verifiable Credentials, but where they go is always under the user's control.
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## Install
|
|
74
|
+
|
|
75
|
+
```sh
|
|
76
|
+
pip install attest-protocol
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Quick start
|
|
80
|
+
|
|
81
|
+
### Create and sign a receipt
|
|
82
|
+
|
|
83
|
+
```python
|
|
84
|
+
from attest_protocol import (
|
|
85
|
+
create_receipt,
|
|
86
|
+
generate_key_pair,
|
|
87
|
+
hash_receipt,
|
|
88
|
+
sign_receipt,
|
|
89
|
+
CreateReceiptInput,
|
|
90
|
+
Chain,
|
|
91
|
+
Issuer,
|
|
92
|
+
Outcome,
|
|
93
|
+
Principal,
|
|
94
|
+
)
|
|
95
|
+
from attest_protocol.receipt.create import ActionInput
|
|
96
|
+
|
|
97
|
+
# Generate an Ed25519 key pair
|
|
98
|
+
keys = generate_key_pair()
|
|
99
|
+
|
|
100
|
+
# Create an unsigned receipt
|
|
101
|
+
unsigned = create_receipt(CreateReceiptInput(
|
|
102
|
+
issuer=Issuer(id="did:agent:my-agent"),
|
|
103
|
+
principal=Principal(id="did:user:alice"),
|
|
104
|
+
action=ActionInput(
|
|
105
|
+
type="filesystem.file.read",
|
|
106
|
+
risk_level="low",
|
|
107
|
+
),
|
|
108
|
+
outcome=Outcome(status="success"),
|
|
109
|
+
chain=Chain(
|
|
110
|
+
sequence=1,
|
|
111
|
+
previous_receipt_hash=None,
|
|
112
|
+
chain_id="chain_session-1",
|
|
113
|
+
),
|
|
114
|
+
))
|
|
115
|
+
|
|
116
|
+
# Sign and hash
|
|
117
|
+
receipt = sign_receipt(unsigned, keys.private_key, "did:agent:my-agent#key-1")
|
|
118
|
+
receipt_hash = hash_receipt(receipt)
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### Verify a receipt
|
|
122
|
+
|
|
123
|
+
```python
|
|
124
|
+
from attest_protocol import verify_receipt
|
|
125
|
+
|
|
126
|
+
valid = verify_receipt(receipt, keys.public_key)
|
|
127
|
+
print(f"Signature valid: {valid}") # True
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### Verify a chain
|
|
131
|
+
|
|
132
|
+
```python
|
|
133
|
+
from attest_protocol import verify_chain
|
|
134
|
+
|
|
135
|
+
# Verify a list of receipts (e.g. [receipt] from the example above)
|
|
136
|
+
result = verify_chain([receipt], keys.public_key)
|
|
137
|
+
print(f"Chain valid: {result.valid}")
|
|
138
|
+
print(f"Receipts verified: {result.length}")
|
|
139
|
+
if not result.valid:
|
|
140
|
+
print(f"Broken at index: {result.broken_at}")
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### Action taxonomy
|
|
144
|
+
|
|
145
|
+
The standardized action taxonomy (action types and risk levels) is defined in the
|
|
146
|
+
[protocol specification](https://github.com/attest-protocol/spec/tree/main/spec/taxonomy).
|
|
147
|
+
Taxonomy classification will be added in a future milestone (M3).
|
|
148
|
+
|
|
149
|
+
## What is an Action Receipt?
|
|
150
|
+
|
|
151
|
+
A [W3C Verifiable Credential](https://www.w3.org/TR/vc-data-model-2.0/) signed with Ed25519, recording:
|
|
152
|
+
|
|
153
|
+
| Field | What it captures |
|
|
154
|
+
|:---|:---|
|
|
155
|
+
| **Action** | What happened, classified by a [standardized taxonomy](https://github.com/attest-protocol/spec/tree/main/spec/taxonomy) |
|
|
156
|
+
| **Principal** | Who authorized it (human or organization) |
|
|
157
|
+
| **Issuer** | Which agent performed it |
|
|
158
|
+
| **Outcome** | Success/failure, reversibility, undo method |
|
|
159
|
+
| **Chain** | SHA-256 hash link to the previous receipt (tamper-evident) |
|
|
160
|
+
| **Privacy** | Parameters are hashed, never stored in plaintext |
|
|
161
|
+
|
|
162
|
+
## API reference
|
|
163
|
+
|
|
164
|
+
### Receipt creation and signing
|
|
165
|
+
|
|
166
|
+
```python
|
|
167
|
+
from attest_protocol import (
|
|
168
|
+
create_receipt, # Build an unsigned receipt from input fields
|
|
169
|
+
generate_key_pair, # Ed25519 key pair (PEM-encoded)
|
|
170
|
+
sign_receipt, # Sign with Ed25519Signature2020 proof
|
|
171
|
+
verify_receipt, # Verify a receipt's signature
|
|
172
|
+
)
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### Hashing and canonicalization
|
|
176
|
+
|
|
177
|
+
```python
|
|
178
|
+
from attest_protocol import (
|
|
179
|
+
canonicalize, # RFC 8785 JSON canonicalization
|
|
180
|
+
hash_receipt, # Hash receipt (excluding proof) -> "sha256:<hex>"
|
|
181
|
+
sha256, # Hash arbitrary data -> "sha256:<hex>"
|
|
182
|
+
)
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### Chain verification
|
|
186
|
+
|
|
187
|
+
```python
|
|
188
|
+
from attest_protocol import (
|
|
189
|
+
verify_chain, # Verify signatures, hash links, and sequence numbering
|
|
190
|
+
)
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
### Types (Pydantic v2 models)
|
|
194
|
+
|
|
195
|
+
```python
|
|
196
|
+
from attest_protocol import (
|
|
197
|
+
ActionReceipt, # Signed receipt with proof
|
|
198
|
+
UnsignedActionReceipt, # Receipt before signing
|
|
199
|
+
Action, ActionTarget, Authorization, Chain,
|
|
200
|
+
CredentialSubject, Intent, Issuer, Operator,
|
|
201
|
+
Outcome, Principal, Proof, StateChange,
|
|
202
|
+
)
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
### Subpackage imports
|
|
206
|
+
|
|
207
|
+
```python
|
|
208
|
+
from attest_protocol.receipt import create_receipt, sign_receipt
|
|
209
|
+
from attest_protocol.receipt.hash import canonicalize
|
|
210
|
+
from attest_protocol.receipt.types import CONTEXT, CREDENTIAL_TYPE
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
### TypeScript SDK compatibility
|
|
214
|
+
|
|
215
|
+
camelCase aliases are available for users coming from the TS SDK:
|
|
216
|
+
|
|
217
|
+
```python
|
|
218
|
+
from attest_protocol import (
|
|
219
|
+
createReceipt, # = create_receipt
|
|
220
|
+
generateKeyPair, # = generate_key_pair
|
|
221
|
+
signReceipt, # = sign_receipt
|
|
222
|
+
verifyReceipt, # = verify_receipt
|
|
223
|
+
hashReceipt, # = hash_receipt
|
|
224
|
+
verifyChain, # = verify_chain
|
|
225
|
+
)
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
## Cross-language compatibility
|
|
229
|
+
|
|
230
|
+
This SDK produces **byte-identical** output to [`@attest-protocol/attest-ts`](https://github.com/attest-protocol/attest-ts):
|
|
231
|
+
|
|
232
|
+
- RFC 8785 canonical JSON matches exactly
|
|
233
|
+
- SHA-256 hashes are identical
|
|
234
|
+
- Ed25519 signatures from either SDK verify in the other
|
|
235
|
+
|
|
236
|
+
Cross-language compatibility is verified by test vectors generated from the TypeScript SDK.
|
|
237
|
+
|
|
238
|
+
## Project structure
|
|
239
|
+
|
|
240
|
+
```
|
|
241
|
+
src/attest_protocol/
|
|
242
|
+
receipt/
|
|
243
|
+
types.py # Pydantic models for all receipt types
|
|
244
|
+
create.py # Receipt creation with auto-generated IDs
|
|
245
|
+
signing.py # Ed25519 signing and verification
|
|
246
|
+
hash.py # RFC 8785 canonicalization + SHA-256
|
|
247
|
+
chain.py # Chain verification
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
## Development
|
|
251
|
+
|
|
252
|
+
```sh
|
|
253
|
+
uv sync --all-extras
|
|
254
|
+
uv run pytest # run tests
|
|
255
|
+
uv run ruff check . # lint
|
|
256
|
+
uv run ruff format . # format
|
|
257
|
+
uv run pyright # type check
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
| | |
|
|
261
|
+
|:---|:---|
|
|
262
|
+
| **Language** | Python 3.11+ |
|
|
263
|
+
| **Types** | Pydantic v2, pyright strict mode |
|
|
264
|
+
| **Linting** | ruff |
|
|
265
|
+
| **Testing** | pytest |
|
|
266
|
+
| **Dependencies** | `pydantic>=2.0`, `cryptography>=41.0` |
|
|
267
|
+
|
|
268
|
+
## Ecosystem
|
|
269
|
+
|
|
270
|
+
| Repository | Description |
|
|
271
|
+
|:---|:---|
|
|
272
|
+
| [attest-protocol/spec](https://github.com/attest-protocol/spec) | Protocol specification, JSON Schemas, canonical taxonomy |
|
|
273
|
+
| [attest-protocol/attest-ts](https://github.com/attest-protocol/attest-ts) | TypeScript SDK ([npm](https://www.npmjs.com/package/@attest-protocol/attest-ts)) |
|
|
274
|
+
| **attest-protocol/attest-py** (this package) | Python SDK |
|
|
275
|
+
| [ojongerius/attest](https://github.com/ojongerius/attest) | MCP proxy + CLI (reference implementation) |
|
|
276
|
+
|
|
277
|
+
## License
|
|
278
|
+
|
|
279
|
+
Apache 2.0 — see [LICENSE](LICENSE).
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
attest_protocol/__init__.py,sha256=kwKaNQHb7xg5-tanXkI-Z4dfYcoU5_vL3fe2YKGyMIc,3343
|
|
2
|
+
attest_protocol/_version.py,sha256=w9Iw7QVvd8lme2wKwEbCo5IgetVjSfFBOOYAcA_Q0Ns,18
|
|
3
|
+
attest_protocol/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
|
+
attest_protocol/receipt/__init__.py,sha256=l1RPZgdyALgAJ49qWSoCuz0FJulfnOIMh6KMgvJl3cs,1362
|
|
5
|
+
attest_protocol/receipt/chain.py,sha256=6pL26ksKoHTW0nyHbYd-IGsVQjVTuP9mNg1w3qKPDrc,2661
|
|
6
|
+
attest_protocol/receipt/create.py,sha256=kEvk7sj534nT3W7ssj8qQ6SGE15p1Hw_Z9IaAeRUS0s,2932
|
|
7
|
+
attest_protocol/receipt/hash.py,sha256=gB8tCvTH2rAleK7GU0mli2Mq85yDVRSUs5ApJVUTh_g,3697
|
|
8
|
+
attest_protocol/receipt/signing.py,sha256=vT62mnnqOxOMhU3ZgvEf6bzvwd2IDYYz8GhsHfP0fDc,4226
|
|
9
|
+
attest_protocol/receipt/types.py,sha256=c9ILfSSfSrffn9-XEs232vk_exyNjSB-FJFFWg0v7dE,3588
|
|
10
|
+
attest_protocol/store/__init__.py,sha256=i50VW5hkZeVNInLOUfL2r2rgqAlZs2hnR2mxbww_DwU,364
|
|
11
|
+
attest_protocol/store/store.py,sha256=BuU_EI7OgcmaZeOS1wHt-vXOgwR4yYSDO4XHrzwamLM,7164
|
|
12
|
+
attest_protocol/store/verify.py,sha256=kzz_3pZCklTivAeDa1SmqFLhyLXuc77FTy9332S02FM,587
|
|
13
|
+
attest_protocol/taxonomy/__init__.py,sha256=M2DLT_4mnVOlblPbVlPz9Z_7TUgWdObAnpjbzRziRR8,791
|
|
14
|
+
attest_protocol/taxonomy/actions.py,sha256=LKgFV_yrbtsfv4LJSZBnkNpIhZ3HJW9GlZDB7QIFYEU,2949
|
|
15
|
+
attest_protocol/taxonomy/classify.py,sha256=lZR6Dv2z59vXkEpjSPayIr_oP5B1hW5d2ZS93xCbt3s,1058
|
|
16
|
+
attest_protocol/taxonomy/config.py,sha256=1NzzNo9U407xEKDnzF3VztOarlamlj_N9dbTmZ-9BA4,1973
|
|
17
|
+
attest_protocol/taxonomy/types.py,sha256=e70VsA-YMLqJfbJPmsRo2i0Ho4LyQv8HtiPmpg3AZR4,512
|
|
18
|
+
agent_receipts-0.2.1.dist-info/METADATA,sha256=9KwactIRXDzVpfAgmJma1HtvV3CbqsJft00EM1NPMV8,9559
|
|
19
|
+
agent_receipts-0.2.1.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
|
|
20
|
+
agent_receipts-0.2.1.dist-info/licenses/LICENSE,sha256=ZNP4Wy2jzAlWYAvfws5JqirwxpamE_Xo-BZfjgZ39i0,1072
|
|
21
|
+
agent_receipts-0.2.1.dist-info/RECORD,,
|
|
@@ -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,139 @@
|
|
|
1
|
+
"""Python SDK for the Action Receipts protocol."""
|
|
2
|
+
|
|
3
|
+
from attest_protocol._version import VERSION
|
|
4
|
+
from attest_protocol.receipt.chain import (
|
|
5
|
+
ChainVerification,
|
|
6
|
+
ReceiptVerification,
|
|
7
|
+
verify_chain,
|
|
8
|
+
)
|
|
9
|
+
from attest_protocol.receipt.create import CreateReceiptInput, create_receipt
|
|
10
|
+
from attest_protocol.receipt.hash import canonicalize, hash_receipt, sha256
|
|
11
|
+
from attest_protocol.receipt.signing import (
|
|
12
|
+
KeyPair,
|
|
13
|
+
generate_key_pair,
|
|
14
|
+
sign_receipt,
|
|
15
|
+
verify_receipt,
|
|
16
|
+
)
|
|
17
|
+
from attest_protocol.receipt.types import (
|
|
18
|
+
CONTEXT,
|
|
19
|
+
CREDENTIAL_TYPE,
|
|
20
|
+
Action,
|
|
21
|
+
ActionReceipt,
|
|
22
|
+
ActionTarget,
|
|
23
|
+
Authorization,
|
|
24
|
+
Chain,
|
|
25
|
+
CredentialSubject,
|
|
26
|
+
Intent,
|
|
27
|
+
Issuer,
|
|
28
|
+
Operator,
|
|
29
|
+
Outcome,
|
|
30
|
+
Principal,
|
|
31
|
+
Proof,
|
|
32
|
+
StateChange,
|
|
33
|
+
UnsignedActionReceipt,
|
|
34
|
+
)
|
|
35
|
+
from attest_protocol.store.store import (
|
|
36
|
+
ReceiptQuery,
|
|
37
|
+
ReceiptStore,
|
|
38
|
+
StoreStats,
|
|
39
|
+
open_store,
|
|
40
|
+
)
|
|
41
|
+
from attest_protocol.store.verify import verify_stored_chain
|
|
42
|
+
from attest_protocol.taxonomy.actions import (
|
|
43
|
+
ALL_ACTIONS,
|
|
44
|
+
FILESYSTEM_ACTIONS,
|
|
45
|
+
SYSTEM_ACTIONS,
|
|
46
|
+
UNKNOWN_ACTION,
|
|
47
|
+
get_action_type,
|
|
48
|
+
resolve_action_type,
|
|
49
|
+
)
|
|
50
|
+
from attest_protocol.taxonomy.classify import ClassificationResult, classify_tool_call
|
|
51
|
+
from attest_protocol.taxonomy.config import load_taxonomy_config
|
|
52
|
+
from attest_protocol.taxonomy.types import ActionTypeEntry, TaxonomyMapping
|
|
53
|
+
|
|
54
|
+
# camelCase aliases for users coming from the TypeScript SDK
|
|
55
|
+
createReceipt = create_receipt
|
|
56
|
+
generateKeyPair = generate_key_pair
|
|
57
|
+
signReceipt = sign_receipt
|
|
58
|
+
verifyReceipt = verify_receipt
|
|
59
|
+
hashReceipt = hash_receipt
|
|
60
|
+
verifyChain = verify_chain
|
|
61
|
+
openStore = open_store
|
|
62
|
+
verifyStoredChain = verify_stored_chain
|
|
63
|
+
classifyToolCall = classify_tool_call
|
|
64
|
+
getActionType = get_action_type
|
|
65
|
+
resolveActionType = resolve_action_type
|
|
66
|
+
loadTaxonomyConfig = load_taxonomy_config
|
|
67
|
+
|
|
68
|
+
# RECEIPT_VERSION is the receipt schema version (from types), not the package version
|
|
69
|
+
from attest_protocol.receipt.types import VERSION as RECEIPT_VERSION # noqa: E402
|
|
70
|
+
|
|
71
|
+
__all__ = [
|
|
72
|
+
# Version
|
|
73
|
+
"VERSION",
|
|
74
|
+
"RECEIPT_VERSION",
|
|
75
|
+
# Types
|
|
76
|
+
"Action",
|
|
77
|
+
"ActionReceipt",
|
|
78
|
+
"ActionTarget",
|
|
79
|
+
"Authorization",
|
|
80
|
+
"Chain",
|
|
81
|
+
"CredentialSubject",
|
|
82
|
+
"Intent",
|
|
83
|
+
"Issuer",
|
|
84
|
+
"Operator",
|
|
85
|
+
"Outcome",
|
|
86
|
+
"Principal",
|
|
87
|
+
"Proof",
|
|
88
|
+
"StateChange",
|
|
89
|
+
"UnsignedActionReceipt",
|
|
90
|
+
# Constants
|
|
91
|
+
"CONTEXT",
|
|
92
|
+
"CREDENTIAL_TYPE",
|
|
93
|
+
# Creation
|
|
94
|
+
"CreateReceiptInput",
|
|
95
|
+
"create_receipt",
|
|
96
|
+
"createReceipt",
|
|
97
|
+
# Hashing
|
|
98
|
+
"canonicalize",
|
|
99
|
+
"hash_receipt",
|
|
100
|
+
"hashReceipt",
|
|
101
|
+
"sha256",
|
|
102
|
+
# Signing
|
|
103
|
+
"KeyPair",
|
|
104
|
+
"generate_key_pair",
|
|
105
|
+
"generateKeyPair",
|
|
106
|
+
"sign_receipt",
|
|
107
|
+
"signReceipt",
|
|
108
|
+
"verify_receipt",
|
|
109
|
+
"verifyReceipt",
|
|
110
|
+
# Chain
|
|
111
|
+
"ChainVerification",
|
|
112
|
+
"ReceiptVerification",
|
|
113
|
+
"verify_chain",
|
|
114
|
+
"verifyChain",
|
|
115
|
+
# Store
|
|
116
|
+
"ReceiptQuery",
|
|
117
|
+
"ReceiptStore",
|
|
118
|
+
"StoreStats",
|
|
119
|
+
"open_store",
|
|
120
|
+
"openStore",
|
|
121
|
+
"verify_stored_chain",
|
|
122
|
+
"verifyStoredChain",
|
|
123
|
+
# Taxonomy
|
|
124
|
+
"ALL_ACTIONS",
|
|
125
|
+
"ActionTypeEntry",
|
|
126
|
+
"ClassificationResult",
|
|
127
|
+
"FILESYSTEM_ACTIONS",
|
|
128
|
+
"SYSTEM_ACTIONS",
|
|
129
|
+
"TaxonomyMapping",
|
|
130
|
+
"UNKNOWN_ACTION",
|
|
131
|
+
"classify_tool_call",
|
|
132
|
+
"classifyToolCall",
|
|
133
|
+
"get_action_type",
|
|
134
|
+
"getActionType",
|
|
135
|
+
"load_taxonomy_config",
|
|
136
|
+
"loadTaxonomyConfig",
|
|
137
|
+
"resolve_action_type",
|
|
138
|
+
"resolveActionType",
|
|
139
|
+
]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
VERSION = "0.2.0"
|
attest_protocol/py.typed
ADDED
|
File without changes
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
from attest_protocol.receipt.chain import (
|
|
2
|
+
ChainVerification,
|
|
3
|
+
ReceiptVerification,
|
|
4
|
+
verify_chain,
|
|
5
|
+
)
|
|
6
|
+
from attest_protocol.receipt.create import CreateReceiptInput, create_receipt
|
|
7
|
+
from attest_protocol.receipt.hash import canonicalize, hash_receipt, sha256
|
|
8
|
+
from attest_protocol.receipt.signing import (
|
|
9
|
+
KeyPair,
|
|
10
|
+
generate_key_pair,
|
|
11
|
+
sign_receipt,
|
|
12
|
+
verify_receipt,
|
|
13
|
+
)
|
|
14
|
+
from attest_protocol.receipt.types import (
|
|
15
|
+
CONTEXT,
|
|
16
|
+
CREDENTIAL_TYPE,
|
|
17
|
+
VERSION,
|
|
18
|
+
Action,
|
|
19
|
+
ActionReceipt,
|
|
20
|
+
ActionTarget,
|
|
21
|
+
Authorization,
|
|
22
|
+
Chain,
|
|
23
|
+
CredentialSubject,
|
|
24
|
+
Intent,
|
|
25
|
+
Issuer,
|
|
26
|
+
Operator,
|
|
27
|
+
Outcome,
|
|
28
|
+
Principal,
|
|
29
|
+
Proof,
|
|
30
|
+
StateChange,
|
|
31
|
+
UnsignedActionReceipt,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
__all__ = [
|
|
35
|
+
# Types
|
|
36
|
+
"Action",
|
|
37
|
+
"ActionReceipt",
|
|
38
|
+
"ActionTarget",
|
|
39
|
+
"Authorization",
|
|
40
|
+
"Chain",
|
|
41
|
+
"CredentialSubject",
|
|
42
|
+
"Intent",
|
|
43
|
+
"Issuer",
|
|
44
|
+
"Operator",
|
|
45
|
+
"Outcome",
|
|
46
|
+
"Principal",
|
|
47
|
+
"Proof",
|
|
48
|
+
"StateChange",
|
|
49
|
+
"UnsignedActionReceipt",
|
|
50
|
+
# Constants
|
|
51
|
+
"CONTEXT",
|
|
52
|
+
"CREDENTIAL_TYPE",
|
|
53
|
+
"VERSION",
|
|
54
|
+
# Creation
|
|
55
|
+
"CreateReceiptInput",
|
|
56
|
+
"create_receipt",
|
|
57
|
+
# Hashing
|
|
58
|
+
"canonicalize",
|
|
59
|
+
"hash_receipt",
|
|
60
|
+
"sha256",
|
|
61
|
+
# Signing
|
|
62
|
+
"KeyPair",
|
|
63
|
+
"generate_key_pair",
|
|
64
|
+
"sign_receipt",
|
|
65
|
+
"verify_receipt",
|
|
66
|
+
# Chain
|
|
67
|
+
"ChainVerification",
|
|
68
|
+
"ReceiptVerification",
|
|
69
|
+
"verify_chain",
|
|
70
|
+
]
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"""Chain verification — validate receipt chains for integrity."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass, field
|
|
6
|
+
from typing import TYPE_CHECKING
|
|
7
|
+
|
|
8
|
+
from attest_protocol.receipt.hash import hash_receipt
|
|
9
|
+
from attest_protocol.receipt.signing import verify_receipt
|
|
10
|
+
|
|
11
|
+
if TYPE_CHECKING:
|
|
12
|
+
from attest_protocol.receipt.types import ActionReceipt
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@dataclass
|
|
16
|
+
class ReceiptVerification:
|
|
17
|
+
"""Result of verifying a single receipt in a chain."""
|
|
18
|
+
|
|
19
|
+
index: int
|
|
20
|
+
receipt_id: str
|
|
21
|
+
signature_valid: bool
|
|
22
|
+
hash_link_valid: bool
|
|
23
|
+
sequence_valid: bool
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@dataclass
|
|
27
|
+
class ChainVerification:
|
|
28
|
+
"""Result of verifying an entire chain."""
|
|
29
|
+
|
|
30
|
+
valid: bool
|
|
31
|
+
length: int
|
|
32
|
+
receipts: list[ReceiptVerification] = field(default_factory=list)
|
|
33
|
+
broken_at: int = -1
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def verify_chain(
|
|
37
|
+
receipts: list[ActionReceipt],
|
|
38
|
+
public_key: str,
|
|
39
|
+
) -> ChainVerification:
|
|
40
|
+
"""Verify a chain of signed receipts.
|
|
41
|
+
|
|
42
|
+
Checks for each receipt:
|
|
43
|
+
1. Ed25519 signature validity
|
|
44
|
+
2. Hash linkage: previous_receipt_hash matches SHA-256 of prior receipt
|
|
45
|
+
3. Sequence numbers are strictly incrementing
|
|
46
|
+
|
|
47
|
+
Receipts must be provided in chain order (by sequence number).
|
|
48
|
+
"""
|
|
49
|
+
if not receipts:
|
|
50
|
+
return ChainVerification(valid=True, length=0)
|
|
51
|
+
|
|
52
|
+
results: list[ReceiptVerification] = []
|
|
53
|
+
broken_at = -1
|
|
54
|
+
previous: ActionReceipt | None = None
|
|
55
|
+
|
|
56
|
+
for i, receipt in enumerate(receipts):
|
|
57
|
+
chain = receipt.credentialSubject.chain
|
|
58
|
+
|
|
59
|
+
signature_valid = verify_receipt(receipt, public_key)
|
|
60
|
+
|
|
61
|
+
if previous is None:
|
|
62
|
+
hash_link_valid = chain.previous_receipt_hash is None
|
|
63
|
+
else:
|
|
64
|
+
previous_hash = hash_receipt(previous)
|
|
65
|
+
hash_link_valid = chain.previous_receipt_hash == previous_hash
|
|
66
|
+
|
|
67
|
+
current_sequence = chain.sequence
|
|
68
|
+
if previous is None:
|
|
69
|
+
sequence_valid = current_sequence >= 1
|
|
70
|
+
else:
|
|
71
|
+
prev_sequence = previous.credentialSubject.chain.sequence
|
|
72
|
+
sequence_valid = current_sequence == prev_sequence + 1
|
|
73
|
+
|
|
74
|
+
verification = ReceiptVerification(
|
|
75
|
+
index=i,
|
|
76
|
+
receipt_id=receipt.id,
|
|
77
|
+
signature_valid=signature_valid,
|
|
78
|
+
hash_link_valid=hash_link_valid,
|
|
79
|
+
sequence_valid=sequence_valid,
|
|
80
|
+
)
|
|
81
|
+
results.append(verification)
|
|
82
|
+
|
|
83
|
+
if broken_at == -1 and (
|
|
84
|
+
not signature_valid or not hash_link_valid or not sequence_valid
|
|
85
|
+
):
|
|
86
|
+
broken_at = i
|
|
87
|
+
|
|
88
|
+
previous = receipt
|
|
89
|
+
|
|
90
|
+
return ChainVerification(
|
|
91
|
+
valid=broken_at == -1,
|
|
92
|
+
length=len(receipts),
|
|
93
|
+
receipts=results,
|
|
94
|
+
broken_at=broken_at,
|
|
95
|
+
)
|