Security middleware for x402 agentic payments — PII redaction, spending policy, and replay detection before blockchain commit
presidio-hardened-x402 is an early-stage Python project in the AI payments / x402 ecosystem, focused on agentic-payments, hardening, pii-detection, privacy. It currently has 13 GitHub stars and 0 forks, and sits alongside related tools like routeweiler-python-sdk, Agent-Layer, SubEtha.
v0.9.1 — security patch: dependency-audit floors for
click/setuptools, missing-payment-header log exposure hardening, andsetup-uvv8.3.2 CI action pin.
Security middleware for the x402 payment protocol.
Intercepts x402 payment requests before transmission to servers and facilitators to enforce:
pay_to and currency (v0.4.0)remote_screening=True mode offloads PII analysis to the hosted screen.presidio-group.eu service via ScreeningClient; avoids the in-process spaCy dependency (v0.4.0)pay_to allowlist blocks payments to attacker-controlled addresses when a DNS-poisoned 402 response substitutes the recipient (chain-06 mitigation) (v0.4.0)ScreeningPipeline) is payment-protocol-independent; everything x402-specific lives in bindings/x402. Embed on a different rail by implementing PaymentProtocolBinding — the security guarantees travel with the core (v0.5.0)python -m presidio_x402.conformance, 7 end-to-end checks, no network), and integration quickstarts for Coinbase CDP, LangChain, and CrewAI (v0.5.0)Part of the presidio-hardened-* toolkit family.
pip install presidio-hardened-x402
For full NLP-based PII detection (PERSON, ORG, location, etc.):
pip install "presidio-hardened-x402[nlp]"
python -m spacy download en_core_web_sm
For production replay guard with cross-process deduplication:
pip install "presidio-hardened-x402[redis]"
For Prometheus metrics export:
pip install "presidio-hardened-x402[prometheus]"
For policy-as-code schema validation:
pip install "presidio-hardened-x402[schema]"
import httpx
async def pay_and_fetch(url: str, signer) -> httpx.Response:
async with httpx.AsyncClient() as client:
resp = await client.get(url)
if resp.status_code == 402:
payment_details = parse_402_header(resp.headers)
# No PII check. No policy check. No replay check.
payment_token = await signer.sign(payment_details)
resp = await client.get(url, headers={"X-PAYMENT": payment_token})
return resp
from presidio_x402 import HardenedX402Client
async def my_signer(details):
# Your existing signing logic (eth-account, solders, CDP SDK, etc.)
...
client = HardenedX402Client(
payment_signer=my_signer,
policy={
"max_per_call_usd": 0.10,
"daily_limit_usd": 5.0,
"per_endpoint": {"https://api.example.com": 1.0},
},
pii_action="redact", # redact | block | warn
pii_entities=["EMAIL_ADDRESS", "PERSON", "US_SSN", "CREDIT_CARD"],
replay_ttl=300, # seconds
)
response = await client.get("https://api.example.com/resource")
What happens transparently:
GET /resource → server returns 402 with payment detailsfrom presidio_x402 import HardenedX402Client, PolicyConfig
policy = PolicyConfig(
max_per_call_usd=0.05, # block if maxAmountRequired > this
daily_limit_usd=2.00, # block if 24h aggregate spend would exceed this
per_endpoint={
"https://premium-api.io": 0.50, # per-endpoint daily limit
},
window_seconds=86400, # time window for aggregate limits (default: 24h)
agent_id="my-agent-v1", # tag audit events with an agent identifier
)
client = HardenedX402Client(payment_signer=signer, policy=policy)
# regex mode (default, zero-setup): catches structured PII
client = HardenedX402Client(
payment_signer=signer,
pii_mode="regex",
pii_entities=["EMAIL_ADDRESS", "PHONE_NUMBER", "US_SSN", "CREDIT_CARD"],
pii_action="redact",
)
# nlp mode: full spaCy NER (requires: pip install presidio-hardened-x402[nlp])
client = HardenedX402Client(
payment_signer=signer,
pii_mode="nlp",
pii_entities=["EMAIL_ADDRESS", "PERSON", "LOCATION", "US_SSN"],
pii_action="block", # raise PIIBlockedError instead of redacting
)
# In-memory (default, single-process)
client = HardenedX402Client(payment_signer=signer, replay_ttl=300)
# Redis-backed (production, cross-process)
client = HardenedX402Client(
payment_signer=signer,
replay_ttl=300,
redis_url="redis://localhost:6379/0",
)
import sys
from presidio_x402 import HardenedX402Client
from presidio_x402.audit_log import StreamAuditWriter
client = HardenedX402Client(
payment_signer=signer,
audit_writer=StreamAuditWriter(sys.stdout), # write JSON-L to stdout
)
For high-value payments that require human or system oversight before execution:
from presidio_x402 import HardenedX402Client
from presidio_x402.mpa import MPAConfig, MPAApproverConfig, MPAEngine
mpa = MPAEngine(MPAConfig(
threshold=2, # require 2 of 3 approvals
min_amount_usd=1.00, # only for payments ≥ $1.00
timeout_seconds=30,
approvers=[
MPAApproverConfig("alice", mode="webhook",
webhook_url="https://approvals.internal/alice"),
MPAApproverConfig("bob", mode="webhook",
webhook_url="https://approvals.internal/bob"),
MPAApproverConfig("charlie", mode="webhook",
webhook_url="https://approvals.internal/charlie"),
],
))
client = HardenedX402Client(payment_signer=signer, mpa_engine=mpa)
Approval endpoints receive a POST with payment details and must return {"approved": true}.
For machine-to-machine approvals, use mode="crypto" with HMAC-SHA256 countersignatures
and pass them via mpa_signatures={"approver_id": "hex_sig", ...} in the request kwargs.
from presidio_x402 import HardenedX402Client
from presidio_x402.metrics import MetricsCollector
collector = MetricsCollector()
client = HardenedX402Client(payment_signer=signer, metrics_collector=collector)
# Expose /metrics endpoint (e.g., FastAPI)
from prometheus_client import generate_latest, CONTENT_TYPE_LATEST
@app.get("/metrics")
def metrics():
return Response(generate_latest(), media_type=CONTENT_TYPE_LATEST)
Available metrics: x402_payments_total, x402_payment_amount_usd (histogram),
x402_pii_detections_total, x402_policy_violations_total,
x402_replay_detections_total, x402_mpa_events_total.
Define spending policy in a TOML or JSON file and validate it against the x402 policy JSON Schema:
# policy.toml
max_per_call_usd = 0.10
daily_limit_usd = 5.00
agent_id = "my-agent"
[per_endpoint]
"https://premium-api.io" = 0.50
[mpa]
threshold = 2
min_amount_usd = 1.00
timeout_seconds = 30
[[mpa.approvers]]
approver_id = "alice"
mode = "webhook"
webhook_url = "https://approvals.internal/alice"
from presidio_x402.x402_policy_schema import load_policy_file
policy = load_policy_file("policy.toml")
client = HardenedX402Client(payment_signer=signer, policy=policy)
Offload PII analysis to the hosted screen.presidio-group.eu
service. Useful when you want to keep the agent process small (no in-process spaCy
model) and centralise screening policy across many agents.
from presidio_x402 import HardenedX402Client
client = HardenedX402Client(
payment_signer=signer,
remote_screening=True,
screening_api_key="...", # obtain by registering at screen.presidio-group.eu
)
The free tier serves the regex-mode screening backend at 100 screenings/day per key. Falls back to local regex / NLP modes if the network is unhealthy, so the client never hard-fails on a screening-service outage.
Defence against DNS-poisoning attacks that substitute the recipient wallet in a 402
response. Configure the trusted pay_to addresses per resource origin:
client = HardenedX402Client(
payment_signer=signer,
trusted_wallets={
"https://api.example.com": {"0xAbC...123"},
},
)
Payments to a pay_to not in the allowlist for the response's origin are blocked
before signing.
Deploy as a sidecar using the bundled Helm chart:
helm install x402 ./helm \
--set x402.agentId=my-agent \
--set x402.maxPerCallUsd=0.10 \
--set x402.dailyLimitUsd=5.00 \
--set serviceMonitor.enabled=true
See docs/soc2-reference-architecture.md for
SOC 2 TSC mapping, GDPR obligations, and deployment patterns.
Market-based SLO enforcement: when infrastructure degrades, the agent autonomously pays for
a capacity upgrade via x402 micropayments instead of relying on pre-provisioned autoscaling.
The broker acts only on a verified degradation signal — a signed
evidence-ref@1 from a trusted presidio-hardened-arch-translucency signer — so a spoofed or
misconfigured signal cannot trigger a payment (authorization, not metric).
from presidio_x402 import (
HardenedX402Client, SLOPaymentBroker, SLOPaymentPolicy,
X402CapacityProvider, ArchTranslucencyAdapter,
)
# arch-translucency's signed degradation feed → verified trigger (fail-closed).
adapter = ArchTranslucencyAdapter(
{"presidio-hardened-arch-translucency": {"alg": "ed25519", "public_key": ARCH_PUB}},
expected_signers=["presidio-hardened-arch-translucency"],
)
client = HardenedX402Client(payment_signer=signer)
broker = SLOPaymentBroker(
client=client,
slo_policy=SLOPaymentPolicy(
cooldown_seconds=300, # anti-drain
max_per_slo_event_usd=0.50,
max_daily_slo_usd=10.00,
tier_escalation_rules=(1.0, 2.0, 4.0), # step-up pricing for repeated degradation
),
provider=X402CapacityProvider("compute", "https://compute.example/v1/capacity", client),
base_event_usd=0.10,
)
for trigger in adapter.build_triggers(signed_envelope): # verified or skipped
await broker.handle_trigger(trigger) # → paid / blocked / skipped, with audit event
The signing key lives in a separate bridge sidecar (arch-translucency stays key-less); x402
holds only the public key. The wire contract is pinned by the evidence-ref@1
golden-vector tests shared with arch-translucency.
payment-decision@1)A decision-ref is a signed, portable record of one payment decision that a third
party verifies offline — without re-running the gateway. It binds the per-control
gate verdicts (pii → trusted_wallet → policy → replay → mpa), the hashed inputs, and
the effective policy hash into the family evidence-ref@1 envelope, with a thin,
recomputable decision_ref correlation id. Emission is opt-in and off by default;
when no emitter is configured, behaviour is byte-identical to prior releases and no
decision-ref code runs. There is no network I/O on the emit path.
from presidio_x402 import HardenedX402Client, DecisionRefEmitter, verify_decision_ref
from presidio_x402.decision_ref import FileDecisionRefWriter
# Signer independence is claim-critical: a policy/approval identity distinct from the
# payment wallet — a self-signed record fails closed on verify (signer_equals_runtime).
emitter = DecisionRefEmitter(
signing_key=POLICY_PRIVATE_KEY_HEX, # Ed25519 (or an HMAC secret)
signer="presidio-hardened-x402-policy",
writer=FileDecisionRefWriter("/var/log/x402-decisions.jsonl"),
)
client = HardenedX402Client(payment_signer=signer, decision_ref_emitter=emitter)
# ... one signed payment-decision@1 record is emitted per paid payment.
# Verify offline against a pinned trust store (fail-closed, distinct reasons):
result = verify_decision_ref(envelope, {"presidio-hardened-x402-policy":
{"alg": "ed25519", "public_key": POLICY_PUBLIC_KEY_HEX}})
assert result.ok and result.verdict == "ALLOW"
The verifier checks signature, hash integrity, that the recorded verdict re-derives
from the recorded controls (verdict == f(controls) — the line between attested and
admissible), that the signer is not the actor's own controller (self-approval fails
closed), and — when the spending policy came from a capability chain
(capability-grant@1) — parent linkage to the chain's terminal grant_hash.
A minimal offline CLI is provided:
python -m presidio_x402.decision_ref envelope.jsonl trust-store.json
Honest bound. A decision-ref proves what the library concluded under a declared
predicate, tamper-evidently and recomputably. It does not prove the process was
uncompromised or that the declared controls are the real controls (no TEE claim). Wire
format and decision_ref derivation are pinned by the conformance fixture under
tests/conformance/decision-ref/ and the design note
plan/presidio-evidence-decision-ref-design.md.
Capacity-upgrade requests can reveal workload type, data classification, or access pattern. Those heuristics are intentionally opt-in so ordinary payment metadata keeps the v0.6 false-positive profile.
from presidio_x402 import PIIFilter, PROVISIONING_ENTITIES
filt = PIIFilter(
entities=list(PROVISIONING_ENTITIES),
redaction_template="<{entity_type}>",
)
clean, entities = filt.scan_and_redact(
"ml training workload over CONFIDENTIAL data: SELECT email FROM customers"
)
# clean:
# "<WORKLOAD_CLASS> workload over <DATA_CLASSIFICATION> data: <QUERY_PATTERN>"
| Exception | Raised when |
|---|---|
PIIBlockedError |
PII detected in metadata and pii_action="block" |
PolicyViolationError |
Payment amount or aggregate spend exceeds configured limit |
ReplayDetectedError |
Payment fingerprint matches a recent transaction |
X402PaymentError |
Upstream payment signing or network error |
MPADeniedError |
Multi-party authorization required but not enough approvals received |
MPATimeoutError |
Multi-party authorization webhook approval timed out |
All exceptions are importable from presidio_x402.
| Artifact | Location | Description |
|---|---|---|
| Synthetic corpus | vstantch/x402-pii-corpus on Hugging Face · corpus/ |
2,000 labelled x402 metadata triples; generator (generate.py, seed=42) + metadata (corpus_meta.json); raw JSONL reproducible from seed |
| Precision/recall sweep | experiments/ |
42-configuration grid search (run_sweep.py); latency benchmark (run_latency.py) |
| Dune Analytics queries | dune/ |
6 Trino SQL queries used to characterise the live x402 ecosystem (20 projects, 96 wallets, 11 chains, ≥79M transactions); see dune/README.md |
| Version | Milestone |
|---|---|
| v0.1.0 | PII redaction + spending policy + replay detection |
| v0.2.0 | Synthetic corpus + 42-configuration precision/recall sweep, LangChain/CrewAI adapters, compliance report · arXiv:2604.11430 |
| v0.2.1 | Live ecosystem characterisation via Dune Analytics (20 projects, 96 wallets, 11 chains, ≥79M transactions); IEEE S&P magazine article submitted; IEEE TIFS paper under review · Corpus: v0.2.1/dataport/, Hugging Face, IEEE DataPort (doi:10.21227/kpsz-nq73) |
| v0.3.0 | Multi-party authorization (mpa.py: n-of-m, webhook + crypto modes) · Policy-as-code JSON Schema (IETF draft candidate) · Prometheus metrics exporter · Kubernetes Helm chart + Docker image · SOC2 reference architecture |
| v0.4.0 | Screening API launch — hosted screen.presidio-group.eu free tier (regex mode, 100 req/day) · ScreeningClient + remote_screening=True mode · per-origin pay_to allowlist (chain-06 mitigation) · audit-cycle hardening (F-A/B 2026-05-03, F-C/D/E 2026-05-10, F1/F2/F3 2026-05-17); see CHANGELOG.md |
| v0.5.0 | OEM embed kit — rail-agnostic ScreeningPipeline core + bindings/x402 layer (PaymentProtocolBinding protocol, hedge against rail fragmentation: ACP/Tempo, AP2) · SEMVER.md stability guarantees · partner conformance suite (python -m presidio_x402.conformance) · CDP/LangChain/CrewAI quickstarts · SBOM in CI · freshness-bound MPA countersignatures (F-8) |
| v0.6.0 | Production hardening + evidence substrate — multi-tenant replay namespaces · enterprise audit sinks (S3 / Splunk / Datadog) · signed evidence-ref@1 verification · SLSA/OIDC release provenance · digest-pinned Docker base + hash-pinned spaCy model · latency SLO and all-matrix coverage CI gates · OTel spans · policy hot-reload · startup key gates · human-pentest retest closure |
| v0.7.0 | SLO payment broker (library) — x402 micropayments as runtime infrastructure bids: SLOPaymentBroker + SLOPaymentPolicy + evidence-anchored ArchTranslucencyAdapter + provisioning PII entities; cross-repo validated against presidio-hardened-arch-translucency. cs.DC preprint + empirical eval tracked separately |
| v0.8.0 | PII completeness + supply-chain hardening (library) — nlp mode is now a structural superset of regex (no structured-identifier misses) · composed-envelope screen_ref leg · action_ref byte-determinism (NFC + non-empty entity sets) · URL-redacted log hygiene · OpenSSF Scorecard workflow/badge + SLSA Build L3 provenance · independent multi-round security audit cleared |
| v0.9.1 | Security patch — dependency-audit floors for click / setuptools, missing-payment-header log exposure hardening, and setup-uv v8.3.2 CI action pin — latest release |
| v0.9.0 | Proof-carrying x402 evidence — capability-grant@1 attenuable spending grants + opt-in payment-decision@1 decision refs with raw-offer digest binding, fail-closed offline verification, capability-chain provenance linkage, and PII-free emitted records |
| Future | Deferred #23 prompt-injection / agent-bound response scanning threat-model work |
See PRESIDIO-REQ.md for full deliberation and rationale.
MIT
This repository is developed under the Presidio hardened-family SDLC: https://github.com/presidio-v/presidio-hardened-docs/blob/main/sdlc/sdlc-report.md.
The neutral micropayment router for autonomous agents. A single HTTP client that intercepts '402 Payment Required', routes across x402, L402, and MPP, persists credentials as recoverable assets, enforces per-envelope budgets, and emits a structured trace.
A wallet for agents. Make payments via x402, use stablecoins, swap assets, earn yield with defi and buy tokenized stocks across the most popular chains.
Private x402 payments on zERC20 — hide who paid whom, not the price tag.