Getting Started
Step-by-step guide to using the Aperture compliance platform. Follow these steps in order to experience the full workflow.
- -Connect your Phantom or Solflare wallet
- -Click the wallet button in the top-right corner
- -Make sure your wallet is set to Devnet
- -Navigate to the Policies tab
- -Click Create Policy
- -Configure: max daily spend, max per transaction, token whitelist
- -Add x402, mpp to allowed endpoint categories (required for agent)
- -Sign the transaction -- policy is registered on Solana Devnet
- -Go to the Payments tab
- -Click Payment
- -A real Groth16 ZK proof (Circom + snarkjs) is generated in ~500 ms
- -Sign the transaction -- proof is verified on Solana with an explorer link
- -In Payments tab, expand the Transfer Hook Test panel
- -Transfer Without Proof -- rejected if wallet has no ComplianceStatus PDA
- -Transfer With Proof -- succeeds after ZK proof verification
- -In Payments tab, click Access Protected Report
- -1 USDC is paid via Solana, ZK proof is generated
- -Compliance report is returned with on-chain verification
- -In Payments tab, click Access MPP Report
- -$0.50 is paid via Stripe, ZK proof is generated
- -Proof is verified on Solana -- dual settlement (Stripe + Solana)
- -Go to the Compliance tab
- -Click Create Batch Attestation, select a time period
- -Sign the transaction -- attestation is anchored on Solana
- -Use Share Audit Link to send to auditors
- -Go to the Agent Activity tab
- -Click Start Agent -- runs autonomously every 30 seconds
- -Watches: policy check, ZK proof, x402 payment, MPP payment, batch attestation
- -Click Stop Agent to halt the loop
- -In Compliance tab, click Share Audit Link on any attestation
- -Auditors see: COMPLIANT badge, proof hash, payment count
- -Verify on Solana button links directly to the on-chain transaction
Introduction
Aperture is a zero-knowledge compliance and privacy layer for AI agent payments on Solana. It enables AI agents to prove they comply with operator-defined policies -- spending limits, sanctions checks, allowed categories, time-based rules -- without revealing any payment details.
As AI agents begin making autonomous payments (API calls, compute resources, data purchases), enterprises need compliance guarantees without sacrificing privacy or speed. Traditional compliance requires exposing transaction details to auditors. Aperture proves compliance cryptographically: Prove compliance. Reveal nothing.
Docker Deployment
The fastest way to run the full Aperture stack. All services run in Docker containers with automatic health checks and dependency ordering.
# 1. Configure environment cp .env.example .env # Edit .env with your Stripe keys, MPP secret, wallet key # 2. Start databases docker compose up -d postgres-policy postgres-compliance # 3. Start backend services docker compose up -d policy-service compliance-api agent-service # 4. Start dashboard docker compose up -d aperture # 5. Run database migrations npm install && npm run migrate # 6. Open dashboard open http://localhost:3000
Services
The prover-service (port 3003) is a Node.js wrapper around snarkjs that consumes the Circom payment-compliance circuit. Runs on any x86/ARM host with Node 20+, no Docker-in-Docker or GPU required.
Authentication
Aperture supports two authentication methods via NextAuth. Both methods create a JWT session that persists across the dashboard.
Connect Phantom or Solflare, sign a message. Signature is verified by the policy-service using ed25519.
Traditional credentials. User accounts are stored in the policy-service database.
How It Works
Aperture operates in four steps:
Operator defines compliance rules: spending limits, token whitelist, blocked addresses, time restrictions. Policy is registered on-chain via the Policy Registry program.
When a payment occurs, a Circom + Groth16 circuit generates a cryptographic proof that the payment complies with the policy -- without revealing payment details. Proof lands in ~500 ms.
The payment is executed via x402 or MPP protocol. The ZK proof is attached to the payment header, enabling the recipient to verify compliance.
The proof is verified on-chain via the Verifier program. A ComplianceStatus PDA is created, enabling the SPL Token-2022 transfer hook to enforce compliance.
Architecture
Aperture consists of four layers:
Anchor program on Solana. Stores operator accounts and policy PDAs with Merkle tree roots for selective disclosure. Supports Squads V4 multisig governance via vault PDA verification.
Circom circuit + snarkjs. Executes 5 compliance checks (per-tx limit, daily limit, token whitelist, blocked addresses, endpoint category) and produces a ~256-byte Groth16 proof on BN254.
Backend service that aggregates proof records into batch attestations with SHA-256 batch hashes.
Anchor program that verifies Groth16 proofs via Solana alt_bn128 pairings (groth16-solana, ~100K CU). Creates ProofRecord + ComplianceStatus PDAs keyed by the Poseidon journal digest.
Tech Stack
Circom + snarkjs (Groth16, BN254), groth16-solana on-chain verifier, Solana (Anchor + pure SDK), SPL Token-2022 Transfer Hook, Light Protocol ZK Compression, Squads V4 Multisig, x402 (Coinbase), MPP (Stripe/Tempo).
SDK Reference
x402 Adapter
import { fetchWithX402 } from '@/lib/x402-client';
const result = await fetchWithX402(
'http://localhost:3002/api/v1/compliance/protected-report?operator_id=...',
connection, // Solana Connection
publicKey, // Wallet PublicKey
sendTransaction // from useWallet()
);
// result.payment.txSignature -- Solana transaction
// result.payment.zkProofHash -- ZK proof hash
// result.data -- Compliance reportProver Client (Rust)
use aperture_prover::ProverClient;
let client = ProverClient::new(ProverServiceConfig {
endpoint: "http://localhost:3003".into(),
timeout_secs: 600,
})?;
let input = client.build_prover_input(&policy, &payment, daily_spent)?;
let request = client.build_proof_request(&input)?;
let result = client.generate_proof(&request).await?;
// result.proof_hash, result.receipt_bytes, result.image_idMPP Client
import { fetchWithMPP } from '@/lib/mpp-client';
const result = await fetchWithMPP(
'http://localhost:3002/api/v1/compliance/mpp-report?operator_id=...',
'pk_test_...' // Stripe publishable key
);
// result.payment.paymentIntentId -- Stripe PaymentIntent
// result.payment.zkProofHash -- ZK proof hash
// result.payment.solanaTxSignature -- Solana TX
// result.data -- Compliance reportAgent SDK
// sdk/agent/src/agent.ts -- runs all 4 steps autonomously
import { PolicyChecker } from './policy-checker';
import { ProverClient } from './prover-client';
import { X402Payer } from './x402-payer';
import { MPPPayer } from './mpp-payer';
// 1. PolicyChecker: loads and enforces operator policy
const checker = new PolicyChecker('http://localhost:3001');
await checker.loadPolicy(operatorId);
const result = checker.checkPayment({ amountLamports, tokenMint, recipient, endpointCategory });
// 2. ProverClient: generates Circom + Groth16 ZK proof
const prover = new ProverClient('http://localhost:3003');
const proof = await prover.generateProof(compiled, amount, mint, recipient, category, dailySpent);
// 3. X402Payer: pays via USDC on Solana with Keypair signing
const x402 = new X402Payer(rpcUrl, wallet, 'http://localhost:3002');
const x402Result = await x402.payForReport(operatorId, proof.proof_hash);
// 4. MPPPayer: pays via Stripe PaymentIntent (server-side)
const mpp = new MPPPayer('http://localhost:3002', stripeSecretKey);
const mppResult = await mpp.payForReport(operatorId);Anchor Instruction Builder
import { buildVerifyPaymentProofIx, deriveOperatorPDA } from '@/lib/anchor-instructions';
const ix = buildVerifyPaymentProofIx(
operator, // PublicKey
payer, // PublicKey
policyPDA, // PublicKey
proofHashBytes, // Uint8Array[32]
imageId, // number[8]
journalDigestBytes, // Uint8Array[32]
receiptBytes // Uint8Array
);x402 Integration
x402 is the Coinbase HTTP 402 payment protocol for machine-to-machine payments. Aperture extends x402 by attaching ZK compliance proofs to payment headers.
Flow
1. Client sends GET request to a protected endpoint. 2. Server returns 402 Payment Required with payment requirements (token, amount, recipient). 3. Client generates a ZK proof via the prover service. 4. Client signs a USDC transfer transaction with the connected wallet. 5. Client retries with x-402-payment header containing the tx signature and ZK proof hash. 6. Server verifies the transaction on-chain and returns the protected resource.
// x-402-payment header format (base64-encoded JSON)
{
"txSignature": "5DY5P9WXPm...",
"payer": "CBDjvUkZZ6uc...",
"zkProofHash": "37b708db1af0..."
}MPP Integration
MPP (Machine Payments Protocol) uses Stripe PaymentIntents with an HTTP 402 challenge/credential/receipt flow. Designed for AI agent-to-service payments with ZK compliance proofs recorded on both Stripe and Solana.
Flow
1. Client sends GET request to /api/v1/compliance/mpp-report. 2. Server creates a Stripe PaymentIntent and returns 402 with an HMAC-bound challenge. 3. Client confirms payment via Stripe.js (test mode: pm_card_visa). 4. Client retries with x-mpp-credential header containing the challenge ID and PaymentIntent ID. 5. Server verifies payment status via Stripe API and returns the compliance report with a Payment-Receipt header.
Dual Settlement
Each MPP payment creates records in both Stripe (PaymentIntent with MPP metadata) and Solana Devnet (ZK proof verified on-chain via the Verifier program).
// x-mpp-credential header format (base64-encoded JSON)
{
"challengeId": "47d5016c73b015e7...",
"paymentIntentId": "pi_3TJsfqFIw4F8j032..."
}Autonomous Agent
Aperture includes a fully autonomous AI agent SDK that enforces compliance policies, generates ZK proofs, makes payments via both x402 and MPP, and anchors attestations on Solana -- all without human intervention. The agent uses a server-side Keypair for Solana transactions (no wallet popup required).
Agent Lifecycle
1. Load active policy from the Policy Service and compile for ZK circuit. 2. For each payment: check policy rules (limits, categories, blocked addresses, token whitelist). 3. Generate a Groth16 ZK proof of compliance via Circom + snarkjs (~500 ms). 4. Execute payment via x402 (USDC on Solana) or MPP (Stripe PaymentIntent). 5. Submit proof record to Compliance API. 6. Create batch attestation and anchor on Solana via verify_batch_attestation.
Running the Agent SDK
# Configure agent wallet cd sdk/agent cp .env.example .env # Set AGENT_WALLET_PRIVATE_KEY (base58 or JSON array) # Run single session npx tsx src/agent.ts
Agent Output
[Aperture Agent] Policy loaded: max_daily=50 USDC, max_per_tx=5 USDC [Aperture Agent] Policy check passed. Generating ZK proof... [Aperture Agent] ZK proof generated in 5.7s (249KB receipt) [Aperture Agent] Paying via x402: 1 USDC [Aperture Agent] Payment verified on-chain: https://explorer.solana.com/tx/... [Aperture Agent] Paying via MPP: $0.50 [Aperture Agent] MPP payment verified: pi_... [Aperture Agent] Batch attestation anchored on Solana [Aperture Agent] Session complete. Policy violations: 0
Agent Service
The Agent Service (services/agent-service/, port 3004) is an HTTP-controllable daemon that runs the agent in a continuous loop. Start and stop the agent remotely via the dashboard or API calls.
API
Pre-Start Validation
The agent validates three conditions before starting: 1. At least one active policy exists. 2. Policy includes x402 and mpp in allowed endpoint categories. 3. Prover service is reachable. If any check fails, the agent returns a descriptive error and does not start.
Dashboard Integration
The Agent Activity tab in the dashboard provides real-time monitoring with Start/Stop controls, a live activity feed (5s auto-refresh), and stats for sessions, x402 payments, MPP payments, ZK proofs, and policy violations. Every on-chain transaction has a Solana explorer link.
Policy Engine
The Circom circuit encodes 5 compliance checks as BN254 constraints:
Merkle Tree Policy Storage
Each policy rule is stored as a leaf node in a binary Merkle tree. The tree root is recorded on-chain, enabling selective disclosure: an auditor can verify that a specific rule (e.g. blocked addresses) exists in the policy without seeing other rules (spending limits, token whitelist, etc.).
GET /api/v1/policies/:id/merkle-proof/blocked_addresses
{
"leaf": "d8459fcf16fa28ac...",
"label": "blocked_addresses",
"proof": ["a4494d5e...", "726c9737...", "8c0513ff..."],
"directions": ["left", "left", "right"],
"root": "4b92078a76c5630a...",
"verified": true
}
// The auditor verifies root matches on-chain PolicyAccount.merkle_root
// Other rules (max_daily_spend, token_whitelist, etc.) remain hiddenPolicy JSON Example
{
"operator_id": "CBDjvUkZZ6uc...",
"name": "Standard Compliance",
"max_daily_spend": 10000,
"max_per_transaction": 5000,
"allowed_endpoint_categories": ["x402", "mpp", "compute", "storage", "api"],
"blocked_addresses": [],
"token_whitelist": ["4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU"],
"time_restrictions": [{
"allowed_days": ["monday","tuesday","wednesday","thursday","friday"],
"allowed_hours_start": 0,
"allowed_hours_end": 23,
"timezone": "UTC"
}]
}ZK Proofs
Circom + Groth16
Aperture uses a Circom circuit (circuits/payment-prover/payment.circom) compiled to R1CS and proven with snarkjs on BN254. Each proof is verified on Solana via the audited groth16-solana crate. Same pipeline Light Protocol runs in production.
Proof Properties
| Metric | Value |
|---|---|
| Proving time | ~500 ms (Node, snarkjs) |
| Proof size | 256 bytes (A+B+C) |
| On-chain verify cost | ~100 K compute units |
| Circuit constraints | ~2 300 (BN254) |
| Trusted setup | Polygon Hermez ptau (54-party) |
| Cryptographic validity | Fully valid, on-chain verified |
Light Protocol ZK Compression
Every compliant proof automatically triggers a compressed attestation token mint via Light Protocol. Each compressed token represents one compliance attestation, reducing on-chain storage costs by 146x (0.000010 SOL vs 0.001462 SOL per proof). The compressed token is minted on Solana Devnet via the Compliance API and the transaction is visible on Solana Explorer.
API Reference
Policy Service (port 3001)
Compliance API (port 3002)
Prover Service (port 3003)
Agent Service (port 3004)
// POST /prove request body
{
"policy_id": "...",
"operator_id": "...",
"max_daily_spend_lamports": 100000000,
"max_per_transaction_lamports": 10000000,
"allowed_endpoint_categories": ["compute"],
"blocked_addresses": [],
"token_whitelist": ["4zMMC9srt5Ri..."],
"payment_amount_lamports": 5000000,
"payment_token_mint": "4zMMC9srt5Ri...",
"payment_recipient": "...",
"payment_endpoint_category": "compute",
"payment_timestamp": "2026-04-07T12:00:00Z",
"daily_spent_so_far_lamports": 0
}
// Response
{
"is_compliant": true,
"journal_digest": "15266963924290...",
"proving_time_ms": 521,
"groth16": {
"proof_a": "...64 bytes base64",
"proof_b": "...128 bytes base64",
"proof_c": "...64 bytes base64",
"public_inputs": ["...32 bytes base64", "...32 bytes base64"]
}
}FAQ
Is this production ready?
Aperture is deployed on Solana Devnet with real Circom + Groth16 proofs verified on-chain (~100K CU via groth16-solana). Trusted setup uses the Polygon Hermez public ceremony (54 contributors). The architecture is production-grade; the deployment is on Devnet for testing.
Which wallets are supported?
Phantom and Solflare are the supported wallets. The dashboard uses the Solana Wallet Adapter standard with sendTransaction for on-chain interactions.
How long does proof generation take?
Around 500 ms on a modern CPU. Circom circuits are purpose-built (~2300 BN254 constraints) so there is no VM overhead, and snarkjs runs comfortably in Node.js without GPUs or Docker-in-Docker.
What tokens are supported?
USDC and USDT on Devnet. vUSDC (SPL Token-2022 with transfer hook) for compliance-enforced transfers. Any SPL token can be added to the whitelist.
How does the transfer hook work?
vUSDC has an on-chain transfer hook that checks the ComplianceStatus PDA of the sender. If no verified compliance record exists, the transfer is rejected by the Token-2022 program.
What is Light Protocol ZK Compression?
Light Protocol stores proof records as compressed tokens instead of regular Solana accounts, reducing storage costs by 146x (~0.00001 SOL vs ~0.00146 SOL per proof).
How does Squads multisig work?
Operators can link a Squads V4 multisig to their account via the Settings tab. The on-chain program verifies the multisig account is owned by the Squads V4 program (SQDS4ep65T869zMMBKyuUq6aD6EgTu8psMjkvj52pCf) and derives the vault PDA. Policy registration and updates via multisig require the Squads vault signature, ensuring multi-party approval for compliance rule changes.
How does the autonomous agent work?
The Aperture agent runs headless with a server-side Keypair. It loads policies from the Policy Service, generates Groth16 ZK proofs via the Circom + snarkjs prover, pays via x402 (USDC on Solana) and MPP (Stripe), submits proof records, mints compressed attestations via Light Protocol, and anchors batch attestations on Solana. The Agent Service (port 3004) provides HTTP Start/Stop control with a 30-second cycle interval.