veritera 0.1.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.
@@ -0,0 +1,14 @@
1
+ Metadata-Version: 2.4
2
+ Name: veritera
3
+ Version: 0.1.0
4
+ Summary: Veritera Forge SDK — Verify AI agent decisions with cryptographic attestation
5
+ Author-email: Veritera AI <engineering@veritera.ai>
6
+ License: MIT
7
+ Project-URL: Homepage, https://veritera.ai
8
+ Project-URL: Documentation, https://veritera.ai/docs
9
+ Project-URL: Repository, https://github.com/VeriteraAI/veritera-website
10
+ Keywords: veritera,forge,ai,verification,agent
11
+ Requires-Python: >=3.9
12
+ Description-Content-Type: text/markdown
13
+ Requires-Dist: httpx>=0.25.0
14
+ Requires-Dist: cryptography>=41.0.0
@@ -0,0 +1,22 @@
1
+ [project]
2
+ name = "veritera"
3
+ version = "0.1.0"
4
+ description = "Veritera Forge SDK — Verify AI agent decisions with cryptographic attestation"
5
+ readme = "README.md"
6
+ license = {text = "MIT"}
7
+ requires-python = ">=3.9"
8
+ authors = [{name = "Veritera AI", email = "engineering@veritera.ai"}]
9
+ keywords = ["veritera", "forge", "ai", "verification", "agent"]
10
+ dependencies = [
11
+ "httpx>=0.25.0",
12
+ "cryptography>=41.0.0",
13
+ ]
14
+
15
+ [project.urls]
16
+ Homepage = "https://veritera.ai"
17
+ Documentation = "https://veritera.ai/docs"
18
+ Repository = "https://github.com/VeriteraAI/veritera-website"
19
+
20
+ [build-system]
21
+ requires = ["setuptools>=68.0"]
22
+ build-backend = "setuptools.build_meta"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,6 @@
1
+ """Veritera Forge SDK — Verify AI agent decisions with cryptographic attestation."""
2
+
3
+ from .client import Veritera, ForgeError, RateLimitError
4
+
5
+ __version__ = "0.1.0"
6
+ __all__ = ["Veritera", "ForgeError", "RateLimitError"]
@@ -0,0 +1,286 @@
1
+ """
2
+ Veritera Forge SDK — Python Client
3
+ Enterprise-grade AI agent decision verification.
4
+
5
+ Usage:
6
+ from veritera import Veritera
7
+
8
+ forge = Veritera(api_key="vt_live_...")
9
+ result = await forge.verify_decision(
10
+ agent_id="agent-1",
11
+ action="payment.create",
12
+ params={"amount": 100, "currency": "USD"},
13
+ policy="finance-controls",
14
+ )
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ import hashlib
20
+ import json
21
+ import time
22
+ from dataclasses import dataclass, field
23
+ from typing import Any, Optional
24
+
25
+ import httpx
26
+ from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey
27
+ from cryptography.hazmat.primitives.serialization import load_der_public_key
28
+ import base64
29
+
30
+
31
+ class ForgeError(Exception):
32
+ """Structured error from the Forge API."""
33
+
34
+ def __init__(self, message: str, code: str = "unknown", status: int = 0, details: Any = None):
35
+ super().__init__(message)
36
+ self.code = code
37
+ self.status = status
38
+ self.details = details
39
+
40
+
41
+ class RateLimitError(ForgeError):
42
+ """Rate limit exceeded."""
43
+
44
+ def __init__(self, message: str, retry_after_ms: int = 60000):
45
+ super().__init__(message, code="rate_limited", status=429)
46
+ self.retry_after_ms = retry_after_ms
47
+
48
+
49
+ @dataclass
50
+ class ConstraintResult:
51
+ type: str
52
+ result: str # "pass" | "fail" | "skip"
53
+ detail: Optional[str] = None
54
+
55
+
56
+ @dataclass
57
+ class VerifyResponse:
58
+ verified: bool
59
+ verdict: str
60
+ proof_id: str
61
+ agent_id: str
62
+ action: str
63
+ policy: Optional[str]
64
+ reason: Optional[str]
65
+ mode: str
66
+ proof_status: str
67
+ evaluated_constraints: list[ConstraintResult]
68
+ verification: dict[str, Any]
69
+ audit_id: str
70
+ latency_ms: float
71
+ timestamp: str
72
+
73
+
74
+ @dataclass
75
+ class LocalVerifyResult:
76
+ valid: bool
77
+ key_version: Optional[int]
78
+ payload_hash: str
79
+ algorithm: str = "Ed25519"
80
+
81
+
82
+ class Veritera:
83
+ """Veritera Forge SDK Client."""
84
+
85
+ def __init__(
86
+ self,
87
+ api_key: str,
88
+ base_url: str = "https://api.veritera.ai",
89
+ timeout: float = 10.0,
90
+ max_retries: int = 2,
91
+ debug: bool = False,
92
+ ):
93
+ if not api_key:
94
+ raise ValueError("Forge SDK: api_key is required")
95
+ self._api_key = api_key
96
+ self._base_url = base_url.rstrip("/")
97
+ self._timeout = timeout
98
+ self._max_retries = max_retries
99
+ self._debug = debug
100
+ import os
101
+ _env = os.environ.get("PYTHON_ENV", os.environ.get("ENV", "production"))
102
+ self._client = httpx.AsyncClient(
103
+ base_url=self._base_url,
104
+ timeout=self._timeout,
105
+ headers={
106
+ "Authorization": f"Bearer {self._api_key}",
107
+ "Content-Type": "application/json",
108
+ "User-Agent": "veritera-python/0.1.0",
109
+ "X-Forge-SDK-Version": "0.1.0",
110
+ "X-Forge-Environment": _env,
111
+ },
112
+ )
113
+
114
+ async def __aenter__(self):
115
+ return self
116
+
117
+ async def __aexit__(self, *args):
118
+ await self.close()
119
+
120
+ async def close(self):
121
+ await self._client.aclose()
122
+
123
+ async def _request(self, method: str, path: str, body: Optional[dict] = None) -> dict:
124
+ """HTTP request with retry and backoff."""
125
+ last_error: Optional[Exception] = None
126
+
127
+ for attempt in range(self._max_retries + 1):
128
+ if attempt > 0:
129
+ delay = min(1.0 * (2 ** (attempt - 1)), 10.0)
130
+ if self._debug:
131
+ print(f"[Forge SDK] Retry {attempt}/{self._max_retries} after {delay:.1f}s")
132
+ await _async_sleep(delay)
133
+
134
+ try:
135
+ response = await self._client.request(
136
+ method, path,
137
+ json=body if body else None,
138
+ )
139
+
140
+ if response.status_code == 429:
141
+ retry_after = int(response.headers.get("Retry-After", "60")) * 1000
142
+ raise RateLimitError("Rate limit exceeded", retry_after)
143
+
144
+ if 400 <= response.status_code < 500:
145
+ data = response.json() if response.content else {}
146
+ raise ForgeError(
147
+ data.get("message", f"Request failed: {response.status_code}"),
148
+ code=data.get("error", "client_error"),
149
+ status=response.status_code,
150
+ details=data,
151
+ )
152
+
153
+ if response.status_code >= 500:
154
+ last_error = ForgeError(f"Server error: {response.status_code}", status=response.status_code)
155
+ continue
156
+
157
+ return response.json()
158
+
159
+ except (httpx.TimeoutException, httpx.ConnectError) as e:
160
+ last_error = ForgeError(str(e), code="network_error", status=0)
161
+ continue
162
+
163
+ raise last_error or ForgeError("Request failed after retries")
164
+
165
+ # ── Core: Verify a Decision ──
166
+
167
+ async def verify_decision(
168
+ self,
169
+ agent_id: str,
170
+ action: str,
171
+ params: Optional[dict[str, Any]] = None,
172
+ policy: Optional[str] = None,
173
+ allowed_actions: Optional[list[str]] = None,
174
+ delegation_id: Optional[str] = None,
175
+ require_zk_proof: bool = False,
176
+ ) -> VerifyResponse:
177
+ """Verify an AI agent decision against policy constraints."""
178
+ body: dict[str, Any] = {"agent_id": agent_id, "action": action}
179
+ if params:
180
+ body["params"] = params
181
+ if policy:
182
+ body["policy"] = policy
183
+ if allowed_actions:
184
+ body["allowed_actions"] = allowed_actions
185
+ if delegation_id:
186
+ body["delegation_id"] = delegation_id
187
+ if require_zk_proof:
188
+ body["require_zk_proof"] = True
189
+
190
+ raw = await self._request("POST", "/v1/verify", body)
191
+ return self._map_verify_response(raw)
192
+
193
+ # ── Proof Retrieval ──
194
+
195
+ async def get_proof(self, proof_id: str) -> dict:
196
+ """Retrieve a verification proof by ID."""
197
+ return await self._request("GET", f"/v1/proofs/{proof_id}")
198
+
199
+ # ── Local Proof Verification ──
200
+
201
+ def verify_proof_locally(
202
+ self,
203
+ attestation: str,
204
+ attestation_payload: dict[str, Any],
205
+ public_key: str,
206
+ key_version: Optional[int] = None,
207
+ ) -> LocalVerifyResult:
208
+ """Verify a DPE attestation locally without any backend call."""
209
+ payload_bytes = json.dumps(attestation_payload).encode("utf-8")
210
+ payload_hash = hashlib.sha256(payload_bytes).hexdigest()
211
+
212
+ try:
213
+ sig_bytes = base64.b64decode(attestation)
214
+ pub_key_der = base64.b64decode(public_key)
215
+ pub_key = load_der_public_key(pub_key_der)
216
+
217
+ if not isinstance(pub_key, Ed25519PublicKey):
218
+ return LocalVerifyResult(valid=False, key_version=key_version, payload_hash=payload_hash)
219
+
220
+ pub_key.verify(sig_bytes, payload_bytes)
221
+ return LocalVerifyResult(valid=True, key_version=key_version, payload_hash=payload_hash)
222
+
223
+ except Exception:
224
+ return LocalVerifyResult(valid=False, key_version=key_version, payload_hash=payload_hash)
225
+
226
+ # ── Delegation ──
227
+
228
+ async def create_delegation(
229
+ self,
230
+ agent_id: str,
231
+ allowed_actions: list[str],
232
+ constraints: Optional[dict] = None,
233
+ expires_in: Optional[str] = None,
234
+ ) -> dict:
235
+ """Create a scoped delegation for an agent."""
236
+ body: dict[str, Any] = {"agent_id": agent_id, "allowed_actions": allowed_actions}
237
+ if constraints:
238
+ body["constraints"] = constraints
239
+ if expires_in:
240
+ body["expires_in"] = expires_in
241
+ return await self._request("POST", "/v1/delegate", body)
242
+
243
+ # ── Usage ──
244
+
245
+ async def get_usage(self, period: Optional[str] = None) -> dict:
246
+ """Get usage statistics for the current billing period."""
247
+ path = f"/v1/usage?period={period}" if period else "/v1/usage"
248
+ return await self._request("GET", path)
249
+
250
+ # ── Health ──
251
+
252
+ async def health(self) -> dict:
253
+ """Check Forge API health."""
254
+ return await self._request("GET", "/v1/health")
255
+
256
+ # ── Response Mapping ──
257
+
258
+ def _map_verify_response(self, raw: dict) -> VerifyResponse:
259
+ verification = raw.get("verification", {})
260
+ constraints = raw.get("evaluated_constraints", [])
261
+
262
+ return VerifyResponse(
263
+ verified=bool(raw.get("verified")),
264
+ verdict=raw.get("verdict", "approved" if raw.get("verified") else "denied"),
265
+ proof_id=str(raw.get("proof_id", "")),
266
+ agent_id=str(raw.get("agent_id", "")),
267
+ action=str(raw.get("action", "")),
268
+ policy=raw.get("policy"),
269
+ reason=raw.get("reason"),
270
+ mode=raw.get("mode", "dpe"),
271
+ proof_status=raw.get("proof_status", "not_requested"),
272
+ evaluated_constraints=[
273
+ ConstraintResult(type=c.get("type", ""), result=c.get("result", "skip"), detail=c.get("detail"))
274
+ for c in constraints
275
+ ],
276
+ verification=verification,
277
+ audit_id=str(raw.get("audit_id", "")),
278
+ latency_ms=float(raw.get("latency_ms", 0)),
279
+ timestamp=str(raw.get("timestamp", "")),
280
+ )
281
+
282
+
283
+ async def _async_sleep(seconds: float):
284
+ """Async sleep helper."""
285
+ import asyncio
286
+ await asyncio.sleep(seconds)
@@ -0,0 +1,14 @@
1
+ Metadata-Version: 2.4
2
+ Name: veritera
3
+ Version: 0.1.0
4
+ Summary: Veritera Forge SDK — Verify AI agent decisions with cryptographic attestation
5
+ Author-email: Veritera AI <engineering@veritera.ai>
6
+ License: MIT
7
+ Project-URL: Homepage, https://veritera.ai
8
+ Project-URL: Documentation, https://veritera.ai/docs
9
+ Project-URL: Repository, https://github.com/VeriteraAI/veritera-website
10
+ Keywords: veritera,forge,ai,verification,agent
11
+ Requires-Python: >=3.9
12
+ Description-Content-Type: text/markdown
13
+ Requires-Dist: httpx>=0.25.0
14
+ Requires-Dist: cryptography>=41.0.0
@@ -0,0 +1,8 @@
1
+ pyproject.toml
2
+ veritera/__init__.py
3
+ veritera/client.py
4
+ veritera.egg-info/PKG-INFO
5
+ veritera.egg-info/SOURCES.txt
6
+ veritera.egg-info/dependency_links.txt
7
+ veritera.egg-info/requires.txt
8
+ veritera.egg-info/top_level.txt
@@ -0,0 +1,2 @@
1
+ httpx>=0.25.0
2
+ cryptography>=41.0.0
@@ -0,0 +1 @@
1
+ veritera