Documentation

Inference privacy
for AI agents

Peek-a-boo is a privacy protocol built for AI agent inference on Bittensor. Shielded payments, anonymous miner participation, and private agent routing — powered by ZK-SNARKs on Subtensor EVM.

01 Overview

When AI agents transact on public blockchains, every inference request, payment, and routing decision is visible. This creates surveillance vectors—anyone can track which agents use which miners, how much they pay, and when.

Peek-a-boo solves this by wrapping agent‑to‑miner interactions in a privacy layer. Agents deposit TAO into a shielded pool, generate ZK proofs to withdraw without linkability, and use stealth addresses so miners can receive payment without revealing their identity.

Core Guarantee

No observer can link a deposit to a withdrawal, or determine which agent paid which miner. The ZK proof proves you had funds in the pool—without revealing which deposit was yours.

ZK-SNARKs (Groth16) Poseidon Merkle Trees ERC-5564 Stealth UTXO Model Bittensor EVM

02 Quickstart

Peek-a-boo is a monorepo built with npm workspaces and Turborepo. All packages build to ESM + CJS + DTS via tsup.

Terminal # Clone and install git clone <repo-url> cd peek-a-boo npm install # Build everything npm run build # Run all 246 tests npm run test # Build a specific package npx turbo run build --filter=@pas/core # Compile smart contracts cd packages/contracts && npx hardhat compile # Build ZK circuits cd packages/contracts && npm run circuits:build
ToolVersionPurpose
Node.jsv24.11.1Runtime
npm11.6.2Package manager
TurborepolatestMonorepo build orchestration
tsuplatestLibrary bundler (ESM + CJS + DTS)
VitestlatestTest runner
HardhatlatestSmart contract toolchain
Circom2.1.9ZK circuit compiler
snarkjslatestGroth16 proving system

03 Architecture

The protocol is organized as a layered stack. Types at the base, core privacy primitives in the middle, chain adapters on top, and an SDK that composes everything into a unified API.

@pas/types
@pas/core
@pas/sdk
@pas/mcp-server
@pas/types
@pas/contracts
@pas/adapter-bittensor
@pas/types
@pas/adapter-railgun

Storage Abstraction

Every core module uses constructor-injected storage adapters. In-memory implementations ship as defaults. SQLite adapters are opt-in via the @pas/storage-sqlite package—core has zero SQLite dependency.

TypeScript // Default: in-memory (no persistence) const engine = createEngine({ }) // Opt-in: SQLite persistence import { createSqliteStores } from '@pas/storage-sqlite' const stores = createSqliteStores('./privacy.db') const engine = createEngine({ stores })

Cryptography

All cryptographic operations use @noble/curves and @noble/hashes—pure JavaScript with no native bindings. The library stays fully platform-agnostic: no node:crypto, no WASM, no system dependencies.


04 Policy Engine

The policy engine enforces spend constraints on agent transactions. Rules are evaluated in priority order—the first matching rule determines the outcome.

Rule TypeDescription
Spend LimitPer-transaction and cumulative limits with configurable time windows
Time BoundsRestrict transactions to specific time ranges with timezone support
WhitelistAllow only specific destination addresses
DenylistBlock specific destination addresses
Chain RestrictionLimit which chains an agent can transact on

Rules are composable and priority-ordered. An agent session might have a per-tx limit of 1 TAO, a daily cumulative limit of 10 TAO, and a whitelist restricting payments to verified miners only.


05 Session Manager

Sessions scope an agent's access to the privacy layer. Each session has a lifecycle:

Created
Active
Expired / Terminated

Sessions auto-expire on access check if their TTL has elapsed. A background cleanup sweep removes stale sessions. Policy rules are attached per-session, so different agents (or different tasks within one agent) can have different spending constraints.


06 Shielded Treasury

The treasury manages a UTXO note set representing shielded balances. Each note is a commitment that can only be spent once—double-spend prevention uses nullifiers.

UTXO Model

Each deposit creates a note (commitment = Poseidon(nullifier, secret)). Spending a note reveals its nullifierHash but not the original commitment—breaking the link between deposits and withdrawals.

The treasury supports multi-token balances, greedy UTXO selection for optimal transaction construction, and filtered transaction history with timestamps.


07 Stealth Addresses

Stealth addresses let miners receive payments without revealing a persistent public address. The protocol implements ERC-5564 using ECDH on secp256k1.

How it works

The sender generates a one-time address from the miner's meta-address. The miner scans on-chain announcements using their viewing key to detect payments—a viewTag enables fast filtering so miners only need to attempt full derivation on ~1/256 of all announcements.

Flow Sender Chain Miner | | | |-- generate(metaAddr) ------->| Announcement event | | |--- scan(viewKey) ----------->| | | | | | viewTag match? derive key | | | |

08 Smart Contracts

Five Solidity contracts handle the on-chain privacy operations. All contracts are ready for deployment on any EVM-compatible chain.

ContractPurpose
ShieldedPoolPrivacy pool with Poseidon Merkle tree, Groth16 ZK verification for withdrawals
StealthAnnouncerERC-5564 announcement registry for stealth address discovery
IncrementalMerkleTreeGas-efficient Merkle tree library with Poseidon hashing
PoseidonHasherOn-chain Poseidon hash wrapper (matches circomlib)
Groth16VerifierAuto-generated ZK proof verifier from snarkjs

Deposit & Withdraw Flow

Deposit TAO
Insert Commitment
Generate ZK Proof
Verify & Withdraw

Deposits insert a commitment into the Poseidon Merkle tree. Withdrawals require a Groth16 proof demonstrating knowledge of the pre-image without revealing which deposit is being spent. A 30-root history buffer prevents race conditions between concurrent deposits and withdrawals.

Deploy Order

Deployment PoseidonT3 // External library (deployed separately)Groth16Verifier // ZK proof verificationShieldedPool(levels, verifierAddr) // 20 levels production, 5 levels testStealthAnnouncer // Independent — ERC-5564 events

09 ZK Circuits

The withdrawal circuit is written in Circom and compiled to a Groth16 proving system. It follows the Tornado Cash pattern, upgraded from keccak256 to Poseidon hashing for ZK efficiency.

Circuit Design

Circom // Commitment construction commitment = Poseidon(nullifier, secret) // Nullifier hash (revealed on spend) nullifierHash = Poseidon(nullifier) // Public inputs (visible on-chain) root, nullifierHash, recipient, amount // Private inputs (known only to prover) nullifier, secret, pathElements[], pathIndices[]
Anti-Front-Running

The recipient and amount are bound to the proof via a square constraint trick. A front-runner cannot change the destination or amount without invalidating the proof.


10 Bittensor EVM

Bittensor's Subtensor EVM is the primary deployment target. The adapter supports both mainnet and testnet.

NetworkChain IDRPCCurrency
Mainnet964lite.chain.opentensor.aiTAO (18 decimals)
Testnet945test.chain.opentensor.aiTAO (18 decimals)

The BittensorAdapter implements the PrivacyBackend interface—the same interface used by the Railgun adapter. Config widening via BittensorAdapterConfig extends BackendConfig allows chain-specific options without breaking the shared interface.


11 Railgun Adapter

The Railgun adapter provides shielded ERC-20 transactions via the Railgun SDK's ZK-SNARK infrastructure. It uses a provider abstraction pattern—all SDK calls are isolated behind a single interface, making the adapter fully testable with mocks.

ModuleResponsibility
WalletManagerEngine initialization, wallet create/load
TransactionBuilderShield / unshield / transfer assembly
ProofServiceGroth16 proof generation & verification
BalanceScannerShielded balance queries
ChainMapChainId ↔ NetworkName mapping

Supported chains: Ethereum (1), BSC (56), Polygon (137), Arbitrum (42161).


12 Chain Strategy

PriorityChainStatusRationale
1st Bittensor EVMAdapter builtPrimary target — TAO privacy for AI agents
2nd RailgunAdapter builtUTXO model matches our treasury, existing privacy infra
3rd Ethereum L2StubCheapest gas, most agent activity
4th AztecStubNative private execution, deepest guarantees

13 Package Map

16 packages total — 13 libraries, 2 config, 1 docs app. 246 tests across 5 packages, all passing.

PackageDescriptionStatus
@pas/typesShared type definitions (9 modules) Live
@pas/corePrivacy engine, policy, sessions, treasury, stealth 65 tests
@pas/storage-sqliteSQLite persistence adapters 41 tests
@pas/contractsSolidity contracts + ZK circuits 41 tests
@pas/adapter-bittensorBittensor Subtensor EVM adapter 25 tests
@pas/adapter-railgunRailgun privacy backend 74 tests
@pas/sdkUniversal developer-facing API Stub
@pas/mcp-serverMCP server for agent tools Stub
@pas/settlementL1/L2 providers, gasless relay Stub
@pas/adapter-ethereumNative Ethereum L2 privacy Stub
@pas/adapter-aztecAztec private execution Stub
@pas/agent-crewaiCrewAI tool wrappers Stub
@pas/agent-langchainLangChain tool wrappers Stub

14 SDK & MCP Server

The @pas/sdk provides a universal privacy interface for AI agents. It composes core modules into high-level operations: shield, unshield, transfer, query balance.

The @pas/mcp-server exposes these operations as MCP tools, so any MCP-compatible agent runtime (Claude, custom agents) can use privacy features without direct SDK integration.

Agent Adapters

Framework-specific wrappers for CrewAI and LangChain are scaffolded in @pas/agent-crewai and @pas/agent-langchain. These translate SDK calls into framework-native tool interfaces.


15 SDK Reference

The PASClient class is the primary developer-facing interface. It wraps a PrivacyBackend and exposes high-level operations for transactions, identity, and policy management.

Constructor & Lifecycle

TypeScript import { PASClient } from '@pas/sdk' const client = new PASClient(backend) // Connect with agent config await client.connect({ agentId: 'agent-001', privacyLevel: 'FULL', allowedOperations: ['pay', 'receive'], ttl: 3600 }) // Disconnect when done await client.disconnect()

Transaction API

MethodParamsReturns
pay(params){ recipient, amount, token, memo? }PASResult<PaymentReceipt>
receive(params){ token, singleUse? }PASResult<ReceiveAddress>
swap(params){ fromToken, toToken, amount, slippageBps? }PASResult<SwapReceipt>
bridge(params){ token, amount, fromChain, toChain }PASResult<BridgeReceipt>

Identity API

MethodParamsReturns
prove(id, disclosure)credentialId: string, disclosure: DisclosureRequestPASResult<Proof>
credential(cred)Credential { type, claims, issuer }PASResult<string> (ID)
disclose(attrs)attributes: string[]PASResult<Proof>

Policy API

TypeScript await client.setPolicy([ { type: 'spend_limit', token: 'TAO', maxAmount: '1000000000000000000', period: 'daily' }, { type: 'whitelist', addresses: ['0x...'], mode: 'allow' }, { type: 'chain_restriction', allowedChains: [964, 945] }, ])

PrivacyBackend Interface

Every chain adapter implements this interface. Swap backends without changing application code.

TypeScript interface PrivacyBackend { readonly name: string readonly supportedChains: ChainId[] initialize(config: BackendConfig): Promise<void> shield(params: ShieldParams): Promise<PASResult<ShieldResult>> unshield(params: UnshieldParams): Promise<PASResult<UnshieldResult>> transfer(params: PrivateTransferParams): Promise<PASResult<TransferResult>> generateProof(params: ProofParams): Promise<PASResult<Proof>> verifyProof(proof: Proof): Promise<boolean> getShieldedBalance(token: TokenInfo, viewingKey: Hex): Promise<bigint> }

16 MCP Tools

The @pas/mcp-server exposes 10 tools via the Model Context Protocol. Any MCP-compatible agent runtime can call these directly—no SDK integration required.

Transaction Tools

ToolDescriptionInputs
pas_paySend a shielded payment—no on-chain link between sender and recipientrecipient, amount, token, memo?
pas_receiveGenerate a single-use stealth receive addresstoken, singleUse?
pas_swapPrivate token swap within the shielded poolfromToken, toToken, amount, slippageBps?
pas_bridgeBridge tokens privately between chainstoken, amount, fromChain, toChain

Treasury Tools

ToolDescriptionInputs
pas_get_balanceQuery shielded balance—computed locally, never exposed on-chaintoken
pas_shield_fundsMove funds from a public address into the shielded pooltoken, amount
pas_unshield_fundsWithdraw from the shielded pool to a public addresstoken, amount, recipient

Identity Tools

ToolDescriptionInputs
pas_proveGenerate a ZK proof for a credential without revealing underlying datacredentialId, attributes[]
pas_credential_storeStore a ZK-provable credential in the vaulttype, claims
pas_discloseSelectively reveal specific attributes for complianceattributes[]

MCP Resources

Read-only resources for querying protocol state without invoking tools.

URIDescription
pas://balance/{token}Current shielded balance for a specific token
pas://policiesCurrently active policy rules
pas://credentialsAvailable ZK-provable credentials
pas://sessionCurrent privacy session state
String Amounts

All MCP tools accept amounts as string (not number) to preserve precision with large token values. Use Wei-denominated strings for TAO and ERC-20 tokens.


17 Roadmap

PhaseMilestoneStatus
0Monorepo scaffolding, type system, configComplete
1Core infrastructure — policy, sessions, treasury, stealthComplete
2Persistence layer — SQLite storage adaptersComplete
3Smart contracts — ShieldedPool, StealthAnnouncerComplete
4ZK circuits — Circom withdraw circuit, Groth16 proofsComplete
5Railgun adapter — provider abstraction patternComplete
6Bittensor EVM adapter — chain config, adapter scaffoldComplete
7Deploy contracts to Bittensor testnetNext
8Wire adapter to deployed contractsPlanned
9SDK, MCP server, agent adaptersPlanned
10Relayer network, key management, security auditPlanned