# zkAPI Markdown version of https://ethbeat.org/privacy/projects/zkapi ## Summary **Warning:** The vault owner can block withdrawals until deposits expire and become sweepable to the treasury. The proofs rely on a single-party trusted setup. - Total Value Locked: $2.05 K (-12.3% compared to seven days ago) - Assets tracked: 1 - Buckets tracked: 1 - Deposits 7D: 2 (-77.7% compared to the previous seven days) - Deposits 30D: 37 - Deposits Total: 107 - Tracked on: Ethereum - Attributes: ZK, Any amount - Reference app: OA Chat (closed source, most private of 3 checked) ### Risks - Privacy: Link privacy. Public observer: Link private (sentiment: good); Chain analyst: Link private (sentiment: good); Network observer: Link private (sentiment: good); Privileged insider: Link exposed (sentiment: bad); Future adversary: Link private (sentiment: good). - Trusted setup: 1 participants (sentiment: bad). zkapi-v2-note-bound-v1 single-party setup: The published request and withdrawal keys were generated by one party. Retained setup secrets can allow forged proofs. - Exit window: None (sentiment: bad). The owner can immediately pause cooperative closes and the start of escapes. An already pending escape can be finalized after 1 day, including during a pause. An active note becomes sweepable after its deposit expiry. This protocol fails the walkaway test: users cannot fully use it if every centralized participant disappears. Starting an escape needs an unpaused vault and the user's latest signed balance. The operator can cancel it with any request proof it holds for that balance, and only a new operator signature restores normal use. - Reproducibility: Partially reproducible (sentiment: warning). The browser app and the local daemon have public source and build instructions. The hosted app adds a private layer for its sign-in pages and analytics, so it does not rebuild from that source. The verifier enclave's attested image rebuilds from public source. The daemon releases and the stations remain unreproduced. ### About Anonymous access to AI models, prepaid with ETH, that hides which deposit pays for which request. ### Links - Website: https://chat.openanonymity.ai - Docs: https://zkapi.openanonymity.ai - Repository: https://github.com/ethereum/zkapi - Contracts explorer (Disco): https://disco.l2beat.com/ui/p/zkapi ## Protocol description zkAPI lets the user prepay AI usage with ETH and chat without revealing which deposit pays for which request. Deposits and withdrawals are public, and the model provider can read the user's prompts. Users trust Open Anonymity, which runs the service and owns the vault, and the inference providers behind OpenRouter. The operator approves withdrawals and sees each request's time and budget tier. The owner can pause the vault, and expired deposits fall to its treasury. Without the operator, the user can start a 1 day escape while the vault is unpaused and the note active. The operator can cancel an escape from a balance the user already spent from by withholding a signed update. ### Architecture Open Anonymity verifies request proofs before issuing API keys. Its signing keys are fixed in the vault. A new key requires proof that the user's balance covers a budget of at least 0.00005 ETH. The user's app keeps the exact balance locally. The operator receives a commitment to the user's deposit. Initially, the balance equals the deposit. The commitment is homomorphic: the operator subtracts a known charge without reading the balance and signs the result. The app checks both and stores the new state. The operator also runs the indexer and a challenger that disputes escapes from outdated balances. ### Flow 1. **Deposit** ETH from your wallet. The vault records its amount and an expiry about 1 month later. 2. **Request access.** The app proves an active note covers the budget tier of your chosen model. The operator returns a short-lived OpenRouter key. 3. **Chat.** Send prompts to OpenRouter with that key. 4. **Settle.** The operator bills the key's usage. 5. **Withdraw.** A cooperative close needs the operator's clearance. The vault publishes the note id and remaining balance. It returns the remainder to your chosen address and pays usage to the treasury. ### Keys Losing the note secret and latest signed balance that are saved by the app loses the funds. ### App OA Chat is a hosted browser app with local build instructions. It fetches the whole note tree and proves in the browser. It relays inference and chain reads through a proxy at refraction.network by default, with a fallback to direct connections. The hosted build serves the [public source](https://github.com/OpenAnonymity/oa-chat/tree/a2623612c17bea09d5efaf8f41b500fe98fb1c8a) with a private layer for the sign-in pages, including Google sign-in, and Fathom analytics. ### Providers and verifier OpenRouter and the model provider can read prompts and responses and link requests under one key. Open Anonymity's stations hold the OpenRouter accounts. A verifier in an attestable Azure confidential container checks that each station's account disables provider logging. Its attested image rebuilds from [public source](https://github.com/OpenAnonymity/oa-verifier/tree/a2916c6af618ee46a851af28dee201606de895d8). A station whose settings it cannot read stays approved for up to seven days after its last pass. OA Chat pins only the verifier's address, so the user must check the attestation personally. When the verifier is unreachable, OA Chat still accepts a station-signed key and shows "Key not verified". ### Exits **Escape.** Without a cooperative close, the user starts an escape with their latest signed balance and finalizes it after 1 day. The operator can cancel it with a request proof for that balance, which publishes that request's link. While the owner pauses the vault, no one can start an escape, but one already started still finalizes. ### Compliance The vault has no onchain screening. The operator can refuse keys or clearance. ### Fees Usage is billed in USD and converted to ETH through a Chainlink feed, with no fee on top of the provider's price. The unused balance returns at withdrawal. ## Privacy **What the protocol promises:** Hides which deposit pays for which API request. Deposits and withdrawals are public, and the provider can read prompts. **Apps** - **[OA Chat](https://chat.openanonymity.ai)** (Reference): The ratings assume this app, since 2026-10-12 · Closed source, build OI3IE56B, checked 2026-10-30 - **zkapi-clientd** (Leaks more): Sends the note id to the operator's indexer before each request, so the operator can tie requests to their deposit by timing. Fix: Fetch the whole note tree, as OA Chat does, instead of one note's path. · [Source](https://github.com/ethereum/zkapi/blob/20aa542ae98e767c0507133fd34b12a56f5ccd3d/crates/zkapi-clientd/src/indexer.rs#L71-L80) - **openzk.app** (Leaks more): Sends request proofs and withdrawals through OpenZK's own server, so OpenZK also sees each request's time and budget. Fix: Send zkAPI calls straight to Open Anonymity's servers, so only the operator sees them, as in OA Chat. · [Source (checked 2026-10-30)](https://openzk.app/zkapi/browser-config.json) [How we pick the reference](https://ethbeat.org/publications/privacy-methodology#reference-app) Ethereum is public by default. Each protocol is rated on its main privacy promise, like hiding who paid, who received, how much, or which deposit became which withdrawal. Against each adversary, the colour says whether it holds for a careful user with the app's own settings: green yes, yellow only with significant extra work, red no. Fields marked at risk stay private only under the condition in their note. ### Public observer Link private (sentiment: good) **Who:** Everyone with a block explorer and some basic tools. Sees every public onchain event, but does no correlation beyond following links. Examples: A curious counterparty, an employer, a journalist. Requests stay offchain, so nothing onchain ties a request to the deposit that pays for it. A deposit publishes the funder, amount, note id and expiry. Closing the note publishes the payout address and the remaining balance, so each note's total spend is public. **Inside** - Sender: exposed - Recipient: exposed. Payments go to the operator treasury. - Amount: at risk. Charges per request stay offchain. Closing the note publishes the total. - Asset: exposed - Link: private. An escape dispute publishes the link for one request. **Sources** - [ZkApiVault](https://ethbeat.org/privacy/projects/zkapi#ZkApiVault) - [Deposit and close events](https://github.com/ethereum/zkapi/blob/045b444ea1b52538d1b40273c7cb6ed09468a052/protocol/contracts/src/libraries/Events.sol#L6-L10) - [Request circuit keeps note id, balance and state signature private](https://github.com/ethereum/zkapi/blob/045b444ea1b52538d1b40273c7cb6ed09468a052/protocol/rust/crates/zkapi-proof/src/groth16.rs#L368-L485) ### Chain analyst Link private (sentiment: good) **Who:** Scrapes all public data and correlates it: timing, amounts, gas and wallet fingerprints, address clusters. Examples: Chain analytics firms, ZachXBT, data brokers. Authorizations never reach the chain, so correlating public data adds nothing to the note record. Inside, the same as for a public observer. **Sources** - [Authorization is an HTTP request to the operator](https://github.com/ethereum/zkapi/blob/045b444ea1b52538d1b40273c7cb6ed09468a052/docs/api-spec.md) ### Network observer Link private (sentiment: good) **Who:** Sits between the user and the services and sees traffic only: ISPs, VPNs, Tor exits, public RPCs and relayers. Ties requests to a user by content, session or timing: what they name, a shared IP or login, and when they arrive. Services the operator runs count as the insider. Examples: Infura or Alchemy, a Tornado relayer, a wallet vendor selling telemetry, Google captcha or analytics in the dapp frontend. The app works in Tor Browser, and no third party sees both a deposit and a request. The deposit reaches the wallet's RPC, or the app's public RPC when the user funds an address the app shows. A relay at refraction.network carries requests to OpenRouter and chain reads by default and sees their timing, and Fathom counts feature use. **Advice:** Use the app in Tor Browser, and send the deposit through an IP-hiding route too. **Inside, compared with a public observer** - Link: at risk. Private only when IP-hiding routes keep the user's IP out of both the deposit and the requests. **Sources** - [IP-hiding routes we checked](https://ethbeat.org/publications/privacy-methodology#ip-hiding-routes) - [App relay on by default, direct fallback](https://github.com/OpenAnonymity/oa-chat/blob/a2623612c17bea09d5efaf8f41b500fe98fb1c8a/chat/services/networkProxy.js#L7-L10) - [Hosted app loads Fathom Analytics (checked 2026-10-30)](https://chat.openanonymity.ai) ### Privileged insider Link exposed (sentiment: bad) **Who:** Holds an operator role, custodies funds, receives keys or plaintext by design, or delivers the app: upgrade admin, custodian, view key holder, enclave operator, association set provider, hosted prover, registry, frontend host. Examples: A compliance backdoor key, a DAO with an upgrade key, a KMS committee, an ASP operator, a custodial bridge. The operator sees each request's time and budget tier but not its note, and too few active notes cover even the default model's budget to hide among. The candidates are the active notes whose balance covers the request's budget tier, which follows the chosen model's price. The provider can read prompts and responses and link requests under one key. **Inside, compared with a public observer** - Recipient: exposed - Amount: exposed. The operator bills each request and the provider meters it. - Link: exposed **Sources** - [Balance commitment re-blinded for every request](https://github.com/ethereum/zkapi/blob/045b444ea1b52538d1b40273c7cb6ed09468a052/docs/note-bound-commitments.md) - [OA Chat sets the budget tier from the model's price](https://github.com/OpenAnonymity/oa-chat/blob/a2623612c17bea09d5efaf8f41b500fe98fb1c8a/chat/zkapi/services/zkapiModelBudget.mjs#L6-L14) - [Provider sees inference content](https://github.com/ethereum/zkapi/blob/045b444ea1b52538d1b40273c7cb6ed09468a052/zkapi-clientd/docs/PRIVACY.md) ### Future adversary Link private (sentiment: good) **Who:** Harvest now, decrypt later. Holds every byte ever published and runs the known quantum attacks: elliptic curves, pairings and RSA fall. Hashes, symmetric ciphers and post-quantum schemes stand until an attack on them is known. Examples: An intelligence agency archiving chain data today, then anyone once a break is public. Note leaves and nullifiers are Poseidon hashes and nothing encrypted is written onchain, so there is nothing to decrypt. Whoever breaks elliptic-curve cryptography opens only the balance commitment that a published dispute carries. Inside, the same as for a public observer. **Sources** - [Note-bound commitments and assumptions](https://github.com/ethereum/zkapi/blob/045b444ea1b52538d1b40273c7cb6ed09468a052/docs/note-bound-commitments.md) ## Value Locked The interactive value chart is shown on [the HTML page](https://ethbeat.org/privacy/projects/zkapi#privacy-tvl). ## Flows The interactive flows chart is shown on [the HTML page](https://ethbeat.org/privacy/projects/zkapi#privacy-flows). ## Assets Breakdown | Asset | Deposits 7D | Deposits 30D | Deposits Total | Value Locked | | --- | --- | --- | --- | --- | | ETH | 2 ($200.18) | 37 ($1.67 K) | 107 ($3.46 K) | $2.05 K | | Total | 2 ($200.18) | 37 ($1.67 K) | 107 ($3.46 K) | $2.05 K | ## Milestones & Incidents - 2026-10-01: [zkAPI mainnet launch](https://blog.ethereum.org/2026/10/01/introducing-zkapi). The Ethereum Foundation and Open Anonymity open the ETH vault, OA Chat and the local client. ## Risk summary ### Funds can be stolen if 1. the owner pauses withdrawals until notes expire and anyone sweeps them to its treasury. 2. the single-party [trusted setup](https://ethbeat.org/privacy/projects/zkapi#trusted-setups) secrets or a circuit flaw allow forged withdrawal proofs. 3. malicious wallet code reads the note secret. ### Funds can be lost if 1. the note secret or the latest signed balance is lost. 2. a note expires before withdrawal. ### Funds can be frozen if 1. the operator withholds a signed balance and cancels the escape with the request it received. ### Privacy can be lost if 1. the operator ties a request to its deposit, since few active notes cover its budget tier (see privileged insider). 2. prompts identify the user to OpenRouter or the model provider (see privileged insider). 3. the operator keeps request transcripts and indexer logs, which later tie requests to deposits (see privileged insider). ## Upgrades & Governance The vault, verifier, signing keys and exit parameters are immutable. The owner EOA can set the treasury, pause deposits, cooperative closes and the start of escapes, and transfer or renounce ownership, all without delay. Pending escapes still finalize during a pause. An instant pause with a subsequent sweep after expiry would steal all funds whose escape was not started before the pause. The operator's services, the hosted app and the daemon releases are controlled by Open Anonymity and can change without notice. ## Trusted setup Risk levels follow the [Trusted Setups Risk Framework](https://forum.l2beat.com/t/the-trusted-setups-framework-for-zk-catalog/381). Yellow (medium risk): all contributions are published and the final output can be verified, the ceremony client is open source, there were at least 30 contributions, participation was open to the public and announced, and participants are publicly identified. Green (lowest risk): everything required for yellow, with at least 150 contributions. Red (highest risk): at least one requirement for yellow is not met. N/A: the proof system needs no trusted setup. ### zkapi-v2-note-bound-v1 single-party setup - Risk: red (highest risk) - Proof systems: Arkworks (Groth16) - Participants: 1 participant The request and withdrawal Groth16 keys were generated by one party. There was no multi-party ceremony. Security requires that this party erased the setup secrets, otherwise it can forge withdrawal proofs. Matching public artifacts cannot prove erasure. [Setup implementation](https://github.com/ethereum/zkapi/blob/045b444ea1b52538d1b40273c7cb6ed09468a052/protocol/rust/crates/zkapi-proof/src/compact.rs#L288-L383), [provenance](https://github.com/ethereum/zkapi/blob/045b444ea1b52538d1b40273c7cb6ed09468a052/protocol/setup/v2/README.md). ## Verifier IDs ### zkapi note-bound request and withdrawal verifier - Verifier ID: `0xd31acf861825869e6a84b6b0025cb6940077f5981a10e73d2e4e8e5d7417c853` - Source: https://github.com/ethereum/zkapi/blob/045b444ea1b52538d1b40273c7cb6ed09468a052/protocol/rust/crates/zkapi-proof/src/groth16.rs - Verification: not verified - Used in: [zkAPI](https://ethbeat.org/privacy/projects/zkapi) **Known deployments** - [0x8e92013Dd7cc86f75b539DBD3814B2e603e0F9C1](https://etherscan.io/address/0x8e92013Dd7cc86f75b539DBD3814B2e603e0F9C1#code) on Ethereum, used in: [zkAPI](https://ethbeat.org/privacy/projects/zkapi) #### Verification steps ##### Contracts and proof artifacts Reviewed [ethereum/zkapi revision](https://github.com/ethereum/zkapi/tree/045b444ea1b52538d1b40273c7cb6ed09468a052), whose vault, adapter and circuits match the deployment revision reported by the production manifest. Compile with the checked-in configuration: solc 0.8.28, optimizer 200, viaIR and Prague. ```bash git clone https://github.com/ethereum/zkapi.git cd zkapi git checkout 045b444ea1b52538d1b40273c7cb6ed09468a052 cd demo/contracts forge build --skip test --build-info cast tx 0xf753fd93c7e672f7b851f261f600e06930e502bdfd73be29ce8a065643df6985 --json --rpc-url "$ZKAPI_RESEARCH_RPC_URL" > vault-tx.json cast tx 0x45f2f5259476afd5f64a6a62a36c9012c3a26efcdd35b048a7fac5ae1ba43780 --json --rpc-url "$ZKAPI_RESEARCH_RPC_URL" > verifier-tx.json ``` The adapter creation bytecode matches exactly, including metadata. The vault matches after linking the discovered Poseidon address. Decode its ten constructor arguments from the remaining transaction input. Sources are verified on Etherscan: [vault](https://etherscan.io/address/0x4386FDbdA35D995beB3BF8625118Ec5982ec81fe#code), [adapter](https://etherscan.io/address/0x8e92013Dd7cc86f75b539DBD3814B2e603e0F9C1#code), [Poseidon](https://etherscan.io/address/0xc6B55e86668d8c446B3D81273AAb9CBb20F28c7f#code). The deployed verification-key coordinates and public inputs match the [published setup artifacts](https://github.com/ethereum/zkapi/tree/045b444ea1b52538d1b40273c7cb6ed09468a052/protocol/setup/v2), and the proving-key hashes match the SDK and production pins. A single-party setup with fresh randomness yields different keys each time, so matching artifacts show that the deployed keys are the published ones. Only the setup party knows whether its secrets were erased. ##### Frontend and local client The [browser frontend](https://github.com/OpenAnonymity/oa-chat/blob/a2623612c17bea09d5efaf8f41b500fe98fb1c8a/README.md#quick-start) and the [local API daemon](https://github.com/ethereum/zkapi/blob/045b444ea1b52538d1b40273c7cb6ed09468a052/zkapi-clientd/README.md) have public source and build instructions. Both still depend on the operator's authorization and inference services. ##### Verifier enclave The mainnet verifier is `verifier-production-20260917.openanonymity.ai`, pinned in the hosted app's [browser config](https://chat.openanonymity.ai/zkapi/browser-config.json). It runs in an Azure confidential container on AMD SEV-SNP and serves a hardware attestation. Its source, [OpenAnonymity/oa-verifier](https://github.com/OpenAnonymity/oa-verifier), is pinned to commit `a2916c6` by the [production rollout](https://github.com/OpenAnonymity/oa-verifier/blob/dccb1310c465e6494daa8f39dd020ce76385ff6c/deploy/production/rollout.py#L27-L29), which names image `sha256:5f84efe3…`, signed on 2026-10-26. ```bash curl -sS "https://verifier-production-20260917.openanonymity.ai/attestation?nonce=$(date +%s)" > attestation.json # 1. the JWT in .token is signed by Azure Attestation (.verify_at), its x-ms-sevsnpvm-hostdata equals sha256 of the decoded .policy # 2. the policy lists one dm-verity layer hash for the verifier container git clone https://github.com/OpenAnonymity/oa-verifier && cd oa-verifier git checkout a2916c6af618ee46a851af28dee201606de895d8 nix build --no-update-lock-file .#container && gunzip -c result > image.tar # 3. the layer root hash equals the attested one go install github.com/Microsoft/hcsshim/cmd/dmverity-vhd@v0.12.9 dmverity-vhd -t image.tar roothash -i oa-verifier:latest ``` The attested policy lists the verifier layer `3e18df8a…` and the Rekor entry `3489318725` of the image signature. That signature comes from the `deploy-production-verifier` workflow of OpenAnonymity/oa-verifier on branch `release/verifier-provider-grace-20261026`. The rebuilt layer root hash equals that layer. The attestation also binds the TLS key of the live endpoint. Both clients pin only the verifier's address, so repeat the check when it matters. ## Ossification & Updates Each date links the update on the HTML page, which also shows its contract diffs. ### [2026-10-07 11:41 UTC](https://ethbeat.org/privacy/projects/zkapi?update=193c1624) (3 changes) Initial discovery of the active native ETH vault, its Groth16 proof adapter and linked Poseidon library. All three contracts were verified from public source. The owner can pause new exits and change the treasury. ## Permissions Explore these contracts and permissions in Disco, ETHBEAT's contract explorer: https://disco.l2beat.com/ui/p/zkapi ### Ethereum #### Actors ##### EOA 1 Addresses: [0x83f7eA47811A288f2052Bc31aaAE0803c761D156](https://etherscan.io/address/0x83f7eA47811A288f2052Bc31aaAE0803c761D156) - Can interact with ZkApiVault - pause and unpause deposits, cooperative withdrawals and new escape withdrawals, and change the treasury receiving consumed balances and expired notes. Pausing can prevent users from exiting before their notes become sweepable ## Smart contracts Explore these contracts and permissions in Disco, ETHBEAT's contract explorer: https://disco.l2beat.com/ui/p/zkapi ### Ethereum #### ZkApiVault Addresses: [0x4386FDbdA35D995beB3BF8625118Ec5982ec81fe](https://etherscan.io/address/0x4386FDbdA35D995beB3BF8625118Ec5982ec81fe#code) Escrows ETH for anonymous API authorizations as publicly numbered notes. A Groth16 proof closes a note with operator clearance, or starts a delayed escape that any request proof with the same nullifier cancels. Closing reveals the note id, payout address and remaining balance and sends the used part to the treasury. Anyone can sweep an expired note entirely to the treasury, also while paused. - Roles: - **owner**: EOA 1 #### Groth16ProofAdapter Addresses: [0x8e92013Dd7cc86f75b539DBD3814B2e603e0F9C1](https://etherscan.io/address/0x8e92013Dd7cc86f75b539DBD3814B2e603e0F9C1#code) BN254 Groth16 verifier containing the request and withdrawal verification keys for zkapi-v2-note-bound-v1. Request proofs hide the note identity, exact balance and state signature. Withdrawal proofs expose the note id, destination, final balance and nullifier. The published circuit-specific keys were generated in a single-party setup. Retained setup secrets can allow forged proofs. #### Bn254Poseidon Addresses: [0xc6B55e86668d8c446B3D81273AAb9CBb20F28c7f](https://etherscan.io/address/0xc6B55e86668d8c446B3D81273AAb9CBb20F28c7f#code) Linked Poseidon hash library used by the vault for active-note leaves and Merkle updates. Its BN254 parameters must agree with the request and withdrawal circuits.