xproof 0.2.6__tar.gz → 0.2.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.
Files changed (36) hide show
  1. xproof-0.2.7/PKG-INFO +800 -0
  2. xproof-0.2.7/README.md +754 -0
  3. xproof-0.2.7/pyproject.toml +132 -0
  4. {xproof-0.2.6 → xproof-0.2.7}/tests/test_client.py +180 -1
  5. xproof-0.2.7/tests/test_confidence_trail.py +360 -0
  6. xproof-0.2.7/tests/test_context_drift.py +244 -0
  7. {xproof-0.2.6 → xproof-0.2.7}/tests/test_integration.py +1 -2
  8. xproof-0.2.7/tests/test_langchain_tool.py +343 -0
  9. xproof-0.2.7/tests/test_policy_check.py +186 -0
  10. {xproof-0.2.6 → xproof-0.2.7}/xproof/__init__.py +13 -1
  11. {xproof-0.2.6 → xproof-0.2.7}/xproof/client.py +32 -21
  12. {xproof-0.2.6 → xproof-0.2.7}/xproof/exceptions.py +19 -0
  13. {xproof-0.2.6 → xproof-0.2.7}/xproof/integrations/autogen.py +141 -5
  14. xproof-0.2.7/xproof/integrations/crewai.py +440 -0
  15. {xproof-0.2.6 → xproof-0.2.7}/xproof/integrations/deerflow.py +1 -1
  16. {xproof-0.2.6 → xproof-0.2.7}/xproof/integrations/fetchai.py +88 -21
  17. {xproof-0.2.6 → xproof-0.2.7}/xproof/integrations/langchain.py +7 -6
  18. {xproof-0.2.6 → xproof-0.2.7}/xproof/integrations/llamaindex.py +6 -5
  19. {xproof-0.2.6 → xproof-0.2.7}/xproof/integrations/openai_agents.py +7 -6
  20. xproof-0.2.7/xproof/langchain_tool.py +314 -0
  21. {xproof-0.2.6 → xproof-0.2.7}/xproof/models.py +78 -1
  22. xproof-0.2.7/xproof.egg-info/PKG-INFO +800 -0
  23. {xproof-0.2.6 → xproof-0.2.7}/xproof.egg-info/SOURCES.txt +5 -0
  24. {xproof-0.2.6 → xproof-0.2.7}/xproof.egg-info/requires.txt +5 -0
  25. xproof-0.2.6/PKG-INFO +0 -283
  26. xproof-0.2.6/README.md +0 -240
  27. xproof-0.2.6/pyproject.toml +0 -73
  28. xproof-0.2.6/xproof/integrations/crewai.py +0 -225
  29. xproof-0.2.6/xproof.egg-info/PKG-INFO +0 -283
  30. {xproof-0.2.6 → xproof-0.2.7}/LICENSE +0 -0
  31. {xproof-0.2.6 → xproof-0.2.7}/setup.cfg +0 -0
  32. {xproof-0.2.6 → xproof-0.2.7}/xproof/integrations/__init__.py +0 -0
  33. {xproof-0.2.6 → xproof-0.2.7}/xproof/py.typed +0 -0
  34. {xproof-0.2.6 → xproof-0.2.7}/xproof/utils.py +0 -0
  35. {xproof-0.2.6 → xproof-0.2.7}/xproof.egg-info/dependency_links.txt +0 -0
  36. {xproof-0.2.6 → xproof-0.2.7}/xproof.egg-info/top_level.txt +0 -0
xproof-0.2.7/PKG-INFO ADDED
@@ -0,0 +1,800 @@
1
+ Metadata-Version: 2.4
2
+ Name: xproof
3
+ Version: 0.2.7
4
+ Summary: Python SDK for xProof — blockchain-anchored proof-of-existence for AI agents on MultiversX
5
+ Author-email: xProof <contact@xproof.app>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://xproof.app
8
+ Project-URL: Repository, https://github.com/jasonxkensei/xproof
9
+ Project-URL: Documentation, https://xproof.app/docs
10
+ Project-URL: Issues, https://github.com/jasonxkensei/xproof/issues
11
+ Project-URL: Changelog, https://github.com/jasonxkensei/xproof/releases
12
+ Keywords: blockchain,multiversx,proof-of-existence,certification,sha256,ai-agents,accountability,audit-trail,web3
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Operating System :: OS Independent
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.9
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Topic :: Security :: Cryptography
22
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
23
+ Classifier: Typing :: Typed
24
+ Requires-Python: >=3.9
25
+ Description-Content-Type: text/markdown
26
+ License-File: LICENSE
27
+ Requires-Dist: requests>=2.25.0
28
+ Provides-Extra: dev
29
+ Requires-Dist: pytest>=7.0; extra == "dev"
30
+ Requires-Dist: pytest-asyncio>=0.21; extra == "dev"
31
+ Requires-Dist: responses>=0.20.0; extra == "dev"
32
+ Requires-Dist: ruff>=0.1.0; extra == "dev"
33
+ Requires-Dist: mypy>=1.0; extra == "dev"
34
+ Requires-Dist: types-requests>=2.25.0; extra == "dev"
35
+ Requires-Dist: pre-commit>=3.0; extra == "dev"
36
+ Provides-Extra: llamaindex
37
+ Requires-Dist: llama-index-core>=0.13.0; extra == "llamaindex"
38
+ Provides-Extra: autogen
39
+ Requires-Dist: pyautogen>=0.2.0; extra == "autogen"
40
+ Provides-Extra: openai-agents
41
+ Requires-Dist: openai-agents>=0.0.3; extra == "openai-agents"
42
+ Provides-Extra: deerflow
43
+ Provides-Extra: fetchai
44
+ Requires-Dist: uagents>=0.9.0; extra == "fetchai"
45
+ Dynamic: license-file
46
+
47
+ # xproof
48
+
49
+ [![Python SDK CI](https://github.com/jasonxkensei/xproof/actions/workflows/python-sdk.yml/badge.svg?branch=main)](https://github.com/jasonxkensei/xproof/actions/workflows/python-sdk.yml) [![PyPI version](https://img.shields.io/pypi/v/xproof)](https://pypi.org/project/xproof/) [![Python versions](https://img.shields.io/badge/python-3.9%20%7C%203.10%20%7C%203.11%20%7C%203.12-blue)](https://pypi.org/project/xproof/)
50
+
51
+ On-chain decision provenance for autonomous agents. **WHY before acting. WHAT after.** Timestamps written by the chain, not your agent.
52
+
53
+ ```bash
54
+ pip install xproof
55
+ ```
56
+
57
+ ---
58
+
59
+ ## 3 steps. 30 seconds.
60
+
61
+ ### Step 1 — Register (no wallet, no payment)
62
+
63
+ ```bash
64
+ curl -X POST https://xproof.app/api/agent/register \
65
+ -H "Content-Type: application/json" \
66
+ -d '{"agent_name": "my-agent"}'
67
+ ```
68
+
69
+ ```json
70
+ { "api_key": "pm_...", "trial": { "remaining": 10 } }
71
+ ```
72
+
73
+ ### Step 2 — Anchor WHY before acting
74
+
75
+ Hash your reasoning and certify it *before* your agent executes.
76
+
77
+ ```bash
78
+ curl -X POST https://xproof.app/api/proof \
79
+ -H "Authorization: Bearer pm_..." \
80
+ -H "Content-Type: application/json" \
81
+ -d '{
82
+ "file_hash": "<sha256_of_reasoning>",
83
+ "file_name": "reasoning.json",
84
+ "author": "my-agent",
85
+ "metadata": { "action_type": "decision" }
86
+ }'
87
+ ```
88
+
89
+ ```json
90
+ { "id": "why-proof-uuid", "transaction_hash": "0x..." }
91
+ ```
92
+
93
+ ### Step 3 — Anchor WHAT after acting
94
+
95
+ Hash your output and link it to the WHY proof.
96
+
97
+ ```bash
98
+ curl -X POST https://xproof.app/api/proof \
99
+ -H "Authorization: Bearer pm_..." \
100
+ -H "Content-Type: application/json" \
101
+ -d '{
102
+ "file_hash": "<sha256_of_output>",
103
+ "file_name": "output.json",
104
+ "author": "my-agent",
105
+ "metadata": { "action_type": "execution", "why_proof_id": "why-proof-uuid" }
106
+ }'
107
+ ```
108
+
109
+ ```json
110
+ { "id": "what-proof-uuid", "transaction_hash": "0x..." }
111
+ ```
112
+
113
+ When something goes wrong, you don't guess. You verify.
114
+
115
+ ---
116
+
117
+ ## Python SDK
118
+
119
+ ```python
120
+ from xproof import XProofClient, hash_string
121
+
122
+ # Register — zero-friction, no wallet, no payment
123
+ client = XProofClient.register("my-agent")
124
+ # 10 free certs, API key stored automatically
125
+
126
+ # Step 2: Anchor WHY before acting
127
+ why = client.certify_hash(
128
+ file_hash=hash_string('{"action": "summarize", "model": "gpt-4"}'),
129
+ file_name="reasoning.json",
130
+ author="my-agent",
131
+ metadata={"action_type": "decision"},
132
+ )
133
+
134
+ # Step 3: Anchor WHAT after acting
135
+ what = client.certify_hash(
136
+ file_hash=hash_string(execution_output),
137
+ file_name="output.json",
138
+ author="my-agent",
139
+ metadata={"action_type": "execution", "why_proof_id": why.id},
140
+ )
141
+
142
+ print(what.transaction_url) # MultiversX explorer link
143
+ ```
144
+
145
+ Or use an existing API key:
146
+
147
+ ```python
148
+ client = XProofClient(api_key="pm_your_api_key")
149
+ ```
150
+
151
+ ---
152
+
153
+ ## Framework Integrations
154
+
155
+ ### LangChain
156
+
157
+ ```python
158
+ from xproof.integrations.langchain import XProofCallbackHandler
159
+
160
+ handler = XProofCallbackHandler(api_key="pm_...")
161
+ llm = ChatOpenAI(callbacks=[handler])
162
+ ```
163
+
164
+ ### CrewAI
165
+
166
+ ```python
167
+ from xproof.integrations.crewai import XProofListener
168
+
169
+ listener = XProofListener(api_key="pm_...")
170
+ ```
171
+
172
+ ### AutoGen
173
+
174
+ ```python
175
+ from xproof.integrations.autogen import XProofHook
176
+
177
+ hook = XProofHook(api_key="pm_...")
178
+ agent.register_hook("process_last_received_message", hook.on_message)
179
+ ```
180
+
181
+ ### LlamaIndex
182
+
183
+ ```python
184
+ from xproof.integrations.llamaindex import XProofCallbackHandler
185
+ from llama_index.core import Settings
186
+ from llama_index.core.callbacks import CallbackManager
187
+
188
+ Settings.callback_manager = CallbackManager([XProofCallbackHandler(api_key="pm_...")])
189
+ ```
190
+
191
+ ---
192
+
193
+ ## 4W Framework (WHO / WHAT / WHEN / WHY)
194
+
195
+ Full accountability metadata on every certification:
196
+
197
+ ```python
198
+ from xproof import XProofClient, hash_bytes
199
+
200
+ client = XProofClient(api_key="pm_your_key")
201
+
202
+ action_data = b'{"action": "generate_report", "model": "gpt-4"}'
203
+ action_hash = hash_bytes(action_data)
204
+
205
+ cert = client.certify_hash(
206
+ file_hash=action_hash,
207
+ file_name="agent-action.json",
208
+ author="research-agent",
209
+ who="erd1abc...or-agent-id",
210
+ what=action_hash,
211
+ when="2026-03-20T12:00:00Z",
212
+ why=hash_bytes(b"Summarize Q1 earnings report"),
213
+ metadata={"model": "gpt-4", "session_id": "sess-123"},
214
+ )
215
+ ```
216
+
217
+ ## Batch Certification
218
+
219
+ Certify up to 50 files in a single API call:
220
+
221
+ ```python
222
+ results = client.batch_certify([
223
+ {"file_hash": "abc123...", "file_name": "file1.pdf", "author": "my-agent"},
224
+ {"file_hash": "def456...", "file_name": "file2.pdf"},
225
+ ])
226
+
227
+ print(results.summary.total) # 2
228
+ print(results.summary.created) # 2
229
+ ```
230
+
231
+ ## Certify a Local File
232
+
233
+ ```python
234
+ # Auto-hashes with SHA-256
235
+ cert = client.certify("path/to/report.pdf", author="my-agent")
236
+ print(cert.id)
237
+ print(cert.transaction_url)
238
+ ```
239
+
240
+ ## Verify a Proof
241
+
242
+ ```python
243
+ # By proof ID
244
+ proof = client.verify("certification-uuid")
245
+ print(proof.file_name, proof.blockchain_status)
246
+
247
+ # By file hash
248
+ proof = client.verify_hash("e3b0c442...")
249
+ ```
250
+
251
+ ## Policy Compliance
252
+
253
+ Check whether a decision meets governance requirements — without fetching the full confidence trail:
254
+
255
+ ```python
256
+ from xproof import XProofClient, PolicyCheckResult
257
+
258
+ client = XProofClient(api_key="pm_your_key")
259
+
260
+ result: PolicyCheckResult = client.get_policy_check("trade-xyz-2026")
261
+
262
+ if result.policy_compliant:
263
+ print("Decision is compliant.")
264
+ else:
265
+ for v in result.policy_violations:
266
+ print(f"VIOLATION [{v.severity}] — {v.rule}")
267
+ print(f" {v.message}")
268
+ ```
269
+
270
+ `get_policy_check()` is a lightweight yes/no compliance check. It returns `result.policy_compliant` (bool) and `result.policy_violations` (list). For the full audit trail including timestamps and intermediate confidence checkpoints, use `get_confidence_trail()` instead.
271
+
272
+ ---
273
+
274
+ ## Governance & Policy Enforcement
275
+
276
+ xProof detects automatically when an agent acted with insufficient confidence on an irreversible action — and writes the evidence on-chain before you ever open an incident report.
277
+
278
+ ### Mark decisions as reversible, costly, or irreversible
279
+
280
+ Add `reversibility_class` to any certified action. The server enforces a policy: **irreversible actions require `confidence_level >= 0.95`**. Anything below that threshold generates a policy violation anchored to the chain.
281
+
282
+ ```python
283
+ # An agent is about to execute a trade it cannot undo.
284
+ # It certifies its reasoning at 0.72 confidence — below the 0.95 threshold.
285
+ cert = client.certify_with_confidence(
286
+ file_hash=hash_string('{"action": "sell", "ticker": "AAPL", "qty": 500}'),
287
+ file_name="trade-decision.json",
288
+ author="trading-agent",
289
+ confidence_level=0.72, # Agent's self-assessed confidence
290
+ threshold_stage="pre-commitment",
291
+ decision_id="trade-xyz-2026",
292
+ reversibility_class="irreversible", # This action cannot be undone
293
+ )
294
+
295
+ # cert.reversibility_class == "irreversible"
296
+ # The server has recorded a policy violation: 0.72 < 0.95 required
297
+ ```
298
+
299
+ ### Check compliance — without fetching the full trail
300
+
301
+ ```python
302
+ from xproof import XProofClient, PolicyCheckResult
303
+
304
+ check: PolicyCheckResult = client.get_policy_check("trade-xyz-2026")
305
+
306
+ if not check.policy_compliant:
307
+ for v in check.policy_violations:
308
+ print(f"VIOLATION [{v.severity}] — {v.rule}")
309
+ print(f" {v.message}")
310
+ # → VIOLATION [error] — irreversible actions require confidence_level >= 0.95
311
+ # → confidence 0.72 is below the required threshold of 0.95
312
+ ```
313
+
314
+ ### Full confidence trail with policy result
315
+
316
+ ```python
317
+ trail = client.get_confidence_trail("trade-xyz-2026")
318
+
319
+ print(trail.policy_compliant) # False
320
+ print(len(trail.policy_violations)) # 1
321
+ print(trail.current_confidence) # 0.72
322
+ print(trail.is_finalized) # False — decision still open
323
+ ```
324
+
325
+ ### End-to-end agent example: document deletion with compliance gate
326
+
327
+ This example shows a realistic governance loop for an autonomous agent that is
328
+ about to permanently delete customer records — an irreversible action that
329
+ requires a confidence level of at least 0.95 before proceeding.
330
+
331
+ #### Option A — One-line LangChain tool (recommended for LangChain / LCEL agents)
332
+
333
+ If you are already running a LangChain or LCEL agent, drop in `XProofCertifyTool`
334
+ to collapse the four-step hash → certify → check → gate loop into a single
335
+ `tool.run()` call.
336
+
337
+ ```python
338
+ import json
339
+ from xproof.langchain_tool import XProofCertifyTool
340
+ from xproof.exceptions import PolicyViolationError
341
+
342
+ certify = XProofCertifyTool(api_key="pm_...", author="data-hygiene-agent")
343
+
344
+ decision = {
345
+ "action": "delete_customer_records",
346
+ "scope": "inactive_accounts",
347
+ "records_affected": 4821,
348
+ "retention_policy_checked": True,
349
+ "legal_hold_clear": True,
350
+ }
351
+ decision_id = "del-run-2026-04-20"
352
+
353
+ try:
354
+ tx_hash = certify.run({
355
+ "decision_text": json.dumps(decision, sort_keys=True),
356
+ "confidence_level": 0.97,
357
+ "threshold_stage": "pre-commitment",
358
+ "decision_id": decision_id,
359
+ "reversibility_class": "irreversible",
360
+ "why": "Scheduled GDPR data-retention cleanup",
361
+ })
362
+ print(f"Policy compliant — proceeding (tx: {tx_hash})")
363
+ # delete_customer_records(decision["scope"]) # your execution here
364
+ except PolicyViolationError as exc:
365
+ for v in exc.violations:
366
+ print(f"BLOCKED [{v.severity.upper()}] {v.rule}: {v.message}")
367
+ raise RuntimeError("Deletion aborted: policy compliance check failed.") from exc
368
+ ```
369
+
370
+ The tool hashes `decision_text` with SHA-256, calls
371
+ `certify_with_confidence`, and immediately runs `get_policy_check`.
372
+ If the check passes it returns the `transaction_hash`; if it fails it raises
373
+ `PolicyViolationError` with the full list of violations attached.
374
+
375
+ The tool accepts every parameter that `certify_with_confidence` does —
376
+ `who`, `what`, `when`, `why`, `reversibility_class`, `metadata`, and per-call
377
+ `author` — so you retain full provenance control. When `who`, `what`, or
378
+ `when` are omitted the tool supplies sensible defaults (resolved author, SHA-256
379
+ hash, current UTC timestamp respectively); explicitly passing any of them
380
+ overrides those defaults. You can also pass a pre-computed `file_hash` instead
381
+ of `decision_text` if you have already hashed the payload externally.
382
+
383
+ **Async support:** `XProofCertifyTool` fully supports async LangChain pipelines.
384
+ `_arun` is implemented via `asyncio.to_thread`, so it is safe to use in async
385
+ LCEL chains and async agent executors without blocking the event loop:
386
+
387
+ ```python
388
+ # Inside an async LCEL chain or async agent executor:
389
+ tx_hash = await certify.arun({
390
+ "decision_text": json.dumps(decision, sort_keys=True),
391
+ "confidence_level": 0.97,
392
+ "threshold_stage": "pre-commitment",
393
+ "decision_id": decision_id,
394
+ "reversibility_class": "irreversible",
395
+ "why": "Scheduled GDPR data-retention cleanup",
396
+ })
397
+ ```
398
+
399
+ You can also bind the tool to a LangChain agent directly:
400
+
401
+ ```python
402
+ from langchain.agents import initialize_agent, AgentType
403
+ from langchain_openai import ChatOpenAI
404
+
405
+ llm = ChatOpenAI(model="gpt-4o")
406
+ agent = initialize_agent(
407
+ tools=[certify],
408
+ llm=llm,
409
+ agent=AgentType.OPENAI_FUNCTIONS,
410
+ )
411
+ ```
412
+
413
+ #### Option A — One-line CrewAI tool (recommended for CrewAI agents)
414
+
415
+ `XProofCrewCertifyTool` provides the same one-call certification loop for
416
+ CrewAI agents. `XProofNativeCrewCertifyTool` wraps it as a native
417
+ `BaseTool` subclass so it can be added directly to a CrewAI agent's
418
+ `tools` list.
419
+
420
+ ```python
421
+ import json
422
+ from xproof.integrations.crewai import XProofCrewCertifyTool
423
+ from xproof.exceptions import PolicyViolationError
424
+
425
+ certify = XProofCrewCertifyTool(api_key="pm_...", author="data-hygiene-agent")
426
+
427
+ decision = {
428
+ "action": "delete_customer_records",
429
+ "scope": "inactive_accounts",
430
+ "records_affected": 4821,
431
+ "retention_policy_checked": True,
432
+ "legal_hold_clear": True,
433
+ }
434
+ decision_id = "del-run-2026-04-20"
435
+
436
+ try:
437
+ tx_hash = certify.run(
438
+ decision_text=json.dumps(decision, sort_keys=True),
439
+ confidence_level=0.97,
440
+ threshold_stage="pre-commitment",
441
+ decision_id=decision_id,
442
+ reversibility_class="irreversible",
443
+ why="Scheduled GDPR data-retention cleanup",
444
+ )
445
+ print(f"Policy compliant — proceeding (tx: {tx_hash})")
446
+ # delete_customer_records(decision["scope"]) # your execution here
447
+ except PolicyViolationError as exc:
448
+ for v in exc.violations:
449
+ print(f"BLOCKED [{v.severity.upper()}] {v.rule}: {v.message}")
450
+ raise RuntimeError("Deletion aborted: policy compliance check failed.") from exc
451
+ ```
452
+
453
+ To attach it natively to a CrewAI `Agent`:
454
+
455
+ ```python
456
+ from crewai import Agent
457
+ from xproof.integrations.crewai import XProofNativeCrewCertifyTool
458
+
459
+ certify_tool = XProofNativeCrewCertifyTool(
460
+ api_key="pm_...", author="data-hygiene-agent"
461
+ )
462
+ agent = Agent(role="analyst", tools=[certify_tool], ...)
463
+ ```
464
+
465
+ #### Option A — One-line AutoGen tool (recommended for AutoGen agents)
466
+
467
+ `xproof_certify_decision` is a plain callable with the same full loop —
468
+ hash → certify → policy check → gate — designed to be registered as a
469
+ function tool on any AutoGen `ConversableAgent`.
470
+
471
+ ```python
472
+ import json
473
+ from xproof.integrations.autogen import xproof_certify_decision
474
+ from xproof.exceptions import PolicyViolationError
475
+
476
+ decision = {
477
+ "action": "delete_customer_records",
478
+ "scope": "inactive_accounts",
479
+ "records_affected": 4821,
480
+ "retention_policy_checked": True,
481
+ "legal_hold_clear": True,
482
+ }
483
+ decision_id = "del-run-2026-04-20"
484
+
485
+ try:
486
+ tx_hash = xproof_certify_decision(
487
+ decision_text=json.dumps(decision, sort_keys=True),
488
+ confidence_level=0.97,
489
+ threshold_stage="pre-commitment",
490
+ decision_id=decision_id,
491
+ reversibility_class="irreversible",
492
+ why="Scheduled GDPR data-retention cleanup",
493
+ author="data-hygiene-agent",
494
+ api_key="pm_...",
495
+ )
496
+ print(f"Policy compliant — proceeding (tx: {tx_hash})")
497
+ # delete_customer_records(decision["scope"]) # your execution here
498
+ except PolicyViolationError as exc:
499
+ for v in exc.violations:
500
+ print(f"BLOCKED [{v.severity.upper()}] {v.rule}: {v.message}")
501
+ raise RuntimeError("Deletion aborted: policy compliance check failed.") from exc
502
+ ```
503
+
504
+ You can also register it as a tool on an AutoGen agent so the LLM can
505
+ invoke it by name:
506
+
507
+ ```python
508
+ from autogen import AssistantAgent, UserProxyAgent
509
+ from functools import partial
510
+ from xproof.integrations.autogen import xproof_certify_decision
511
+
512
+ # Bind api_key once; the agent passes the decision fields per call.
513
+ certify = partial(xproof_certify_decision, api_key="pm_...", author="analyst-agent")
514
+
515
+ assistant = AssistantAgent("analyst", llm_config={...})
516
+ user_proxy = UserProxyAgent("user_proxy", human_input_mode="NEVER")
517
+
518
+ assistant.register_for_llm(name="certify_decision", description="Certify a decision on-chain")(certify)
519
+ user_proxy.register_for_execution(name="certify_decision")(certify)
520
+ ```
521
+
522
+ #### Option B — Manual four-step loop (framework-agnostic)
523
+
524
+ ```python
525
+ import hashlib, json
526
+ from xproof import XProofClient
527
+
528
+ client = XProofClient(api_key="pm_...")
529
+
530
+ def hash_string(s: str) -> str:
531
+ return hashlib.sha256(s.encode()).hexdigest()
532
+
533
+ # ── Step 1: Agent produces its reasoning ─────────────────────────────────────
534
+ # (In a real LangChain / CrewAI / AutoGen agent, this would be the structured
535
+ # chain-of-thought or tool-call output produced just before execution.)
536
+
537
+ decision = {
538
+ "action": "delete_customer_records",
539
+ "scope": "inactive_accounts",
540
+ "records_affected": 4821,
541
+ "retention_policy_checked": True,
542
+ "legal_hold_clear": True,
543
+ "agent": "data-hygiene-agent",
544
+ "run_id": "del-run-2026-04-20",
545
+ }
546
+ decision_id = "del-run-2026-04-20"
547
+ reasoning_hash = hash_string(json.dumps(decision, sort_keys=True))
548
+
549
+ # ── Step 2: Certify BEFORE executing ─────────────────────────────────────────
550
+ # The agent self-assesses its confidence. Because the action is irreversible,
551
+ # the policy requires confidence_level >= 0.95.
552
+
553
+ cert = client.certify_with_confidence(
554
+ file_hash=reasoning_hash,
555
+ file_name="delete-decision.json",
556
+ author="data-hygiene-agent",
557
+ confidence_level=0.97, # Agent is highly confident
558
+ threshold_stage="pre-commitment", # valid: "initial", "partial", "pre-commitment", "final"
559
+ decision_id=decision_id,
560
+ reversibility_class="irreversible", # Deletion cannot be undone
561
+ )
562
+
563
+ # cert.transaction_hash is the on-chain anchor — written before any records move
564
+
565
+ # ── Step 3: Compliance gate ───────────────────────────────────────────────────
566
+ # Immediately check policy compliance. This is a lightweight call — it does NOT
567
+ # re-fetch the full trail. Gate the destructive action on the result.
568
+
569
+ check = client.get_policy_check(decision_id)
570
+
571
+ if not check.policy_compliant:
572
+ # Policy violated — log every violation and abort.
573
+ for v in check.policy_violations:
574
+ print(f"BLOCKED [{v.severity.upper()}] {v.rule}")
575
+ print(f" {v.message}")
576
+ raise RuntimeError("Deletion aborted: policy compliance check failed.")
577
+
578
+ # ── Step 4: Execute only when compliant ──────────────────────────────────────
579
+ print(f"Policy compliant — proceeding with deletion (cert: {cert.transaction_hash})")
580
+ # delete_customer_records(decision["scope"]) # your actual execution here
581
+ ```
582
+
583
+ **What happens if the agent's confidence is too low?**
584
+
585
+ Drop `confidence_level` to `0.82` and the same gate blocks execution:
586
+
587
+ ```python
588
+ cert = client.certify_with_confidence(
589
+ ...
590
+ confidence_level=0.82, # Below the 0.95 irreversible threshold
591
+ reversibility_class="irreversible",
592
+ )
593
+
594
+ check = client.get_policy_check(decision_id)
595
+
596
+ if not check.policy_compliant:
597
+ for v in check.policy_violations:
598
+ print(f"BLOCKED [{v.severity.upper()}] {v.rule}")
599
+ # → BLOCKED [ERROR] irreversible actions require confidence_level >= 0.95
600
+ print(f" {v.message}")
601
+ # → confidence 0.82 is below the required threshold of 0.95
602
+
603
+ raise RuntimeError("Deletion aborted: policy compliance check failed.")
604
+ ```
605
+
606
+ The violation is written on-chain at certification time — before your code
607
+ ever reaches the gate — so the audit trail exists even if your agent crashes
608
+ between `certify_with_confidence` and `get_policy_check`.
609
+
610
+ ### Observability — surfacing violations in dashboards
611
+
612
+ Raising a `RuntimeError` is enough to halt execution, but it gives your
613
+ observability stack nothing structured to alert on. The pattern below emits a
614
+ machine-readable JSON log line for each violation and optionally fires a
615
+ webhook, so Datadog / Grafana / CloudWatch log-based alerts can pick up
616
+ violations without grepping free-form text.
617
+
618
+ ```python
619
+ import json, logging, urllib.request
620
+ from xproof import XProofClient
621
+
622
+ logger = logging.getLogger("xproof.compliance")
623
+ logging.basicConfig(level=logging.INFO)
624
+
625
+ client = XProofClient(api_key="pm_...")
626
+
627
+ # Optional: set a webhook URL to receive violation payloads
628
+ VIOLATION_WEBHOOK_URL = None # e.g. "https://hooks.example.com/compliance"
629
+
630
+ def _emit_violation(decision_id: str, violation) -> None:
631
+ """Emit one structured log line and, optionally, a webhook call."""
632
+ payload = {
633
+ "event": "policy_violation",
634
+ "decision_id": decision_id,
635
+ "rule": violation.rule,
636
+ "severity": violation.severity,
637
+ "message": violation.message,
638
+ }
639
+ # ── Structured JSON log (ingested by Datadog / CloudWatch / Loki) ─────────
640
+ logger.error(json.dumps(payload))
641
+
642
+ # ── Optional webhook / alerting callback (best-effort) ───────────────────
643
+ if VIOLATION_WEBHOOK_URL:
644
+ try:
645
+ body = json.dumps(payload).encode()
646
+ req = urllib.request.Request(
647
+ VIOLATION_WEBHOOK_URL,
648
+ data=body,
649
+ headers={"Content-Type": "application/json"},
650
+ method="POST",
651
+ )
652
+ with urllib.request.urlopen(req, timeout=5):
653
+ pass # fire-and-forget; add retry logic as needed
654
+ except Exception as exc:
655
+ # Best-effort delivery — a webhook failure must NOT swallow the
656
+ # compliance violation itself. Log and continue to the raise below.
657
+ logger.warning(json.dumps({"event": "webhook_error", "detail": str(exc)}))
658
+
659
+ check = client.get_policy_check(decision_id)
660
+
661
+ if not check.policy_compliant:
662
+ for v in check.policy_violations:
663
+ _emit_violation(decision_id, v)
664
+
665
+ # ── Full audit trail for post-mortem / SIEM export ────────────────────────
666
+ # get_confidence_trail() returns a ConfidenceTrail object containing every
667
+ # certification event — confidence levels, timestamps, transaction hashes —
668
+ # so you can attach the complete chain-of-evidence to an incident ticket or
669
+ # ship it to your SIEM without a separate lookup.
670
+ # trail.raw is the unmodified API response dict; use trail.stages for
671
+ # programmatic access to individual ConfidenceTrailStage entries.
672
+ # Note: redact sensitive fields from trail.raw before logging or exporting
673
+ # to centralised logs / SIEM in production environments.
674
+ trail = client.get_confidence_trail(decision_id)
675
+ logger.error(json.dumps({
676
+ "event": "audit_trail",
677
+ "decision_id": decision_id,
678
+ "trail": trail.raw,
679
+ }))
680
+
681
+ raise RuntimeError("Deletion aborted: policy compliance check failed.")
682
+ ```
683
+
684
+ Each `logger.error(...)` call writes a single-line JSON object that log
685
+ shippers (Fluentd, the Datadog Agent, the CloudWatch agent) forward verbatim.
686
+ Create a log-based metric or alert on `event = "policy_violation"` to get
687
+ dashboard counts and threshold alerts with no extra instrumentation.
688
+
689
+ ### Three classes, one parameter
690
+
691
+ | `reversibility_class` | What it means | Policy threshold |
692
+ |---|---|---|
693
+ | `"reversible"` | Action can be undone (e.g. draft, preview) | None — any confidence accepted |
694
+ | `"costly"` | Undoable but expensive (e.g. API call, DB write) | None — any confidence accepted |
695
+ | `"irreversible"` | Cannot be undone (e.g. trade, deletion, send) | `confidence_level >= 0.95` required |
696
+
697
+ > The threshold is configured server-side (`IRREVERSIBLE_CONFIDENCE_THRESHOLD=0.95`). All violations are written on-chain and cannot be amended.
698
+
699
+ ---
700
+
701
+ ## Pricing
702
+
703
+ ```python
704
+ pricing = client.get_pricing()
705
+ print(pricing.price_usd) # e.g. 0.05
706
+ ```
707
+
708
+ ## API Reference
709
+
710
+ ### `XProofClient(api_key=None, base_url="https://xproof.app", timeout=30)`
711
+
712
+ | Parameter | Type | Default |
713
+ |------------|-------|------------------------|
714
+ | `api_key` | `str` | `None` |
715
+ | `base_url` | `str` | `"https://xproof.app"` |
716
+ | `timeout` | `int` | `30` (seconds) |
717
+
718
+ ### Methods
719
+
720
+ | Method | Description |
721
+ |--------|-------------|
722
+ | `XProofClient.register(agent_name)` | Register agent, get trial key |
723
+ | `certify(path, author, *, reversibility_class?, **fourW)` | Certify file (hashes locally) |
724
+ | `certify_hash(file_hash, file_name, author, *, reversibility_class?, **fourW)` | Certify by pre-computed hash |
725
+ | `certify_with_confidence(hash, name, author, confidence_level, threshold_stage, decision_id, *, reversibility_class?, **fourW)` | Certify with confidence + governance class |
726
+ | `batch_certify(files)` | Batch certify (up to 50) |
727
+ | `verify(proof_id)` | Look up by proof ID |
728
+ | `verify_hash(file_hash)` | Look up by file hash |
729
+ | `get_confidence_trail(decision_id)` | Full trail with `policy_compliant` + violations |
730
+ | `get_policy_check(decision_id)` | Lightweight compliance check — no full trail |
731
+ | `get_pricing()` | Get current pricing |
732
+
733
+ ## Development
734
+
735
+ Install dev dependencies and run the checks locally:
736
+
737
+ ```bash
738
+ pip install -e ".[dev]"
739
+
740
+ # Lint (ruff — catches unused imports, duplicate class definitions, and more)
741
+ make lint
742
+
743
+ # Type-check with mypy
744
+ make typecheck
745
+
746
+ # Unit tests (excludes live-API integration tests)
747
+ make test
748
+
749
+ # Lint + typecheck + test together
750
+ make check
751
+ ```
752
+
753
+ ### Pre-commit hooks
754
+
755
+ A pre-commit hook runs `mypy` automatically before every commit so type errors
756
+ are caught locally rather than in CI.
757
+
758
+ Run the one-time setup from the `python-sdk/` directory:
759
+
760
+ ```bash
761
+ make install-hooks
762
+ # Equivalent (run from the repo root):
763
+ # pre-commit install --config python-sdk/.pre-commit-config.yaml
764
+ ```
765
+
766
+ After that, every `git commit` will run `make typecheck`, which checks the
767
+ entire `xproof/` package with mypy. If mypy reports any errors the commit is
768
+ blocked until they are fixed.
769
+
770
+ To run the hooks manually against all files without committing (from the repo root):
771
+
772
+ ```bash
773
+ pre-commit run --all-files --config python-sdk/.pre-commit-config.yaml
774
+ ```
775
+
776
+ The linter is configured in `pyproject.toml` under `[tool.ruff]`. Rule `F811` will flag
777
+ duplicate top-level class definitions — the kind of silent overwrite that prompted this setup.
778
+
779
+ ### VS Code setup
780
+
781
+ A `.vscode/settings.json` is included in this directory. It configures the
782
+ [Ruff extension](https://marketplace.visualstudio.com/items?itemName=charliermarsh.ruff)
783
+ as the default Python formatter and enables **format on save**, so unused imports are
784
+ removed and imports are sorted automatically every time you save a file.
785
+
786
+ Install the extension once and the settings take effect immediately:
787
+
788
+ ```bash
789
+ code --install-extension charliermarsh.ruff
790
+ ```
791
+
792
+ ## Links
793
+
794
+ - [xproof.app](https://xproof.app) — dashboard & docs
795
+ - [npm SDK](https://www.npmjs.com/package/@xproof/xproof) — `npm install @xproof/xproof`
796
+ - [Examples](https://github.com/jasonxkensei/xproof-examples) — LangChain, CrewAI, AutoGen, LlamaIndex
797
+
798
+ ## License
799
+
800
+ MIT