agentguard-python-sdk 0.2.4__py3-none-any.whl → 0.2.6__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.
- agentguard/__init__.py +1 -1
- agentguard/client.py +5 -0
- agentguard_crypto/models.py +109 -108
- agentguard_crypto/signing.py +87 -76
- agentguard_python_sdk-0.2.6.dist-info/METADATA +127 -0
- {agentguard_python_sdk-0.2.4.dist-info → agentguard_python_sdk-0.2.6.dist-info}/RECORD +9 -9
- agentguard_python_sdk-0.2.4.dist-info/METADATA +0 -94
- {agentguard_python_sdk-0.2.4.dist-info → agentguard_python_sdk-0.2.6.dist-info}/WHEEL +0 -0
- {agentguard_python_sdk-0.2.4.dist-info → agentguard_python_sdk-0.2.6.dist-info}/licenses/LICENSE +0 -0
- {agentguard_python_sdk-0.2.4.dist-info → agentguard_python_sdk-0.2.6.dist-info}/top_level.txt +0 -0
agentguard/__init__.py
CHANGED
agentguard/client.py
CHANGED
|
@@ -81,6 +81,10 @@ class AgentGuardClient:
|
|
|
81
81
|
self.base_url = self.config.backend_url.rstrip("/")
|
|
82
82
|
self.private_key = private_key
|
|
83
83
|
|
|
84
|
+
# Derive Signer ID (Master Account) for cryptographic verification
|
|
85
|
+
from agentguard_crypto.signing import get_signer_address
|
|
86
|
+
self.signer_id = get_signer_address(self.private_key) if self.private_key else self.wallet_address
|
|
87
|
+
|
|
84
88
|
# Priority 6: Segmented Timeouts injected from Config
|
|
85
89
|
self.timeout = httpx.Timeout(
|
|
86
90
|
connect=self.config.timeout_connect,
|
|
@@ -194,6 +198,7 @@ class AgentGuardClient:
|
|
|
194
198
|
from agentguard_crypto.models import CanonicalPaymentPayload
|
|
195
199
|
|
|
196
200
|
payload_model = CanonicalPaymentPayload(
|
|
201
|
+
signer_id=self.signer_id,
|
|
197
202
|
resource_url=resource_url,
|
|
198
203
|
microalgos=actual_microalgos,
|
|
199
204
|
purpose=purpose,
|
agentguard_crypto/models.py
CHANGED
|
@@ -1,108 +1,109 @@
|
|
|
1
|
-
from typing import Optional
|
|
2
|
-
from pydantic import BaseModel, Field, StrictInt
|
|
3
|
-
from .canonical import canonicalize_payload
|
|
4
|
-
|
|
5
|
-
SCHEMA_VERSION = 1
|
|
6
|
-
|
|
7
|
-
class CanonicalPayloadMixin:
|
|
8
|
-
"""
|
|
9
|
-
PROTOCOL LAYER: Single Serialization Authority.
|
|
10
|
-
|
|
11
|
-
ALL serialization MUST derive from _serialize().
|
|
12
|
-
Direct model_dump() calls are FORBIDDEN in protocol code.
|
|
13
|
-
|
|
14
|
-
Invariant enforced by construction:
|
|
15
|
-
wire_payload == signed_payload == verified_payload
|
|
16
|
-
"""
|
|
17
|
-
|
|
18
|
-
def _serialize(self) -> dict:
|
|
19
|
-
"""SINGLE INTERNAL SERIALIZATION SOURCE.
|
|
20
|
-
Every external serialization method MUST derive from this.
|
|
21
|
-
Uses exclude_unset=True to prevent default injection drift."""
|
|
22
|
-
return self.model_dump(exclude_unset=True)
|
|
23
|
-
|
|
24
|
-
def to_canonical_bytes(self) -> bytes:
|
|
25
|
-
"""Returns deterministic canonical bytes for signing/verification."""
|
|
26
|
-
return canonicalize_payload(self._serialize())
|
|
27
|
-
|
|
28
|
-
def to_wire_dict(self) -> dict:
|
|
29
|
-
"""Returns the EXACT dict for HTTP transport.
|
|
30
|
-
Guaranteed identical field set to what is signed/verified."""
|
|
31
|
-
return self._serialize()
|
|
32
|
-
|
|
33
|
-
# Backward compatibility alias (used by signing.py, consent.py, payment.py)
|
|
34
|
-
get_canonical_bytes = to_canonical_bytes
|
|
35
|
-
|
|
36
|
-
class CanonicalPaymentPayload(BaseModel, CanonicalPayloadMixin):
|
|
37
|
-
"""
|
|
38
|
-
SINGLE SOURCE OF TRUTH (Phase 2).
|
|
39
|
-
Represents the exact fields that are signed and verified for Payment.
|
|
40
|
-
"""
|
|
41
|
-
# Core Identity
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
1
|
+
from typing import Optional
|
|
2
|
+
from pydantic import BaseModel, Field, StrictInt
|
|
3
|
+
from .canonical import canonicalize_payload
|
|
4
|
+
|
|
5
|
+
SCHEMA_VERSION = 1
|
|
6
|
+
|
|
7
|
+
class CanonicalPayloadMixin:
|
|
8
|
+
"""
|
|
9
|
+
PROTOCOL LAYER: Single Serialization Authority.
|
|
10
|
+
|
|
11
|
+
ALL serialization MUST derive from _serialize().
|
|
12
|
+
Direct model_dump() calls are FORBIDDEN in protocol code.
|
|
13
|
+
|
|
14
|
+
Invariant enforced by construction:
|
|
15
|
+
wire_payload == signed_payload == verified_payload
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
def _serialize(self) -> dict:
|
|
19
|
+
"""SINGLE INTERNAL SERIALIZATION SOURCE.
|
|
20
|
+
Every external serialization method MUST derive from this.
|
|
21
|
+
Uses exclude_unset=True to prevent default injection drift."""
|
|
22
|
+
return self.model_dump(exclude_unset=True)
|
|
23
|
+
|
|
24
|
+
def to_canonical_bytes(self) -> bytes:
|
|
25
|
+
"""Returns deterministic canonical bytes for signing/verification."""
|
|
26
|
+
return canonicalize_payload(self._serialize())
|
|
27
|
+
|
|
28
|
+
def to_wire_dict(self) -> dict:
|
|
29
|
+
"""Returns the EXACT dict for HTTP transport.
|
|
30
|
+
Guaranteed identical field set to what is signed/verified."""
|
|
31
|
+
return self._serialize()
|
|
32
|
+
|
|
33
|
+
# Backward compatibility alias (used by signing.py, consent.py, payment.py)
|
|
34
|
+
get_canonical_bytes = to_canonical_bytes
|
|
35
|
+
|
|
36
|
+
class CanonicalPaymentPayload(BaseModel, CanonicalPayloadMixin):
|
|
37
|
+
"""
|
|
38
|
+
SINGLE SOURCE OF TRUTH (Phase 2).
|
|
39
|
+
Represents the exact fields that are signed and verified for Payment.
|
|
40
|
+
"""
|
|
41
|
+
# Core Identity
|
|
42
|
+
signer_id: str
|
|
43
|
+
principal_id: str
|
|
44
|
+
resource_url: str
|
|
45
|
+
purpose: str
|
|
46
|
+
microalgos: StrictInt = Field(gt=0)
|
|
47
|
+
|
|
48
|
+
# Target
|
|
49
|
+
receiver_address: Optional[str] = None
|
|
50
|
+
receiver: Optional[str] = None
|
|
51
|
+
|
|
52
|
+
# Cryptographic Nonces
|
|
53
|
+
nonce: str
|
|
54
|
+
timestamp: int
|
|
55
|
+
|
|
56
|
+
# Authorization Context
|
|
57
|
+
authorization_id: Optional[str] = None
|
|
58
|
+
consent_hash: str
|
|
59
|
+
|
|
60
|
+
# Correlation & Idempotency
|
|
61
|
+
idempotency_key: str
|
|
62
|
+
correlation_id: str
|
|
63
|
+
session_id: Optional[str] = None
|
|
64
|
+
|
|
65
|
+
# Fiduciary Metadata (Phase A1)
|
|
66
|
+
fiduciary_name: Optional[str] = None
|
|
67
|
+
fiduciary_version: Optional[str] = None
|
|
68
|
+
hash_version: Optional[int] = 1
|
|
69
|
+
|
|
70
|
+
# Versioning (Phase 5)
|
|
71
|
+
schema_version: int = Field(default=SCHEMA_VERSION)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
class CanonicalAuthorizationPayload(BaseModel, CanonicalPayloadMixin):
|
|
75
|
+
"""
|
|
76
|
+
STRICTLY contains ONLY authorization lifecycle fields.
|
|
77
|
+
"""
|
|
78
|
+
principal_id: str
|
|
79
|
+
logic_sig_b64: str
|
|
80
|
+
max_amount: StrictInt = Field(gt=0)
|
|
81
|
+
expires_at_unix: StrictInt
|
|
82
|
+
receiver: Optional[str] = None
|
|
83
|
+
purpose_hash: Optional[str] = None
|
|
84
|
+
authorization_id: Optional[str] = None
|
|
85
|
+
timestamp: int
|
|
86
|
+
nonce: str
|
|
87
|
+
schema_version: int = Field(default=SCHEMA_VERSION)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class CanonicalRevokePayload(BaseModel, CanonicalPayloadMixin):
|
|
91
|
+
"""
|
|
92
|
+
STRICTLY contains ONLY revocation fields.
|
|
93
|
+
"""
|
|
94
|
+
principal_id: str
|
|
95
|
+
authorization_id: str
|
|
96
|
+
timestamp: int
|
|
97
|
+
nonce: str
|
|
98
|
+
schema_version: int = Field(default=SCHEMA_VERSION)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
class CanonicalRegistrationPayload(BaseModel, CanonicalPayloadMixin):
|
|
102
|
+
"""
|
|
103
|
+
STRICTLY contains ONLY registration fields.
|
|
104
|
+
"""
|
|
105
|
+
principal_id: str
|
|
106
|
+
budget_microalgos: Optional[StrictInt] = None
|
|
107
|
+
timestamp: int
|
|
108
|
+
nonce: str
|
|
109
|
+
schema_version: int = Field(default=SCHEMA_VERSION)
|
agentguard_crypto/signing.py
CHANGED
|
@@ -1,76 +1,87 @@
|
|
|
1
|
-
import base64
|
|
2
|
-
import nacl.signing
|
|
3
|
-
import nacl.encoding
|
|
4
|
-
from typing import Any
|
|
5
|
-
from .models import CanonicalPaymentPayload
|
|
6
|
-
|
|
7
|
-
def sign_payload(payload: Any, private_key_b64: str) -> str:
|
|
8
|
-
"""
|
|
9
|
-
Signs a CanonicalPaymentPayload (Phase 4).
|
|
10
|
-
Expects private_key_b64 (32-byte seed in b64).
|
|
11
|
-
"""
|
|
12
|
-
seed = base64.b64decode(private_key_b64)
|
|
13
|
-
if len(seed) > 32:
|
|
14
|
-
seed = seed[:32]
|
|
15
|
-
|
|
16
|
-
signing_key = nacl.signing.SigningKey(seed)
|
|
17
|
-
canonical_bytes = payload.get_canonical_bytes()
|
|
18
|
-
|
|
19
|
-
signature = signing_key.sign(canonical_bytes)
|
|
20
|
-
return base64.b64encode(signature.signature).decode("utf-8")
|
|
21
|
-
|
|
22
|
-
def verify_signature(payload: Any, signature_b64: str, wallet_address: str) -> bool:
|
|
23
|
-
"""
|
|
24
|
-
Verifies a CanonicalPaymentPayload signature (Phase 4).
|
|
25
|
-
wallet_address is used as the public key.
|
|
26
|
-
"""
|
|
27
|
-
from algosdk import encoding
|
|
28
|
-
import nacl.exceptions
|
|
29
|
-
|
|
30
|
-
# 1. Get canonical bytes
|
|
31
|
-
canonical_bytes = payload.get_canonical_bytes()
|
|
32
|
-
|
|
33
|
-
# 2. Decode signature and key
|
|
34
|
-
sig_bytes = base64.b64decode(signature_b64)
|
|
35
|
-
pub_key_bytes = encoding.decode_address(wallet_address)
|
|
36
|
-
verify_key = nacl.signing.VerifyKey(pub_key_bytes)
|
|
37
|
-
|
|
38
|
-
# 3. Verify
|
|
39
|
-
try:
|
|
40
|
-
verify_key.verify(canonical_bytes, sig_bytes)
|
|
41
|
-
return True
|
|
42
|
-
except nacl.exceptions.BadSignatureError:
|
|
43
|
-
return False
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
def verify_raw_signature(raw_payload_dict: dict, signature_b64: str, wallet_address: str) -> bool:
|
|
47
|
-
"""
|
|
48
|
-
PRODUCTION-GRADE: Verifies signature against raw transport payload.
|
|
49
|
-
|
|
50
|
-
NO model reconstruction. Canonicalizes the raw wire dict directly.
|
|
51
|
-
This eliminates Pydantic hydration drift, default injection, and
|
|
52
|
-
triple-reconstruction attacks.
|
|
53
|
-
|
|
54
|
-
Flow: raw_wire_dict → canonicalize → verify
|
|
55
|
-
|
|
56
|
-
This is how Stripe, AWS SigV4, and WebAuthn verify signatures:
|
|
57
|
-
against exact transport bytes, never reconstructed model state.
|
|
58
|
-
"""
|
|
59
|
-
from .canonical import canonicalize_payload
|
|
60
|
-
from algosdk import encoding
|
|
61
|
-
import nacl.exceptions
|
|
62
|
-
|
|
63
|
-
# 1. Canonicalize raw transport payload directly
|
|
64
|
-
canonical_bytes = canonicalize_payload(raw_payload_dict)
|
|
65
|
-
|
|
66
|
-
# 2. Decode signature and public key
|
|
67
|
-
sig_bytes = base64.b64decode(signature_b64)
|
|
68
|
-
pub_key_bytes = encoding.decode_address(wallet_address)
|
|
69
|
-
verify_key = nacl.signing.VerifyKey(pub_key_bytes)
|
|
70
|
-
|
|
71
|
-
# 3. Verify
|
|
72
|
-
try:
|
|
73
|
-
verify_key.verify(canonical_bytes, sig_bytes)
|
|
74
|
-
return True
|
|
75
|
-
except nacl.exceptions.BadSignatureError:
|
|
76
|
-
return False
|
|
1
|
+
import base64
|
|
2
|
+
import nacl.signing
|
|
3
|
+
import nacl.encoding
|
|
4
|
+
from typing import Any
|
|
5
|
+
from .models import CanonicalPaymentPayload
|
|
6
|
+
|
|
7
|
+
def sign_payload(payload: Any, private_key_b64: str) -> str:
|
|
8
|
+
"""
|
|
9
|
+
Signs a CanonicalPaymentPayload (Phase 4).
|
|
10
|
+
Expects private_key_b64 (32-byte seed in b64).
|
|
11
|
+
"""
|
|
12
|
+
seed = base64.b64decode(private_key_b64)
|
|
13
|
+
if len(seed) > 32:
|
|
14
|
+
seed = seed[:32]
|
|
15
|
+
|
|
16
|
+
signing_key = nacl.signing.SigningKey(seed)
|
|
17
|
+
canonical_bytes = payload.get_canonical_bytes()
|
|
18
|
+
|
|
19
|
+
signature = signing_key.sign(canonical_bytes)
|
|
20
|
+
return base64.b64encode(signature.signature).decode("utf-8")
|
|
21
|
+
|
|
22
|
+
def verify_signature(payload: Any, signature_b64: str, wallet_address: str) -> bool:
|
|
23
|
+
"""
|
|
24
|
+
Verifies a CanonicalPaymentPayload signature (Phase 4).
|
|
25
|
+
wallet_address is used as the public key.
|
|
26
|
+
"""
|
|
27
|
+
from algosdk import encoding
|
|
28
|
+
import nacl.exceptions
|
|
29
|
+
|
|
30
|
+
# 1. Get canonical bytes
|
|
31
|
+
canonical_bytes = payload.get_canonical_bytes()
|
|
32
|
+
|
|
33
|
+
# 2. Decode signature and key
|
|
34
|
+
sig_bytes = base64.b64decode(signature_b64)
|
|
35
|
+
pub_key_bytes = encoding.decode_address(wallet_address)
|
|
36
|
+
verify_key = nacl.signing.VerifyKey(pub_key_bytes)
|
|
37
|
+
|
|
38
|
+
# 3. Verify
|
|
39
|
+
try:
|
|
40
|
+
verify_key.verify(canonical_bytes, sig_bytes)
|
|
41
|
+
return True
|
|
42
|
+
except nacl.exceptions.BadSignatureError:
|
|
43
|
+
return False
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def verify_raw_signature(raw_payload_dict: dict, signature_b64: str, wallet_address: str) -> bool:
|
|
47
|
+
"""
|
|
48
|
+
PRODUCTION-GRADE: Verifies signature against raw transport payload.
|
|
49
|
+
|
|
50
|
+
NO model reconstruction. Canonicalizes the raw wire dict directly.
|
|
51
|
+
This eliminates Pydantic hydration drift, default injection, and
|
|
52
|
+
triple-reconstruction attacks.
|
|
53
|
+
|
|
54
|
+
Flow: raw_wire_dict → canonicalize → verify
|
|
55
|
+
|
|
56
|
+
This is how Stripe, AWS SigV4, and WebAuthn verify signatures:
|
|
57
|
+
against exact transport bytes, never reconstructed model state.
|
|
58
|
+
"""
|
|
59
|
+
from .canonical import canonicalize_payload
|
|
60
|
+
from algosdk import encoding
|
|
61
|
+
import nacl.exceptions
|
|
62
|
+
|
|
63
|
+
# 1. Canonicalize raw transport payload directly
|
|
64
|
+
canonical_bytes = canonicalize_payload(raw_payload_dict)
|
|
65
|
+
|
|
66
|
+
# 2. Decode signature and public key
|
|
67
|
+
sig_bytes = base64.b64decode(signature_b64)
|
|
68
|
+
pub_key_bytes = encoding.decode_address(wallet_address)
|
|
69
|
+
verify_key = nacl.signing.VerifyKey(pub_key_bytes)
|
|
70
|
+
|
|
71
|
+
# 3. Verify
|
|
72
|
+
try:
|
|
73
|
+
verify_key.verify(canonical_bytes, sig_bytes)
|
|
74
|
+
return True
|
|
75
|
+
except nacl.exceptions.BadSignatureError:
|
|
76
|
+
return False
|
|
77
|
+
|
|
78
|
+
def get_signer_address(private_key_b64: str) -> str:
|
|
79
|
+
"""
|
|
80
|
+
Derives the Algorand address (signer_id) from a b64 private key seed.
|
|
81
|
+
"""
|
|
82
|
+
from algosdk import encoding
|
|
83
|
+
seed = base64.b64decode(private_key_b64)
|
|
84
|
+
if len(seed) > 32:
|
|
85
|
+
seed = seed[:32]
|
|
86
|
+
signing_key = nacl.signing.SigningKey(seed)
|
|
87
|
+
return encoding.encode_address(signing_key.verify_key.encode())
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: agentguard-python-sdk
|
|
3
|
+
Version: 0.2.6
|
|
4
|
+
Summary: A production-grade middleware for AI agents to perform on-chain payments and verifiable consent.
|
|
5
|
+
Author: AgentGuard Team
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Requires-Python: >=3.9
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Requires-Dist: httpx>=0.27.0
|
|
11
|
+
Requires-Dist: pydantic>=2.0.0
|
|
12
|
+
Requires-Dist: pydantic-settings>=2.0.0
|
|
13
|
+
Requires-Dist: pynacl>=1.5.0
|
|
14
|
+
Requires-Dist: py-algorand-sdk>=2.0.0
|
|
15
|
+
Dynamic: license-file
|
|
16
|
+
|
|
17
|
+
# AgentGuard: Secure Payment & Consent Middleware for AI Agents
|
|
18
|
+
|
|
19
|
+
[](https://pypi.org/project/agentguard-python-sdk/)
|
|
20
|
+
[](https://pypi.org/project/agentguard-python-sdk/)
|
|
21
|
+
[](https://opensource.org/licenses/MIT)
|
|
22
|
+
|
|
23
|
+
**AgentGuard** is a production-grade cryptographic middleware that enables AI agents to perform autonomous on-chain payments and generate verifiable consent proofs. It provides a zero-trust bridge between AI agents and the Algorand blockchain.
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## ⚡ The Problem: Key Vulnerability in AI Agents
|
|
28
|
+
AI Agents often need to handle money (micropayments) and user data (consent). If you give an agent a private key, you risk:
|
|
29
|
+
1. **Key Theft**: The agent's memory or logs leaking the seed.
|
|
30
|
+
2. **Unintended Spending**: The agent's LLM "hallucinating" a purchase.
|
|
31
|
+
3. **Non-Compliance**: No audit trail of *why* a payment was made (violating DPDP standards).
|
|
32
|
+
|
|
33
|
+
## 🛡️ The Solution: AgentGuard Architecture
|
|
34
|
+
AgentGuard solves this using a **Vaulted Delegation** architecture:
|
|
35
|
+
- **Hardened Vault (MCP)**: Your sensitive keys stay in a secure isolated server.
|
|
36
|
+
- **Verifiable Consent**: The SDK signs a cryptographic intent (Purpose + Amount + ID) which the Vault verifies before signing a blockchain transaction.
|
|
37
|
+
- **Zero-Trust**: The Backend never sees your keys; it only relays signed instructions.
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## 📦 Installation
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
pip install agentguard-python-sdk
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## 🚀 Quick Start (Request Signing)
|
|
50
|
+
|
|
51
|
+
AgentGuard uses **dual-identity cryptography**. Your **Signer ID** (Master Account) authenticates the request, while your **Principal ID** (Vault/LogicSig) handles the funds.
|
|
52
|
+
|
|
53
|
+
```python
|
|
54
|
+
import asyncio
|
|
55
|
+
from agentguard import AgentGuardClient
|
|
56
|
+
|
|
57
|
+
async def main():
|
|
58
|
+
# Initialize client with your Signer Identity
|
|
59
|
+
# Note: private_key is used ONLY for request signing, never for on-chain spend.
|
|
60
|
+
async with AgentGuardClient(
|
|
61
|
+
principal_id="LOGICSIG_OR_VAULT_ADDRESS",
|
|
62
|
+
private_key="MASTER_ACCOUNT_SEED_B64",
|
|
63
|
+
backend_url="https://agentguard-backend-9s8j.onrender.com"
|
|
64
|
+
) as client:
|
|
65
|
+
|
|
66
|
+
# Access a premium resource
|
|
67
|
+
# The SDK automatically handles: 402 Challenge -> Consent Signing -> Payment Relay
|
|
68
|
+
receipt = await client.pay_and_fetch(
|
|
69
|
+
resource_url="https://premium-intel.service/data",
|
|
70
|
+
purpose="Market Analysis",
|
|
71
|
+
amount_algo="0.1" # Optional: SDK can auto-probe price
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
print(f"✅ Success! Data: {receipt.data}")
|
|
75
|
+
print(f"🔗 Audit Trail: {receipt.audit_url}")
|
|
76
|
+
|
|
77
|
+
if __name__ == "__main__":
|
|
78
|
+
asyncio.run(main())
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## 🧩 Core Concepts
|
|
84
|
+
|
|
85
|
+
### 1. Identity Separation (Zero-Trust)
|
|
86
|
+
AgentGuard separates the **Signer** from the **Payer**:
|
|
87
|
+
* **Signer (`signer_id`)**: Your Master Account. It signs the "Intent to Pay" via the SDK.
|
|
88
|
+
* **Payer (`principal_id`)**: A LogicSig or Vault account. It contains the funds and has a pre-approved policy to pay only when the Master Account signs.
|
|
89
|
+
|
|
90
|
+
### 2. Purpose-Bound Consent (DPDP)
|
|
91
|
+
Every payment requires a `purpose` string. AgentGuard hashes this purpose into the blockchain transaction. This creates an irrefutable audit trail proving *exactly* what the user (or agent) consented to at the time of payment.
|
|
92
|
+
|
|
93
|
+
### 3. Capability Delegation (LogicSigs)
|
|
94
|
+
You can authorize specific amounts or windows of time for your agent:
|
|
95
|
+
```python
|
|
96
|
+
from agentguard.types import DelegatedAuthorization, AuthorizationConstraints
|
|
97
|
+
|
|
98
|
+
auth = DelegatedAuthorization(
|
|
99
|
+
authorization_id="auth-uuid-123",
|
|
100
|
+
principal_id="LOGICSIG_ADDRESS",
|
|
101
|
+
logic_sig_b64="...",
|
|
102
|
+
constraints=AuthorizationConstraints(
|
|
103
|
+
max_amount_microalgos=5000000, # 5 ALGO limit
|
|
104
|
+
expires_at_unix=1716300000
|
|
105
|
+
)
|
|
106
|
+
)
|
|
107
|
+
await client.authorize(auth)
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## 🛠️ API Reference
|
|
113
|
+
|
|
114
|
+
### `AgentGuardClient`
|
|
115
|
+
* `pay_and_fetch(resource_url, purpose, ...)`: The primary method. Probes a resource, extracts the price, signs consent, performs payment via the vault, and returns the data.
|
|
116
|
+
* `authorize(delegated_auth)`: Registers a LogicSig or delegated capability in the vault.
|
|
117
|
+
* `verify(tx_id)`: Checks the blockchain and audit logs to verify a previous payment's validity.
|
|
118
|
+
* `revoke(authorization_id)`: Immediately invalidates a delegated capability.
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
## 🔒 Security Policy
|
|
123
|
+
The SDK uses `ED25519` for all request signatures. All payloads are **canonicalized** (Deterministic JSON sorting + NFC normalization) before signing to prevent "In-Flight Mutation" attacks.
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
© 2026 AgentGuard Team. Built for the Algorand Advanced Agentic Coding.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
agentguard/__init__.py,sha256=
|
|
1
|
+
agentguard/__init__.py,sha256=qxApREzbSt6hurLWvxkjxFhn1UY7N9miFa3OjYmxB2c,1368
|
|
2
2
|
agentguard/auth.py,sha256=fDMc79ObNCcRwd1d0zD5CB2kvW54Acx-rJfT3KSN6Qg,603
|
|
3
|
-
agentguard/client.py,sha256=
|
|
3
|
+
agentguard/client.py,sha256=oE-LuUklI9NmitsWOvb6uBv-ga9tvprUbR-oD3tGiIQ,30940
|
|
4
4
|
agentguard/config.py,sha256=zK7IMox6-CPMtnoUC6bJSh9ZGfIS2Y5vMrdeSIVje7s,1525
|
|
5
5
|
agentguard/consent.py,sha256=jv6EPuSQl29JY-ddI9EmSujtnzwb7lDZPOY7os99ifo,1991
|
|
6
6
|
agentguard/errors.py,sha256=wIFcTLHYq6fIc2ibgsh45Zfq7CSZTl0fkgj1VHYfZuc,4983
|
|
@@ -8,10 +8,10 @@ agentguard/observability.py,sha256=2xSySYDtPhtiNUKXH0zQ0N4WG7j3ssQqPtOBDK6V2EQ,1
|
|
|
8
8
|
agentguard/types.py,sha256=bk6PRB2mUjYaIV3EjrHl1p4Z404T0cRyNCA1yx9qdMg,1868
|
|
9
9
|
agentguard_crypto/__init__.py,sha256=fhK2gyw9TRWCxr_ax6XUItkbBCmlCi5pXEOLdMRKxDU,44
|
|
10
10
|
agentguard_crypto/canonical.py,sha256=TRz_ZW2JxobIXAkXEDIHR4Jk-alAzl6VSOSWpKwm9ag,1188
|
|
11
|
-
agentguard_crypto/models.py,sha256=
|
|
12
|
-
agentguard_crypto/signing.py,sha256=
|
|
13
|
-
agentguard_python_sdk-0.2.
|
|
14
|
-
agentguard_python_sdk-0.2.
|
|
15
|
-
agentguard_python_sdk-0.2.
|
|
16
|
-
agentguard_python_sdk-0.2.
|
|
17
|
-
agentguard_python_sdk-0.2.
|
|
11
|
+
agentguard_crypto/models.py,sha256=t8p87XvoXhzMdnVTwBog0jbtevpYape_81TN9yFpcXg,3235
|
|
12
|
+
agentguard_crypto/signing.py,sha256=nzFuZQhxYORJNp6ZXF3d-ddLBjS1BibTlX_RgvsNZ6c,2906
|
|
13
|
+
agentguard_python_sdk-0.2.6.dist-info/licenses/LICENSE,sha256=Z1Q553BLX5ho8O0gS_1VmndaVnz2NWgKMcH9aoKwqmM,1093
|
|
14
|
+
agentguard_python_sdk-0.2.6.dist-info/METADATA,sha256=eB6dD7_VLrnz4KankZfwWBVF9Hq0IUBD-0IhymA6Sx0,5359
|
|
15
|
+
agentguard_python_sdk-0.2.6.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
16
|
+
agentguard_python_sdk-0.2.6.dist-info/top_level.txt,sha256=8cQ4cluQzmuln1QOqGfNlEpAt3QCW5szoNTbbgwWxFY,29
|
|
17
|
+
agentguard_python_sdk-0.2.6.dist-info/RECORD,,
|
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: agentguard-python-sdk
|
|
3
|
-
Version: 0.2.4
|
|
4
|
-
Summary: A production-grade middleware for AI agents to perform on-chain payments and verifiable consent.
|
|
5
|
-
Author: AgentGuard Team
|
|
6
|
-
License-Expression: MIT
|
|
7
|
-
Requires-Python: >=3.9
|
|
8
|
-
Description-Content-Type: text/markdown
|
|
9
|
-
License-File: LICENSE
|
|
10
|
-
Requires-Dist: httpx>=0.27.0
|
|
11
|
-
Requires-Dist: pydantic>=2.0.0
|
|
12
|
-
Requires-Dist: pydantic-settings>=2.0.0
|
|
13
|
-
Requires-Dist: pynacl>=1.5.0
|
|
14
|
-
Requires-Dist: py-algorand-sdk>=2.0.0
|
|
15
|
-
Dynamic: license-file
|
|
16
|
-
|
|
17
|
-
# AgentGuard SDK
|
|
18
|
-
|
|
19
|
-
AgentGuard is a production-grade middleware that enables AI agents to perform on-chain payments and generate verifiable consent proofs in compliance with DPDP (Digital Personal Data Protection) standards.
|
|
20
|
-
|
|
21
|
-
By isolating private keys in a hardened **MCP Server (Vault)** and using the **AgentGuard Backend (Dispatcher)** as a secure proxy, the SDK allows developers to add monetization and compliance to their agents with zero blockchain complexity.
|
|
22
|
-
|
|
23
|
-
## Installation
|
|
24
|
-
|
|
25
|
-
```bash
|
|
26
|
-
pip install agentguard-python-sdk
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
## Quick Start (3-Line Usage)
|
|
30
|
-
|
|
31
|
-
```python
|
|
32
|
-
import asyncio
|
|
33
|
-
from agentguard import AgentGuardClient
|
|
34
|
-
from agentguard.types import DelegatedAuthorization, AuthorizationConstraints, NonceContext, AuthorizationMetadata
|
|
35
|
-
|
|
36
|
-
async def main():
|
|
37
|
-
# 1. Initialize the client
|
|
38
|
-
# NOTE: The backend requires a cryptographic signature on `/pay`.
|
|
39
|
-
# Provide `private_key` as a base64-encoded Algorand private key seed for request signing.
|
|
40
|
-
async with AgentGuardClient(
|
|
41
|
-
wallet_address="YOUR_WALLET_ADDRESS",
|
|
42
|
-
private_key="YOUR_PRIVATE_KEY_B64",
|
|
43
|
-
backend_url="https://agentguard-backend-9s8j.onrender.com"
|
|
44
|
-
) as client:
|
|
45
|
-
|
|
46
|
-
# 1.5 Authorize a capability (LogicSig)
|
|
47
|
-
auth = DelegatedAuthorization(
|
|
48
|
-
authorization_id="YOUR_AUTHORIZATION_ID",
|
|
49
|
-
principal_id="YOUR_WALLET_ADDRESS",
|
|
50
|
-
logic_sig_b64="YOUR_SIGNED_LOGICSIG_BASE64",
|
|
51
|
-
constraints=AuthorizationConstraints(
|
|
52
|
-
max_amount_microalgos=1000000,
|
|
53
|
-
expires_at_unix=1900000000,
|
|
54
|
-
max_txns_per_window=10,
|
|
55
|
-
window_seconds=3600
|
|
56
|
-
),
|
|
57
|
-
nonce_context=NonceContext(lease_strategy="deterministic", scope="session"),
|
|
58
|
-
metadata=AuthorizationMetadata(created_at=1716000000, sdk_version="0.2.2", delegation_type="LogicSig")
|
|
59
|
-
)
|
|
60
|
-
await client.authorize(auth)
|
|
61
|
-
|
|
62
|
-
# 2. Perform a secure payment (includes automatic DPDP consent hashing)
|
|
63
|
-
receipt = await client.pay_and_fetch(
|
|
64
|
-
resource_url="https://api.stock.com/reliance",
|
|
65
|
-
amount_algo="0.05",
|
|
66
|
-
purpose="Financial Analysis",
|
|
67
|
-
authorization_id="YOUR_AUTHORIZATION_ID"
|
|
68
|
-
)
|
|
69
|
-
print(f"Payment Successful! TX ID: {receipt.tx_id}")
|
|
70
|
-
|
|
71
|
-
# 3. Verify compliance audit trail
|
|
72
|
-
proof = await client.verify(receipt.tx_id)
|
|
73
|
-
print(f"On-Chain Verified: {proof.verified}")
|
|
74
|
-
|
|
75
|
-
if __name__ == "__main__":
|
|
76
|
-
asyncio.run(main())
|
|
77
|
-
```
|
|
78
|
-
|
|
79
|
-
## Architecture
|
|
80
|
-
|
|
81
|
-
1. **SDK**: Generates UUID nonces and SHA256 consent hashes.
|
|
82
|
-
2. **Backend**: Proxies requests to the internal vault and maintains audit records.
|
|
83
|
-
3. **MCP Server (Vault)**: A hardened, isolated environment that signs transactions using Algorand.
|
|
84
|
-
4. **Algorand Blockchain**: Provides the irrefutable proof-of-consent and payment settlement.
|
|
85
|
-
|
|
86
|
-
## Features
|
|
87
|
-
|
|
88
|
-
- **Async First**: Built on `httpx` for high-performance agentic loops.
|
|
89
|
-
- **Zero-Trust**: Private keys never leave the vault.
|
|
90
|
-
- **DPDP Compliant**: Automatic cryptographic logging of purpose-bound consent.
|
|
91
|
-
- **Simple Audit**: Direct links to blockchain explorers for every transaction.
|
|
92
|
-
|
|
93
|
-
---
|
|
94
|
-
© 2026 AgentGuard Team. Built for the Algorand Ecosystem.
|
|
File without changes
|
{agentguard_python_sdk-0.2.4.dist-info → agentguard_python_sdk-0.2.6.dist-info}/licenses/LICENSE
RENAMED
|
File without changes
|
{agentguard_python_sdk-0.2.4.dist-info → agentguard_python_sdk-0.2.6.dist-info}/top_level.txt
RENAMED
|
File without changes
|