Xona Agent
Live on x402 — age-gated AI image generation.
Xona Agent serves premium AI image and video generation through x402 micro-payments. zkRune adds a privacy-preserving eligibility gate in front of Xona's image endpoint: callers prove they are 18+ on Base mainnet without exposing the underlying birth year. Deployed at api.xona-agent.com/zkrune/image/flux-2-flex — every request hits the live gate.
What this demo shows
Three real steps, end to end. The gate is deployed on Xona's live x402 endpoint — every real request to api.xona-agent.com/zkrune/image/flux-2-flex runs through the on-chain verifier before the image is served.
Real ZK proof, in your browser
snarkjs runs the production age-verification circuit on your input. ~0.5s. Your birth year never leaves the page — only the boolean 'age ≥ 18' is exposed.
Real on-chain verification on Base
The proof is checked against the deployed zkRune verifier on Base mainnet via verifyProofStatic. View call — no wallet, no gas. Anyone can replay it.
Live on Xona's x402 endpoint
The same gate runs in production at api.xona-agent.com/zkrune/image/flux-2-flex. Missing proof → 403 challenge. Valid proof → handed off to x402 for payment. Try the curl evidence below.
Try the gate
Live ZK · Live BaseStays in the browser. Only the boolean "age ≥ 18" is exposed on-chain.
Groth16 proof generated client-side
On-chain verification on Base
x402 image-gen call to Xona
x402 request
Simulated{
"method": "POST",
"url": "https://api.xona-agent.com/v1/x402/image/generate",
"headers": {
"Content-Type": "application/json",
"X-Payment": "<base64(x402-v1 USDC transfer · $0.04)>",
"X-zkRune-Proof": "0x<groth16-proof-hash>",
"X-zkRune-Circuit": "age-verification",
"X-zkRune-Verifier": "base:0xa03A353d…"
},
"body": {
"prompt": "A neon-lit Solana validator at midnight, cinematic, photorealistic",
"model": "xona-image-v1",
"ratio": "1:1"
}
}Endpoint URL is illustrative. The X-zkRune-Proof header and verifier reference show how a partner could enforce both payment and eligibility at the HTTP layer.
Output preview
SimulatedOutput appears once the gate passes.
Live on Xona today
ProductionThe gate is deployed on Xona's x402 endpoint. Every real request to api.xona-agent.com/zkrune/image/flux-2-flex runs through @zkrune/x402-verify before the image is served. Three cases, copy-pasteable.
Request
curl -X POST https://api.xona-agent.com/zkrune/image/flux-2-flex \
-H "Content-Type: application/json" \
-d '{"prompt":"x"}'Response
{
"error": "zkrune_eligibility_required",
"reason": "missing_headers",
"circuit": "age-verification",
"verifier": "base:0xa03A353d…9E849EA",
"generateProofAt": "https://zkrune.com",
"message": "This endpoint requires a zkRune age-verification proof..."
}Gate emits the 403 challenge — same shape as x402's 402, self-describing so the caller knows exactly which proof to generate.
Request
curl -X POST https://api.xona-agent.com/zkrune/image/flux-2-flex \
-H "X-zkRune-Proof: <base64 envelope>" \
-H "X-zkRune-Circuit: age-verification" \
-d '{"prompt":"a cyberpunk fox holding a zk rune"}'Response
{
"x402Version": 2,
"error": "Payment Required",
"resource": {
"url": "https://api.xona-agent.com/zkrune/image/flux-2-flex",
"description": "...ZK age-verified, Base Mainnet."
},
"accepts": [{
"scheme": "exact",
"network": "eip155:8453",
"amount": "60000",
"asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"
}]
}Proof passed on-chain verification — control handed to x402, which now asks for 0.06 USDC on Base. Both gates run in production; only the success path needs a paying wallet.
Request
curl -X POST https://api.xona-agent.com/zkrune/image/flux-2-flex \
-H "X-zkRune-Proof: not-a-real-proof" \
-H "X-zkRune-Circuit: age-verification" \
-d '{"prompt":"x"}'Response
{
"error": "zkrune_eligibility_required",
"reason": "malformed_proof",
"circuit": "age-verification",
"message": "The X-zkRune-Proof header is not a valid base64-encoded proof envelope."
}Garbage rejected at the gate — never reaches the verifier or the payment layer.
Why a ZK gate?
x402 makes payment permissionless. Eligibility — age, jurisdiction, license, brand-safety tier — is a separate question, and it has not been answered well in the agent economy. The options today all either leak data or break the agent flow.
Without zkRune
- Trust the caller's word. Operator carries legal liability for any false claim. Not a real option for regulated content.
- Demand ID upload. Kills agent UX. Storing PII opens a separate compliance burden — data-minimization issues, breach exposure.
- Plug in a hosted KYC vendor. Per-check cost, redirect flows that break agent loops, mostly built for human verification — not headless agents.
- Token-only gating. Proves the wallet paid. Proves nothing about who is calling, where, or under what license.
With zkRune
- 200-byte proof, ~0.5s, fully client-side. No upload, no redirect, no third-party round-trip. The math runs in the user's browser.
- Zero raw PII transmitted. Not to Xona, not to zkRune, not to any RPC. Only the boolean claim and a Groth16 proof.
- Same primitive for humans and agents. Browser SDK for users on a webpage; Node SDK or plain HTTP for headless agents calling x402 endpoints.
- On-chain verifier, audit trail by default. Every match leaves a cryptographic record on Base mainnet — regulator-shaped, no extra logging stack.
Same x402 endpoint, same payment flow — just one extra header carrying a proof a verifier already trusts.
Architecture
User submits private input
Birth year stays in the browser. Never sent to any server, never written to logs.
Groth16 proof generated client-side
snarkjs runs in the browser against the age-verification circuit. Output: a 200-byte SNARK plus public signals (currentYear, minimumAge).
Verified on Base mainnet
verifyProofStatic view call against the deployed zkRune verifier. No wallet, no gas, no off-chain trust.
x402 image-gen call to Xona
Live at api.xona-agent.com/zkrune/image/flux-2-flex. The endpoint runs the same gate behind every real request.
Endpoint integration
The gate ships as a published npm package — @louisstein/x402-verify. It reads HTTP headers only, never the request body, so it drops in front of any x402 endpoint regardless of stack. Xona's handler added three lines; the payment middleware and the handler stayed unchanged. Same code is now running in their production endpoint.
// Xona's x402 image endpoint — add the zkRune gate in three lines.
// @louisstein/x402-verify · reads headers only, never the request body.
import { Hono } from "hono";
import { zkRuneHonoMiddleware } from "@louisstein/x402-verify";
app.post(
"/image/flux-2-flex",
// zkRune eligibility gate — rejects callers who have not proven 18+.
// A missing or invalid proof gets a 403 challenge that mirrors x402's 402.
zkRuneHonoMiddleware({
requiredCircuit: "age-verification",
validatePublicSignals: (s) => {
const [isValid, year, minAge] = s.map(Number);
return isValid === 1 &&
year >= new Date().getUTCFullYear() - 1 &&
minAge >= 18;
},
}),
x402(), // existing payment middleware — unchanged
fluxHandler, // existing handler — unchanged
);Deployed. Live on Xona's production endpoint — api.xona-agent.com/zkrune/image/flux-2-flex. The package is on npm with a comprehensive smoke test (13/13 checks against the live Base verifier) — both sides shipped.
Why it fits
Same chain footprint
Xona ships on SKALE on Base. zkRune's Base verifier (0xa03A353d…) is already live on mainnet — no new infra to deploy.
Orthogonal to x402
x402 answers who paid. zkRune answers who is allowed. Together they enforce payment and eligibility at the HTTP layer, with no shared state.
Compliance-ready
Age, jurisdiction, and license proofs map directly to the regulatory pressure shaping AI generation. Zero raw PII retained.
Already live — call it now
The integration is deployed on both sides: gate on Xona's production endpoint, package on npm. If you run an x402 service and want the same drop-in eligibility layer, the codepath is now a reference customers can verify themselves with a single curl.
