attesto 0.1.2__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.
- attesto-0.1.2/PKG-INFO +236 -0
- attesto-0.1.2/README.md +208 -0
- attesto-0.1.2/pyproject.toml +49 -0
- attesto-0.1.2/setup.cfg +4 -0
- attesto-0.1.2/src/attesto/__init__.py +124 -0
- attesto-0.1.2/src/attesto/_retry.py +107 -0
- attesto-0.1.2/src/attesto/async_client.py +342 -0
- attesto-0.1.2/src/attesto/client.py +390 -0
- attesto-0.1.2/src/attesto/connectors.py +45 -0
- attesto-0.1.2/src/attesto/errors.py +45 -0
- attesto-0.1.2/src/attesto/models.py +309 -0
- attesto-0.1.2/src/attesto/proofstream.py +75 -0
- attesto-0.1.2/src/attesto/version.py +3 -0
- attesto-0.1.2/src/attesto.egg-info/PKG-INFO +236 -0
- attesto-0.1.2/src/attesto.egg-info/SOURCES.txt +19 -0
- attesto-0.1.2/src/attesto.egg-info/dependency_links.txt +1 -0
- attesto-0.1.2/src/attesto.egg-info/requires.txt +9 -0
- attesto-0.1.2/src/attesto.egg-info/top_level.txt +1 -0
- attesto-0.1.2/tests/test_client.py +1136 -0
- attesto-0.1.2/tests/test_connectors.py +33 -0
- attesto-0.1.2/tests/test_proofstream_golden_vectors.py +116 -0
attesto-0.1.2/PKG-INFO
ADDED
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: attesto
|
|
3
|
+
Version: 0.1.2
|
|
4
|
+
Summary: Attesto AI Python SDK — log verifiable AI events to Polygon via one function call.
|
|
5
|
+
Author-email: Attesto <info@attesto.eu>
|
|
6
|
+
License-Expression: Apache-2.0
|
|
7
|
+
Project-URL: Homepage, https://attesto.eu
|
|
8
|
+
Project-URL: Documentation, https://docs.attesto.eu/manuals/sdks.html
|
|
9
|
+
Project-URL: Security, https://attesto.eu/security
|
|
10
|
+
Keywords: ai,compliance,eu-ai-act,polygon,merkle,audit
|
|
11
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
18
|
+
Requires-Python: >=3.10
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
Requires-Dist: httpx>=0.27
|
|
21
|
+
Requires-Dist: pydantic>=2
|
|
22
|
+
Provides-Extra: dev
|
|
23
|
+
Requires-Dist: build>=1.2; extra == "dev"
|
|
24
|
+
Requires-Dist: pytest>=8; extra == "dev"
|
|
25
|
+
Requires-Dist: pytest-asyncio>=0.24; extra == "dev"
|
|
26
|
+
Requires-Dist: pytest-httpx>=0.35; extra == "dev"
|
|
27
|
+
Requires-Dist: ruff>=0.7; extra == "dev"
|
|
28
|
+
|
|
29
|
+
# Attesto Python SDK
|
|
30
|
+
|
|
31
|
+
Log every AI decision to a verifiable, on-chain audit trail with one call.
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
pip install attesto
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Quick start
|
|
38
|
+
|
|
39
|
+
```python
|
|
40
|
+
from attesto import AttestoClient
|
|
41
|
+
|
|
42
|
+
attesto = AttestoClient(api_key="atto_live_...") # issued when you register a system
|
|
43
|
+
ack = attesto.log_event(
|
|
44
|
+
type="inference",
|
|
45
|
+
status="verified",
|
|
46
|
+
latency_ms=42,
|
|
47
|
+
input_hash="sha256:deadbeef...",
|
|
48
|
+
output_hash="sha256:cafebabe...",
|
|
49
|
+
payload={"score": 0.87, "model": "gpt-4o"},
|
|
50
|
+
)
|
|
51
|
+
print(ack.id, ack.system_id, ack.ts)
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Async
|
|
55
|
+
|
|
56
|
+
```python
|
|
57
|
+
from attesto import AsyncAttestoClient
|
|
58
|
+
|
|
59
|
+
async with AsyncAttestoClient(api_key="atto_live_...") as attesto:
|
|
60
|
+
ack = await attesto.log_event(type="inference", payload={"score": 0.87})
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Proofstream v2
|
|
64
|
+
|
|
65
|
+
```python
|
|
66
|
+
import os
|
|
67
|
+
from attesto import AttestoV2Client
|
|
68
|
+
|
|
69
|
+
with AttestoV2Client(api_key="atto_live_...") as attesto:
|
|
70
|
+
receipt_signer_public_key_hex = os.environ["ATTESTO_RECEIPT_SIGNER_PUBLIC_KEY_HEX"]
|
|
71
|
+
stream = attesto.create_stream(
|
|
72
|
+
use_case="ai-decision-history",
|
|
73
|
+
policy_id="policy-2026-01",
|
|
74
|
+
)
|
|
75
|
+
receipt = attesto.log_event(
|
|
76
|
+
stream_id=stream.stream_id,
|
|
77
|
+
source_ref="upstream-event-123",
|
|
78
|
+
event_type="decision",
|
|
79
|
+
payload={"decision": "approve", "score": 91},
|
|
80
|
+
)
|
|
81
|
+
batch = attesto.log_events(
|
|
82
|
+
stream.stream_id,
|
|
83
|
+
[
|
|
84
|
+
{"source_ref": "upstream-event-124", "payload": {"score": 88}},
|
|
85
|
+
{
|
|
86
|
+
"source_ref": "upstream-event-125",
|
|
87
|
+
"event_type": "decision",
|
|
88
|
+
"payload": {"decision": "review"},
|
|
89
|
+
},
|
|
90
|
+
],
|
|
91
|
+
)
|
|
92
|
+
assert batch.accepted == 2
|
|
93
|
+
stored = attesto.get_receipt(receipt.stream_event_id)
|
|
94
|
+
report = attesto.verify_receipt(
|
|
95
|
+
receipt=stored.receipt,
|
|
96
|
+
public_key_hex=receipt_signer_public_key_hex,
|
|
97
|
+
stream_event_id=receipt.stream_event_id,
|
|
98
|
+
)
|
|
99
|
+
assert report.ok
|
|
100
|
+
|
|
101
|
+
consistency = attesto.get_checkpoint_consistency(
|
|
102
|
+
"chk_current",
|
|
103
|
+
from_checkpoint_id="chk_previous",
|
|
104
|
+
)
|
|
105
|
+
assert consistency.step_count >= 1
|
|
106
|
+
|
|
107
|
+
policy = attesto.get_witness_policy("policy-ai-credit-v1")
|
|
108
|
+
assert policy.policy_hash
|
|
109
|
+
|
|
110
|
+
# Bundle export is intentionally stricter than receipt ingest: every
|
|
111
|
+
# checkpoint in the selected range must already have witness quorum
|
|
112
|
+
# evidence and a confirmed anchor epoch.
|
|
113
|
+
bundle = attesto.build_verifier_bundle(
|
|
114
|
+
from_checkpoint_id="chk_previous",
|
|
115
|
+
to_checkpoint_id="chk_current",
|
|
116
|
+
)
|
|
117
|
+
assert bundle.bundle_hash
|
|
118
|
+
|
|
119
|
+
# Present only after the checkpoint has confirmed on-chain.
|
|
120
|
+
anchor = attesto.get_anchor_epoch("aep_...")
|
|
121
|
+
assert anchor.status == "confirmed"
|
|
122
|
+
|
|
123
|
+
offline = attesto.verify_object(kind="bundle", proof_object=bundle.bundle)
|
|
124
|
+
assert offline.ok
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
`AttestoV2Client` talks to the production `/v2/streams`,
|
|
128
|
+
`/v2/streams/{stream_id}/events`,
|
|
129
|
+
`/v2/streams/{stream_id}/events/batch`, `/v2/receipts`, `/v2/windows`,
|
|
130
|
+
`/v2/checkpoints`, `/v2/checkpoints/{checkpoint_id}/consistency`,
|
|
131
|
+
`/v2/witness/policies/{policy_id}`, `/v2/anchors/{anchor_epoch_id}`,
|
|
132
|
+
`/v2/ivc/epochs/{ivc_epoch_id}`, `/v2/audit/packs`, and `/v2/verify`
|
|
133
|
+
APIs. Single and batch writes both return signed receipts. It exposes witness
|
|
134
|
+
policy and review-gated IVC epoch visibility. Receipt ingest can run before
|
|
135
|
+
enforced rollout gates; verifier-bundle export requires witnessed and confirmed
|
|
136
|
+
anchored checkpoints. Nova proof production remains review-gated until that
|
|
137
|
+
rollout gate is enabled.
|
|
138
|
+
|
|
139
|
+
## Signed webhook connectors
|
|
140
|
+
|
|
141
|
+
Use the connector helper when an external source posts to a signed-webhook
|
|
142
|
+
connector endpoint:
|
|
143
|
+
|
|
144
|
+
```python
|
|
145
|
+
import json
|
|
146
|
+
from attesto import signed_connector_webhook_headers
|
|
147
|
+
|
|
148
|
+
body = json.dumps({"sourceRef": "evt_123"}, separators=(",", ":")).encode()
|
|
149
|
+
headers = signed_connector_webhook_headers(connector_secret, body)
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
The helper signs `timestamp + "." + raw_body_bytes` and returns the exact
|
|
153
|
+
`X-Attesto-Connector-*` headers expected by
|
|
154
|
+
`/v2/connectors/signed-webhooks/{connectorId}/events`.
|
|
155
|
+
|
|
156
|
+
## Batching
|
|
157
|
+
|
|
158
|
+
```python
|
|
159
|
+
attesto.log_events([
|
|
160
|
+
{"type": "inference", "latency_ms": 40},
|
|
161
|
+
{"type": "inference", "latency_ms": 33},
|
|
162
|
+
{"type": "decision", "status": "pending", "payload": {"threshold": 0.7}},
|
|
163
|
+
])
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
Up to 1000 events per batch. The Attesto worker then groups them into a
|
|
167
|
+
Merkle tree and commits the root on Polygon mainnet within your tenant's
|
|
168
|
+
configured cadence (6h / 1h / per-event).
|
|
169
|
+
|
|
170
|
+
## Configuration
|
|
171
|
+
|
|
172
|
+
| arg | default | purpose |
|
|
173
|
+
|-----|---------|---------|
|
|
174
|
+
| `api_key` | — | Required. Must match `atto_live_<32 lowercase hex chars>` or `atto_test_<32 lowercase hex chars>`. |
|
|
175
|
+
| `base_url` | `https://verify.attesto.eu` | Public Attesto API origin. Override only for private/staging deployments. |
|
|
176
|
+
| `timeout_s` | `10.0` | Per-request timeout. |
|
|
177
|
+
| `max_retries` | `3` | Retries on 5xx / 429 / transport errors, with jittered exponential backoff. |
|
|
178
|
+
| `user_agent` | `attesto-python/0.1.2` | Sent as the UA header. |
|
|
179
|
+
|
|
180
|
+
## Error handling
|
|
181
|
+
|
|
182
|
+
```python
|
|
183
|
+
from attesto import (
|
|
184
|
+
AttestoClient,
|
|
185
|
+
AuthError,
|
|
186
|
+
RateLimitError,
|
|
187
|
+
ServerError,
|
|
188
|
+
ValidationError,
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
try:
|
|
192
|
+
attesto.log_event(type="inference")
|
|
193
|
+
except AuthError as exc: # 401 / 403 — bad key
|
|
194
|
+
...
|
|
195
|
+
except RateLimitError as exc: # 429 — exceeded tenant rate limit
|
|
196
|
+
...
|
|
197
|
+
except ValidationError as exc: # 4xx payload problem
|
|
198
|
+
...
|
|
199
|
+
except ServerError as exc: # 5xx after all retries exhausted
|
|
200
|
+
...
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
All Attesto SDK exceptions expose `status` and `detail` when the server
|
|
204
|
+
returned an HTTP response. Transport failures keep both as `None`.
|
|
205
|
+
|
|
206
|
+
## What you get
|
|
207
|
+
|
|
208
|
+
Every event:
|
|
209
|
+
|
|
210
|
+
1. Canonicalised to byte-exact JSON (sort keys, no whitespace, ASCII-safe).
|
|
211
|
+
2. SHA-256 hashed into a Merkle leaf.
|
|
212
|
+
3. Batched with other events at your cadence.
|
|
213
|
+
4. The Merkle root is committed on-chain via APSProvenance.
|
|
214
|
+
5. Every anchored event gets a tenant-authenticated proof from
|
|
215
|
+
`GET /v1/events/{id}/proof`. The proof payload contains
|
|
216
|
+
`canonicalJson`, `proof`, and `batchId`; submit those fields to
|
|
217
|
+
`POST https://verify.attesto.eu/v1/public/verify` or paste them into
|
|
218
|
+
the `/verify` page for independent verification.
|
|
219
|
+
|
|
220
|
+
You never handle keys, wallets, or gas — Attesto pays the gas and handles
|
|
221
|
+
the on-chain flow.
|
|
222
|
+
|
|
223
|
+
## Production behavior
|
|
224
|
+
|
|
225
|
+
- Defaults to `https://verify.attesto.eu`; override `base_url` only for
|
|
226
|
+
private or staging deployments.
|
|
227
|
+
- Use this SDK from server-side code only. Attesto system API keys are bearer
|
|
228
|
+
secrets and must never be embedded in browser bundles, mobile apps, logs, or
|
|
229
|
+
client-visible environment variables.
|
|
230
|
+
- Validates the key shape locally before making network calls. Production
|
|
231
|
+
system keys are shown once when the system is registered in Attesto.
|
|
232
|
+
- Validates `base_url` locally and accepts only `http` or `https` origins.
|
|
233
|
+
- Adds an `Idempotency-Key` header automatically for single-event and batch writes.
|
|
234
|
+
- Retries transient 429, 5xx, and transport failures with exponential backoff.
|
|
235
|
+
- Caps batch ingestion at 1000 events per request.
|
|
236
|
+
- Never handles wallets, private keys, or gas in application code.
|
attesto-0.1.2/README.md
ADDED
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
# Attesto Python SDK
|
|
2
|
+
|
|
3
|
+
Log every AI decision to a verifiable, on-chain audit trail with one call.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
pip install attesto
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
## Quick start
|
|
10
|
+
|
|
11
|
+
```python
|
|
12
|
+
from attesto import AttestoClient
|
|
13
|
+
|
|
14
|
+
attesto = AttestoClient(api_key="atto_live_...") # issued when you register a system
|
|
15
|
+
ack = attesto.log_event(
|
|
16
|
+
type="inference",
|
|
17
|
+
status="verified",
|
|
18
|
+
latency_ms=42,
|
|
19
|
+
input_hash="sha256:deadbeef...",
|
|
20
|
+
output_hash="sha256:cafebabe...",
|
|
21
|
+
payload={"score": 0.87, "model": "gpt-4o"},
|
|
22
|
+
)
|
|
23
|
+
print(ack.id, ack.system_id, ack.ts)
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Async
|
|
27
|
+
|
|
28
|
+
```python
|
|
29
|
+
from attesto import AsyncAttestoClient
|
|
30
|
+
|
|
31
|
+
async with AsyncAttestoClient(api_key="atto_live_...") as attesto:
|
|
32
|
+
ack = await attesto.log_event(type="inference", payload={"score": 0.87})
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Proofstream v2
|
|
36
|
+
|
|
37
|
+
```python
|
|
38
|
+
import os
|
|
39
|
+
from attesto import AttestoV2Client
|
|
40
|
+
|
|
41
|
+
with AttestoV2Client(api_key="atto_live_...") as attesto:
|
|
42
|
+
receipt_signer_public_key_hex = os.environ["ATTESTO_RECEIPT_SIGNER_PUBLIC_KEY_HEX"]
|
|
43
|
+
stream = attesto.create_stream(
|
|
44
|
+
use_case="ai-decision-history",
|
|
45
|
+
policy_id="policy-2026-01",
|
|
46
|
+
)
|
|
47
|
+
receipt = attesto.log_event(
|
|
48
|
+
stream_id=stream.stream_id,
|
|
49
|
+
source_ref="upstream-event-123",
|
|
50
|
+
event_type="decision",
|
|
51
|
+
payload={"decision": "approve", "score": 91},
|
|
52
|
+
)
|
|
53
|
+
batch = attesto.log_events(
|
|
54
|
+
stream.stream_id,
|
|
55
|
+
[
|
|
56
|
+
{"source_ref": "upstream-event-124", "payload": {"score": 88}},
|
|
57
|
+
{
|
|
58
|
+
"source_ref": "upstream-event-125",
|
|
59
|
+
"event_type": "decision",
|
|
60
|
+
"payload": {"decision": "review"},
|
|
61
|
+
},
|
|
62
|
+
],
|
|
63
|
+
)
|
|
64
|
+
assert batch.accepted == 2
|
|
65
|
+
stored = attesto.get_receipt(receipt.stream_event_id)
|
|
66
|
+
report = attesto.verify_receipt(
|
|
67
|
+
receipt=stored.receipt,
|
|
68
|
+
public_key_hex=receipt_signer_public_key_hex,
|
|
69
|
+
stream_event_id=receipt.stream_event_id,
|
|
70
|
+
)
|
|
71
|
+
assert report.ok
|
|
72
|
+
|
|
73
|
+
consistency = attesto.get_checkpoint_consistency(
|
|
74
|
+
"chk_current",
|
|
75
|
+
from_checkpoint_id="chk_previous",
|
|
76
|
+
)
|
|
77
|
+
assert consistency.step_count >= 1
|
|
78
|
+
|
|
79
|
+
policy = attesto.get_witness_policy("policy-ai-credit-v1")
|
|
80
|
+
assert policy.policy_hash
|
|
81
|
+
|
|
82
|
+
# Bundle export is intentionally stricter than receipt ingest: every
|
|
83
|
+
# checkpoint in the selected range must already have witness quorum
|
|
84
|
+
# evidence and a confirmed anchor epoch.
|
|
85
|
+
bundle = attesto.build_verifier_bundle(
|
|
86
|
+
from_checkpoint_id="chk_previous",
|
|
87
|
+
to_checkpoint_id="chk_current",
|
|
88
|
+
)
|
|
89
|
+
assert bundle.bundle_hash
|
|
90
|
+
|
|
91
|
+
# Present only after the checkpoint has confirmed on-chain.
|
|
92
|
+
anchor = attesto.get_anchor_epoch("aep_...")
|
|
93
|
+
assert anchor.status == "confirmed"
|
|
94
|
+
|
|
95
|
+
offline = attesto.verify_object(kind="bundle", proof_object=bundle.bundle)
|
|
96
|
+
assert offline.ok
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
`AttestoV2Client` talks to the production `/v2/streams`,
|
|
100
|
+
`/v2/streams/{stream_id}/events`,
|
|
101
|
+
`/v2/streams/{stream_id}/events/batch`, `/v2/receipts`, `/v2/windows`,
|
|
102
|
+
`/v2/checkpoints`, `/v2/checkpoints/{checkpoint_id}/consistency`,
|
|
103
|
+
`/v2/witness/policies/{policy_id}`, `/v2/anchors/{anchor_epoch_id}`,
|
|
104
|
+
`/v2/ivc/epochs/{ivc_epoch_id}`, `/v2/audit/packs`, and `/v2/verify`
|
|
105
|
+
APIs. Single and batch writes both return signed receipts. It exposes witness
|
|
106
|
+
policy and review-gated IVC epoch visibility. Receipt ingest can run before
|
|
107
|
+
enforced rollout gates; verifier-bundle export requires witnessed and confirmed
|
|
108
|
+
anchored checkpoints. Nova proof production remains review-gated until that
|
|
109
|
+
rollout gate is enabled.
|
|
110
|
+
|
|
111
|
+
## Signed webhook connectors
|
|
112
|
+
|
|
113
|
+
Use the connector helper when an external source posts to a signed-webhook
|
|
114
|
+
connector endpoint:
|
|
115
|
+
|
|
116
|
+
```python
|
|
117
|
+
import json
|
|
118
|
+
from attesto import signed_connector_webhook_headers
|
|
119
|
+
|
|
120
|
+
body = json.dumps({"sourceRef": "evt_123"}, separators=(",", ":")).encode()
|
|
121
|
+
headers = signed_connector_webhook_headers(connector_secret, body)
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
The helper signs `timestamp + "." + raw_body_bytes` and returns the exact
|
|
125
|
+
`X-Attesto-Connector-*` headers expected by
|
|
126
|
+
`/v2/connectors/signed-webhooks/{connectorId}/events`.
|
|
127
|
+
|
|
128
|
+
## Batching
|
|
129
|
+
|
|
130
|
+
```python
|
|
131
|
+
attesto.log_events([
|
|
132
|
+
{"type": "inference", "latency_ms": 40},
|
|
133
|
+
{"type": "inference", "latency_ms": 33},
|
|
134
|
+
{"type": "decision", "status": "pending", "payload": {"threshold": 0.7}},
|
|
135
|
+
])
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Up to 1000 events per batch. The Attesto worker then groups them into a
|
|
139
|
+
Merkle tree and commits the root on Polygon mainnet within your tenant's
|
|
140
|
+
configured cadence (6h / 1h / per-event).
|
|
141
|
+
|
|
142
|
+
## Configuration
|
|
143
|
+
|
|
144
|
+
| arg | default | purpose |
|
|
145
|
+
|-----|---------|---------|
|
|
146
|
+
| `api_key` | — | Required. Must match `atto_live_<32 lowercase hex chars>` or `atto_test_<32 lowercase hex chars>`. |
|
|
147
|
+
| `base_url` | `https://verify.attesto.eu` | Public Attesto API origin. Override only for private/staging deployments. |
|
|
148
|
+
| `timeout_s` | `10.0` | Per-request timeout. |
|
|
149
|
+
| `max_retries` | `3` | Retries on 5xx / 429 / transport errors, with jittered exponential backoff. |
|
|
150
|
+
| `user_agent` | `attesto-python/0.1.2` | Sent as the UA header. |
|
|
151
|
+
|
|
152
|
+
## Error handling
|
|
153
|
+
|
|
154
|
+
```python
|
|
155
|
+
from attesto import (
|
|
156
|
+
AttestoClient,
|
|
157
|
+
AuthError,
|
|
158
|
+
RateLimitError,
|
|
159
|
+
ServerError,
|
|
160
|
+
ValidationError,
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
try:
|
|
164
|
+
attesto.log_event(type="inference")
|
|
165
|
+
except AuthError as exc: # 401 / 403 — bad key
|
|
166
|
+
...
|
|
167
|
+
except RateLimitError as exc: # 429 — exceeded tenant rate limit
|
|
168
|
+
...
|
|
169
|
+
except ValidationError as exc: # 4xx payload problem
|
|
170
|
+
...
|
|
171
|
+
except ServerError as exc: # 5xx after all retries exhausted
|
|
172
|
+
...
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
All Attesto SDK exceptions expose `status` and `detail` when the server
|
|
176
|
+
returned an HTTP response. Transport failures keep both as `None`.
|
|
177
|
+
|
|
178
|
+
## What you get
|
|
179
|
+
|
|
180
|
+
Every event:
|
|
181
|
+
|
|
182
|
+
1. Canonicalised to byte-exact JSON (sort keys, no whitespace, ASCII-safe).
|
|
183
|
+
2. SHA-256 hashed into a Merkle leaf.
|
|
184
|
+
3. Batched with other events at your cadence.
|
|
185
|
+
4. The Merkle root is committed on-chain via APSProvenance.
|
|
186
|
+
5. Every anchored event gets a tenant-authenticated proof from
|
|
187
|
+
`GET /v1/events/{id}/proof`. The proof payload contains
|
|
188
|
+
`canonicalJson`, `proof`, and `batchId`; submit those fields to
|
|
189
|
+
`POST https://verify.attesto.eu/v1/public/verify` or paste them into
|
|
190
|
+
the `/verify` page for independent verification.
|
|
191
|
+
|
|
192
|
+
You never handle keys, wallets, or gas — Attesto pays the gas and handles
|
|
193
|
+
the on-chain flow.
|
|
194
|
+
|
|
195
|
+
## Production behavior
|
|
196
|
+
|
|
197
|
+
- Defaults to `https://verify.attesto.eu`; override `base_url` only for
|
|
198
|
+
private or staging deployments.
|
|
199
|
+
- Use this SDK from server-side code only. Attesto system API keys are bearer
|
|
200
|
+
secrets and must never be embedded in browser bundles, mobile apps, logs, or
|
|
201
|
+
client-visible environment variables.
|
|
202
|
+
- Validates the key shape locally before making network calls. Production
|
|
203
|
+
system keys are shown once when the system is registered in Attesto.
|
|
204
|
+
- Validates `base_url` locally and accepts only `http` or `https` origins.
|
|
205
|
+
- Adds an `Idempotency-Key` header automatically for single-event and batch writes.
|
|
206
|
+
- Retries transient 429, 5xx, and transport failures with exponential backoff.
|
|
207
|
+
- Caps batch ingestion at 1000 events per request.
|
|
208
|
+
- Never handles wallets, private keys, or gas in application code.
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "attesto"
|
|
3
|
+
version = "0.1.2"
|
|
4
|
+
description = "Attesto AI Python SDK — log verifiable AI events to Polygon via one function call."
|
|
5
|
+
requires-python = ">=3.10"
|
|
6
|
+
readme = "README.md"
|
|
7
|
+
license = "Apache-2.0"
|
|
8
|
+
authors = [
|
|
9
|
+
{ name = "Attesto", email = "info@attesto.eu" },
|
|
10
|
+
]
|
|
11
|
+
keywords = ["ai", "compliance", "eu-ai-act", "polygon", "merkle", "audit"]
|
|
12
|
+
classifiers = [
|
|
13
|
+
"Development Status :: 5 - Production/Stable",
|
|
14
|
+
"Intended Audience :: Developers",
|
|
15
|
+
"Programming Language :: Python :: 3",
|
|
16
|
+
"Programming Language :: Python :: 3.10",
|
|
17
|
+
"Programming Language :: Python :: 3.11",
|
|
18
|
+
"Programming Language :: Python :: 3.12",
|
|
19
|
+
"Topic :: Software Development :: Libraries",
|
|
20
|
+
]
|
|
21
|
+
dependencies = [
|
|
22
|
+
"httpx>=0.27",
|
|
23
|
+
"pydantic>=2",
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
[project.optional-dependencies]
|
|
27
|
+
dev = [
|
|
28
|
+
"build>=1.2",
|
|
29
|
+
"pytest>=8",
|
|
30
|
+
"pytest-asyncio>=0.24",
|
|
31
|
+
"pytest-httpx>=0.35",
|
|
32
|
+
"ruff>=0.7",
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
[project.urls]
|
|
36
|
+
Homepage = "https://attesto.eu"
|
|
37
|
+
Documentation = "https://docs.attesto.eu/manuals/sdks.html"
|
|
38
|
+
Security = "https://attesto.eu/security"
|
|
39
|
+
|
|
40
|
+
[build-system]
|
|
41
|
+
requires = ["setuptools>=68"]
|
|
42
|
+
build-backend = "setuptools.build_meta"
|
|
43
|
+
|
|
44
|
+
[tool.setuptools.packages.find]
|
|
45
|
+
where = ["src"]
|
|
46
|
+
|
|
47
|
+
[tool.pytest.ini_options]
|
|
48
|
+
asyncio_mode = "auto"
|
|
49
|
+
testpaths = ["tests"]
|
attesto-0.1.2/setup.cfg
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
"""Attesto AI Python SDK.
|
|
2
|
+
|
|
3
|
+
Log every AI decision to a verifiable on-chain audit trail with one call:
|
|
4
|
+
|
|
5
|
+
from attesto import AttestoClient
|
|
6
|
+
|
|
7
|
+
attesto = AttestoClient(api_key="atto_live_...")
|
|
8
|
+
attesto.log_event(type="inference", status="verified", payload={"score": 0.87})
|
|
9
|
+
|
|
10
|
+
Async? Use the same ergonomics:
|
|
11
|
+
|
|
12
|
+
from attesto import AsyncAttestoClient
|
|
13
|
+
|
|
14
|
+
async with AsyncAttestoClient(api_key="atto_live_...") as attesto:
|
|
15
|
+
await attesto.log_event(type="inference", status="verified")
|
|
16
|
+
|
|
17
|
+
Every event you log is batched by the Attesto worker, hashed into a Merkle
|
|
18
|
+
tree, and committed to Polygon mainnet. Your events become verifiable evidence
|
|
19
|
+
for auditors under the EU AI Act.
|
|
20
|
+
"""
|
|
21
|
+
from attesto.client import DEFAULT_BASE_URL, AttestoClient, AttestoV2Client, VBPClient
|
|
22
|
+
from attesto.connectors import (
|
|
23
|
+
sign_connector_webhook_payload,
|
|
24
|
+
signed_connector_webhook_headers,
|
|
25
|
+
)
|
|
26
|
+
from attesto.async_client import (
|
|
27
|
+
AsyncAttestoClient,
|
|
28
|
+
AsyncAttestoV2Client,
|
|
29
|
+
AsyncVBPClient,
|
|
30
|
+
)
|
|
31
|
+
from attesto.errors import (
|
|
32
|
+
AttestoError,
|
|
33
|
+
AuthError,
|
|
34
|
+
RateLimitError,
|
|
35
|
+
ServerError,
|
|
36
|
+
ValidationError,
|
|
37
|
+
)
|
|
38
|
+
from attesto.models import (
|
|
39
|
+
BatchResponse,
|
|
40
|
+
Event,
|
|
41
|
+
EventResponse,
|
|
42
|
+
ProofstreamAnchorEpoch,
|
|
43
|
+
ProofstreamCheckpoint,
|
|
44
|
+
ProofstreamCheckpointWindow,
|
|
45
|
+
ProofstreamConsistencyCheckpoint,
|
|
46
|
+
ProofstreamConsistencyProof,
|
|
47
|
+
ProofstreamEvent,
|
|
48
|
+
ProofstreamEventBatchResponse,
|
|
49
|
+
ProofstreamEventReceipt,
|
|
50
|
+
ProofstreamIvcEpoch,
|
|
51
|
+
ProofstreamOfflineVerifyReport,
|
|
52
|
+
ProofstreamReceiptSignature,
|
|
53
|
+
ProofstreamReceiptVerifyReport,
|
|
54
|
+
ProofstreamSignedReceipt,
|
|
55
|
+
ProofstreamStream,
|
|
56
|
+
ProofstreamStreamCreate,
|
|
57
|
+
ProofstreamStreamHead,
|
|
58
|
+
ProofstreamVerifyKind,
|
|
59
|
+
ProofstreamVerifierBundle,
|
|
60
|
+
ProofstreamWitnessPolicy,
|
|
61
|
+
ProofstreamWindow,
|
|
62
|
+
ProofstreamWindowLeaf,
|
|
63
|
+
)
|
|
64
|
+
from attesto.proofstream import (
|
|
65
|
+
PROOFSTREAM_DOMAINS,
|
|
66
|
+
PROOFSTREAM_PROTOCOL,
|
|
67
|
+
PROOFSTREAM_PROTOCOL_VERSION,
|
|
68
|
+
canonical_json,
|
|
69
|
+
canonical_json_bytes,
|
|
70
|
+
canonical_json_hex,
|
|
71
|
+
domain_hash_hex,
|
|
72
|
+
sha256_hex,
|
|
73
|
+
)
|
|
74
|
+
from attesto.version import __version__
|
|
75
|
+
|
|
76
|
+
__all__ = [
|
|
77
|
+
"VBPClient",
|
|
78
|
+
"AttestoClient",
|
|
79
|
+
"AttestoV2Client",
|
|
80
|
+
"DEFAULT_BASE_URL",
|
|
81
|
+
"AsyncVBPClient",
|
|
82
|
+
"AsyncAttestoClient",
|
|
83
|
+
"AsyncAttestoV2Client",
|
|
84
|
+
"Event",
|
|
85
|
+
"EventResponse",
|
|
86
|
+
"BatchResponse",
|
|
87
|
+
"ProofstreamStreamCreate",
|
|
88
|
+
"ProofstreamStream",
|
|
89
|
+
"ProofstreamStreamHead",
|
|
90
|
+
"ProofstreamWitnessPolicy",
|
|
91
|
+
"ProofstreamAnchorEpoch",
|
|
92
|
+
"ProofstreamVerifierBundle",
|
|
93
|
+
"ProofstreamWindowLeaf",
|
|
94
|
+
"ProofstreamWindow",
|
|
95
|
+
"ProofstreamCheckpointWindow",
|
|
96
|
+
"ProofstreamCheckpoint",
|
|
97
|
+
"ProofstreamConsistencyCheckpoint",
|
|
98
|
+
"ProofstreamConsistencyProof",
|
|
99
|
+
"ProofstreamIvcEpoch",
|
|
100
|
+
"ProofstreamEvent",
|
|
101
|
+
"ProofstreamEventBatchResponse",
|
|
102
|
+
"ProofstreamReceiptSignature",
|
|
103
|
+
"ProofstreamSignedReceipt",
|
|
104
|
+
"ProofstreamEventReceipt",
|
|
105
|
+
"ProofstreamReceiptVerifyReport",
|
|
106
|
+
"ProofstreamOfflineVerifyReport",
|
|
107
|
+
"ProofstreamVerifyKind",
|
|
108
|
+
"PROOFSTREAM_DOMAINS",
|
|
109
|
+
"PROOFSTREAM_PROTOCOL",
|
|
110
|
+
"PROOFSTREAM_PROTOCOL_VERSION",
|
|
111
|
+
"canonical_json",
|
|
112
|
+
"canonical_json_bytes",
|
|
113
|
+
"canonical_json_hex",
|
|
114
|
+
"domain_hash_hex",
|
|
115
|
+
"sha256_hex",
|
|
116
|
+
"sign_connector_webhook_payload",
|
|
117
|
+
"signed_connector_webhook_headers",
|
|
118
|
+
"AttestoError",
|
|
119
|
+
"AuthError",
|
|
120
|
+
"RateLimitError",
|
|
121
|
+
"ServerError",
|
|
122
|
+
"ValidationError",
|
|
123
|
+
"__version__",
|
|
124
|
+
]
|