aotrust-protocol 2.3.5__tar.gz → 2.3.7__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aotrust-protocol
3
- Version: 2.3.5
3
+ Version: 2.3.7
4
4
  Summary: Asyncio-native SDK for A&O Trust Layer — Agent Notary Service
5
5
  Author: A&O Trust Layer
6
6
  License: MIT
@@ -27,7 +27,7 @@ Dynamic: license-file
27
27
 
28
28
  # aotrust-protocol SDK
29
29
 
30
- <!-- mcp-name: io.github.GitSerge-crypto/aotrust-notary -->
30
+ <!-- mcp-name: link.aotrust/notary -->
31
31
 
32
32
  Asyncio-native Python SDK for the AOTrust Notary API — cryptographic proof-of-existence for AI agent outputs.
33
33
 
@@ -39,6 +39,37 @@ pip install aotrust-protocol
39
39
 
40
40
  ## Quickstart
41
41
 
42
+ ### Free tier — no API key, no wallet (fastest way to try)
43
+
44
+ ```python
45
+ import asyncio, hashlib
46
+ from agent_notary import NotaryClient
47
+
48
+ async def main():
49
+ # IMPORTANT: no /v1 suffix — the SDK appends API paths itself.
50
+ client = NotaryClient(base_url="https://api.aotrust.link")
51
+
52
+ work_hash = hashlib.sha256(b"my artifact text").hexdigest()
53
+ result = await client.shield_free(work_hash)
54
+ job_id = result["job_id"] # keep this UUID — it is the handle
55
+ print("job_id:", job_id)
56
+
57
+ # Check status BY job_id (not by the artifact text!)
58
+ status = await client.get_status(job_id)
59
+ print("status:", status.status) # PENDING → anchored (anchor batches)
60
+
61
+ # Fetch + verify the PDR
62
+ pdr = await client.get_pdr(job_id)
63
+ print("verify in browser:", pdr.verify_url)
64
+ assert (await client.verify_pdr(pdr.pdr_b64))["valid"]
65
+
66
+ asyncio.run(main())
67
+ ```
68
+
69
+ Free tier: 5 PDR / 24h per IP. Examples: [`examples/04_free_tier.py`](examples/04_free_tier.py).
70
+
71
+ ### Witness mode (pre-paid tx_hash)
72
+
42
73
  ```python
43
74
  import asyncio
44
75
  from agent_notary import NotaryClient, NotarizeRequest
@@ -46,7 +77,7 @@ from agent_notary import NotaryClient, NotarizeRequest
46
77
  async def main():
47
78
  client = NotaryClient(
48
79
  api_key="your-api-key",
49
- base_url="https://api.aotrust.link/v1"
80
+ base_url="https://api.aotrust.link" # no /v1 suffix!
50
81
  )
51
82
 
52
83
  # Submit notarization
@@ -59,7 +90,7 @@ async def main():
59
90
  result = await client.notarize(req)
60
91
  print(f"Job: {result.job_id}")
61
92
 
62
- # Poll for PDR
93
+ # Poll for PDR — status/pdr calls always take the job_id returned above
63
94
  status = await client.wait_for_pdr(result.job_id, timeout=60)
64
95
  if status.is_anchored:
65
96
  pdr = await client.get_pdr(result.job_id)
@@ -69,6 +100,11 @@ async def main():
69
100
  asyncio.run(main())
70
101
  ```
71
102
 
103
+ > **Why no `/v1`?** `base_url` must be `https://api.aotrust.link`. The SDK
104
+ > already builds `/v1/...` request paths; adding `/v1` yourself produces
105
+ > `/v1/v1/...` → HTTP 404 on every call. This bit a real user (2026-09-29:
106
+ > quickstart copy-paste + status lookups by artifact text instead of job_id).
107
+
72
108
  ## Configuration
73
109
 
74
110
  | Env Var | Constructor Arg | Description |
@@ -100,7 +136,15 @@ Uses NEP-413 raw-buffer verification. No SHA256 pre-hash.
100
136
  ## Error Handling
101
137
 
102
138
  ```python
103
- from agent_notary.exceptions import NotaryAuthError, NotaryPaymentError, NotaryNotFoundError
139
+ from agent_notary.exceptions import (
140
+ NotaryAuthError, # 401 (authenticated CI surfaces; anonymous x402 gives 402 instead)
141
+ NotaryPaymentError, # 402 x402-challenge / 403
142
+ NotaryNotFoundError, # 404
143
+ NotaryValidationError, # 400/422 — invalid params, INVALID_AGENT_SIGNATURE
144
+ NotaryConflictError, # 409 — tx_hash/job_id already used (idempotency)
145
+ NotaryServerError, # 5xx, carries status_code
146
+ NotaryError, # base — safety net
147
+ )
104
148
 
105
149
  try:
106
150
  result = await client.notarize(req)
@@ -108,6 +152,12 @@ except NotaryAuthError:
108
152
  print("Invalid API key")
109
153
  except NotaryPaymentError as e:
110
154
  print(f"Payment/sig failed: {e}")
155
+ except NotaryValidationError as e:
156
+ print(f"Bad request or invalid signature: {e}")
157
+ except NotaryConflictError:
158
+ print("This tx_hash/job_id was already used — generate a new one")
159
+ except NotaryError as e:
160
+ print(f"Other SDK error: {e}")
111
161
  ```
112
162
 
113
163
  ## Development
@@ -0,0 +1,141 @@
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
+ ### Free tier — no API key, no wallet (fastest way to try)
16
+
17
+ ```python
18
+ import asyncio, hashlib
19
+ from agent_notary import NotaryClient
20
+
21
+ async def main():
22
+ # IMPORTANT: no /v1 suffix — the SDK appends API paths itself.
23
+ client = NotaryClient(base_url="https://api.aotrust.link")
24
+
25
+ work_hash = hashlib.sha256(b"my artifact text").hexdigest()
26
+ result = await client.shield_free(work_hash)
27
+ job_id = result["job_id"] # keep this UUID — it is the handle
28
+ print("job_id:", job_id)
29
+
30
+ # Check status BY job_id (not by the artifact text!)
31
+ status = await client.get_status(job_id)
32
+ print("status:", status.status) # PENDING → anchored (anchor batches)
33
+
34
+ # Fetch + verify the PDR
35
+ pdr = await client.get_pdr(job_id)
36
+ print("verify in browser:", pdr.verify_url)
37
+ assert (await client.verify_pdr(pdr.pdr_b64))["valid"]
38
+
39
+ asyncio.run(main())
40
+ ```
41
+
42
+ Free tier: 5 PDR / 24h per IP. Examples: [`examples/04_free_tier.py`](examples/04_free_tier.py).
43
+
44
+ ### Witness mode (pre-paid tx_hash)
45
+
46
+ ```python
47
+ import asyncio
48
+ from agent_notary import NotaryClient, NotarizeRequest
49
+
50
+ async def main():
51
+ client = NotaryClient(
52
+ api_key="your-api-key",
53
+ base_url="https://api.aotrust.link" # no /v1 suffix!
54
+ )
55
+
56
+ # Submit notarization
57
+ req = NotarizeRequest(
58
+ tx_hash="EzrfDW5b...",
59
+ work_hash="599d6999...",
60
+ agent_sig="base64_sig_A...",
61
+ agent_pubkey="aff91a18...",
62
+ )
63
+ result = await client.notarize(req)
64
+ print(f"Job: {result.job_id}")
65
+
66
+ # Poll for PDR — status/pdr calls always take the job_id returned above
67
+ status = await client.wait_for_pdr(result.job_id, timeout=60)
68
+ if status.is_anchored:
69
+ pdr = await client.get_pdr(result.job_id)
70
+ assert pdr.verify(client.notary_pubkey)
71
+ print("PDR Valid: YES")
72
+
73
+ asyncio.run(main())
74
+ ```
75
+
76
+ > **Why no `/v1`?** `base_url` must be `https://api.aotrust.link`. The SDK
77
+ > already builds `/v1/...` request paths; adding `/v1` yourself produces
78
+ > `/v1/v1/...` → HTTP 404 on every call. This bit a real user (2026-09-29:
79
+ > quickstart copy-paste + status lookups by artifact text instead of job_id).
80
+
81
+ ## Configuration
82
+
83
+ | Env Var | Constructor Arg | Description |
84
+ |---------|-----------------|-------------|
85
+ | `NOTARY_API_KEY` | `api_key` | API key for auth |
86
+ | `NOTARY_API_URL` | `base_url` | API base URL |
87
+ | `NOTARY_PUBKEY` | `notary_pubkey` | Notary Ed25519 public key (hex) |
88
+
89
+ ## Endpoints
90
+
91
+ | Method | Path | Description |
92
+ |--------|------|-------------|
93
+ | POST | `/v1/notarize` | Submit notarization |
94
+ | GET | `/v1/status/{job_id}` | Poll job status |
95
+ | GET | `/v1/pdr/{job_id}` | Get PDR |
96
+ | POST | `/v1/notarize/quote` | Get price quote |
97
+ | GET | `/.well-known/agent.json` | MCP Server Card |
98
+ | GET | `/openapi.json` | OpenAPI 3.0 spec |
99
+
100
+ ## PDR Verification
101
+
102
+ ```python
103
+ pdr = await client.get_pdr(job_id)
104
+ is_valid = pdr.verify(notary_pubkey_hex="9c7d64bb...")
105
+ ```
106
+
107
+ Uses NEP-413 raw-buffer verification. No SHA256 pre-hash.
108
+
109
+ ## Error Handling
110
+
111
+ ```python
112
+ from agent_notary.exceptions import (
113
+ NotaryAuthError, # 401 (authenticated CI surfaces; anonymous x402 gives 402 instead)
114
+ NotaryPaymentError, # 402 x402-challenge / 403
115
+ NotaryNotFoundError, # 404
116
+ NotaryValidationError, # 400/422 — invalid params, INVALID_AGENT_SIGNATURE
117
+ NotaryConflictError, # 409 — tx_hash/job_id already used (idempotency)
118
+ NotaryServerError, # 5xx, carries status_code
119
+ NotaryError, # base — safety net
120
+ )
121
+
122
+ try:
123
+ result = await client.notarize(req)
124
+ except NotaryAuthError:
125
+ print("Invalid API key")
126
+ except NotaryPaymentError as e:
127
+ print(f"Payment/sig failed: {e}")
128
+ except NotaryValidationError as e:
129
+ print(f"Bad request or invalid signature: {e}")
130
+ except NotaryConflictError:
131
+ print("This tx_hash/job_id was already used — generate a new one")
132
+ except NotaryError as e:
133
+ print(f"Other SDK error: {e}")
134
+ ```
135
+
136
+ ## Development
137
+
138
+ ```bash
139
+ pip install -e ".[dev]"
140
+ pytest tests/ -v
141
+ ```
@@ -1,6 +1,6 @@
1
1
  """A&O Trust Layer — Agent Notary SDK (asyncio-native)."""
2
2
 
3
- __version__ = "2.3.5"
3
+ __version__ = "2.3.7"
4
4
 
5
5
  from .client import NotaryClient
6
6
  from .models import NotarizeRequest, NotarizeResult, PdrResult, StatusResult
@@ -15,6 +15,8 @@ from .exceptions import (
15
15
  NotaryNotFoundError,
16
16
  NotaryPaymentError,
17
17
  NotaryServerError,
18
+ NotaryValidationError,
19
+ NotaryConflictError,
18
20
  )
19
21
 
20
22
  DEFAULT_TIMEOUT = 30
@@ -119,6 +121,11 @@ class NotaryClient:
119
121
  raise NotaryNotFoundError(body.get("error", "Not found"))
120
122
  if status == 429:
121
123
  raise NotaryError(body.get("error", body.get("message", "Rate limit reached")))
124
+ if status == 409:
125
+ raise NotaryConflictError(body.get("error", body.get("message", "Conflict")))
126
+ if status in (400, 422):
127
+ detail = body.get("message") or body.get("detail") or body.get("error") or f"Invalid request ({status})"
128
+ raise NotaryValidationError(f"{detail} (HTTP {status})")
122
129
  if status >= 500:
123
130
  raise NotaryServerError(body.get("error", f"Server error {status}"), status_code=status)
124
131
 
@@ -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."""
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aotrust-protocol
3
- Version: 2.3.5
3
+ Version: 2.3.7
4
4
  Summary: Asyncio-native SDK for A&O Trust Layer — Agent Notary Service
5
5
  Author: A&O Trust Layer
6
6
  License: MIT
@@ -27,7 +27,7 @@ Dynamic: license-file
27
27
 
28
28
  # aotrust-protocol SDK
29
29
 
30
- <!-- mcp-name: io.github.GitSerge-crypto/aotrust-notary -->
30
+ <!-- mcp-name: link.aotrust/notary -->
31
31
 
32
32
  Asyncio-native Python SDK for the AOTrust Notary API — cryptographic proof-of-existence for AI agent outputs.
33
33
 
@@ -39,6 +39,37 @@ pip install aotrust-protocol
39
39
 
40
40
  ## Quickstart
41
41
 
42
+ ### Free tier — no API key, no wallet (fastest way to try)
43
+
44
+ ```python
45
+ import asyncio, hashlib
46
+ from agent_notary import NotaryClient
47
+
48
+ async def main():
49
+ # IMPORTANT: no /v1 suffix — the SDK appends API paths itself.
50
+ client = NotaryClient(base_url="https://api.aotrust.link")
51
+
52
+ work_hash = hashlib.sha256(b"my artifact text").hexdigest()
53
+ result = await client.shield_free(work_hash)
54
+ job_id = result["job_id"] # keep this UUID — it is the handle
55
+ print("job_id:", job_id)
56
+
57
+ # Check status BY job_id (not by the artifact text!)
58
+ status = await client.get_status(job_id)
59
+ print("status:", status.status) # PENDING → anchored (anchor batches)
60
+
61
+ # Fetch + verify the PDR
62
+ pdr = await client.get_pdr(job_id)
63
+ print("verify in browser:", pdr.verify_url)
64
+ assert (await client.verify_pdr(pdr.pdr_b64))["valid"]
65
+
66
+ asyncio.run(main())
67
+ ```
68
+
69
+ Free tier: 5 PDR / 24h per IP. Examples: [`examples/04_free_tier.py`](examples/04_free_tier.py).
70
+
71
+ ### Witness mode (pre-paid tx_hash)
72
+
42
73
  ```python
43
74
  import asyncio
44
75
  from agent_notary import NotaryClient, NotarizeRequest
@@ -46,7 +77,7 @@ from agent_notary import NotaryClient, NotarizeRequest
46
77
  async def main():
47
78
  client = NotaryClient(
48
79
  api_key="your-api-key",
49
- base_url="https://api.aotrust.link/v1"
80
+ base_url="https://api.aotrust.link" # no /v1 suffix!
50
81
  )
51
82
 
52
83
  # Submit notarization
@@ -59,7 +90,7 @@ async def main():
59
90
  result = await client.notarize(req)
60
91
  print(f"Job: {result.job_id}")
61
92
 
62
- # Poll for PDR
93
+ # Poll for PDR — status/pdr calls always take the job_id returned above
63
94
  status = await client.wait_for_pdr(result.job_id, timeout=60)
64
95
  if status.is_anchored:
65
96
  pdr = await client.get_pdr(result.job_id)
@@ -69,6 +100,11 @@ async def main():
69
100
  asyncio.run(main())
70
101
  ```
71
102
 
103
+ > **Why no `/v1`?** `base_url` must be `https://api.aotrust.link`. The SDK
104
+ > already builds `/v1/...` request paths; adding `/v1` yourself produces
105
+ > `/v1/v1/...` → HTTP 404 on every call. This bit a real user (2026-09-29:
106
+ > quickstart copy-paste + status lookups by artifact text instead of job_id).
107
+
72
108
  ## Configuration
73
109
 
74
110
  | Env Var | Constructor Arg | Description |
@@ -100,7 +136,15 @@ Uses NEP-413 raw-buffer verification. No SHA256 pre-hash.
100
136
  ## Error Handling
101
137
 
102
138
  ```python
103
- from agent_notary.exceptions import NotaryAuthError, NotaryPaymentError, NotaryNotFoundError
139
+ from agent_notary.exceptions import (
140
+ NotaryAuthError, # 401 (authenticated CI surfaces; anonymous x402 gives 402 instead)
141
+ NotaryPaymentError, # 402 x402-challenge / 403
142
+ NotaryNotFoundError, # 404
143
+ NotaryValidationError, # 400/422 — invalid params, INVALID_AGENT_SIGNATURE
144
+ NotaryConflictError, # 409 — tx_hash/job_id already used (idempotency)
145
+ NotaryServerError, # 5xx, carries status_code
146
+ NotaryError, # base — safety net
147
+ )
104
148
 
105
149
  try:
106
150
  result = await client.notarize(req)
@@ -108,6 +152,12 @@ except NotaryAuthError:
108
152
  print("Invalid API key")
109
153
  except NotaryPaymentError as e:
110
154
  print(f"Payment/sig failed: {e}")
155
+ except NotaryValidationError as e:
156
+ print(f"Bad request or invalid signature: {e}")
157
+ except NotaryConflictError:
158
+ print("This tx_hash/job_id was already used — generate a new one")
159
+ except NotaryError as e:
160
+ print(f"Other SDK error: {e}")
111
161
  ```
112
162
 
113
163
  ## Development
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "aotrust-protocol"
7
- version = "2.3.5"
7
+ version = "2.3.7"
8
8
  description = "Asyncio-native SDK for A&O Trust Layer — Agent Notary Service"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -1,91 +0,0 @@
1
- # aotrust-protocol SDK
2
-
3
- <!-- mcp-name: io.github.GitSerge-crypto/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
- ```