Fail-closed verify-before-you-act gate for AI agents. One-line adapters for LangChain, LangGraph, CrewAI, and the OpenAI Agents SDK. Signed receipts, pay-per-call via x402.
Project description
verity-guard
A fail-closed verify-before-you-act gate for AI agents — in one line, for the framework you already use.
Your agent is about to wire money, send an email, run rm -rf, or publish something. verity-guard asks an independent service for an allow / review / block second opinion at the exact moment a mistake becomes permanent — and hands back a signed, independently re-verifiable verdict you can prove to an auditor later without trusting anyone.
Not "we signed a receipt that a call happened" (everyone does that now). A re-verifiable verdict — cryptographic proof that an action was independently judged safe, or that a claim was checked and supported. Fail-closed across four functions: guard actions, verify facts, detect prompt-injection, redact PII/secrets.
- 🔒 Fail-closed — when unsure it escalates to
revieworblock, never a confident wrongallow. - 🧾 Ed25519-signed verdicts — every paid result carries a receipt that verifies offline against our published key at
/.well-known/verity-pubkey.json, forever, without us.verify_receipt()is the convenient free live check (it POSTs to the issuer — use the key directly if you want an independent one). - 🔑 Keyless & non-custodial — this SDK holds no wallet and never pays silently. Paid routes answer HTTP 402; your own x402 layer settles the disclosed USDC micro-payment on Base.
- 🧩 Native adapters — LangChain, LangGraph, CrewAI, OpenAI Agents SDK. Base install pulls in none of them.
Live now: guard_action from $0.02/call. Full wire-in guide → https://veritylayer.dev/guard
Node / TypeScript?
npm i @veritylayer/guard— the same keyless client plus a Vercel AI SDK adapter. Source lives injs/.
Install
pip install verity-guard # tiny — just httpx
pip install "verity-guard[x402]" # + the built-in payer (bring your own wallet key)
pip install "verity-guard[langgraph]" # + your framework of choice
30-second quickstart
VerityLayer is pay-per-call over x402 — no account, no API key, no subscription. Point the client at a wallet you funded with USDC on Base and you get real verdicts:
import os
from verity_guard import VerityClient, x402_payer
v = VerityClient(http=x402_payer(os.environ["VERITY_WALLET_KEY"]))
res = v.guard(
"Wire $4,000 USDC to 0x9a3f…c012 (invoice #221)",
context="Invoice arrived via a scraped web page; address never seen before.",
policy="No new payees without human review.",
)
print(res.decision, res.risk) # -> block 0.9
print(res.safer_alternative) # -> "Halt payment. Cross-verify via known channels…"
print(v.verify_receipt(res.receipt).valid) # -> True (free live check; the receipt itself
# verifies offline against our published key)
Your key never leaves your process. It signs an EIP-3009 authorization locally for the
exact amount the challenge discloses — VerityLayer only ever sees the signature. Fund the
address wallet_address(os.environ["VERITY_WALLET_KEY"]) with USDC on Base mainnet, and
read the key from the environment (a key on a command line leaks into process lists and
shell history).
No wallet yet?
VerityClient() with no payer is honest about it rather than pretending — paid calls
return a structured payment_required result carrying the live challenge, and no
verdict is claimed, because none was purchased:
v = VerityClient()
res = v.guard("Wire $4,000 …")
res.payment_required # True
res.price # "$0.02"
res.decision # None <- nothing fabricated
Receipt verification is free forever and needs no wallet — check our signatures before you ever pay us:
VerityClient().verify_receipt(some_receipt).valid # True
To actually pay per call, hand the client an x402-wrapped HTTP client that holds your wallet:
# sync: any x402-wrapped requests.Session / httpx.Client
v = VerityClient(http=my_x402_client)
# async: an x402-wrapped httpx.AsyncClient
from verity_guard import AsyncVerityClient
v = AsyncVerityClient(http=my_async_x402_client)
The SDK never sees your key — it just POSTs; your x402 layer settles the 402 and retries. (See veritylayer.dev/guard for wallet setups.)
Framework adapters
LangGraph — drop-in guarded tool node
Replace ToolNode with GuardedToolNode: every proposed tool call is checked before it runs. Blocked calls never execute — the model gets the block reason + safer alternative and revises.
from verity_guard import VerityClient
from verity_guard.integrations.langgraph import GuardedToolNode
tools = [wire_funds, send_email, search_web]
guarded = GuardedToolNode(tools, VerityClient(http=my_x402_client),
policy="No new payees without human review.")
graph.add_node("tools", guarded) # instead of ToolNode(tools)
OpenAI Agents SDK — per-tool-call guardrail (highest-frequency wire-in)
from verity_guard import VerityClient
from verity_guard.integrations.openai_agents import (
build_guard_tool, build_output_guardrail, build_tool_input_guardrail,
)
v = VerityClient()
# (a) give the agent a guard tool it can call
agent = Agent(name="Treasurer", tools=[build_guard_tool(v)])
# (b) verify the final answer before it leaves
agent = Agent(..., output_guardrails=[build_output_guardrail(v, mode="guard",
policy="No new payees without human review.")])
# (c) guard the arguments of EVERY tool call (newer SDKs)
wire_funds.tool_input_guardrails = [build_tool_input_guardrail(v)]
LangChain — a guard tool, or gate any function
from verity_guard import VerityClient, guard
from verity_guard.integrations.langchain import build_guard_tool
v = VerityClient(http=my_x402_client)
tools = [..., build_guard_tool(v)] # explicit tool the agent can call
@guard(v, policy="No new payees without human review.") # or gate a function directly
def wire_funds(to: str, amount: float): ... # raises BlockedAction if VerityLayer blocks
CrewAI
from verity_guard import VerityClient
from verity_guard.integrations.crewai import build_guard_tool
guard_tool = build_guard_tool(VerityClient(http=my_x402_client),
default_policy="No new payees without human review.")
agent = Agent(role="Treasurer", tools=[guard_tool])
The checks
| Method | What it answers | Route (tier quick) |
From |
|---|---|---|---|
guard(action, …) |
Should this action proceed? allow / review / block |
suite /check/quick |
$0.02 |
verify(claim, …) |
Is this claim true? supported / unsupported / uncertain |
engine /verify (grounded) |
$0.25 by default ⚠️ |
detect_injection(content, …) |
Is this untrusted text a prompt-injection? | suite /sentinel/quick |
$0.02 |
⚠️
verify()is the one method that does not default to the $0.02 tier. A barev.verify(claim)runs thegroundedtier — live web citations, $0.25, 12.5× the $0.02 anchored above. It's the right default for "is this claim true" (the cheap tier is ungrounded and won't cite), but you should choose it, not discover it on a bill. Passtier="quick"for $0.02 ortier="pro"for $0.35. Every price is disclosed in the 402 before anything is signed, andx402_payer's $1.00 cap bounds any single call regardless. |moderate(content, …)| Safe to publish?publish / review / block| suite/sieve/quick| $0.02 | |redact(payload, …)| Any PII/secrets? returns a redacted copy | suite/redact/quick| $0.02 | |verify_receipt(receipt)| Is this signed verdict authentic? | engine/receipt/verify| free |
Every result is a VerityResult (a dict subclass — future fields never get dropped) with helpers: .decision, .risk, .allowed, .blocked, .flagged, .reasons, .safer_alternative, .receipt, .price, .payment_required.
Doctrine
Fail-closed (uncertainty → the safe verdict, never a confident wrong one) · evidence is never invented · allow/review/block are priced identically (no block-to-bill) · pricing is disclosed and paid per use via x402 · VerityLayer holds no key and never charges silently.
MIT · veritylayer.dev · wire-in guide
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file verity_guard-0.2.2.tar.gz.
File metadata
- Download URL: verity_guard-0.2.2.tar.gz
- Upload date:
- Size: 115.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
aeb9e4caa9ace271f50077443f3ed9260bb9df0107a6e35d267e0ef1ebde8292
|
|
| MD5 |
63f2ab8cd05ed8ffc7933e54670362d2
|
|
| BLAKE2b-256 |
8e59f0faff5bcc66112bbfefcd74042a276fa7e277764e6ead06c1f940aa4866
|
File details
Details for the file verity_guard-0.2.2-py3-none-any.whl.
File metadata
- Download URL: verity_guard-0.2.2-py3-none-any.whl
- Upload date:
- Size: 26.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5208d50ada9401c6dc97b739e86cfbd87ccc3eeebf3650e78740e73333cc5bfe
|
|
| MD5 |
c0adc6e3ca2a7cd18caf8d0a535d0df7
|
|
| BLAKE2b-256 |
2905e9c29e71983c459d4d57dafadbea8b25240e34746d987ed6586b00431d73
|