aotrust-protocol 2.3.6__tar.gz → 2.4.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.
- aotrust_protocol-2.4.0/PKG-INFO +199 -0
- aotrust_protocol-2.4.0/README.md +172 -0
- {aotrust_protocol-2.3.6 → aotrust_protocol-2.4.0}/agent_notary/__init__.py +1 -1
- {aotrust_protocol-2.3.6 → aotrust_protocol-2.4.0}/agent_notary/client.py +65 -1
- {aotrust_protocol-2.3.6 → aotrust_protocol-2.4.0}/agent_notary/exceptions.py +9 -1
- {aotrust_protocol-2.3.6 → aotrust_protocol-2.4.0}/agent_notary/models.py +11 -0
- aotrust_protocol-2.4.0/aotrust_protocol.egg-info/PKG-INFO +199 -0
- {aotrust_protocol-2.3.6 → aotrust_protocol-2.4.0}/pyproject.toml +1 -1
- aotrust_protocol-2.3.6/PKG-INFO +0 -118
- aotrust_protocol-2.3.6/README.md +0 -91
- aotrust_protocol-2.3.6/aotrust_protocol.egg-info/PKG-INFO +0 -118
- {aotrust_protocol-2.3.6 → aotrust_protocol-2.4.0}/LICENSE +0 -0
- {aotrust_protocol-2.3.6 → aotrust_protocol-2.4.0}/agent_notary/tools.py +0 -0
- {aotrust_protocol-2.3.6 → aotrust_protocol-2.4.0}/aotrust_protocol.egg-info/SOURCES.txt +0 -0
- {aotrust_protocol-2.3.6 → aotrust_protocol-2.4.0}/aotrust_protocol.egg-info/dependency_links.txt +0 -0
- {aotrust_protocol-2.3.6 → aotrust_protocol-2.4.0}/aotrust_protocol.egg-info/requires.txt +0 -0
- {aotrust_protocol-2.3.6 → aotrust_protocol-2.4.0}/aotrust_protocol.egg-info/top_level.txt +0 -0
- {aotrust_protocol-2.3.6 → aotrust_protocol-2.4.0}/setup.cfg +0 -0
- {aotrust_protocol-2.3.6 → aotrust_protocol-2.4.0}/tests/test_client.py +0 -0
- {aotrust_protocol-2.3.6 → aotrust_protocol-2.4.0}/tests/test_live_smoke.py +0 -0
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: aotrust-protocol
|
|
3
|
+
Version: 2.4.0
|
|
4
|
+
Summary: Asyncio-native SDK for A&O Trust Layer — Agent Notary Service
|
|
5
|
+
Author: A&O Trust Layer
|
|
6
|
+
License: MIT
|
|
7
|
+
Classifier: Development Status :: 4 - Beta
|
|
8
|
+
Classifier: Intended Audience :: Developers
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
13
|
+
Classifier: Framework :: AsyncIO
|
|
14
|
+
Classifier: Topic :: Security :: Cryptography
|
|
15
|
+
Requires-Python: >=3.10
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
License-File: LICENSE
|
|
18
|
+
Requires-Dist: aiohttp>=3.9
|
|
19
|
+
Requires-Dist: pydantic>=2.0
|
|
20
|
+
Requires-Dist: pynacl>=1.5
|
|
21
|
+
Provides-Extra: dev
|
|
22
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
23
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
|
|
24
|
+
Requires-Dist: pytest-cov>=4.0; extra == "dev"
|
|
25
|
+
Requires-Dist: aioresponses>=0.7; extra == "dev"
|
|
26
|
+
Dynamic: license-file
|
|
27
|
+
|
|
28
|
+
# aotrust-protocol SDK
|
|
29
|
+
|
|
30
|
+
<!-- mcp-name: link.aotrust/notary -->
|
|
31
|
+
|
|
32
|
+
Asyncio-native Python SDK for the AOTrust Notary API — cryptographic proof-of-existence for AI agent outputs.
|
|
33
|
+
|
|
34
|
+
> **Why protocol-level proofs?** An ERC-8004 empirical study (2026) measured that **>98% of agent feedback records carry no proof of an underlying interaction** (no payment proof, no task linkage); 59–91% of reviewers on three chains participated in coordinated Sybil campaigns, and manipulating a reputation score past a trust threshold costs as little as **$0.0027**. The agentic economy is moving from *statements* to *verifiable claims*: a PDR (239 bytes, Ed25519-signed, hash-chained, NEAR-anchored) turns any output or payment into independent evidence — verifiable by anyone, without trusting the provider's database.
|
|
35
|
+
> *(Refs: arXiv:2610.12948 [cs.CR], 2026; ERC-8004 Sybil measurements, Xiong et al. 2026.)*
|
|
36
|
+
|
|
37
|
+
## Install
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
pip install aotrust-protocol
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Quickstart
|
|
44
|
+
|
|
45
|
+
### Free tier — no API key, no wallet (fastest way to try)
|
|
46
|
+
|
|
47
|
+
```python
|
|
48
|
+
import asyncio, hashlib
|
|
49
|
+
from agent_notary import NotaryClient
|
|
50
|
+
|
|
51
|
+
async def main():
|
|
52
|
+
# IMPORTANT: no /v1 suffix — the SDK appends API paths itself.
|
|
53
|
+
client = NotaryClient(base_url="https://api.aotrust.link")
|
|
54
|
+
|
|
55
|
+
work_hash = hashlib.sha256(b"my artifact text").hexdigest()
|
|
56
|
+
result = await client.shield_free(work_hash)
|
|
57
|
+
job_id = result["job_id"] # keep this UUID — it is the handle
|
|
58
|
+
print("job_id:", job_id)
|
|
59
|
+
|
|
60
|
+
# Check status BY job_id (not by the artifact text!)
|
|
61
|
+
status = await client.get_status(job_id)
|
|
62
|
+
print("status:", status.status) # PENDING → anchored (anchor batches)
|
|
63
|
+
|
|
64
|
+
# Fetch + verify the PDR
|
|
65
|
+
pdr = await client.get_pdr(job_id)
|
|
66
|
+
print("verify in browser:", pdr.verify_url)
|
|
67
|
+
assert (await client.verify_pdr(pdr.pdr_b64))["valid"]
|
|
68
|
+
|
|
69
|
+
asyncio.run(main())
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Free tier: 5 PDR / 24h per IP. Examples: [`examples/04_free_tier.py`](examples/04_free_tier.py).
|
|
73
|
+
|
|
74
|
+
### Witness mode (pre-paid tx_hash)
|
|
75
|
+
|
|
76
|
+
```python
|
|
77
|
+
import asyncio
|
|
78
|
+
from agent_notary import NotaryClient, NotarizeRequest
|
|
79
|
+
|
|
80
|
+
async def main():
|
|
81
|
+
client = NotaryClient(
|
|
82
|
+
api_key="your-api-key",
|
|
83
|
+
base_url="https://api.aotrust.link" # no /v1 suffix!
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
# Submit notarization
|
|
87
|
+
req = NotarizeRequest(
|
|
88
|
+
tx_hash="EzrfDW5b...",
|
|
89
|
+
work_hash="599d6999...",
|
|
90
|
+
agent_sig="base64_sig_A...",
|
|
91
|
+
agent_pubkey="aff91a18...",
|
|
92
|
+
)
|
|
93
|
+
result = await client.notarize(req)
|
|
94
|
+
print(f"Job: {result.job_id}")
|
|
95
|
+
|
|
96
|
+
# Poll for PDR — status/pdr calls always take the job_id returned above
|
|
97
|
+
status = await client.wait_for_pdr(result.job_id, timeout=60)
|
|
98
|
+
if status.is_anchored:
|
|
99
|
+
pdr = await client.get_pdr(result.job_id)
|
|
100
|
+
assert pdr.verify(client.notary_pubkey)
|
|
101
|
+
print("PDR Valid: YES")
|
|
102
|
+
|
|
103
|
+
asyncio.run(main())
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
> **Why no `/v1`?** `base_url` must be `https://api.aotrust.link`. The SDK
|
|
107
|
+
> already builds `/v1/...` request paths; adding `/v1` yourself produces
|
|
108
|
+
> `/v1/v1/...` → HTTP 404 on every call. This bit a real user (2026-09-29:
|
|
109
|
+
> quickstart copy-paste + status lookups by artifact text instead of job_id).
|
|
110
|
+
|
|
111
|
+
## Configuration
|
|
112
|
+
|
|
113
|
+
| Env Var | Constructor Arg | Description |
|
|
114
|
+
|---------|-----------------|-------------|
|
|
115
|
+
| `NOTARY_API_KEY` | `api_key` | API key for auth |
|
|
116
|
+
| `NOTARY_API_URL` | `base_url` | API base URL |
|
|
117
|
+
| `NOTARY_PUBKEY` | `notary_pubkey` | Notary Ed25519 public key (hex) |
|
|
118
|
+
|
|
119
|
+
## Endpoints
|
|
120
|
+
|
|
121
|
+
| Method | Path | Description |
|
|
122
|
+
|--------|------|-------------|
|
|
123
|
+
| POST | `/v1/notarize` | Submit notarization |
|
|
124
|
+
| GET | `/v1/status/{job_id}` | Poll job status |
|
|
125
|
+
| GET | `/v1/pdr/{job_id}` | Get PDR |
|
|
126
|
+
| POST | `/v1/notarize/quote` | Get price quote |
|
|
127
|
+
| GET | `/.well-known/agent.json` | MCP Server Card |
|
|
128
|
+
| GET | `/openapi.json` | OpenAPI 3.0 spec |
|
|
129
|
+
|
|
130
|
+
## PDR Verification
|
|
131
|
+
|
|
132
|
+
```python
|
|
133
|
+
pdr = await client.get_pdr(job_id)
|
|
134
|
+
is_valid = pdr.verify(notary_pubkey_hex="9c7d64bb...")
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Uses NEP-413 raw-buffer verification. No SHA256 pre-hash.
|
|
138
|
+
|
|
139
|
+
## Error Handling
|
|
140
|
+
|
|
141
|
+
```python
|
|
142
|
+
from agent_notary.exceptions import (
|
|
143
|
+
NotaryAuthError, # 401 (authenticated CI surfaces; anonymous x402 gives 402 instead)
|
|
144
|
+
NotaryPaymentError, # 402 x402-challenge / 403
|
|
145
|
+
NotaryNotFoundError, # 404
|
|
146
|
+
NotaryValidationError, # 400/422 — invalid params, INVALID_AGENT_SIGNATURE
|
|
147
|
+
NotaryConflictError, # 409 — tx_hash/job_id already used (idempotency)
|
|
148
|
+
NotaryServerError, # 5xx, carries status_code
|
|
149
|
+
NotaryError, # base — safety net
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
try:
|
|
153
|
+
result = await client.notarize(req)
|
|
154
|
+
except NotaryAuthError:
|
|
155
|
+
print("Invalid API key")
|
|
156
|
+
except NotaryPaymentError as e:
|
|
157
|
+
print(f"Payment/sig failed: {e}")
|
|
158
|
+
except NotaryValidationError as e:
|
|
159
|
+
print(f"Bad request or invalid signature: {e}")
|
|
160
|
+
except NotaryConflictError:
|
|
161
|
+
print("This tx_hash/job_id was already used — generate a new one")
|
|
162
|
+
except NotaryError as e:
|
|
163
|
+
print(f"Other SDK error: {e}")
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
## Development
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
pip install -e ".[dev]"
|
|
170
|
+
pytest tests/ -v
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
## Payment binding (NEAR rail)
|
|
174
|
+
|
|
175
|
+
NEAR payments must be provably linked to the account that sent them. Pass
|
|
176
|
+
`payer_account` (the NEAR account that paid) together with `bind_sig` — a
|
|
177
|
+
base64 Ed25519 **NEP-413 raw-buffer** signature (no SHA-256 pre-hash) over:
|
|
178
|
+
|
|
179
|
+
```
|
|
180
|
+
AOTrust AITP-BIND v1
|
|
181
|
+
work_hash:<64 hex>
|
|
182
|
+
tx_hash:<tx hash exactly as submitted>
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
The server resolves the account's public key via RPC and checks
|
|
186
|
+
`transaction.signer_id == payer_account`; a mismatch is rejected with
|
|
187
|
+
`403 PAYMENT_BIND_SIGNER_MISMATCH`, a bad signature with
|
|
188
|
+
`403 PAYMENT_BIND_INVALID`.
|
|
189
|
+
|
|
190
|
+
If `NEAR_PRIVATE_KEY` is set in the environment (or `near_private_key=` is
|
|
191
|
+
passed), the SDK signs the canonical message itself and fills `bind_sig`:
|
|
192
|
+
|
|
193
|
+
```python
|
|
194
|
+
req = NotarizeRequest(tx_hash=tx, work_hash=wh)
|
|
195
|
+
await client.notarize(req, payer_account="alice.near")
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
Server mode `AITP_BIND_MODE=warn` (default) accepts unbound requests with a
|
|
199
|
+
`PAYMENT_UNBOUND` log line; `enforce` requires the fields.
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
# aotrust-protocol SDK
|
|
2
|
+
|
|
3
|
+
<!-- mcp-name: link.aotrust/notary -->
|
|
4
|
+
|
|
5
|
+
Asyncio-native Python SDK for the AOTrust Notary API — cryptographic proof-of-existence for AI agent outputs.
|
|
6
|
+
|
|
7
|
+
> **Why protocol-level proofs?** An ERC-8004 empirical study (2026) measured that **>98% of agent feedback records carry no proof of an underlying interaction** (no payment proof, no task linkage); 59–91% of reviewers on three chains participated in coordinated Sybil campaigns, and manipulating a reputation score past a trust threshold costs as little as **$0.0027**. The agentic economy is moving from *statements* to *verifiable claims*: a PDR (239 bytes, Ed25519-signed, hash-chained, NEAR-anchored) turns any output or payment into independent evidence — verifiable by anyone, without trusting the provider's database.
|
|
8
|
+
> *(Refs: arXiv:2610.12948 [cs.CR], 2026; ERC-8004 Sybil measurements, Xiong et al. 2026.)*
|
|
9
|
+
|
|
10
|
+
## Install
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
pip install aotrust-protocol
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Quickstart
|
|
17
|
+
|
|
18
|
+
### Free tier — no API key, no wallet (fastest way to try)
|
|
19
|
+
|
|
20
|
+
```python
|
|
21
|
+
import asyncio, hashlib
|
|
22
|
+
from agent_notary import NotaryClient
|
|
23
|
+
|
|
24
|
+
async def main():
|
|
25
|
+
# IMPORTANT: no /v1 suffix — the SDK appends API paths itself.
|
|
26
|
+
client = NotaryClient(base_url="https://api.aotrust.link")
|
|
27
|
+
|
|
28
|
+
work_hash = hashlib.sha256(b"my artifact text").hexdigest()
|
|
29
|
+
result = await client.shield_free(work_hash)
|
|
30
|
+
job_id = result["job_id"] # keep this UUID — it is the handle
|
|
31
|
+
print("job_id:", job_id)
|
|
32
|
+
|
|
33
|
+
# Check status BY job_id (not by the artifact text!)
|
|
34
|
+
status = await client.get_status(job_id)
|
|
35
|
+
print("status:", status.status) # PENDING → anchored (anchor batches)
|
|
36
|
+
|
|
37
|
+
# Fetch + verify the PDR
|
|
38
|
+
pdr = await client.get_pdr(job_id)
|
|
39
|
+
print("verify in browser:", pdr.verify_url)
|
|
40
|
+
assert (await client.verify_pdr(pdr.pdr_b64))["valid"]
|
|
41
|
+
|
|
42
|
+
asyncio.run(main())
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Free tier: 5 PDR / 24h per IP. Examples: [`examples/04_free_tier.py`](examples/04_free_tier.py).
|
|
46
|
+
|
|
47
|
+
### Witness mode (pre-paid tx_hash)
|
|
48
|
+
|
|
49
|
+
```python
|
|
50
|
+
import asyncio
|
|
51
|
+
from agent_notary import NotaryClient, NotarizeRequest
|
|
52
|
+
|
|
53
|
+
async def main():
|
|
54
|
+
client = NotaryClient(
|
|
55
|
+
api_key="your-api-key",
|
|
56
|
+
base_url="https://api.aotrust.link" # no /v1 suffix!
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
# Submit notarization
|
|
60
|
+
req = NotarizeRequest(
|
|
61
|
+
tx_hash="EzrfDW5b...",
|
|
62
|
+
work_hash="599d6999...",
|
|
63
|
+
agent_sig="base64_sig_A...",
|
|
64
|
+
agent_pubkey="aff91a18...",
|
|
65
|
+
)
|
|
66
|
+
result = await client.notarize(req)
|
|
67
|
+
print(f"Job: {result.job_id}")
|
|
68
|
+
|
|
69
|
+
# Poll for PDR — status/pdr calls always take the job_id returned above
|
|
70
|
+
status = await client.wait_for_pdr(result.job_id, timeout=60)
|
|
71
|
+
if status.is_anchored:
|
|
72
|
+
pdr = await client.get_pdr(result.job_id)
|
|
73
|
+
assert pdr.verify(client.notary_pubkey)
|
|
74
|
+
print("PDR Valid: YES")
|
|
75
|
+
|
|
76
|
+
asyncio.run(main())
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
> **Why no `/v1`?** `base_url` must be `https://api.aotrust.link`. The SDK
|
|
80
|
+
> already builds `/v1/...` request paths; adding `/v1` yourself produces
|
|
81
|
+
> `/v1/v1/...` → HTTP 404 on every call. This bit a real user (2026-09-29:
|
|
82
|
+
> quickstart copy-paste + status lookups by artifact text instead of job_id).
|
|
83
|
+
|
|
84
|
+
## Configuration
|
|
85
|
+
|
|
86
|
+
| Env Var | Constructor Arg | Description |
|
|
87
|
+
|---------|-----------------|-------------|
|
|
88
|
+
| `NOTARY_API_KEY` | `api_key` | API key for auth |
|
|
89
|
+
| `NOTARY_API_URL` | `base_url` | API base URL |
|
|
90
|
+
| `NOTARY_PUBKEY` | `notary_pubkey` | Notary Ed25519 public key (hex) |
|
|
91
|
+
|
|
92
|
+
## Endpoints
|
|
93
|
+
|
|
94
|
+
| Method | Path | Description |
|
|
95
|
+
|--------|------|-------------|
|
|
96
|
+
| POST | `/v1/notarize` | Submit notarization |
|
|
97
|
+
| GET | `/v1/status/{job_id}` | Poll job status |
|
|
98
|
+
| GET | `/v1/pdr/{job_id}` | Get PDR |
|
|
99
|
+
| POST | `/v1/notarize/quote` | Get price quote |
|
|
100
|
+
| GET | `/.well-known/agent.json` | MCP Server Card |
|
|
101
|
+
| GET | `/openapi.json` | OpenAPI 3.0 spec |
|
|
102
|
+
|
|
103
|
+
## PDR Verification
|
|
104
|
+
|
|
105
|
+
```python
|
|
106
|
+
pdr = await client.get_pdr(job_id)
|
|
107
|
+
is_valid = pdr.verify(notary_pubkey_hex="9c7d64bb...")
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Uses NEP-413 raw-buffer verification. No SHA256 pre-hash.
|
|
111
|
+
|
|
112
|
+
## Error Handling
|
|
113
|
+
|
|
114
|
+
```python
|
|
115
|
+
from agent_notary.exceptions import (
|
|
116
|
+
NotaryAuthError, # 401 (authenticated CI surfaces; anonymous x402 gives 402 instead)
|
|
117
|
+
NotaryPaymentError, # 402 x402-challenge / 403
|
|
118
|
+
NotaryNotFoundError, # 404
|
|
119
|
+
NotaryValidationError, # 400/422 — invalid params, INVALID_AGENT_SIGNATURE
|
|
120
|
+
NotaryConflictError, # 409 — tx_hash/job_id already used (idempotency)
|
|
121
|
+
NotaryServerError, # 5xx, carries status_code
|
|
122
|
+
NotaryError, # base — safety net
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
try:
|
|
126
|
+
result = await client.notarize(req)
|
|
127
|
+
except NotaryAuthError:
|
|
128
|
+
print("Invalid API key")
|
|
129
|
+
except NotaryPaymentError as e:
|
|
130
|
+
print(f"Payment/sig failed: {e}")
|
|
131
|
+
except NotaryValidationError as e:
|
|
132
|
+
print(f"Bad request or invalid signature: {e}")
|
|
133
|
+
except NotaryConflictError:
|
|
134
|
+
print("This tx_hash/job_id was already used — generate a new one")
|
|
135
|
+
except NotaryError as e:
|
|
136
|
+
print(f"Other SDK error: {e}")
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## Development
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
pip install -e ".[dev]"
|
|
143
|
+
pytest tests/ -v
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## Payment binding (NEAR rail)
|
|
147
|
+
|
|
148
|
+
NEAR payments must be provably linked to the account that sent them. Pass
|
|
149
|
+
`payer_account` (the NEAR account that paid) together with `bind_sig` — a
|
|
150
|
+
base64 Ed25519 **NEP-413 raw-buffer** signature (no SHA-256 pre-hash) over:
|
|
151
|
+
|
|
152
|
+
```
|
|
153
|
+
AOTrust AITP-BIND v1
|
|
154
|
+
work_hash:<64 hex>
|
|
155
|
+
tx_hash:<tx hash exactly as submitted>
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
The server resolves the account's public key via RPC and checks
|
|
159
|
+
`transaction.signer_id == payer_account`; a mismatch is rejected with
|
|
160
|
+
`403 PAYMENT_BIND_SIGNER_MISMATCH`, a bad signature with
|
|
161
|
+
`403 PAYMENT_BIND_INVALID`.
|
|
162
|
+
|
|
163
|
+
If `NEAR_PRIVATE_KEY` is set in the environment (or `near_private_key=` is
|
|
164
|
+
passed), the SDK signs the canonical message itself and fills `bind_sig`:
|
|
165
|
+
|
|
166
|
+
```python
|
|
167
|
+
req = NotarizeRequest(tx_hash=tx, work_hash=wh)
|
|
168
|
+
await client.notarize(req, payer_account="alice.near")
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
Server mode `AITP_BIND_MODE=warn` (default) accepts unbound requests with a
|
|
172
|
+
`PAYMENT_UNBOUND` log line; `enforce` requires the fields.
|
|
@@ -9,12 +9,48 @@ from typing import Optional
|
|
|
9
9
|
import aiohttp
|
|
10
10
|
|
|
11
11
|
from .models import NotarizeRequest, NotarizeResult, StatusResult, PdrResult
|
|
12
|
+
NEP413_TAG = 2147484061
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def canonical_bind_message(work_hash: str, tx_hash: str) -> bytes:
|
|
16
|
+
"""Canonical AOTrust AITP-BIND v1 message — identical to the server module."""
|
|
17
|
+
return f"AOTrust AITP-BIND v1\nwork_hash:{work_hash}\ntx_hash:{tx_hash}".encode("utf-8")
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _decode_ed25519_key(raw: str) -> bytes:
|
|
21
|
+
"""Accepts hex, base58, or NEAR's 'ed25519:<base58>' key format."""
|
|
22
|
+
import base58
|
|
23
|
+
key = raw.strip()
|
|
24
|
+
if key.startswith("ed25519:"):
|
|
25
|
+
key = key.split(":", 1)[1]
|
|
26
|
+
try:
|
|
27
|
+
if len(key) == 64 and all(c in "0123456789abcdefABCDEF" for c in key):
|
|
28
|
+
return bytes.fromhex(key)
|
|
29
|
+
except Exception:
|
|
30
|
+
pass
|
|
31
|
+
return base58.b58decode(key)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _sign_bind_message(private_key: str, work_hash: str, tx_hash: str) -> str:
|
|
35
|
+
"""NEP-413 RAW buffer signature (no SHA-256 pre-hash) over the bind message."""
|
|
36
|
+
import base64
|
|
37
|
+
import struct
|
|
38
|
+
from nacl.signing import SigningKey
|
|
39
|
+
|
|
40
|
+
msg = canonical_bind_message(work_hash, tx_hash)
|
|
41
|
+
buf = struct.pack("<II", NEP413_TAG, len(msg)) + msg
|
|
42
|
+
sk = SigningKey(_decode_ed25519_key(private_key))
|
|
43
|
+
return base64.b64encode(sk.sign(buf).signature).decode()
|
|
44
|
+
|
|
45
|
+
|
|
12
46
|
from .exceptions import (
|
|
13
47
|
NotaryError,
|
|
14
48
|
NotaryAuthError,
|
|
15
49
|
NotaryNotFoundError,
|
|
16
50
|
NotaryPaymentError,
|
|
17
51
|
NotaryServerError,
|
|
52
|
+
NotaryValidationError,
|
|
53
|
+
NotaryConflictError,
|
|
18
54
|
)
|
|
19
55
|
|
|
20
56
|
DEFAULT_TIMEOUT = 30
|
|
@@ -119,22 +155,50 @@ class NotaryClient:
|
|
|
119
155
|
raise NotaryNotFoundError(body.get("error", "Not found"))
|
|
120
156
|
if status == 429:
|
|
121
157
|
raise NotaryError(body.get("error", body.get("message", "Rate limit reached")))
|
|
158
|
+
if status == 409:
|
|
159
|
+
raise NotaryConflictError(body.get("error", body.get("message", "Conflict")))
|
|
160
|
+
if status in (400, 422):
|
|
161
|
+
detail = body.get("message") or body.get("detail") or body.get("error") or f"Invalid request ({status})"
|
|
162
|
+
raise NotaryValidationError(f"{detail} (HTTP {status})")
|
|
122
163
|
if status >= 500:
|
|
123
164
|
raise NotaryServerError(body.get("error", f"Server error {status}"), status_code=status)
|
|
124
165
|
|
|
125
|
-
async def notarize(
|
|
166
|
+
async def notarize(
|
|
167
|
+
self,
|
|
168
|
+
request: NotarizeRequest,
|
|
169
|
+
payer_account: Optional[str] = None,
|
|
170
|
+
bind_sig: Optional[str] = None,
|
|
171
|
+
near_private_key: Optional[str] = None,
|
|
172
|
+
) -> NotarizeResult:
|
|
126
173
|
"""POST /notarize — anonymous x402 path.
|
|
127
174
|
|
|
128
175
|
Without payment the server answers 402 with the x402 challenge
|
|
129
176
|
(x402Version/accepts[0].resource=...); NotaryPaymentError carries it.
|
|
130
177
|
Payload built for the live x402 contract: work_hash at top level
|
|
131
178
|
(the CI-channel envelope with tx_hash/payload.work_hash is NOT used here).
|
|
179
|
+
|
|
180
|
+
Payment binding (V5-A', NEAR rail): pass payer_account + bind_sig, or
|
|
181
|
+
let the SDK build them. If `near_private_key` (or env NEAR_PRIVATE_KEY)
|
|
182
|
+
is set and payer_account is provided, the SDK signs the canonical
|
|
183
|
+
AOTrust AITP-BIND v1 message with the NEP-413 raw buffer and fills
|
|
184
|
+
bind_sig. Without a key, fields are simply not added (server warn mode
|
|
185
|
+
passes unbound requests).
|
|
132
186
|
"""
|
|
187
|
+
payer = payer_account or request.payer_account
|
|
188
|
+
sig = bind_sig or request.bind_sig
|
|
189
|
+
key = near_private_key or os.environ.get("NEAR_PRIVATE_KEY")
|
|
190
|
+
if not sig and key and payer and request.work_hash and request.tx_hash:
|
|
191
|
+
sig = _sign_bind_message(key, request.work_hash, request.tx_hash)
|
|
192
|
+
|
|
133
193
|
payload: dict = {"work_hash": request.work_hash or ""}
|
|
134
194
|
if request.agent_sig:
|
|
135
195
|
payload["agent_sig"] = request.agent_sig
|
|
136
196
|
if request.agent_pubkey:
|
|
137
197
|
payload["agent_pubkey"] = request.agent_pubkey
|
|
198
|
+
if payer:
|
|
199
|
+
payload["payer_account"] = payer
|
|
200
|
+
if sig:
|
|
201
|
+
payload["bind_sig"] = sig
|
|
138
202
|
status, body = await self._request("POST", "/notarize", json_data=payload)
|
|
139
203
|
self._check_status(status, body)
|
|
140
204
|
if status not in (200, 202):
|
|
@@ -22,4 +22,12 @@ class NotaryServerError(NotaryError):
|
|
|
22
22
|
|
|
23
23
|
def __init__(self, message: str, status_code: int = 0):
|
|
24
24
|
super().__init__(message)
|
|
25
|
-
self.status_code = status_code
|
|
25
|
+
self.status_code = status_code
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class NotaryValidationError(NotaryError):
|
|
29
|
+
"""400/422 — request payload rejected: invalid params or signature/content mismatch."""
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class NotaryConflictError(NotaryError):
|
|
33
|
+
"""409 — idempotency conflict: this tx_hash/job_id was already used."""
|
|
@@ -33,6 +33,13 @@ class NotarizeRequest(BaseModel):
|
|
|
33
33
|
work_hash: Optional[str] = Field(None, description="SHA-256 hex digest (64 chars)")
|
|
34
34
|
agent_sig: Optional[str] = Field(None, description="Base64 Ed25519 signature (sig_A)")
|
|
35
35
|
agent_pubkey: Optional[str] = Field(None, description="Hex Ed25519 public key")
|
|
36
|
+
payer_account: Optional[str] = Field(
|
|
37
|
+
None, description="NEAR account that paid/signed the tx (payment binding)"
|
|
38
|
+
)
|
|
39
|
+
bind_sig: Optional[str] = Field(
|
|
40
|
+
None,
|
|
41
|
+
description="Base64 Ed25519 NEP-413 signature over the AOTrust AITP-BIND v1 message",
|
|
42
|
+
)
|
|
36
43
|
|
|
37
44
|
def to_api_payload(self) -> dict:
|
|
38
45
|
payload: dict = {"tx_hash": self.tx_hash}
|
|
@@ -42,6 +49,10 @@ class NotarizeRequest(BaseModel):
|
|
|
42
49
|
payload["agent_sig"] = self.agent_sig
|
|
43
50
|
if self.agent_pubkey:
|
|
44
51
|
payload["agent_pubkey"] = self.agent_pubkey
|
|
52
|
+
if self.payer_account:
|
|
53
|
+
payload["payer_account"] = self.payer_account
|
|
54
|
+
if self.bind_sig:
|
|
55
|
+
payload["bind_sig"] = self.bind_sig
|
|
45
56
|
return payload
|
|
46
57
|
|
|
47
58
|
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: aotrust-protocol
|
|
3
|
+
Version: 2.4.0
|
|
4
|
+
Summary: Asyncio-native SDK for A&O Trust Layer — Agent Notary Service
|
|
5
|
+
Author: A&O Trust Layer
|
|
6
|
+
License: MIT
|
|
7
|
+
Classifier: Development Status :: 4 - Beta
|
|
8
|
+
Classifier: Intended Audience :: Developers
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
13
|
+
Classifier: Framework :: AsyncIO
|
|
14
|
+
Classifier: Topic :: Security :: Cryptography
|
|
15
|
+
Requires-Python: >=3.10
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
License-File: LICENSE
|
|
18
|
+
Requires-Dist: aiohttp>=3.9
|
|
19
|
+
Requires-Dist: pydantic>=2.0
|
|
20
|
+
Requires-Dist: pynacl>=1.5
|
|
21
|
+
Provides-Extra: dev
|
|
22
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
23
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
|
|
24
|
+
Requires-Dist: pytest-cov>=4.0; extra == "dev"
|
|
25
|
+
Requires-Dist: aioresponses>=0.7; extra == "dev"
|
|
26
|
+
Dynamic: license-file
|
|
27
|
+
|
|
28
|
+
# aotrust-protocol SDK
|
|
29
|
+
|
|
30
|
+
<!-- mcp-name: link.aotrust/notary -->
|
|
31
|
+
|
|
32
|
+
Asyncio-native Python SDK for the AOTrust Notary API — cryptographic proof-of-existence for AI agent outputs.
|
|
33
|
+
|
|
34
|
+
> **Why protocol-level proofs?** An ERC-8004 empirical study (2026) measured that **>98% of agent feedback records carry no proof of an underlying interaction** (no payment proof, no task linkage); 59–91% of reviewers on three chains participated in coordinated Sybil campaigns, and manipulating a reputation score past a trust threshold costs as little as **$0.0027**. The agentic economy is moving from *statements* to *verifiable claims*: a PDR (239 bytes, Ed25519-signed, hash-chained, NEAR-anchored) turns any output or payment into independent evidence — verifiable by anyone, without trusting the provider's database.
|
|
35
|
+
> *(Refs: arXiv:2610.12948 [cs.CR], 2026; ERC-8004 Sybil measurements, Xiong et al. 2026.)*
|
|
36
|
+
|
|
37
|
+
## Install
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
pip install aotrust-protocol
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Quickstart
|
|
44
|
+
|
|
45
|
+
### Free tier — no API key, no wallet (fastest way to try)
|
|
46
|
+
|
|
47
|
+
```python
|
|
48
|
+
import asyncio, hashlib
|
|
49
|
+
from agent_notary import NotaryClient
|
|
50
|
+
|
|
51
|
+
async def main():
|
|
52
|
+
# IMPORTANT: no /v1 suffix — the SDK appends API paths itself.
|
|
53
|
+
client = NotaryClient(base_url="https://api.aotrust.link")
|
|
54
|
+
|
|
55
|
+
work_hash = hashlib.sha256(b"my artifact text").hexdigest()
|
|
56
|
+
result = await client.shield_free(work_hash)
|
|
57
|
+
job_id = result["job_id"] # keep this UUID — it is the handle
|
|
58
|
+
print("job_id:", job_id)
|
|
59
|
+
|
|
60
|
+
# Check status BY job_id (not by the artifact text!)
|
|
61
|
+
status = await client.get_status(job_id)
|
|
62
|
+
print("status:", status.status) # PENDING → anchored (anchor batches)
|
|
63
|
+
|
|
64
|
+
# Fetch + verify the PDR
|
|
65
|
+
pdr = await client.get_pdr(job_id)
|
|
66
|
+
print("verify in browser:", pdr.verify_url)
|
|
67
|
+
assert (await client.verify_pdr(pdr.pdr_b64))["valid"]
|
|
68
|
+
|
|
69
|
+
asyncio.run(main())
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Free tier: 5 PDR / 24h per IP. Examples: [`examples/04_free_tier.py`](examples/04_free_tier.py).
|
|
73
|
+
|
|
74
|
+
### Witness mode (pre-paid tx_hash)
|
|
75
|
+
|
|
76
|
+
```python
|
|
77
|
+
import asyncio
|
|
78
|
+
from agent_notary import NotaryClient, NotarizeRequest
|
|
79
|
+
|
|
80
|
+
async def main():
|
|
81
|
+
client = NotaryClient(
|
|
82
|
+
api_key="your-api-key",
|
|
83
|
+
base_url="https://api.aotrust.link" # no /v1 suffix!
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
# Submit notarization
|
|
87
|
+
req = NotarizeRequest(
|
|
88
|
+
tx_hash="EzrfDW5b...",
|
|
89
|
+
work_hash="599d6999...",
|
|
90
|
+
agent_sig="base64_sig_A...",
|
|
91
|
+
agent_pubkey="aff91a18...",
|
|
92
|
+
)
|
|
93
|
+
result = await client.notarize(req)
|
|
94
|
+
print(f"Job: {result.job_id}")
|
|
95
|
+
|
|
96
|
+
# Poll for PDR — status/pdr calls always take the job_id returned above
|
|
97
|
+
status = await client.wait_for_pdr(result.job_id, timeout=60)
|
|
98
|
+
if status.is_anchored:
|
|
99
|
+
pdr = await client.get_pdr(result.job_id)
|
|
100
|
+
assert pdr.verify(client.notary_pubkey)
|
|
101
|
+
print("PDR Valid: YES")
|
|
102
|
+
|
|
103
|
+
asyncio.run(main())
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
> **Why no `/v1`?** `base_url` must be `https://api.aotrust.link`. The SDK
|
|
107
|
+
> already builds `/v1/...` request paths; adding `/v1` yourself produces
|
|
108
|
+
> `/v1/v1/...` → HTTP 404 on every call. This bit a real user (2026-09-29:
|
|
109
|
+
> quickstart copy-paste + status lookups by artifact text instead of job_id).
|
|
110
|
+
|
|
111
|
+
## Configuration
|
|
112
|
+
|
|
113
|
+
| Env Var | Constructor Arg | Description |
|
|
114
|
+
|---------|-----------------|-------------|
|
|
115
|
+
| `NOTARY_API_KEY` | `api_key` | API key for auth |
|
|
116
|
+
| `NOTARY_API_URL` | `base_url` | API base URL |
|
|
117
|
+
| `NOTARY_PUBKEY` | `notary_pubkey` | Notary Ed25519 public key (hex) |
|
|
118
|
+
|
|
119
|
+
## Endpoints
|
|
120
|
+
|
|
121
|
+
| Method | Path | Description |
|
|
122
|
+
|--------|------|-------------|
|
|
123
|
+
| POST | `/v1/notarize` | Submit notarization |
|
|
124
|
+
| GET | `/v1/status/{job_id}` | Poll job status |
|
|
125
|
+
| GET | `/v1/pdr/{job_id}` | Get PDR |
|
|
126
|
+
| POST | `/v1/notarize/quote` | Get price quote |
|
|
127
|
+
| GET | `/.well-known/agent.json` | MCP Server Card |
|
|
128
|
+
| GET | `/openapi.json` | OpenAPI 3.0 spec |
|
|
129
|
+
|
|
130
|
+
## PDR Verification
|
|
131
|
+
|
|
132
|
+
```python
|
|
133
|
+
pdr = await client.get_pdr(job_id)
|
|
134
|
+
is_valid = pdr.verify(notary_pubkey_hex="9c7d64bb...")
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Uses NEP-413 raw-buffer verification. No SHA256 pre-hash.
|
|
138
|
+
|
|
139
|
+
## Error Handling
|
|
140
|
+
|
|
141
|
+
```python
|
|
142
|
+
from agent_notary.exceptions import (
|
|
143
|
+
NotaryAuthError, # 401 (authenticated CI surfaces; anonymous x402 gives 402 instead)
|
|
144
|
+
NotaryPaymentError, # 402 x402-challenge / 403
|
|
145
|
+
NotaryNotFoundError, # 404
|
|
146
|
+
NotaryValidationError, # 400/422 — invalid params, INVALID_AGENT_SIGNATURE
|
|
147
|
+
NotaryConflictError, # 409 — tx_hash/job_id already used (idempotency)
|
|
148
|
+
NotaryServerError, # 5xx, carries status_code
|
|
149
|
+
NotaryError, # base — safety net
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
try:
|
|
153
|
+
result = await client.notarize(req)
|
|
154
|
+
except NotaryAuthError:
|
|
155
|
+
print("Invalid API key")
|
|
156
|
+
except NotaryPaymentError as e:
|
|
157
|
+
print(f"Payment/sig failed: {e}")
|
|
158
|
+
except NotaryValidationError as e:
|
|
159
|
+
print(f"Bad request or invalid signature: {e}")
|
|
160
|
+
except NotaryConflictError:
|
|
161
|
+
print("This tx_hash/job_id was already used — generate a new one")
|
|
162
|
+
except NotaryError as e:
|
|
163
|
+
print(f"Other SDK error: {e}")
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
## Development
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
pip install -e ".[dev]"
|
|
170
|
+
pytest tests/ -v
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
## Payment binding (NEAR rail)
|
|
174
|
+
|
|
175
|
+
NEAR payments must be provably linked to the account that sent them. Pass
|
|
176
|
+
`payer_account` (the NEAR account that paid) together with `bind_sig` — a
|
|
177
|
+
base64 Ed25519 **NEP-413 raw-buffer** signature (no SHA-256 pre-hash) over:
|
|
178
|
+
|
|
179
|
+
```
|
|
180
|
+
AOTrust AITP-BIND v1
|
|
181
|
+
work_hash:<64 hex>
|
|
182
|
+
tx_hash:<tx hash exactly as submitted>
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
The server resolves the account's public key via RPC and checks
|
|
186
|
+
`transaction.signer_id == payer_account`; a mismatch is rejected with
|
|
187
|
+
`403 PAYMENT_BIND_SIGNER_MISMATCH`, a bad signature with
|
|
188
|
+
`403 PAYMENT_BIND_INVALID`.
|
|
189
|
+
|
|
190
|
+
If `NEAR_PRIVATE_KEY` is set in the environment (or `near_private_key=` is
|
|
191
|
+
passed), the SDK signs the canonical message itself and fills `bind_sig`:
|
|
192
|
+
|
|
193
|
+
```python
|
|
194
|
+
req = NotarizeRequest(tx_hash=tx, work_hash=wh)
|
|
195
|
+
await client.notarize(req, payer_account="alice.near")
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
Server mode `AITP_BIND_MODE=warn` (default) accepts unbound requests with a
|
|
199
|
+
`PAYMENT_UNBOUND` log line; `enforce` requires the fields.
|
aotrust_protocol-2.3.6/PKG-INFO
DELETED
|
@@ -1,118 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: aotrust-protocol
|
|
3
|
-
Version: 2.3.6
|
|
4
|
-
Summary: Asyncio-native SDK for A&O Trust Layer — Agent Notary Service
|
|
5
|
-
Author: A&O Trust Layer
|
|
6
|
-
License: MIT
|
|
7
|
-
Classifier: Development Status :: 4 - Beta
|
|
8
|
-
Classifier: Intended Audience :: Developers
|
|
9
|
-
Classifier: Programming Language :: Python :: 3
|
|
10
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
11
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
12
|
-
Classifier: Programming Language :: Python :: 3.12
|
|
13
|
-
Classifier: Framework :: AsyncIO
|
|
14
|
-
Classifier: Topic :: Security :: Cryptography
|
|
15
|
-
Requires-Python: >=3.10
|
|
16
|
-
Description-Content-Type: text/markdown
|
|
17
|
-
License-File: LICENSE
|
|
18
|
-
Requires-Dist: aiohttp>=3.9
|
|
19
|
-
Requires-Dist: pydantic>=2.0
|
|
20
|
-
Requires-Dist: pynacl>=1.5
|
|
21
|
-
Provides-Extra: dev
|
|
22
|
-
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
23
|
-
Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
|
|
24
|
-
Requires-Dist: pytest-cov>=4.0; extra == "dev"
|
|
25
|
-
Requires-Dist: aioresponses>=0.7; extra == "dev"
|
|
26
|
-
Dynamic: license-file
|
|
27
|
-
|
|
28
|
-
# aotrust-protocol SDK
|
|
29
|
-
|
|
30
|
-
<!-- mcp-name: link.aotrust/notary -->
|
|
31
|
-
|
|
32
|
-
Asyncio-native Python SDK for the AOTrust Notary API — cryptographic proof-of-existence for AI agent outputs.
|
|
33
|
-
|
|
34
|
-
## Install
|
|
35
|
-
|
|
36
|
-
```bash
|
|
37
|
-
pip install aotrust-protocol
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
## Quickstart
|
|
41
|
-
|
|
42
|
-
```python
|
|
43
|
-
import asyncio
|
|
44
|
-
from agent_notary import NotaryClient, NotarizeRequest
|
|
45
|
-
|
|
46
|
-
async def main():
|
|
47
|
-
client = NotaryClient(
|
|
48
|
-
api_key="your-api-key",
|
|
49
|
-
base_url="https://api.aotrust.link/v1"
|
|
50
|
-
)
|
|
51
|
-
|
|
52
|
-
# Submit notarization
|
|
53
|
-
req = NotarizeRequest(
|
|
54
|
-
tx_hash="EzrfDW5b...",
|
|
55
|
-
work_hash="599d6999...",
|
|
56
|
-
agent_sig="base64_sig_A...",
|
|
57
|
-
agent_pubkey="aff91a18...",
|
|
58
|
-
)
|
|
59
|
-
result = await client.notarize(req)
|
|
60
|
-
print(f"Job: {result.job_id}")
|
|
61
|
-
|
|
62
|
-
# Poll for PDR
|
|
63
|
-
status = await client.wait_for_pdr(result.job_id, timeout=60)
|
|
64
|
-
if status.is_anchored:
|
|
65
|
-
pdr = await client.get_pdr(result.job_id)
|
|
66
|
-
assert pdr.verify(client.notary_pubkey)
|
|
67
|
-
print("PDR Valid: YES")
|
|
68
|
-
|
|
69
|
-
asyncio.run(main())
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
## Configuration
|
|
73
|
-
|
|
74
|
-
| Env Var | Constructor Arg | Description |
|
|
75
|
-
|---------|-----------------|-------------|
|
|
76
|
-
| `NOTARY_API_KEY` | `api_key` | API key for auth |
|
|
77
|
-
| `NOTARY_API_URL` | `base_url` | API base URL |
|
|
78
|
-
| `NOTARY_PUBKEY` | `notary_pubkey` | Notary Ed25519 public key (hex) |
|
|
79
|
-
|
|
80
|
-
## Endpoints
|
|
81
|
-
|
|
82
|
-
| Method | Path | Description |
|
|
83
|
-
|--------|------|-------------|
|
|
84
|
-
| POST | `/v1/notarize` | Submit notarization |
|
|
85
|
-
| GET | `/v1/status/{job_id}` | Poll job status |
|
|
86
|
-
| GET | `/v1/pdr/{job_id}` | Get PDR |
|
|
87
|
-
| POST | `/v1/notarize/quote` | Get price quote |
|
|
88
|
-
| GET | `/.well-known/agent.json` | MCP Server Card |
|
|
89
|
-
| GET | `/openapi.json` | OpenAPI 3.0 spec |
|
|
90
|
-
|
|
91
|
-
## PDR Verification
|
|
92
|
-
|
|
93
|
-
```python
|
|
94
|
-
pdr = await client.get_pdr(job_id)
|
|
95
|
-
is_valid = pdr.verify(notary_pubkey_hex="9c7d64bb...")
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
Uses NEP-413 raw-buffer verification. No SHA256 pre-hash.
|
|
99
|
-
|
|
100
|
-
## Error Handling
|
|
101
|
-
|
|
102
|
-
```python
|
|
103
|
-
from agent_notary.exceptions import NotaryAuthError, NotaryPaymentError, NotaryNotFoundError
|
|
104
|
-
|
|
105
|
-
try:
|
|
106
|
-
result = await client.notarize(req)
|
|
107
|
-
except NotaryAuthError:
|
|
108
|
-
print("Invalid API key")
|
|
109
|
-
except NotaryPaymentError as e:
|
|
110
|
-
print(f"Payment/sig failed: {e}")
|
|
111
|
-
```
|
|
112
|
-
|
|
113
|
-
## Development
|
|
114
|
-
|
|
115
|
-
```bash
|
|
116
|
-
pip install -e ".[dev]"
|
|
117
|
-
pytest tests/ -v
|
|
118
|
-
```
|
aotrust_protocol-2.3.6/README.md
DELETED
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
# aotrust-protocol SDK
|
|
2
|
-
|
|
3
|
-
<!-- mcp-name: link.aotrust/notary -->
|
|
4
|
-
|
|
5
|
-
Asyncio-native Python SDK for the AOTrust Notary API — cryptographic proof-of-existence for AI agent outputs.
|
|
6
|
-
|
|
7
|
-
## Install
|
|
8
|
-
|
|
9
|
-
```bash
|
|
10
|
-
pip install aotrust-protocol
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
## Quickstart
|
|
14
|
-
|
|
15
|
-
```python
|
|
16
|
-
import asyncio
|
|
17
|
-
from agent_notary import NotaryClient, NotarizeRequest
|
|
18
|
-
|
|
19
|
-
async def main():
|
|
20
|
-
client = NotaryClient(
|
|
21
|
-
api_key="your-api-key",
|
|
22
|
-
base_url="https://api.aotrust.link/v1"
|
|
23
|
-
)
|
|
24
|
-
|
|
25
|
-
# Submit notarization
|
|
26
|
-
req = NotarizeRequest(
|
|
27
|
-
tx_hash="EzrfDW5b...",
|
|
28
|
-
work_hash="599d6999...",
|
|
29
|
-
agent_sig="base64_sig_A...",
|
|
30
|
-
agent_pubkey="aff91a18...",
|
|
31
|
-
)
|
|
32
|
-
result = await client.notarize(req)
|
|
33
|
-
print(f"Job: {result.job_id}")
|
|
34
|
-
|
|
35
|
-
# Poll for PDR
|
|
36
|
-
status = await client.wait_for_pdr(result.job_id, timeout=60)
|
|
37
|
-
if status.is_anchored:
|
|
38
|
-
pdr = await client.get_pdr(result.job_id)
|
|
39
|
-
assert pdr.verify(client.notary_pubkey)
|
|
40
|
-
print("PDR Valid: YES")
|
|
41
|
-
|
|
42
|
-
asyncio.run(main())
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
## Configuration
|
|
46
|
-
|
|
47
|
-
| Env Var | Constructor Arg | Description |
|
|
48
|
-
|---------|-----------------|-------------|
|
|
49
|
-
| `NOTARY_API_KEY` | `api_key` | API key for auth |
|
|
50
|
-
| `NOTARY_API_URL` | `base_url` | API base URL |
|
|
51
|
-
| `NOTARY_PUBKEY` | `notary_pubkey` | Notary Ed25519 public key (hex) |
|
|
52
|
-
|
|
53
|
-
## Endpoints
|
|
54
|
-
|
|
55
|
-
| Method | Path | Description |
|
|
56
|
-
|--------|------|-------------|
|
|
57
|
-
| POST | `/v1/notarize` | Submit notarization |
|
|
58
|
-
| GET | `/v1/status/{job_id}` | Poll job status |
|
|
59
|
-
| GET | `/v1/pdr/{job_id}` | Get PDR |
|
|
60
|
-
| POST | `/v1/notarize/quote` | Get price quote |
|
|
61
|
-
| GET | `/.well-known/agent.json` | MCP Server Card |
|
|
62
|
-
| GET | `/openapi.json` | OpenAPI 3.0 spec |
|
|
63
|
-
|
|
64
|
-
## PDR Verification
|
|
65
|
-
|
|
66
|
-
```python
|
|
67
|
-
pdr = await client.get_pdr(job_id)
|
|
68
|
-
is_valid = pdr.verify(notary_pubkey_hex="9c7d64bb...")
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
Uses NEP-413 raw-buffer verification. No SHA256 pre-hash.
|
|
72
|
-
|
|
73
|
-
## Error Handling
|
|
74
|
-
|
|
75
|
-
```python
|
|
76
|
-
from agent_notary.exceptions import NotaryAuthError, NotaryPaymentError, NotaryNotFoundError
|
|
77
|
-
|
|
78
|
-
try:
|
|
79
|
-
result = await client.notarize(req)
|
|
80
|
-
except NotaryAuthError:
|
|
81
|
-
print("Invalid API key")
|
|
82
|
-
except NotaryPaymentError as e:
|
|
83
|
-
print(f"Payment/sig failed: {e}")
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
## Development
|
|
87
|
-
|
|
88
|
-
```bash
|
|
89
|
-
pip install -e ".[dev]"
|
|
90
|
-
pytest tests/ -v
|
|
91
|
-
```
|
|
@@ -1,118 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: aotrust-protocol
|
|
3
|
-
Version: 2.3.6
|
|
4
|
-
Summary: Asyncio-native SDK for A&O Trust Layer — Agent Notary Service
|
|
5
|
-
Author: A&O Trust Layer
|
|
6
|
-
License: MIT
|
|
7
|
-
Classifier: Development Status :: 4 - Beta
|
|
8
|
-
Classifier: Intended Audience :: Developers
|
|
9
|
-
Classifier: Programming Language :: Python :: 3
|
|
10
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
11
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
12
|
-
Classifier: Programming Language :: Python :: 3.12
|
|
13
|
-
Classifier: Framework :: AsyncIO
|
|
14
|
-
Classifier: Topic :: Security :: Cryptography
|
|
15
|
-
Requires-Python: >=3.10
|
|
16
|
-
Description-Content-Type: text/markdown
|
|
17
|
-
License-File: LICENSE
|
|
18
|
-
Requires-Dist: aiohttp>=3.9
|
|
19
|
-
Requires-Dist: pydantic>=2.0
|
|
20
|
-
Requires-Dist: pynacl>=1.5
|
|
21
|
-
Provides-Extra: dev
|
|
22
|
-
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
23
|
-
Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
|
|
24
|
-
Requires-Dist: pytest-cov>=4.0; extra == "dev"
|
|
25
|
-
Requires-Dist: aioresponses>=0.7; extra == "dev"
|
|
26
|
-
Dynamic: license-file
|
|
27
|
-
|
|
28
|
-
# aotrust-protocol SDK
|
|
29
|
-
|
|
30
|
-
<!-- mcp-name: link.aotrust/notary -->
|
|
31
|
-
|
|
32
|
-
Asyncio-native Python SDK for the AOTrust Notary API — cryptographic proof-of-existence for AI agent outputs.
|
|
33
|
-
|
|
34
|
-
## Install
|
|
35
|
-
|
|
36
|
-
```bash
|
|
37
|
-
pip install aotrust-protocol
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
## Quickstart
|
|
41
|
-
|
|
42
|
-
```python
|
|
43
|
-
import asyncio
|
|
44
|
-
from agent_notary import NotaryClient, NotarizeRequest
|
|
45
|
-
|
|
46
|
-
async def main():
|
|
47
|
-
client = NotaryClient(
|
|
48
|
-
api_key="your-api-key",
|
|
49
|
-
base_url="https://api.aotrust.link/v1"
|
|
50
|
-
)
|
|
51
|
-
|
|
52
|
-
# Submit notarization
|
|
53
|
-
req = NotarizeRequest(
|
|
54
|
-
tx_hash="EzrfDW5b...",
|
|
55
|
-
work_hash="599d6999...",
|
|
56
|
-
agent_sig="base64_sig_A...",
|
|
57
|
-
agent_pubkey="aff91a18...",
|
|
58
|
-
)
|
|
59
|
-
result = await client.notarize(req)
|
|
60
|
-
print(f"Job: {result.job_id}")
|
|
61
|
-
|
|
62
|
-
# Poll for PDR
|
|
63
|
-
status = await client.wait_for_pdr(result.job_id, timeout=60)
|
|
64
|
-
if status.is_anchored:
|
|
65
|
-
pdr = await client.get_pdr(result.job_id)
|
|
66
|
-
assert pdr.verify(client.notary_pubkey)
|
|
67
|
-
print("PDR Valid: YES")
|
|
68
|
-
|
|
69
|
-
asyncio.run(main())
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
## Configuration
|
|
73
|
-
|
|
74
|
-
| Env Var | Constructor Arg | Description |
|
|
75
|
-
|---------|-----------------|-------------|
|
|
76
|
-
| `NOTARY_API_KEY` | `api_key` | API key for auth |
|
|
77
|
-
| `NOTARY_API_URL` | `base_url` | API base URL |
|
|
78
|
-
| `NOTARY_PUBKEY` | `notary_pubkey` | Notary Ed25519 public key (hex) |
|
|
79
|
-
|
|
80
|
-
## Endpoints
|
|
81
|
-
|
|
82
|
-
| Method | Path | Description |
|
|
83
|
-
|--------|------|-------------|
|
|
84
|
-
| POST | `/v1/notarize` | Submit notarization |
|
|
85
|
-
| GET | `/v1/status/{job_id}` | Poll job status |
|
|
86
|
-
| GET | `/v1/pdr/{job_id}` | Get PDR |
|
|
87
|
-
| POST | `/v1/notarize/quote` | Get price quote |
|
|
88
|
-
| GET | `/.well-known/agent.json` | MCP Server Card |
|
|
89
|
-
| GET | `/openapi.json` | OpenAPI 3.0 spec |
|
|
90
|
-
|
|
91
|
-
## PDR Verification
|
|
92
|
-
|
|
93
|
-
```python
|
|
94
|
-
pdr = await client.get_pdr(job_id)
|
|
95
|
-
is_valid = pdr.verify(notary_pubkey_hex="9c7d64bb...")
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
Uses NEP-413 raw-buffer verification. No SHA256 pre-hash.
|
|
99
|
-
|
|
100
|
-
## Error Handling
|
|
101
|
-
|
|
102
|
-
```python
|
|
103
|
-
from agent_notary.exceptions import NotaryAuthError, NotaryPaymentError, NotaryNotFoundError
|
|
104
|
-
|
|
105
|
-
try:
|
|
106
|
-
result = await client.notarize(req)
|
|
107
|
-
except NotaryAuthError:
|
|
108
|
-
print("Invalid API key")
|
|
109
|
-
except NotaryPaymentError as e:
|
|
110
|
-
print(f"Payment/sig failed: {e}")
|
|
111
|
-
```
|
|
112
|
-
|
|
113
|
-
## Development
|
|
114
|
-
|
|
115
|
-
```bash
|
|
116
|
-
pip install -e ".[dev]"
|
|
117
|
-
pytest tests/ -v
|
|
118
|
-
```
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{aotrust_protocol-2.3.6 → aotrust_protocol-2.4.0}/aotrust_protocol.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|