Overview
Evane sits between your AI agents and the model providers they call. It has three parts.
- evane-clientd, a daemon on your machine. Agents call it like the OpenAI API. It applies each agent’s policy and daily cap, removes personal data, and signs every request with the key of a burner sub-account.
- The gateway. It checks the signature and the sub-account’s escrow on-chain, strips metadata that identifies you, and calls the provider with its own credentials. In production it runs inside a hardware enclave (AWS Nitro Enclaves or Intel TDX).
- EscrowVault, a contract that holds each sub-account’s budget. The gateway settles what you used in batches, and never more than your daemon signed for.
Preview status
The preview runs on the Ethereum Sepolia testnet (chain ID 11155111). Testnet ETH has no monetary value.
- Gateway:
https://gateway.evane.dev. It runs with a development attestation: it is not yet inside an enclave, so its operator could read request contents. Send test data only. - EscrowVault: 0x7B9D941929512A5ab6FAEf13bD49D93AAa4B6757 (opens in a new tab)
- Model prices on the preview gateway are placeholders, not provider list prices.
Live checks are on the status page.
Install
Download evane-clientd 0.1.0 for your platform. Each archive holds the program, a README and an example configuration. The Linux builds are static, so they run on any distribution.
| Platform | Download | Size |
|---|---|---|
| Windows (x64) | evane-clientd-0.1.0-windows-x64.zip | 5.3 MB |
| Linux (x64) | evane-clientd-0.1.0-linux-x64.tar.gz | 5.4 MB |
| Linux (arm64) | evane-clientd-0.1.0-linux-arm64.tar.gz | 5.0 MB |
There is no macOS build yet. The Windows build is not code-signed yet, so Windows may warn before running it: check its checksum first.
Check the download
The SHA-256 of each archive, also published as SHA256SUMS:
f07e4ec7108006b1d6e416336f3511fa3fce9f208f164174dd5806f873724af9 evane-clientd-0.1.0-windows-x64.zip
a38f35fe3f2dc4afd6b856c840c549e3c939bdc175a37d336cc3ae84f0c74b85 evane-clientd-0.1.0-linux-x64.tar.gz
bf10b126c013410e6ca010c788901aa928572686c7328eccf4baf3e5ad73f36e evane-clientd-0.1.0-linux-arm64.tar.gzsha256sum --ignore-missing -c SHA256SUMS(Get-FileHash .\evane-clientd-0.1.0-windows-x64.zip -Algorithm SHA256).HashPut it on your path
tar -xzf evane-clientd-0.1.0-linux-x64.tar.gz
sudo install -m 755 evane-clientd-0.1.0-linux-x64/evane-clientd /usr/local/bin/
evane-clientd --versionOn ARM, use the linux-arm64 archive the same way.
Expand-Archive .\evane-clientd-0.1.0-windows-x64.zip -DestinationPath $HOME\evane
cd $HOME\evane\evane-clientd-0.1.0-windows-x64
Unblock-File .\evane-clientd.exe
.\evane-clientd.exe --versionQuickstart
- Install evane-clientd.
- Choose Connect Wallet at the top of this page, connect a browser wallet on Sepolia, set a budget and choose Fund account. When the account is funded, download
session.keyandclientd.toml. Faucets (opens in a new tab) give testnet ETH for free. - Move both files into the
.evanefolder in your home directory.Linux mkdir -p ~/.evane mv ~/Downloads/session.key ~/Downloads/clientd.toml ~/.evane/ chmod 600 ~/.evane/session.keyWindows PowerShell New-Item -ItemType Directory -Force $HOME\.evane | Out-Null Move-Item $HOME\Downloads\session.key, $HOME\Downloads\clientd.toml $HOME\.evane\ - Start the daemon.
evane-clientd - Point any OpenAI SDK at it. The API key is ignored unless it matches an agent’s token.
Python from openai import OpenAI client = OpenAI(base_url="http://127.0.0.1:9090/v1", api_key="unused") reply = client.chat.completions.create( model="claude-sonnet-5-5", messages=[{"role": "user", "content": "Hello"}], ) print(reply.choices[0].message.content)TypeScript import OpenAI from "openai"; const client = new OpenAI({ baseURL: "http://127.0.0.1:9090/v1", apiKey: "unused" }); const reply = await client.chat.completions.create({ model: "claude-sonnet-5-5", messages: [{ role: "user", content: "Hello" }], }); console.log(reply.choices[0].message.content); - Check what the agent has spent today.
curl http://127.0.0.1:9090/v1/agent/status - When you are done, open Connect Wallet again. Revoke the account, reclaim what is left once its settlement window closes, and withdraw it to your wallet.
Keep the key out of the browser
The panel can fund a key that evane-clientd made instead. Run keys new, choose From evane-clientd in the panel, and paste the session key it prints. The secret never leaves the daemon’s key file.
evane-clientd keys new --master <your wallet address>Or skip the site: write ~/.evane/clientd.toml yourself and fund the sub-account from any wallet that can call depositAndSpawn. Foundry’s cast is shown.
# ~/.evane/clientd.toml
listen = "127.0.0.1:9090"
gateway_url = "https://gateway.evane.dev"
chain_id = 11155111
vault = "0x7B9D941929512A5ab6FAEf13bD49D93AAa4B6757"
master = "<your wallet address>"
[agents.default]
daily_cap_usd = "10.00" # resets at 00:00 UTCcast send 0x7B9D941929512A5ab6FAEf13bD49D93AAa4B6757 "depositAndSpawn(bytes32,uint256)" \
<session key> 86400 --value 0.01ether \
--rpc-url https://ethereum-sepolia-rpc.publicnode.com --account <your keystore>How a request flows
- Your agent calls the daemon on
127.0.0.1. Requests that carry anOriginheader, as browsers send, and requests for unexpected host names are refused, so web pages cannot reach it. - The daemon identifies the agent, runs its policy, and removes personal data from the message text.
- It holds the request’s maximum cost against the agent’s daily cap, then signs the request and a receipt, the running total it agrees it owes, with the sub-account’s Ed25519 key.
- The gateway checks the timestamp, the nonce, the sub-account on-chain, both signatures, the receipt and the escrow balance. A request that fails any check never reaches a provider.
- It removes
user,metadata,safety_identifier,prompt_cache_keyand any user location, builds a fresh request with its own provider key, and relays it. Nothing from your connection is forwarded. - It charges the price of the usage the provider reports, never more than the reservation. The daemon checks each charge against the published price table before acknowledging it in its next receipt.
- Every 100 requests or 5 minutes, the gateway settles each sub-account on-chain for the lower of its latest receipt and what it charged.
Configuration
The daemon reads ~/.evane/clientd.toml, or the file named by --config. Relative paths resolve against the file’s directory.
| Setting | Default | Meaning |
|---|---|---|
listen | 127.0.0.1:9090 | Where agents connect. Other machines need allow_remote. |
gateway_url | Required | The gateway. HTTPS is required except for localhost. |
chain_id | Required | 11155111 for Sepolia. |
vault | Required | The EscrowVault address. |
master | Required | The wallet that owns the sub-account. |
key_file | session.key | The session key, created by keys new. |
state_file | state.json | Receipts and today’s spending. |
proxy | None | A proxy for the gateway connection, such as socks5h://127.0.0.1:9050 for Tor. |
allow_remote | false | Accept connections from other machines. |
allowed_hosts | [] | Extra host names to accept. |
require_tee | false | Refuse to start unless the gateway proves it runs one of the enclave images in tee_pcr0. |
tee_pcr0 | [] | Accepted AWS Nitro enclave images (PCR0, 96 hex characters each). Required with require_tee. |
idle_receipt_seconds | 30 | Send the latest receipt on its own after this idle time. |
redaction.enabled | true | Remove personal data from message text. |
redaction.custom | None | Extra patterns, each with a name and a regular expression. |
Agents, caps and policies
Each request belongs to an agent: the one whose token matches the bearer API key, or the one named in X-Evane-Agent if it has no token, or else default. An agent with a token can only be used with that token, so one local process cannot spend another’s allowance.
[agents.research]
token_env = "EVANE_RESEARCH_TOKEN" # the agent sends it as its API key
daily_cap_usd = "5.00"
hard_cap = true
policy = "policies/research.wasm"Daily caps
Caps are in US dollars and reset at 00:00 UTC. A hard cap refuses a request whose maximum cost could cross it, counting requests still in flight. A soft cap, hard_cap = false, refuses requests only once the cap is reached. The maximum cost of a request is the price of ceil(body bytes / 3) input tokens plus its output limit: the larger of max_tokens and max_completion_tokens, or 4096 if neither is set, times n.
Policies
A policy is a WebAssembly module that exports memory, alloc(len) -> ptr and check(ptr, len) -> i32, and imports nothing, so it has no access to files or the network. The daemon writes {"agent": ..., "request": ...} as JSON, before redaction, and calls check: 0 allows the request and anything else denies it. Each check runs in a fresh instance with 50 million units of fuel and 32 MiB of memory. A trap or an exhausted budget denies.
(module
(memory (export "memory") 1)
(func (export "alloc") (param i32) (result i32) i32.const 0)
;; Deny any request larger than 64 KiB.
(func (export "check") (param $ptr i32) (param $len i32) (result i32)
local.get $len
i32.const 65536
i32.gt_u))Redaction
Before a request is signed, the daemon replaces personal data in message text, in this order:
| Data | Replaced with | Matched |
|---|---|---|
| Email addresses | [email] | Standard address syntax. |
| IP addresses | [ip] | IPv4 and IPv6, except loopback and unspecified addresses. |
| Payment cards | [card] | 13 to 19 digits that pass the Luhn check. |
| Phone numbers | [phone] | 7 to 15 digits with a +, parentheses or separators. Dates, decimals and version numbers are left alone. |
| Custom patterns | [name] | Regular expressions from the configuration. |
Before: Summarize the invoice from priya@example.com and call +1 555 0142 if it's over $5k.
After: Summarize the invoice from [email] and call [phone] if it's over $5k.Fields such as user and metadata are removed by the gateway, so they never reach the provider either.
API reference
Daemon, on http://127.0.0.1:9090
| Endpoint | Purpose |
|---|---|
POST /v1/chat/completions | OpenAI chat completions, including streaming. Responses carry X-Evane-Charge-Wei and X-Evane-Cumulative-Wei. |
GET /v1/models | The models the gateway serves. |
GET /v1/agent/status | The calling agent’s spending today, its cap, the sub-account and the gateway’s attestation. |
GET /healthz | Liveness. |
Gateway
| Endpoint | Purpose |
|---|---|
POST /v1/chat/completions | A signed completion request. |
POST /v1/receipts | A signed receipt with the body {}: acknowledges a total without calling a model. |
GET /v1/pricing | Prices in micro-USD per million tokens and the USD per ETH rate used for wei. |
GET /v1/attestation?nonce=… | In an AWS Nitro enclave, a document signed by the Nitro hypervisor that binds the nonce, the enclave image (PCR0), the deployment and the enclave’s TLS key. The daemon verifies it, then talks only to that key. 501 when the gateway has no attestation. |
POST /v1/settlement/cosign | Validators co-sign each other’s settlement batches. |
GET /healthz | Liveness. |
Signed request headers
| Header | Value |
|---|---|
X-Evane-Sub-Account | The sub-account address. |
X-Evane-Timestamp | Unix seconds. Rejected if more than 60 seconds off. |
X-Evane-Nonce | 16 random bytes as hex. Rejected if reused within 120 seconds. |
X-Evane-Receipt | The cumulative wei the client agrees it owes. |
X-Evane-Receipt-Signature | Ed25519 signature over the receipt. |
X-Evane-Signature | Ed25519 signature over the chain, vault, sub-account, method, path, timestamp, nonce, receipt and body hash. |
Errors
Errors use the OpenAI error shape, so SDKs surface them as usual.
{"error": {"message": "the sub-account's escrow cannot cover this request", "type": "evane_error", "code": "budget_exhausted"}}| Code | Status | Meaning |
|---|---|---|
invalid_signature | 401 | Missing, malformed or wrong signature headers. |
stale_request | 401 | The timestamp is more than 60 seconds off. |
replayed_request | 401 | The nonce was already used. |
sub_account_inactive | 403 | The sub-account does not exist, has expired or was revoked. |
receipt_behind | 402 | The receipt does not cover past charges. The daemon catches up and retries automatically. |
budget_exhausted | 402 | The escrow cannot cover the request’s maximum cost. |
daily_cap_reached | 429 | The agent’s daily cap would be crossed. |
policy_denied | 403 | The agent’s policy denied the request, or failed. |
forbidden | 403 | A browser request, an unknown agent, or a missing agent token. |
model_not_found | 400 | The gateway does not serve that model. |
invalid_request | 400 | The body is not a valid chat completion request. |
payload_too_large | 413 | The body is over 16 MiB. |
upstream_rate_limited | 429 | The provider is rate limiting the gateway. Retry shortly. |
upstream_error | 502 | The provider failed or could not be reached. Nothing was charged. |
gateway_unavailable | 502 | The daemon cannot reach the gateway, or the gateway cannot read the chain. |
charge_mismatch | 502 | The gateway claimed more than the daemon can verify, so the daemon refused to acknowledge it. |
attestation_unavailable | 501 | The gateway has no enclave attestation. |
Escrow and settlement
EscrowVault holds budgets in ETH. Your wallet is the master owner of every sub-account it opens; the sub-account address is derived from your wallet and the session key, so nobody else can claim it. The Connect Wallet panel calls sub-accounts just accounts.
| Function | What it does |
|---|---|
deposit | Adds ETH to your master balance. |
withdraw | Returns unused master balance to your wallet. |
depositAndSpawn | Deposits and opens a sub-account with the whole deposit as its budget, in one transaction. |
spawnSubAccount | Opens a sub-account from your master balance. Validity is 60 seconds to 30 days. |
topUp | Moves more of your master balance into a live sub-account. |
revokeSubAccount | Ends a sub-account now. The gateway keeps a grace period to settle what was already used. |
reclaim | After expiry and the grace period, returns the remaining budget to your master balance. Anyone can call it. |
Validators sign settlement batches with EIP-712, and the vault accepts a batch only with enough validator signatures. Each item moves at most what the escrow holds, and settling the same total twice changes nothing. The gateway settles the lower of your latest receipt and what it charged: never more than your daemon signed for.