Getting Started

Step-by-step guide to using the Aperture compliance platform. Follow these steps in order to experience the full workflow.

1Connect Wallet
  • -Connect your Phantom or Solflare wallet
  • -Click the wallet button in the top-right corner
  • -Make sure your wallet is set to Devnet
2Create a Policy
  • -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
3Make a Payment
  • -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
4Test Transfer Hook
  • -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
5Access Protected Report (x402)
  • -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
6Access MPP Report
  • -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)
7Create Batch Attestation
  • -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
8Run the Agent
  • -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
9View Audit Page
  • -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

3000apertureNext.js dashboard
3001policy-servicePolicy CRUD + auth API
3002compliance-apiCompliance, x402, MPP endpoints
3004agent-serviceAutonomous agent daemon
5432postgres-policyPolicy database
5433postgres-complianceCompliance database

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.

Wallet Signing

Connect Phantom or Solflare, sign a message. Signature is verified by the policy-service using ed25519.

Email / Password

Traditional credentials. User accounts are stored in the policy-service database.

How It Works

Aperture operates in four steps:

1Policy

Operator defines compliance rules: spending limits, token whitelist, blocked addresses, time restrictions. Policy is registered on-chain via the Policy Registry program.

2Proof

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.

3Pay

The payment is executed via x402 or MPP protocol. The ZK proof is attached to the payment header, enabling the recipient to verify compliance.

4Verify

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:

Policy RegistryFXD7ycSg...

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.

ZK Payment Proverservices...

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.

Compliance Aggregatorservices...

Backend service that aggregates proof records into batch attestations with SHA-256 batch hashes.

On-chain VerifierAzKirEv7...

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 report

Prover 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_id

MPP 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 report

Agent 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

POST/startValidate policy + prover, start agent loop
POST/stopStop the agent loop
GET/statusRunning state, operator ID, stats
GET/activityLive activity feed (last 200 records)
GET/healthService health check

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:

1.Per-transaction limit: payment amount <= max_per_transaction
2.Daily spending limit: daily_spent + amount <= max_daily_spend
3.Token whitelist: payment token must be in allowed list
4.Blocked addresses: recipient must not be on sanctions list
5.Endpoint category: payment category must be in allowed list

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 hidden

Policy 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

MetricValue
Proving time~500 ms (Node, snarkjs)
Proof size256 bytes (A+B+C)
On-chain verify cost~100 K compute units
Circuit constraints~2 300 (BN254)
Trusted setupPolygon Hermez ptau (54-party)
Cryptographic validityFully 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)

POST/api/v1/policiesCreate policy
GET/api/v1/policies/operator/:operatorIdList policies
GET/api/v1/policies/:idGet policy
PUT/api/v1/policies/:idUpdate policy
DELETE/api/v1/policies/:idDelete policy
GET/api/v1/policies/:id/compileCompile for circuit
GET/api/v1/policies/:id/merkle-treeGet Merkle tree (root, leaves, labels)
GET/api/v1/policies/:id/merkle-proof/:ruleSelective disclosure proof for a rule

Compliance API (port 3002)

POST/api/v1/proofsSubmit proof record
GET/api/v1/proofs/operator/:operatorIdList proofs
PATCH/api/v1/proofs/:id/tx-signatureStore tx signature
POST/api/v1/attestations/batchCreate batch attestation
GET/api/v1/attestations/operator/:operatorIdList attestations
PATCH/api/v1/attestations/:id/tx-signatureStore tx signature
GET/api/v1/compliance/protected-reportx402 protected report (1 USDC)
GET/api/v1/compliance/mpp-reportMPP protected report ($0.50)
POST/api/v1/compliance/compress-attestationMint compressed attestation (Light Protocol)
GET/api/v1/compliance/light-statusLight Protocol configuration status

Prover Service (port 3003)

GET/healthHealth check
POST/proveGenerate Groth16 ZK proof (Circom + snarkjs)

Agent Service (port 3004)

POST/startStart agent loop
POST/stopStop agent loop
GET/statusAgent status and stats
GET/activityLive activity feed
GET/healthHealth check
// 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.