# Introduction (/docs) Solrouter is a private AI layer for Solana. On the encrypted path, your prompt is encrypted on your device, stays encrypted through the Solrouter backend, and is decrypted only inside a hardware-isolated enclave. The backend never sees it in plaintext. **In words** * A typical AI API reads your prompt as plain text on its own server. * With Solrouter, your device encrypts the prompt first. The backend relays ciphertext and never sees the words. * Only the enclave holds the key. It runs the model on a Nosana GPU node, then encrypts the reply so only your device can read it. Private inference runs only on self-hosted, open-weight models on the Nosana GPU network. Solrouter does not send your prompt to OpenAI, Anthropic, or Google. The Nosana node that runs the model sees the prompt during inference, and Solrouter does not control that hardware. ## Choose your surface [#choose-your-surface] Chat in the browser. No code. Turn on Maximum Privacy Mode to encrypt each prompt. Add encrypted AI to your app or agent with the SDK, the MCP server, or the REST API. The enclave, encryption, attestation, and the on-chain proof, each with the code. Paste a lock link and watch the verification pass in your browser. New to the tech? Start with [What is a TEE?](/docs/how-it-works/what-is-a-tee) and [What is private here](/docs/use/what-is-private). # Glossary (/docs/glossary) This page defines each term in plain words first, then the technical name. Every entry links to the page that explains it in full. Some entries use an analogy and say where it stops being accurate. ## A [#a] **A2A agent card.** A small public file that lists what an AI agent can do, in a format other agents read. Solrouter serves one at `/.well-known/agent-card.json` (Live). See [Discovery documents](/docs/api-reference/overview). **/agent endpoint.** The address (`POST /agent`) where a program sends a research question and gets a tool-built answer. By default it runs a tool loop: the model picks a tool, runs it, and repeats up to 8 times. See [Agent endpoint and guided reasoning](/docs/how-it-works/agent-reasoning). **Agent Privacy API.** A separate set of endpoints under `/agents/v1` for autonomous software agents. It covers private token swaps (Soon) and pay-per-call encrypted answers with no account (Live). It is not the `/agent` endpoint above. See [Agent Privacy API](/docs/build/agent-privacy-api). **Agent Tools SDK.** A planned code package, `@solrouter/agent-tools`, that would wrap the Agent Privacy API. It is not on npm yet (Soon). See [Agent Tools SDK](/docs/build/agent-tools-sdk). **Anonymity set.** The group of deposits a mixer cannot tell apart from yours. A bigger group gives more privacy. `GET /agents/v1/anonymity-set` reports the size for an amount bucket (Live). See [Private swaps internals](/docs/build/agent-privacy-api). **API key.** A secret string that starts with `sk_solrouter_`. Send it with a request so Solrouter knows which prepaid balance to charge. Treat it like a password. See [Get an API key](/docs/build/api-key). **Arcium.** The company whose software library Solrouter uses to encrypt prompts on your device. The chat app labels this "encrypted with Arcium". Arcium also runs a network for computing on encrypted data, which Solrouter does not use for inference today. See [RescueCipher and X25519](/docs/how-it-works/encryption). **Attestation.** A signed statement from the computer chip. It says a real Intel chip runs this sealed program, and the program owns this public key. Think of it as a tamper-evident seal. The seal proves the hardware and the key. Solrouter has not published reference values, so you cannot yet prove which program image is inside. See [What is a TEE?](/docs/how-it-works/what-is-a-tee) and [TDX attestation](/docs/how-it-works/attestation). ## B [#b] **Backend.** The ordinary Solrouter servers that receive your request, charge your balance, and forward the encrypted message to the enclave. On the encrypted path the backend holds no key and cannot read your prompt. It is a blind courier. See [What is private here](/docs/use/what-is-private). **BRAID.** Solrouter's guided reasoning feature. A normal agent asks the model what to do at each step. BRAID instead follows a fixed plan (a GRD), gathers data with tools, then calls the model once to write the answer. Request it with `reasoning: 'braid'`. Older material calls this SERV. See [Agent endpoint and guided reasoning](/docs/how-it-works/agent-reasoning). ## C [#c] **Ciphertext and plaintext.** Plaintext is text anyone can read. Ciphertext is the scrambled form that only a key holder can turn back into text. On the encrypted path your prompt leaves your device as ciphertext. See [What is private here](/docs/use/what-is-private). **Confidential VM (CVM).** A virtual computer whose memory the chip encrypts, so the owner of the physical machine cannot look inside. Solrouter's enclave is a CVM on Intel TDX hardware hosted by Phala. See [What is a TEE?](/docs/how-it-works/what-is-a-tee). ## D [#d] **DEK and KEK.** Two keys for managed swap wallets. The DEK (data encryption key) locks one wallet's secret. The KEK (key encryption key) is a wrapping key the backend holds, and it locks the DEK. Both are handled in the backend process, not the enclave. See [Private swaps internals](/docs/build/agent-privacy-api). **Discovery documents.** Public files a program can fetch to learn what Solrouter offers and what each call costs. They include the A2A agent card, the x402 manifest, an OpenAPI file (a machine-readable list of endpoints), and `/agents/v1/capabilities`. All are Live. See [Discovery documents](/docs/api-reference/overview). ## E [#e] **ed25519 signature.** A digital signature scheme. The enclave creates an ed25519 signing key at boot and signs the encryption proof for each private reply. The signature lets anyone check that the enclave, not the backend, produced the receipt. See [On-chain encryption proof](/docs/how-it-works/proof). **Enclave.** The sealed program that decrypts your prompt. In these docs "enclave" and "Confidential VM" mean the same running service. Picture a locked room with one mail slot: encrypted letters in, encrypted replies out. The analogy breaks here: the enclave sends your decrypted prompt to a GPU computer outside the room to run the model. See [What is a TEE?](/docs/how-it-works/what-is-a-tee). **Encryption proof.** A receipt for one private reply, written to the Solana blockchain. The enclave signs a summary of your encrypted prompt, and Solrouter stores it in a compressed account. Anyone with the lock link can check it. It proves the enclave handled that exact ciphertext, not what the model said. See [Check a reply yourself](/docs/verify). ## F [#f] **Facilitator (x402).** The third-party service that checks and settles a pay-per-call payment. In production the manifest names Coinbase's facilitator. See [x402 payments](/docs/build/api-key). **FDV (fully diluted valuation).** The value of every token that will ever exist, at today's price. Solrouter's fundraising sells tokens in steps tied to FDV bands. See [$ROUTER token](/docs/token). **FHE, MPC, and ZK.** Three families of maths for working with data while it stays encrypted. Solrouter does not use any of them to run the model today. It chose the cipher so a future move in that direction would not change the client side. See [RescueCipher and X25519](/docs/how-it-works/encryption). ## G [#g] **GRD (Guided Reasoning Diagram).** A fixed plan for one kind of question. It tells BRAID which tools to run and in what order. Six exist: comparison, DeFi analysis, general research, market overview, token research, and wallet analysis. See [Agent endpoint and guided reasoning](/docs/how-it-works/agent-reasoning). **Guest mode.** Using the chat app without a wallet. Guests get 5 free messages per day per network address. See [Chat app](/docs/use/chat-app). ## I [#i] **Intel DCAP.** Intel's free software for checking that a TDX quote came from real Intel hardware. A security researcher can run it against the quote Solrouter returns. See [Check a reply yourself](/docs/verify). **Intel TDX.** The Intel chip feature that creates Confidential VMs and signs attestation quotes. TDX stands for Trust Domain Extensions. Solrouter's enclave reports its type as `INTEL-TDX-PHALA`. See [What is a TEE?](/docs/how-it-works/what-is-a-tee). ## J [#j] **Jupiter.** A Solana service that finds the best price across many exchanges for a token swap. The private swap worker uses Jupiter for the swap step (Soon). See [Private swaps internals](/docs/build/agent-privacy-api). ## L [#l] **Lamports.** The smallest unit of SOL, Solana's native coin. One SOL is one billion lamports. API amounts use these base units, so `10000000` means 0.01 SOL. See [Agent Privacy API](/docs/build/agent-privacy-api). **Light Protocol compressed account.** A cheap record on the Solana blockchain. Solrouter stores each encryption proof in one. Older material called this record a "PDA"; the current record is a compressed account. Its address comes from the hash of your ciphertext. See [On-chain encryption proof](/docs/how-it-works/proof). **Liquidity pool.** A shared pot of two tokens on an exchange that lets people trade one for the other at any time. Part of the $ROUTER supply is placed in one at launch. See [$ROUTER token](/docs/token). ## M [#m] **Managed wallet (Mode A).** A swap mode where Solrouter creates and holds a wallet for your agent. You fund it once and run swaps from it. Solrouter holds the key, so this is custody, not self-custody. Swap execution is Soon. See [Agent Privacy API](/docs/build/agent-privacy-api). **Maximum Privacy Mode.** A chat app setting. When on, your messages are encrypted on your device and never stored. Refresh the page and the conversation is gone. It is off by default. See [Chat app](/docs/use/chat-app). **MCP (Model Context Protocol).** A standard that lets desktop AI apps such as Claude Desktop or Cursor call outside tools. Solrouter's MCP server adds its tools to those apps (Live). Only some of those tools use the encrypted path. See [MCP server](/docs/build/mcp-server). **Memory (wallet-encrypted).** A chat app feature that remembers facts across conversations. The facts are encrypted with a key made from your wallet's signature, so only your wallet can unlock them. The backend stores only the sealed form. See [Chat app](/docs/use/chat-app). **Mint address.** The unique on-chain address that identifies one token type on Solana, such as USDC or $ROUTER. Swap requests name tokens by mint address. See [Agent Privacy API](/docs/build/agent-privacy-api). **Mixer.** A shared on-chain pool that breaks the link between the wallet that puts money in and the one that takes it out. Picture people dropping same-size envelopes into one box, then each taking one out. The analogy breaks here: using the box is public, and amounts at the edges of the pool are visible. Solrouter uses the Umbra mixer (Soon). See [Private swaps internals](/docs/build/agent-privacy-api). ## N [#n] **Nonce.** A random number used once per encrypted message, so two identical prompts never make the same ciphertext. The nonce is stored in the encryption proof. See [RescueCipher and X25519](/docs/how-it-works/encryption). **Nosana GPU node.** A rented computer with a graphics card on the Nosana network. It runs the AI model. The enclave sends it your decrypted prompt over an encrypted connection. The model runs outside the enclave, so the node operator could read the prompt at that moment. Solrouter does not control that hardware, and the request is not tied to your identity there. See [What is private here](/docs/use/what-is-private). **Nosana job.** One running task on the Nosana network. Solrouter runs each model as its own job, so each model has its own node and address. After idle time a node can answer "Nosana GPU node is warming up"; wait and retry. See [Models and Nosana nodes](/docs/how-it-works/models). ## O [#o] **Ollama.** Free software that runs open-weight models and answers requests in the common OpenAI format. Each Nosana node runs Ollama to serve its model. See [Models and Nosana nodes](/docs/how-it-works/models). **One-shot swap (Mode B).** A swap mode where your agent keeps its own wallet. Solrouter returns an unsigned funding transaction, your agent signs it, and a worker does the rest. Swap execution is Soon. See [Agent Privacy API](/docs/build/agent-privacy-api). **Open-weight model.** An AI model whose files are public, so anyone can download and run it on their own hardware. This is what makes private hosting possible. Solrouter runs `gpt-oss:20b` (Live), `qwen3.8:27b` (Live), and `gemma4:31b` (Soon). See [Models and Nosana nodes](/docs/how-it-works/models). ## P [#p] **Persistent Privacy Mode.** The chat app default. Messages are encrypted for transport, then saved so your history survives a reload. Saved history is encrypted at rest with a key the backend holds. That protects against a stolen database copy, but does not hide history from Solrouter. See [Chat app](/docs/use/chat-app). **Phala dStack.** The hosting platform that runs Solrouter's Confidential VM on Intel TDX hardware. It also provides the small service (tappd) that hands out attestation quotes. See [TDX attestation](/docs/how-it-works/attestation). **Plaintext mode.** Sending a prompt with `encrypted: false` in the SDK. The prompt travels unencrypted through the backend to the same models. You give up every privacy guarantee, and it unlocks no other model. See [Privacy SDK](/docs/build/privacy-sdk). **Prepaid balance.** Money you add to your Solrouter account before use, in USDC or $ROUTER. Each call deducts from it. Adding money is called a top-up. See [Pricing and balance](/docs/use/pricing). ## Q [#q] **Quote (TDX quote).** The signed attestation document produced by the Intel chip through Phala's dStack service. Its `report_data` field pins the enclave's public key. `GET /tee/attestation` returns one (Live). A reply's quote is `null` with a `tdxQuoteError` when the enclave cannot reach dStack. See [TDX attestation](/docs/how-it-works/attestation). ## R [#r] **RAG (retrieval-augmented generation).** Asking questions over your own uploaded documents. The chat app splits documents into pieces and finds the relevant pieces before the model answers. Those pieces are stored unencrypted on the backend. See [Chat app](/docs/use/chat-app) and [Data at rest](/docs/use/what-is-private). **report\_data.** A 64-byte field inside a TDX quote that the enclave fills before the chip signs it. Solrouter puts a hash of its public key there. Two formulas exist: `GET /tee/attestation` pins the X25519 key alone, and per-reply quotes pin the X25519 key with the ed25519 signing key. See [TDX attestation](/docs/how-it-works/attestation). **RescueCipher.** The cipher (scrambling method) from Arcium that Solrouter uses to encrypt your prompt on your device. It works on numbers in a mathematical field instead of raw bytes, so the SDK packs 31 bytes into each number. See [RescueCipher and X25519](/docs/how-it-works/encryption). **$ROUTER.** Solrouter's own token on Solana. You can pay for calls with it instead of USDC. Solrouter can buy it back and burn it with revenue; the configured ratios are not published. See [$ROUTER token](/docs/token). ## S [#s] **SERV.** The older name for the guided reasoning feature now called BRAID. It is not the "OpenServ" line in the token allocation table, which names a token drop to that community. See [Agent endpoint and guided reasoning](/docs/how-it-works/agent-reasoning). **Skill graph.** A set of 44 linked notes with expert knowledge on Solana, DeFi, and research method. When your question matches a note's trigger words, the `/agent` endpoint adds that note to the model's instructions. It runs on the plaintext path only. See [Agent endpoint and guided reasoning](/docs/how-it-works/agent-reasoning). **Solana wallet.** An app that holds your Solana keys and signs actions for you. Solrouter uses your wallet as your login, with no email and no identity check. Phantom, Solflare, or a Privy embedded wallet all work. See [Get an API key](/docs/build/api-key). ## T [#t] **tappd.** The small program inside a Phala dStack CVM that asks the Intel chip for a quote. Solrouter's enclave talks to it over a local socket. When the socket is missing, quote requests fail with `tdx_quote_unavailable`. See [TDX attestation](/docs/how-it-works/attestation). **TEE (Trusted Execution Environment).** A sealed area of a computer where code and data are hidden from the machine's owner. Solrouter's TEE is an Intel TDX Confidential VM. Picture a sealed room: the landlord owns the building but cannot see inside. The analogy breaks here: the model runs on a separate GPU computer outside the room. See [What is a TEE?](/docs/how-it-works/what-is-a-tee). **TGE (token generation event).** The moment a token first goes live and can be traded. Vesting schedules count from this date. See [$ROUTER token](/docs/token). ## U [#u] **Umbra.** The Solana privacy protocol whose mixer Solrouter uses for private swaps (Soon). The MCP tools that start with `umbra_` move real funds. See [Private swaps internals](/docs/build/agent-privacy-api). **USDC.** A digital dollar on Solana, meant to stay worth one US dollar. Solrouter prices calls in USDC and accepts it for top-ups and pay-per-call payments. See [Pricing and balance](/docs/use/pricing). ## V [#v] **Vesting (cliff and linear).** Rules for when locked tokens become spendable. A cliff is a waiting period with no release. Linear vesting then releases an equal amount at each step. See [$ROUTER token](/docs/token). ## W [#w] **Wallet address.** The public name of a wallet, a long string of letters and numbers. You can share it to receive funds. It reveals nothing secret, but everything sent to it is visible on the public ledger. See [Get an API key](/docs/build/api-key). ## X [#x] **X25519.** A method for two parties to agree on a shared secret key without ever sending it. Your device makes a fresh, single-use keypair per session and combines it with the enclave's public key. The enclave's key is made at boot and changes on every restart. It is also called the sealing key. See [RescueCipher and X25519](/docs/how-it-works/encryption). **x402.** A way to pay for one web request at the moment you make it, using HTTP status code 402 ("payment required"). The server answers with a price, your agent pays in USDC, and the request goes through. No account or API key is needed. Encrypted x402 inference costs 0.005 USDC per call (Live). See [x402 payments](/docs/build/api-key). # $ROUTER Token (/docs/token) The token and its supply schedule are Live. Buyback and burn is Soon: the configured ratios are not published. Most utility tokens bury their value behind staking lockups, emissions schedules, and governance you have to opt into. `$ROUTER` does the opposite: it is a way to pay for what you use, and nothing more. `$ROUTER` is Solrouter's utility token on Solana. You spend it on API calls across every Solrouter product (the Privacy SDK, Agent Privacy API, MCP server, and chat app). There are no emissions, no staking rewards, and no governance complexity. One mechanic drives the whole design: protocol revenue can buy back and burn `$ROUTER`, so supply tightens as usage grows. ## Token details [#token-details] The core facts you need to find, verify, and trade the token. | Property | Value | | ------------------ | -------------------------------------------------------------------------------------------------- | | Ticker | `$ROUTER` | | Contract | `6SjVTj1VGwFSXn7wEjwFm77LvACeTqB7sQUebYKX8Ds5` | | Network | Solana | | Total supply | 1,000,000,000 | | Circulating at TGE | 53.04% | | Buy on Jupiter | [jup.ag/swap/SOL-6SjVTj1...](https://jup.ag/swap/SOL-6SjVTj1VGwFSXn7wEjwFm77LvACeTqB7sQUebYKX8Ds5) | ## Token distribution [#token-distribution] Here is where the 1B supply goes, and how quickly each slice becomes spendable. | Allocation | Amount | % | Vesting | | ----------------------- | ----------- | ----- | ------------------------------------------ | | Liquidity Pool | 450,000,000 | 45% | Unlocked at launch | | Treasury | 245,000,000 | 24.5% | 9.5% at TGE · 24-month linear thereafter | | Team | 200,000,000 | 20% | 3-month cliff · 12-month linear | | OpenServ (SERV drop) | 50,000,000 | 5% | Unlocked at TGE | | Algorithmic Fundraising | 50,000,000 | 5% | 14 FDV bands · unlocks as each band clears | | Superteam Germany | 5,000,000 | 0.5% | Verified members · TGE unlock | ## Circulating supply schedule [#circulating-supply-schedule] Locked supply cannot be sold, so this is how the float (and the potential sell pressure) grows over time. Tokens enter circulation over 24 months. The Liquidity Pool, OpenServ, and Superteam Germany allocations unlock at TGE (Token Generation Event, the moment the token first goes live). Treasury releases 9.5% at TGE and vests the rest linearly over 24 months. The Team allocation has a 3-month cliff (zero unlocks until month three), then vests linearly over the following 12 months. Algorithmic Fundraising unlocks per FDV band as each band clears, so the schedule below is indicative. The real cadence depends on demand. | Milestone | Circulating % | Circulating Tokens | | --------- | ------------- | ------------------ | | TGE | 53.04% | 530,400,000 | | 6 months | 66.50% | 665,000,000 | | 12 months | 83.29% | 832,900,000 | | 24 months | 100% | 1,000,000,000 | ## Algorithmic fundraising [#algorithmic-fundraising] Instead of selling the raise allocation all at once, Solrouter releases it in steps tied to the token's own valuation. Capital comes in only as the market values the protocol higher. 5% of total supply (50,000,000 `$ROUTER`) sells across 14 FDV (Fully Diluted Valuation, the value of the entire 1B supply at the current price) bands ranging from $500K to $100M. Each band unlocks only after the previous band's valuation threshold clears, raising capital progressively as demand grows. Total estimated capital across all 14 bands is roughly $807,750, calculated using each band's midpoint valuation. | Band | Valuation (USD) | % of Supply | Capital Raised | Cumulative | | --------- | --------------- | ----------- | -------------- | ------------ | | 1 | $500K to $750K | 0.30% | $1,875 | $1,875 | | 2 | $750K to $1M | 0.30% | $2,625 | $4,500 | | 3 | $1M to $1.5M | 0.35% | $4,375 | $8,875 | | 4 | $1.5M to $2M | 0.35% | $6,125 | $15,000 | | 5 | $2M to $3M | 0.40% | $10,000 | $25,000 | | 6 | $3M to $5M | 0.40% | $16,000 | $41,000 | | 7 | $5M to $8M | 0.45% | $29,250 | $70,250 | | 8 | $8M to $12M | 0.45% | $45,000 | $115,250 | | 9 | $12M to $18M | 0.50% | $75,000 | $190,250 | | 10 | $18M to $25M | 0.50% | $107,500 | $297,750 | | 11 | $25M to $40M | 0.40% | $130,000 | $427,750 | | 12 | $40M to $60M | 0.30% | $150,000 | $577,750 | | 13 | $60M to $80M | 0.20% | $140,000 | $717,750 | | 14 | $80M to $100M | 0.10% | $90,000 | $807,750 | | **Total** | | **5.00%** | **$807,750** | **$807,750** | *Capital estimated using each band's midpoint valuation.* ## Launch protection [#launch-protection] Open token launches are a magnet for sniping bots that grab huge chunks of supply in the first seconds. Solrouter caps how much any single transfer or wallet can buy during the opening window to keep that supply spread across real buyers. For the first 130 seconds after the liquidity pool opens, per-transfer and per-wallet buy caps apply. The limits lift automatically once that window passes. | Time After Open | Max Per Transfer | Max Per Wallet (Buys) | | ----------------- | ---------------- | --------------------- | | 0 to 70 seconds | 100,000 (0.01%) | 1,000,000 (0.1%) | | 70 to 130 seconds | 1,000,000 (0.1%) | 5,000,000 (0.5%) | | After 130 seconds | No limit | No limit | ## Buyback and burn [#buyback-and-burn] Status: Soon. This is the engine that ties token value to real usage: the more people pay Solrouter, the more `$ROUTER` can leave circulation. The mechanism exists in code. The ratios are runtime settings (`BURN_USDC_BPS`, `BURN_OUTPUT_TOKEN_BPS`, `BURN_TOKEN_BPS`), and the worker skips its run while they are zero. Status: Soon. A configured share of USDC revenue buys back `$ROUTER` on the open market through Jupiter. Status: Soon. A configured share of each buyback is burned. The rest stays in treasury. Status: Soon. A configured share of `$ROUTER` fees paid directly is burned on receipt. The rest stays in treasury. Configured ratios: not published. `GET /payments/buyback/log` returns the recent buyback worker ticks, including skipped ones. The model stays simple on purpose. No emissions mint new tokens, no staking curves add complexity, and no tiered discount programs fragment the tokenomics. To pay for API calls with `$ROUTER`, see [Pricing](/docs/use/pricing). # Use cases (/docs/use-cases) Each card carries its own Status word. The Live cards for privacy modes, memory, guest mode, and team accounts depend on the owner's browser check in PR 5. Every card below starts with one Status word. We checked each against the code on 2026-08-26. **Live** works today. **Soon** exists in code but is not switched on or not confirmed end to end. **Archived** was removed. Many cards mention a sealed machine. That is a TEE (Trusted Execution Environment). Its own operator cannot look inside while it runs. [What is a TEE?](/docs/how-it-works/what-is-a-tee) explains it with a picture. Other terms are in the [Glossary](/docs/glossary): [wallet](/docs/glossary), [x402](/docs/glossary), and [mixer](/docs/glossary). When encryption is on, Solrouter's own servers never see your words in readable form. The GPU computer that runs the AI model does see them while it writes the answer. Solrouter does not own that computer. Read [What is private here](/docs/use/what-is-private). ## For people [#for-people] These are for anyone who uses the chat app at solrouter.com. Encryption in the chat app is a switch called Maximum Privacy mode. It is off when you first open the app. While it is on, the app keeps no history. **Live.** You want a summary of a contract, but you do not want an AI company to keep a copy. Turn on Maximum Privacy mode and paste the text, because an attached file is not encrypted. **Live.** You want feedback on code that is not public. Turn on Maximum Privacy mode and paste the code, and Solrouter's servers never see it in readable form. **Live.** You want help with a sensitive message, such as a letter to a doctor or a lawyer. Turn on Maximum Privacy mode, write the draft, and close the tab, because nothing is saved. **Live.** Some chats should survive a reload and others should leave no trace. Persistent mode saves history encrypted with a key that Solrouter holds, and Maximum Privacy mode saves nothing at all. **Live.** You want the assistant to remember facts across chats without Solrouter holding a readable profile of you. Your wallet signs a fixed message to make the key, so Solrouter stores only a locked blob. **Live.** You will not connect a wallet before you have seen the product work. Open the guest page and send up to five free messages a day. **Live.** You want to ask about a PDF, a spreadsheet, or a screenshot. You can attach it, but attachments are not encrypted, and documents are stored in readable form on a file server. **Live.** A privacy promise on a web page is not proof. Click the lock link under a reply, or paste it into the checker, and see the receipt on the Solana blockchain. **Archived.** Image and video generation was switched off in the chat app, and the services that made them were removed. ## For developers [#for-developers] These are for people who write code and want to add private AI to their own app, editor, or backend. **Live.** Every mainstream AI service can read what your app sends it, so you cannot promise your users privacy. Install `@solrouter/sdk`, call `client.chat()`, and the prompt is encrypted on your machine before it leaves. **Live.** Research done through a normal assistant shows the model provider which tokens and wallets you look at. Paste one config block, and only the synthesis step runs encrypted; web searches and price lookups stay readable. **Live.** A normal agent asks the model what to do at every step, which is slow and hard to predict. Send `reasoning: 'braid'` and the agent walks a fixed diagram of steps, but on this path your prompt travels in readable form. **Live** over the REST endpoint, and Soon in the SDK. An agent that searches the web and reads prices normally shows every question to the server. Send an encrypted prompt to `POST /agent`, and the loop runs inside the sealed machine with five approved tools. **Live.** Signing up for an AI service means handing over your identity and a card. Connect a Solana wallet, make an API key, and top up a balance in USDC or $ROUTER. ## For agents [#for-agents] These are for software agents that hold a Solana wallet and act on their own, with no person to manage keys. **Live.** An agent that starts on demand has no person to sign up or hold a key. The first call gets a price back, the agent pays a small amount of USDC, and the call goes through. **Soon.** On Solana every transfer is public, so anyone can link the paying wallet to where the money went. The agent signs one funding step, and Solrouter routes the swap through a mixer so the two ends are not linked. **Soon.** A long-running agent should not sign a fresh funding step for every trade. Solrouter creates a wallet for the agent, keeps its key locked on the server, and runs each swap on request. **Live.** Not every language has an SDK, and some agents find services on their own. Call the OpenAI-style endpoint directly, and read the discovery documents that describe every route and price. ## For traders [#for-traders] These are for people who trade on Solana and want their intent and their wallet links kept out of provider logs. **Soon.** A trader wants to swap without the public ledger linking their main wallet to the destination. Type the swap in chat, and a widget walks you through a mixer step and the swap with your own wallet signing. **Soon.** Phoenix Copilot: describe a trading strategy in plain English and test it against past prices inside the sealed machine. **Soon.** RouterChan: trade from a Telegram app with a managed wallet and hard spending limits. ## For teams [#for-teams] These are for small companies that want private AI over their own documents with shared billing. **Live.** You want answers grounded in your contracts, specs, or research instead of the model's general knowledge. Upload files to a knowledge base, but know that the stored pieces are not encrypted at rest. **Live.** A company cannot run private AI on one person's wallet. Create an organization, invite members by link, and see usage per member on one shared balance. # Verify an encryption proof (/docs/verify) ## The question [#the-question] "Solrouter says my message was handled inside a sealed computer. How do I know that is true?" You do not have to trust the claim. Every private reply comes with a receipt. The receipt lives on the Solana blockchain, and your browser can check it. This page has two checks. The first takes ten seconds. The second takes one click. ## Check 1: the receipt for one reply [#check-1-the-receipt-for-one-reply] When you send a message with Maximum Privacy Mode on, or through the Privacy SDK, the sealed computer (the enclave) signs a short receipt. The receipt says: "I received this exact scrambled message." Solrouter then writes the receipt to Solana. Solrouter cannot write a valid receipt on its own, because the signing key exists only inside the enclave. In the chat app, every private reply shows a lock icon. Click it and copy the link. Paste the link below. You can also paste the receipt address, the transaction signature, or the 64-character hash of your scrambled prompt. What a passing check means: * A real Intel TDX enclave received your exact scrambled message and signed for it. * The receipt is on Solana, so nobody can quietly edit it later. * The signing key is named inside the enclave's hardware note (the attestation), so an impostor cannot sign in its place. What it does not mean: * It does not prove that the Solana program checked the signature. The program stores the receipt fields; your browser checks the signature. * It does not verify Intel's full signature chain on the hardware note. Check 3 below covers that. ## Check 2: the live enclave key [#check-2-the-live-enclave-key] Your device encrypts every private message to the enclave's public key. This check confirms that the published key is the one named in the live hardware note. **In words** * Your browser fetches the published public key and the live hardware note from `api.solrouter.com`. * It computes the fingerprint (SHA-256) of the key. * It compares that fingerprint with the `report_data` field inside the note. * A match means: the key you encrypt to belongs to this exact enclave, not to a machine in the middle. The enclave makes a fresh key pair each time it starts. The private half never leaves the enclave. If you run this check on two days and see two keys, that is expected. ## Check 3: the deep checks [#check-3-the-deep-checks] These steps are for auditors and engineers. They use the same two endpoints plus Intel's tools. ### Fetch the note and the key [#fetch-the-note-and-the-key] ```bash curl https://api.solrouter.com/tee/public-key curl https://api.solrouter.com/tee/attestation ``` The first call returns `publicKey`, `publicKeySha256`, `algorithm`, and `teeType`. The second returns `teePublicKey`, `teePublicKeySha256`, `reportDataHex`, `tdxQuote`, and `generatedAt`. Both answered on 2026-08-26 with `teeType: "INTEL-TDX-PHALA"`. ### Verify the quote signature [#verify-the-quote-signature] Run the `tdxQuote.quote` bytes through Intel DCAP tools or a compatible TDX verifier. This proves the note came from real Intel TDX hardware, not from a simulator. ### Check the report\_data binding [#check-the-report_data-binding] For `GET /tee/attestation`, `report_data` equals `sha256(X25519 public key)`. For the per-request note inside a `/tee/process` response, `report_data` equals `sha256(X25519 public key || ed25519 signing key)`. The two formulas are different on purpose: the second one also pins the key that signs receipts. ### Verify one receipt by hand [#verify-one-receipt-by-hand] Read the receipt with `GET /attestation/by-tx/`. Rebuild the signed bytes: `"SOLR-ATTEST-v2"`, the SHA-256 of the scrambled prompt, the enclave sealing key, the nonce, your ephemeral key, then the model and provider names with one length byte each. Verify the 64-byte signature (`enclaveSigR` plus `enclaveSigS`) against `enclavePubkey` with any Ed25519 library. ### Compare the program measurement (Soon) [#compare-the-program-measurement-soon] A full audit compares the measurements inside the note with published reference values for Solrouter's enclave image. Solrouter does not publish those values yet. Until it does, these checks prove real hardware and correct key binding, but not which image is running. ## Read more [#read-more] # POST /agent (/docs/api-reference/agent) The `/agent` endpoint runs your prompt through Solrouter's agent pipeline. Unlike a direct chat completion, the agent can call built-in tools: web search, on-chain data, DEX quotes, token prices, and more. The request body selects one of three paths. * Default: a standard tool loop with up to 8 model calls (`MAX_ITERATIONS = 8`). * `reasoning: 'braid'`: guided reasoning (BRAID). A fixed Guided Reasoning Diagram (GRD) collects data, then one synthesis call writes the reply. Older material calls this SERV. * `encryptedPrompt`: encrypted mode. The tool loop runs inside the CVM (a confidential virtual machine, which is a TEE, trusted execution environment) with a 5-tool allowlist. ## Endpoint [#endpoint] ``` POST https://api.solrouter.com/agent ``` ## Request [#request] | Field | Type | Description | | ----------------- | ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `prompt` | string (required) | The question or task. Research, comparisons, on-chain analysis, swap quotes, or any task the built-in tools cover. | | `model` | string | The model that runs the loop and writes the reply. Models run on Nosana GPU nodes. `gpt-oss:20b` (default) is Live. `qwen3.8:27b` is Live. `gemma4:31b` is Soon. `qwen3:8b` is retired (Archived). A model with no configured Nosana endpoint returns 501 `nosana_not_configured`. | | `useTools` | boolean | Defaults to `true`. When `true`, the model can call any built-in tool before it writes the reply. Set `false` to run one plain completion with no tools. This field does not select guided reasoning. | | `chatId` | string | Optional conversation id. When present, the backend stores the turn in that chat's history and sends prior turns as context. When absent, the call is stateless. | | `reasoning` | string | Set `'braid'` to run guided reasoning instead of the tool loop. | | `braidOptions` | object | BRAID only. `includeTrace: true` adds `braidTrace` to the response. `forceGrdId` picks a GRD by id instead of intent detection. | | `encryptedPrompt` | string (JSON) | Encrypted mode. The prompt is encrypted client-side to the enclave public key and packaged as a JSON string with `ciphertext`, `nonce`, `publicKey`, `algorithm`, and `version`. The backend forwards it to the CVM without reading it. Status: Live for REST callers who send `encryptedPrompt`. Soon for the SDK. Not used by the chat app, whose agent mode runs the plaintext tool loop. | ## Example request [#example-request] ```bash curl -X POST "https://api.solrouter.com/agent" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "prompt": "Compare Marginfi vs Kamino lending on Solana", "model": "gpt-oss:20b", "useTools": true }' ``` ## Example response (default path) [#example-response-default-path] ```json { "success": true, "reply": "## Marginfi vs Kamino Lending Comparison\n\n...", "toolCalls": [ { "tool": "web_search", "args": { "query": "Marginfi vs Kamino lending Solana" }, "result": { "...": "..." } }, { "tool": "token_price", "args": { "token": "MNDE" }, "result": { "...": "..." } } ], "usage": { "promptTokens": 0, "completionTokens": 0, "totalTokens": 0 }, "iterations": 4, "model": "gpt-oss:20b", "provider": "nosana", "billing": null, "freeMessagesRemaining": 0 } ``` ## Response fields (default path) [#response-fields-default-path] | Field | Type | Description | | ----------------------- | -------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `success` | boolean | `true` when the request completed. A caller with no free messages and no USDC balance gets HTTP 200 with `success: false`, `requiresDeposit: true`, and `reason: "free_trial_exhausted"`. | | `reply` | string | The final reply in Markdown. | | `toolCalls` | array | Every tool the agent called, in order. Each entry has `tool`, `args`, and `result`. | | `usage` | object | `promptTokens`, `completionTokens`, and `totalTokens`, summed over every model call in the loop. | | `iterations` | number | The number of model calls the loop made. At most 8. | | `model` | string | The model id that ran. | | `provider` | string | Always `"nosana"`. | | `billing` | object or null | The result of the billing step. `null` when billing failed or did not run. | | `freeMessagesRemaining` | number | Free messages left on the account after this call. | The response has no `skillGraph` field. The skill graph shapes the system prompt only. ## Example response (BRAID path) [#example-response-braid-path] ```json { "success": true, "reply": "## Marginfi vs Kamino Lending Comparison\n\n...", "reasoning": "braid", "braidTrace": { "grdId": "comparison", "intent": "comparison", "nodes": [ { "nodeId": "...", "label": "...", "type": "action", "status": "completed" } ], "totalDurationMs": 0, "totalTokens": 0 }, "usage": { "promptTokens": 0, "completionTokens": 0, "totalTokens": 0 }, "iterations": 1, "model": "gpt-oss:20b", "provider": "nosana", "cost": "FREE", "freeMessagesRemaining": 0 } ``` `braidTrace` is present only when `braidOptions.includeTrace` is `true`. On this path `iterations` counts GRD nodes, not model calls. If BRAID fails, the route falls back to the default tool loop. ## Example response (encrypted mode) [#example-response-encrypted-mode] ```json { "success": true, "encrypted": true, "encryptedResponse": { "...": "..." }, "toolCallsCount": 2, "attestation": { "...": "..." }, "privacyGuarantee": { "backendSawPlaintext": false, "toolsExecutedInTEE": true }, "freeMessagesRemaining": 0 } ``` The reply is encrypted to the `publicKey` inside `encryptedPrompt`. Any enclave error returns HTTP 500. The route does not fall back to a plaintext path. ## Available tools [#available-tools] The default path registers 18 tools. They are listed on the [Agent Framework](/docs/how-it-works/agent-reasoning) page: `web_search`, `scrape_url`, `crawl_url`, `solana_balance`, `token_price`, `swap_quote`, `trending_tokens`, `deepwiki`, `colosseum_search`, `colosseum_archives`, `paysh_search_apis`, `paysh_call_api`, `github_list_repos`, `github_issues`, `github_read_file`, `notion_search`, `notion_get_page`, and `notion_query_database`. The model picks the tools at each step of the loop. Encrypted mode allows 5 tools inside the CVM: `web_search` (SearXNG inside the CVM), `token_price`, `trending_tokens`, `swap_quote`, and `solana_balance`. With `@solrouter/sdk` 1.1.0, `client.chat(prompt, { reasoning: 'braid' })` sends the request to `/agent`. The SDK sends the prompt in plaintext on this path and returns `encrypted: false`. The SDK does not send `encryptedPrompt` to `/agent`. Encrypted agent mode is Live over REST and Soon in the SDK. # Errors (/docs/api-reference/errors) Every endpoint returns standard HTTP status codes. The common ones are below. Endpoint-specific errors are listed on each endpoint's reference page. | Status | Meaning | Fix | | --------------------------- | ------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------- | | `400` Bad Request | A required field is missing. The x402 and `/api/v1/chat/completions` routes require `encryptedPrompt` and `model`. | Send every required field. | | `401` Unauthorized | The `Authorization` header is missing, or the key does not begin with `sk_solrouter_`. | Send `Authorization: Bearer sk_solrouter_...`. | | `402` Payment Required | Your prepaid balance is empty, or the route is x402-paywalled and no payment was attached. | Top up at [solrouter.com/sdk](https://solrouter.com/sdk), or pay per call with x402 and retry. | | `403` Forbidden | Which endpoints return 403, and when, is not determined. The API-key check itself returns 401, not 403. | Not applicable. | | `502` TEE unreachable | The backend could not reach the enclave. | Retry. If it persists, the enclave is down. | | `503` TDX quote unavailable | The enclave is up but could not produce an attestation quote. | Retry. The reply path still works; only the quote is missing. | Never expose your API key in client-side code or public repositories. Anyone with your key can spend your prepaid balance. If a key leaks, rotate it at [solrouter.com/sdk](https://solrouter.com/sdk). # Overview (/docs/api-reference/overview) Chat, agent, and TEE endpoints are Live. Agent Privacy API swap execution is Soon. The Solrouter REST API is the direct HTTP interface to Solrouter's private AI infrastructure. With it you can send encrypted chat completions and run agent reasoning with tool calls. You can also read the TEE (trusted execution environment) key and attestation quote. Private on-chain swaps use the same base URL. All of it sits on one authenticated surface with no email, credit card, or KYC. Raw REST is plaintext unless you send `encryptedPrompt`. When you use `@solrouter/sdk`, chat requests are encrypted before they leave your machine, and the Solrouter backend relays the ciphertext to the TEE without reading it. If you call `POST /agent` with `curl` and a plain `prompt`, you are sending plaintext to the backend. `POST /tee/process` and `POST /api/v1/chat/completions` reject requests without `encryptedPrompt`. ## Base URL [#base-url] Every API request is made against the following base URL: ``` https://api.solrouter.com ``` ## Authentication [#authentication] All requests require a Bearer token in the `Authorization` header. You generate your key at [solrouter.com/sdk](https://solrouter.com/sdk) by connecting a Solana wallet. No email or credit card is required. Balance is prepaid in USDC or `$ROUTER`. ``` Authorization: Bearer sk_solrouter_... ``` For agents that do not hold an API key, Solrouter also supports **x402 per-call USDC payment** on Solana mainnet. The live manifest advertises Coinbase (`api.cdp.coinbase.com/x402`) as the facilitator. The manifest does not show which facilitator settles payments. See the [Authentication](/docs/build/api-key) page for full details on both methods. ## Endpoints [#endpoints] Status: Live. **POST /api/v1/chat/completions** takes an API key and requires `encryptedPrompt` and `model`; it returns 400 `bad_request` without them. `GET /api/v1/models`, `/api/v1/balance`, and `/api/v1/usage` sit beside it. `@solrouter/sdk` calls **POST /tee/process** and encrypts client-side. Keyless agents pay per call on `POST /api/v1/x402/chat/completions`. Status: Live. **POST /agent** runs your prompt through the tool-calling agent, with built-in tools for web search, on-chain data, DEX quotes, and Solana wallet inspection. Send `reasoning: 'braid'` for guided reasoning. Status: Live. **GET /tee/public-key** returns the enclave's live X25519 public key. **GET /tee/attestation** returns the TDX quote when the CVM can reach the dStack agent; otherwise `tdxQuote` is null and `tdxQuoteError` says why. See the [attestation guide](/docs/how-it-works/attestation) for the verification flow. **/agents/v1/**\* is the agent-first surface for private on-chain swaps, managed wallets, and x402-paywalled encrypted inference. Discovery, quote, and anonymity-set reads are Live. Swap execution is Soon. Full OpenAPI spec at `/agents/v1/openapi.json`. ## OpenAPI Spec and Discovery [#openapi-spec-and-discovery] The full OpenAPI 3.1 specification for the Agent Privacy API is available at: ``` GET /agents/v1/openapi.json ``` You can import this spec directly into tools like Postman, Insomnia, or any OpenAPI-compatible client to explore all `/agents/v1/*` endpoints with type-safe request and response schemas. Two well-known discovery documents are also published for agent and payment interoperability. Both are served by the API host, `https://api.solrouter.com`. ``` GET /.well-known/agent-card.json # A2A protocol v1.0 card with the full skill list GET /.well-known/x402 # x402 paywall manifest with per-call USDC pricing ``` ## Response Format [#response-format] All endpoints return JSON. A top-level `success` field exists on `POST /agent` and `POST /tee/process` only: * `success: true`: the request succeeded, and the other fields carry the result. * `success: false`: the request failed, and an `error` field names the failure. The TEE key and attestation reads, x402 inference, the discovery documents, and `/agents/v1` responses have no `success` field. They return the result object directly, or an `error` field with a non-2xx status. For error codes on authentication, see the [Authentication](/docs/build/api-key) page, which covers `401`, `402`, and `403` responses. # Agent Privacy API (/docs/build/agent-privacy-api) Swap execution is Soon: no mainnet swap run is on record. Discovery, quote, and anonymity-set reads are Live. When an agent moves tokens on Solana, the transaction graph shows who paid whom. Anyone can trace the funding source to the destination. The Agent Privacy API (`/agents/v1`) breaks that link. It serves two agent needs: private on-chain swaps and encrypted inference. The main Solrouter SDK covers encrypted chat and research. One agent type swaps tokens privately. The other has no API key and pays per inference call with x402, a pay-per-request HTTP standard. The Solrouter backend and a swap worker run the swaps. The TEE (trusted execution environment, a hardware-isolated enclave) does not. Only the x402 inference endpoint on this page uses the encrypted TEE path. No sanctions screening runs today. Agents are responsible for their own compliance. ## Feature status [#feature-status] | Feature | Status | | -------------------------------------------------------------------------------------------- | ------ | | Discovery documents (`/.well-known/*`, `/agents/v1/openapi.json`, `/agents/v1/capabilities`) | Live | | `GET /agents/v1/quote` and `GET /agents/v1/anonymity-set` | Live | | Mode A managed-wallet swaps | Soon | | Mode B one-shot swaps | Soon | | `POST /api/v1/x402/chat/completions` | Live | | `@solrouter/agent-tools` npm package | Soon | ## Execution modes [#execution-modes] Pick a mode by one question: does your agent keep a funded wallet with Solrouter, or sign each operation on the fly? The API supports both. Both swap modes are Soon. The code path exists, but no mainnet swap run is on record as of 2026-08-26. The samples below show the request shapes the routes accept today. Fund once and forget the setup. Your agent provisions a long-lived managed Umbra wallet through the API, funds it once, then runs as many private swaps as it needs. Use it for agents that swap often, accumulate balance, or run on a schedule. Wallet routes require an API key. **How custody works:** * Solrouter holds the wallet keypair. The per-wallet Data Encryption Key (DEK) is wrapped with a Key Encryption Key (KEK) that the backend reads from `WALLET_VAULT_KEK`. * The DEK is unwrapped inside the backend API and the swap worker for one operation, then wiped. * The wallet vault runs in the backend process, not inside a TEE. ```bash # Provision a managed wallet for this agent curl -X POST "https://api.solrouter.com/agents/v1/wallets" \ -H "Authorization: Bearer sk_solrouter_..." \ -H "Content-Type: application/json" \ -d '{}' # -> { "walletId": "...", "umbraAddress": "...", "network": "mainnet", "fundingHint": "..." } # Fund umbraAddress, then run a swap from the managed wallet curl -X POST "https://api.solrouter.com/agents/v1/wallets/WALLET_ID/swap" \ -H "Authorization: Bearer sk_solrouter_..." \ -H "Content-Type: application/json" \ -d '{ "fromMint": "So11111111111111111111111111111111111111112", "toMint": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", "amount": "10000000", "destinationPubkey": "YOUR_FRESH_DESTINATION_ADDRESS" }' # -> { "sessionId": "...", "status": "running", "estimatedSeconds": 70 } ``` Use this mode when your agent has its own wallet and holds no balance with Solrouter. Nothing is provisioned. Your agent gets an unsigned funding transaction, signs it, submits the signature, and the worker handles the rest. Good for stateless agents and single-operation workflows. **The 7-step pipeline:** 1. Agent requests a one-shot session with payer pubkey, mints, amount, and destination 2. API returns an unsigned funding transaction 3. Agent signs the transaction with its own wallet and broadcasts it 4. Agent submits the transaction signature to the API to start execution 5. Worker runs the Umbra mixer round-trip to break the on-chain link 6. Jupiter aggregator executes the swap 7. Worker forwards the proceeds to the destination, with no on-chain link to the payer ```bash # 1. Create the session curl -X POST "https://api.solrouter.com/agents/v1/swaps/oneshot" \ -H "Content-Type: application/json" \ -d '{ "payerPubkey": "YOUR_AGENT_WALLET_PUBKEY", "fromMint": "So11111111111111111111111111111111111111112", "toMint": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", "amount": "10000000", "destinationPubkey": "FRESH_DESTINATION_ADDRESS" }' # -> { "sessionId": "...", "ephemeralPubkey": "...", "fundingTx": "", "expectedSeconds": 75, ... } # 2. Sign fundingTx with your wallet and broadcast it. # 3. Submit the confirmed signature: curl -X POST "https://api.solrouter.com/agents/v1/swaps/oneshot/SESSION_ID/execute" \ -H "Content-Type: application/json" \ -d '{"fundingTxSig": "..."}' # 4. Poll until state is settled curl "https://api.solrouter.com/agents/v1/sessions/SESSION_ID" ``` **What Solrouter stores per session:** `from_mint`, `to_mint`, `amount_base_units`, `destination_pubkey`, `payer_user_id`, `ephemeral_pubkey`, the wrapped ephemeral key, `final_tx_sig`, and `actual_out`. Solrouter's backend can read every column. Retention period: not published. ## Discovery endpoints [#discovery-endpoints] So your agent does not hardcode URLs, the API publishes its own configuration. A2A-compatible agents (the Agent-to-Agent interop protocol) and x402-aware runtimes read these endpoints to self-configure at runtime. | Endpoint | Description | Status | | ------------------------------ | --------------------------------------------------------------- | ------ | | `/.well-known/agent-card.json` | A2A protocol v1.0 card with the full skill list | Live | | `/.well-known/x402` | x402 paywall manifest: per-call USDC pricing for keyless agents | Live | | `/agents/v1/openapi.json` | Full OpenAPI 3.1 specification | Live | | `/agents/v1/capabilities` | Capability summary for runtime introspection | Live | The manifest is served by the API host: `https://api.solrouter.com/.well-known/x402`. ## x402 encrypted inference [#x402-encrypted-inference] Not every agent has an API key, and account creation is friction. For those cases Solrouter exposes a pay-per-call encrypted inference endpoint. Your agent pays in USDC on Solana mainnet, with no account and no key management. x402 is the standard. The live manifest advertises Coinbase (`api.cdp.coinbase.com/x402`) as the facilitator. The manifest shows `X402_FACILITATOR_URL` when set, or a built-in default when not. It does not show which facilitator settles payments. pay.sh is a catalog that lists the endpoint. * **Endpoint:** `POST /api/v1/x402/chat/completions` * **Pricing:** $0.005 per call, settled via x402 USDC on Solana mainnet * **Encryption:** Arcium-encrypted prompt in, encrypted response out. The same TEE path as the SDK. * **Discovery:** `/.well-known/x402`. Any x402-aware runtime can auto-discover pricing and payment instructions. ```bash # x402 paywalled encrypted inference. No API key needed. # `encryptedPrompt` MUST be an Arcium ciphertext produced client-side. # Use encrypt(message, baseUrl) and packageForTEE(encryptedData) from @solrouter/sdk. # `model` is required. curl -X POST "https://api.solrouter.com/api/v1/x402/chat/completions" \ -H "Content-Type: application/json" \ -d '{"encryptedPrompt": "", "model": "gpt-oss:20b"}' ``` Call the endpoint without payment and the server replies `402 Payment Required` with the price, the network, and the `payTo` address. An x402-aware runtime signs a USDC payment payload with the agent's wallet key and retries with the `X-PAYMENT` header. Solrouter's server sends that payload to its facilitator to verify and settle the transfer, then returns 200. The agent never talks to the facilitator. The paywall charges $0.005 per call. The response body currently reports `paid.amount: 0.02` . This is a backend follow-up; the manifest price is the one charged. This endpoint returns the encrypted reply only. It does not commit an on-chain receipt. Receipts are created for `POST /tee/process` , the route the SDK uses. See [Encryption Proof](/docs/how-it-works/proof) . # Agent Tools SDK (/docs/build/agent-tools-sdk) The @solrouter/agent-tools package is not on npm yet. `@solrouter/agent-tools` is not published on npm. The package exists in the Solrouter repository at version 1.0.0, but you cannot install it today. The code samples on this page show the planned API. To use the Agent Privacy API now, call `POST /agents/v1/*` over HTTP or use the `umbra_*` tools in the [MCP server](/docs/build/mcp-server). An AI agent that swaps tokens on Solana leaves a link from payer to destination. To break that link you normally wire up quoting, signing, mixing, and settlement yourself. `@solrouter/agent-tools` will do that work. It gives your agent typed tools for the Agent Privacy API (`/agents/v1`). The agent can quote, run, and settle private swaps, and call encrypted inference. It ships with a Vercel AI SDK adapter, so you can drop it into an agent without an orchestration layer. Not on the AI SDK? The raw `TOOLS` (JSON Schema definitions) and the `callTool()` function are also exported, so any function-calling framework works. ## What to use today [#what-to-use-today] * **HTTP.** Call the Agent Privacy API directly. Quotes and anonymity-set reads are live at `GET /agents/v1/quote` and `GET /agents/v1/anonymity-set`. Swap execution routes are Soon. * **MCP.** The [MCP server](/docs/build/mcp-server) wraps the same routes as `umbra_*` tools for Claude Desktop and Cursor. ## Vercel AI SDK quickstart (Soon) [#vercel-ai-sdk-quickstart-soon] This will be the fastest path: let an LLM decide when to swap, and let the adapter handle the plumbing. Pass `aiSdkTools(solrouter)` into `generateText` or `streamText`. The adapter wires up the tool schemas, validates the model's arguments, and returns results to the model. ```typescript import { generateText } from "ai"; import { SolrouterAgentClient } from "@solrouter/agent-tools"; import { aiSdkTools } from "@solrouter/agent-tools/ai-sdk"; const solrouter = new SolrouterAgentClient({ apiKey: process.env.SOLROUTER_API_KEY, }); const result = await generateText({ model: yourModel, // any AI SDK model provider tools: aiSdkTools(solrouter), prompt: "Privately swap 0.01 SOL to USDC and send the USDC to AAA...XXX", }); ``` ## Direct usage, no LLM (Soon) [#direct-usage-no-llm-soon] Sometimes you do not want a model in the loop. You want to drive the privacy pipeline yourself in code. Mode B one-shot swaps do that. Your agent signs the funding transaction with its own wallet. Solrouter then runs the mixer round trip, the Jupiter swap, and the forward to the destination. ```typescript import { SolrouterAgentClient } from "@solrouter/agent-tools"; const client = new SolrouterAgentClient({ apiKey: "sk_solrouter_..." }); const session = await client.swapOneshot({ payerPubkey: "...", fromMint: "So11111111111111111111111111111111111111112", // SOL toMint: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", // USDC amount: "10000000", destinationPubkey: "...", // use a fresh address }); // Sign session.fundingTx with your wallet, broadcast, then: await client.swapOneshotExecute(session.sessionId, fundingTxSig); const settled = await client.pollUntilSettled(session.sessionId); ``` ## Authentication modes [#authentication-modes] How your agent proves it can pay shapes how you deploy it, so pick the mode that fits before you build. The Agent Privacy API supports two. * **API key.** Include `Authorization: Bearer sk_solrouter_...` in every request. Usage bills against your prepaid balance in USDC or `$ROUTER`. Best when your agent is long-lived and you have already funded an account. * **x402 (keyless).** Pay per call in USDC on Solana mainnet through an x402 facilitator, with no account at all. The live manifest advertises Coinbase (`api.cdp.coinbase.com/x402`). The manifest shows `X402_FACILITATOR_URL` when it is set, or a built-in default when it is not. It does not show which facilitator settles payments. Discover pricing and payment endpoints at [`/.well-known/x402`](https://api.solrouter.com/.well-known/x402). x402 fits agents that run without a pre-registered API key, for example autonomous agents that start on demand and pay for exactly the calls they make. No balance top-up or account creation is required. The agent still holds a wallet private key to sign payments. ## Available tools [#available-tools] These are the building blocks your agent will call, through `aiSdkTools()` or the raw `TOOLS` / `callTool()` exports. Each one maps to one step of the private-swap or encrypted-inference flow. The Status column describes the API route behind the tool, not the package. | Tool | Description | Status | | ---------------------------- | --------------------------------------------------------------------------------------- | ------ | | `umbra_quote` | Quote a private swap with anonymity-set sizing | Live | | `umbra_anonymity_set` | Inspect the current anonymity set for a mint pair | Live | | `umbra_swap_oneshot` | Open a one-shot swap session. The agent signs the funding transaction | Soon | | `umbra_swap_oneshot_execute` | Submit the signed funding transaction to start execution | Soon | | `umbra_session_status` | Poll session state until `settled` | Soon | | `umbra_create_wallet` | Provision a managed wallet for an agent | Soon | | `umbra_swap_managed` | Run a swap from a managed wallet | Soon | | `umbra_encrypt` | Convert a balance to an encrypted balance on the same wallet | Soon | | `umbra_shield` | Mixer round trip, withdraw, then forward to a fresh address | Soon | | `umbra_balance` | Read the encrypted balance of a managed wallet | Soon | | `umbra_attestation` | Read the settlement record of a swap session (`GET /agents/v1/attestations/:sessionId`) | Soon | | `private_inference_paid` | x402-paywalled encrypted inference, no API key | Live | # Get an API key (/docs/build/api-key) Most AI APIs make you sign up with an email, verify your identity, and add a credit card before your first request. Solrouter skips all of that. You authenticate with an API key sent as a bearer token. You get that key by connecting a Solana wallet. There is no email sign-up, no KYC (Know Your Customer identity check), and no card. Fund a prepaid balance in USDC or $ROUTER, and you can make calls in minutes. ## Getting your API key [#getting-your-api-key] Here is the full path from zero to your first authenticated request. It has four steps, all in the browser. ### Go to solrouter.com/sdk [#go-to-solroutercomsdk] Open [solrouter.com/sdk](https://solrouter.com/sdk) in your browser. ### Connect your Solana wallet [#connect-your-solana-wallet] Connect Phantom, Solflare, or a Privy embedded wallet (any Wallet Standard wallet). Your wallet is your only identity credential. Solrouter collects no email and no personal information. ### Generate an API key [#generate-an-api-key] Click **Generate API Key**. Your key is issued at once and starts with `sk_solrouter_...`. Copy it and store it somewhere safe. It is not shown again. ### Top up your balance [#top-up-your-balance] Add funds to your prepaid account in **USDC** or **$ROUTER**. Solrouter meters every API call and deducts the cost from this balance. There is no monthly bill. You pay only for what you use. ## Using your API key [#using-your-api-key] Once you have a key, you attach it to requests in one of two ways: through the SDK, which handles it for you, or directly over HTTP. ### With the SDK [#with-the-sdk] Pass your API key when you create the `SolRouter` client. From then on, the SDK attaches it to every request. You never touch the header yourself. ```typescript import { SolRouter } from '@solrouter/sdk'; const client = new SolRouter({ apiKey: 'sk_solrouter_...', baseUrl: 'https://api.solrouter.com', }); ``` ### Direct HTTP (REST API) [#direct-http-rest-api] If you are not using the SDK, send the key yourself as a bearer token in the `Authorization` header. ```bash curl -X POST "https://api.solrouter.com/agent" \ -H "Authorization: Bearer sk_solrouter_..." \ -H "Content-Type: application/json" \ -d '{"prompt": "Hello", "model": "gpt-oss:20b"}' ``` ## Authentication tiers [#authentication-tiers] External callers have two ways to authenticate. Pick the row that matches how your code runs. | Tier | How it works | Best for | | ------------------ | -------------------------------------------------------------------------- | ------------------------------------------- | | **API key** | `Authorization: Bearer sk_solrouter_...`, billed from your prepaid balance | Most applications and development workflows | | **x402 (keyless)** | Per-call USDC settlement on Solana mainnet | Autonomous agents that do not hold API keys | x402 settles through a facilitator. The live manifest advertises Coinbase (`api.cdp.coinbase.com/x402`). Service discovery is at [`/.well-known/x402`](https://api.solrouter.com/.well-known/x402). The manifest shows `X402_FACILITATOR_URL` when it is set, or a built-in default when it is not. The manifest does not show which facilitator settles payments. Wire-level facilitator: not determined. The x402 tier is worth a closer look if you build agents. Instead of storing a long-lived key, an agent pays for each call on the spot in USDC. x402 removes the API key, not the wallet key. The agent still holds a wallet private key to sign payments, so protect that key the same way. ## Keeping your API key safe [#keeping-your-api-key-safe] Your key can spend real money, so treat it with the same care as a password. The practices below keep it out of the wrong hands. * **Never commit your API key to source control.** Treat `sk_solrouter_...` like a password. Keep it out of Git history, checked-in `.env` files, and any public repository. * **Use environment variables.** Store your key in `SOLROUTER_API_KEY` and read it at runtime, so the secret never lives in your code: ```typescript const client = new SolRouter({ apiKey: process.env.SOLROUTER_API_KEY, baseUrl: 'https://api.solrouter.com', }); ``` * **Rotate compromised keys at once.** If a key is exposed, go to [solrouter.com/sdk](https://solrouter.com/sdk), delete the affected key, and generate a new one. Never expose your API key in client-side code or public repositories. Anyone with your key can spend your prepaid balance. If you suspect a key has leaked, rotate it at once at solrouter.com/sdk. # MCP Server (/docs/build/mcp-server) The server is on npm. Encrypted chat is live. Swap execution tools are Soon. Read the status word beside each tool. You already work inside Claude Desktop or Cursor. The Solrouter MCP server lets you run Solrouter's encrypted chat and Agent Privacy API tools right there. You need no separate app. MCP (Model Context Protocol, an open standard for connecting AI clients to external tools) is the bridge. The server runs on your machine as `npx @solrouter/mcp-server`. It holds your API key and encrypts prompts for the `encrypted_chat` tool and for the AI synthesis step of the research tools. Other calls leave your machine in plaintext. The tables below say which. ## Configuration [#configuration] This is the one-time setup that registers Solrouter as a tool provider in your client. Add the following block to your MCP configuration file. For **Claude Desktop**, that file is `~/Library/Application Support/Claude/claude_desktop_config.json` on macOS (or the equivalent path on Windows). For **Cursor**, add it under `mcpServers` in `~/.cursor/mcp.json`. ```json { "mcpServers": { "solrouter": { "command": "npx", "args": ["@solrouter/mcp-server"], "env": { "SOLROUTER_API_KEY": "sk_solrouter_...", "SOLROUTER_API_URL": "https://api.solrouter.com" } } } } ``` Set `SOLROUTER_API_URL`. Without it, the server defaults to a Render host, not `api.solrouter.com`. Set `BRAVE_API_KEY` too: `private_research` and `private_token_analysis` fail without it. `HELIUS_RPC_URL` is optional (default: `https://api.mainnet-beta.solana.com`). Save the config, then restart Claude Desktop or reload the Cursor window. The Solrouter tools appear in the tool list. There is nothing else to install. ## Privacy and research tools [#privacy-and-research-tools] Use these tools for encrypted AI inference or on-chain research from inside your client. Only the AI step is encrypted. The data-gathering steps call third parties directly from your machine in plaintext. | Tool | Description | Leaves your machine in plaintext | | ------------------------------- | ------------------------------------------------------------------------------ | -------------------------------------------------------------------------------- | | `encrypted_chat` (Live) | Encrypted AI query through `/tee/process` | Nothing. The prompt is encrypted on your machine | | `private_research` (Live) | Web search, DEX data, and on-chain lookups, then an encrypted AI synthesis | Query to Brave; token symbols to DexScreener; wallet addresses to the Solana RPC | | `private_token_analysis` (Live) | DEX data, price, and web results for one token, then an encrypted AI synthesis | The token to DexScreener, CoinGecko, and Brave | | `private_wallet_audit` (Live) | Holdings of one wallet, then an encrypted AI synthesis | The wallet address to the Solana RPC and DexScreener | | `list_models` (Live) | Models from `GET /api/v1/models` with pricing | Nothing sensitive | | `account_balance` (Live) | Your credit balance from `GET /api/v1/balance` | Nothing sensitive | ## Agent and swap helper tools [#agent-and-swap-helper-tools] These tools call the Solrouter backend in plaintext or return text without any network call. | Tool | Description | Leaves your machine in plaintext | | ------------------------------------------- | ----------------------------------------------------------------------------------------------- | ------------------------------------- | | `agent_run` (Live) | Tool-augmented agent completion through `POST /agent` (web search, Solana data, paid APIs) | Your prompt, to the Solrouter backend | | `umbra_describe` (Live) | Explains private swaps. Makes no network call | Nothing | | `umbra_anonymity_stats` (Soon) | Deposit count for one denomination bucket from `GET /umbra/anonymity-set` | The bucket and network | | `umbra_initiate_private_swap_widget` (Soon) | Returns a directive that tells the Solrouter web chat to show the swap widget. Executes nothing | Suggested tokens and network | ## Agent Privacy API tools [#agent-privacy-api-tools] These tools wrap `POST` and `GET /agents/v1/*` over HTTPS with your API key. Swap parameters travel in plaintext to the Solrouter backend. Swap execution is Soon. Tools marked Soon can move real funds once they go live, so read the session state before you act on it. | Tool | Description | | ----------------------------------- | ---------------------------------------------------------------------------------------- | | `umbra_quote` (Live) | Quote a private swap with anonymity-set sizing | | `umbra_anonymity_set` (Live) | Inspect the current anonymity set for a mint pair | | `umbra_swap_oneshot` (Soon) | Open a one-shot swap session. The agent signs the funding transaction | | `umbra_swap_oneshot_execute` (Soon) | Submit the signed funding transaction to start execution | | `umbra_session_status` (Soon) | Poll session state until `settled` | | `umbra_create_wallet` (Soon) | Provision a managed wallet for an agent | | `umbra_swap_managed` (Soon) | Run a swap from a managed wallet | | `umbra_encrypt` (Soon) | Convert a balance to an encrypted balance on the same wallet | | `umbra_shield` (Soon) | Mixer round trip, withdraw, then forward to a fresh address | | `umbra_balance` (Soon) | Read the encrypted balance of a managed wallet | | `umbra_attestation` (Soon) | Read the settlement record of a swap session (`GET /agents/v1/attestations/:sessionId`) | | `private_inference_paid` (Live) | x402-paywalled encrypted inference, no API key. You supply the encrypted prompt yourself | Not every request through the MCP server is encrypted. `encrypted_chat` and the AI synthesis step of the three research tools use the Privacy SDK path: RescueCipher and Intel TDX. The Solrouter backend never sees that plaintext. Web search (Brave), DexScreener, CoinGecko, and Solana RPC lookups go from your machine to those third parties in plaintext. `agent_run` and the `umbra_*` tools send their inputs to the Solrouter backend in plaintext. # Privacy SDK (/docs/build/privacy-sdk) Most AI APIs read your prompts in the clear. The Solrouter Privacy SDK encrypts your prompt before it leaves your machine, so the Solrouter backend cannot read it. The SDK handles the cryptography for you. Here is what happens when you call `client.chat()`. The SDK fetches the enclave's X25519 public key from `GET /tee/public-key`. It does not fetch or verify the attestation quote. That check is a manual step. See the [attestation guide](/docs/how-it-works/attestation). The SDK then encrypts your prompt on your machine with Arcium's RescueCipher. It sends the encrypted blob through the Solrouter backend, which forwards it without decrypting it. The TEE (Trusted Execution Environment, a CPU-isolated confidential VM) decrypts the prompt and calls the model on a Nosana GPU node. The node runs the model outside the enclave, so it sees the prompt and the reply in plaintext during inference. The reply comes back encrypted, and the SDK decrypts it with your session key. ## Installation [#installation] Install the SDK from your package manager of choice. ```bash npm install @solrouter/sdk ``` ```bash yarn add @solrouter/sdk ``` ```bash pnpm add @solrouter/sdk ``` ## Basic usage [#basic-usage] This section walks through the calls you will make most often, starting with the default encrypted chat. ### Encrypted chat (default) [#encrypted-chat-default] Every call is encrypted unless you opt out. Create a `SolRouter` client with your API key and the production `baseUrl`, then start chatting. ```typescript import { SolRouter } from '@solrouter/sdk'; const client = new SolRouter({ apiKey: 'sk_solrouter_...', baseUrl: 'https://api.solrouter.com', }); // Encrypted on your machine. The Solrouter backend never sees plaintext. const response = await client.chat('What are the risks of this DeFi protocol?'); console.log(response.message); ``` Set `baseUrl` in every client. Without it, SDK 1.1.0 defaults to a Render host, not `api.solrouter.com`. ### Choosing a model [#choosing-a-model] Solrouter runs self-hosted open-weight models on Nosana GPU nodes. The backend catalog has three ids. The SDK type allows two strings. At runtime any other string passes through unchanged. | SDK string | Backend id | Status | Note | | ------------- | ------------- | -------- | -------------------------------------------------------------------------------------------------------- | | `gpt-oss-20b` | `gpt-oss:20b` | Live | Default. Use this with SDK 1.1.0. | | `qwen3-8b` | `qwen3:8b` | Archived | Alias of a retired node. Do not use. | | none yet | `qwen3.8:27b` | Live | Reachable with a type cast: `model: 'nosana:qwen3.8:27b' as any`. A typed alias needs a new SDK release. | | none yet | `gemma4:31b` | Soon | Encrypted path not confirmed end to end. | A new SDK release with the current model map is Soon. ```typescript const response = await client.chat('Summarize the latest Solana validator outage', { model: 'gpt-oss-20b', // the only typed live model string in SDK 1.1.0 }); ``` After an idle period, a node can answer with a retryable "warming up" error. Wait a moment and send the request again. ### Opt out of encryption (plaintext) [#opt-out-of-encryption-plaintext] You can turn off client-side encryption for one call. The prompt then goes to the Solrouter backend in plaintext. The backend reads it and routes it to the same self-hosted Nosana models. No proprietary model is reachable this way. ```typescript const response = await client.chat('Hello', { encrypted: false }); ``` In words, with `encrypted: true` (the default): * Your device encrypts the prompt with RescueCipher and an X25519 shared secret. * The Solrouter backend forwards the ciphertext. It cannot read it. * The TEE decrypts the prompt and calls the model at the configured Nosana endpoint URL. The node sees the prompt in plaintext. * The reply comes back encrypted to your session key. In words, with `encrypted: false`: * Your device sends the prompt as plaintext. * The Solrouter backend reads the prompt and routes it to the same Nosana model. * The TEE is not used. No on-chain receipt is created. * The reply comes back in plaintext. ### Guided reasoning (BRAID, agent path) [#guided-reasoning-braid-agent-path] For questions that need structured analysis, route through the agent endpoint. Setting `reasoning: 'braid'` runs your request through BRAID guided reasoning. BRAID walks a fixed Guided Reasoning Diagram (GRD) of tool steps, then makes one synthesis call to the model. Older material calls this SERV. This path is plaintext. The SDK sends the prompt to `POST /agent` without encryption and the response reports `encrypted: false`. ```typescript const response = await client.chat('Compare Marginfi vs Kamino lending on Solana', { reasoning: 'braid', // plaintext path through the agent endpoint }); ``` ### Check balance [#check-balance] You pay per call from a prepaid balance. Check what is left at any time. ```typescript const { balance, balanceFormatted } = await client.getBalance(); ``` ## SDK options reference [#sdk-options-reference] Each of these options goes in the object you pass as the second argument to `client.chat()`. On the encrypted path, this leaves your machine: the ciphertext bundle (`ciphertext`, `nonce`, `publicKey`, `version`), plus in plaintext your API key, the model id, `chatId`, and any `systemPrompt`, `useRAG`, `ragCollection`, or `useLiveSearch` you set. The backend forwards only the bundle and the model id to the CVM. | Option | Type | Default | Description | | --------------- | ------- | ------------- | ----------------------------------------------------------------------------------------------------------------------------------- | | `model` | string | `gpt-oss-20b` | Model string. Use `gpt-oss-20b` with SDK 1.1.0. `qwen3-8b` maps to a retired node. Other catalog ids pass through with a type cast. | | `encrypted` | boolean | `true` | Turn client-side encryption on or off for this call | | `reasoning` | string | none | Set to `'braid'` for guided reasoning. This path is plaintext. | | `chatId` | string | none | Sent in plaintext to the backend. Not forwarded to the CVM. | | `systemPrompt` | string | none | Sent in plaintext to the backend. Dropped before the CVM, so it never reaches the model. | | `useRAG` | boolean | none | Sent in plaintext to the backend and ignored on the encrypted path. | | `ragCollection` | string | none | Sent in plaintext to the backend and ignored on the encrypted path. | | `useLiveSearch` | boolean | none | Sent in plaintext to the backend and ignored on the encrypted path. | ## No KYC required [#no-kyc-required] Privacy starts at sign-up. You do not need an email address, a credit card, or any personal information to use the SDK. Connect your Solana wallet at [solrouter.com/sdk](https://solrouter.com/sdk), generate an API key, and top up your balance in USDC or `$ROUTER`. Pricing is metered per call from your prepaid balance. Solrouter runs only self-hosted, open-weight models on the Nosana decentralized GPU network. The catalog ids are `gpt-oss:20b` (Live), `qwen3.8:27b` (Live), and `gemma4:31b` (Soon). There are no third-party model APIs: no OpenAI, Anthropic, or Google. Your prompts never go to an external model provider. The Nosana node that runs the model does see the prompt in plaintext during inference. Solrouter does not control that hardware. # Quickstart (/docs/build/quickstart) Most AI APIs can read every prompt you send them. Solrouter does not: your message is encrypted on your machine before it leaves, and the backend relays it without seeing the plaintext. The next five steps set that up end to end. You sign in with a Solana wallet, install the SDK, and send your first encrypted request. No email and no credit card: you need a wallet and a few lines of TypeScript. ### Get an API key [#get-an-api-key] Your key is how Solrouter authenticates you and bills your usage. It is tied to your wallet, not your identity. Go to [solrouter.com/sdk](https://solrouter.com/sdk), connect your Solana wallet, and generate an API key. Top up your prepaid balance in USDC or $ROUTER to start making calls. No email, no credit card, and no KYC required. Your API key is tied to your wallet. ### Install the SDK [#install-the-sdk] The SDK handles encryption, routing, and decryption for you, so you write normal TypeScript and get privacy by default. Add `@solrouter/sdk` to your project using your preferred package manager. ```bash npm install @solrouter/sdk ``` ```bash yarn add @solrouter/sdk ``` ```bash pnpm add @solrouter/sdk ``` ### Initialize the client [#initialize-the-client] One call gets you a configured client. Pass your API key and the API base URL. The SDK fetches the enclave's published public key from `GET /tee/public-key` before your first encrypted request. That key is the encryption target for your prompts. The SDK does not fetch or verify the attestation quote. To check the enclave yourself, see [Attestation](/docs/how-it-works/attestation). ```typescript import { SolRouter } from '@solrouter/sdk'; const client = new SolRouter({ apiKey: 'sk_solrouter_...', baseUrl: 'https://api.solrouter.com', }); ``` Set `baseUrl` explicitly. The SDK default points at a Render host, not at `api.solrouter.com`. ### Send your first encrypted chat [#send-your-first-encrypted-chat] Here the privacy guarantee pays off. Your prompt travels encrypted through the Solrouter backend. Only the TEE (Trusted Execution Environment: hardware that isolates code and data from the machine's operator) can decrypt it. The enclave then sends the plaintext to an open-weight model on a Nosana GPU node. Call `client.chat()` to send a message. The SDK encrypts it client-side with Arcium's RescueCipher. It sends the encrypted blob through the Solrouter backend, which cannot read it. Then it decrypts the response for you. With the current SDK, use the default model `gpt-oss-20b`. Other catalog ids pass through with a type cast; see [Models](/docs/how-it-works/models). ```typescript // Encrypted end to end: the Solrouter backend never sees plaintext const response = await client.chat('What are the risks of this DeFi protocol?'); console.log(response.message); ``` If the GPU node was idle, the first reply can say "Nosana GPU node is warming up". Wait a moment and send the request again. ### Check your balance [#check-your-balance] Solrouter bills against prepaid funds, so you can check your remaining balance at any time. For example, check it before a batch of calls, or show it in your own UI. Query your prepaid balance: ```typescript const { balance, balanceFormatted } = await client.getBalance(); console.log(`Balance: ${balanceFormatted}`); ``` ## Next steps [#next-steps] You have sent an encrypted request and checked your balance. That is the core loop. Where you go next depends on what you build: the full SDK surface, keyless payments, or direct HTTP access. Full `@solrouter/sdk` documentation: model selection, plaintext mode (`encrypted: false`), BRAID reasoning (`reasoning: 'braid'`), and more. Learn about API key auth, x402 keyless payments, and keeping your credentials safe. Browse the full REST API, including the agent endpoint and x402 paywall spec. # Agent reasoning (/docs/how-it-works/agent-reasoning) `POST /agent` has three paths. The request body picks the path. Older material calls the guided path SERV; the code, the SDK option, and the API value call it BRAID. ## Three paths [#three-paths] | Path | Trigger | Where it runs | Model calls | | ------------------------ | -------------------- | ------------------ | ------------------------------ | | Tool loop (default) | `useTools: true` | Backend | Up to 8 (`MAX_ITERATIONS = 8`) | | Guided reasoning (BRAID) | `reasoning: 'braid'` | Backend | One synthesis call | | Encrypted agent mode | `encryptedPrompt` | Inside the enclave | Loop inside the enclave | The encrypted path is Live for REST callers who send `encryptedPrompt`, and Soon for the SDK. The chat app's agent mode runs the plaintext tool loop. ## The tool loop [#the-tool-loop] The default path is a standard loop. The model picks a tool, the backend runs it, the model reads the result, and it repeats up to eight times. The last call has no tools, so the model must write the answer. The backend registers 18 tools: * **Web:** `web_search`, `scrape_url`, `crawl_url` * **On-chain and markets:** `solana_balance`, `token_price`, `swap_quote`, `trending_tokens` * **Research:** `deepwiki`, `colosseum_search`, `colosseum_archives` * **Paid APIs:** `paysh_search_apis`, `paysh_call_api` * **Connected accounts:** `github_list_repos`, `github_issues`, `github_read_file`, `notion_search`, `notion_get_page`, `notion_query_database` The encrypted path runs a 5-tool allowlist inside the enclave: `web_search` (SearXNG in the same enclave), `token_price`, `trending_tokens`, `swap_quote`, and `solana_balance`. Every other tool fails closed in that mode. ## Guided reasoning (BRAID) [#guided-reasoning-braid] A standard loop asks the model what to do at every step, one model call per step. BRAID splits the two jobs an agent does: deciding what data to gather, and writing the answer. It walks a Guided Reasoning Diagram (GRD), a fixed graph that sets which tools run and in what order, then calls the model once at the end to write the reply. A query passes through four stages: 1. **Intent detection.** Keyword rules map the prompt to one of six GRDs (`comparison`, `defi-analysis`, `general-research`, `market-overview`, `token-research`, `wallet-analysis`). No model call. 2. **GRD execution.** BRAID walks the graph node by node. Branch nodes use a rule first; when no rule applies, the model answers a short one-word question to pick the branch. 3. **Skill-graph injection.** If the prompt matches skill nodes, their notes are added to the synthesis prompt. 4. **Synthesis.** BRAID calls the model once to turn the collected data into a reply. A swap prompt with two known tokens, or a prompt with three or more known tokens, skips the walk and calls the tools directly before the same single synthesis call. BRAID spends one synthesis call plus the occasional one-word branch call, instead of one model call per step. That is the whole mechanism behind the cost and latency claim. No benchmark numbers are published. Send `reasoning: 'braid'` to `POST /agent`, or `client.chat(prompt, { reasoning: 'braid' })` in the SDK. The SDK path is plaintext today (`encrypted: false`). See [POST /agent](/docs/api-reference/agent) for the `braidTrace` envelope. ## The skill graph [#the-skill-graph] Before the synthesis call, the engine matches your query against 44 knowledge nodes. It scores every node against the prompt, walks the graph from the top three scoring nodes, follows edges to a depth of 2, and stops at 5 nodes. The reached nodes add domain notes to the system prompt. A prompt that matches no node gets no extra tokens, and the response never returns the walked path. Click a node to see its edges. The highlighted path is a DeFi protocol comparison. **The 44 nodes, by cluster** * **Research and analysis (15):** research-core, source-eval, defi-analysis, liquidity-risk, token-economics, market-analysis, on-chain-analysis, wallet-analysis, privacy-research, risk-assessment, smart-contract-risk, comparative-analysis, data-synthesis, colosseum-research, colosseum-archives. * **Ecosystem (2):** arcium-mpc, solana-ecosystem. * **DeFi protocols (10):** jupiter-defi, raydium-defi, orca-defi, meteora-defi, kamino-defi, sanctum-staking, pump-fun, lulo-lending, ranger-perps, prediction-markets. * **Infrastructure and oracles (8):** helius-infra, light-protocol-zk, metaplex-nfts, pyth-oracle, switchboard-oracle, squads-multisig, debridge-cross-chain, coingecko-analytics. * **Solana development (9):** solana-kit-dev, anchor-dev, pinocchio-dev, framework-kit-frontend, solana-testing, solana-security-audit, token2022-extensions, quicknode-infra, magicblock-gaming. # Attestation (/docs/how-it-works/attestation) The quote endpoints and on-chain receipts are live. Reference measurements to compare against are not published yet. When you send a prompt to a private inference service, how do you know it ran where the service claims? How do you know the code is the published code and not a tampered copy that logs your data? Solrouter answers that with attestation: hardware-signed proof you can check yourself. The Solrouter TEE (Trusted Execution Environment: hardware that isolates code and data even from the machine's owner) publishes an Intel-signed TDX quote. That quote binds the enclave's public key to the code measurement running inside the Confidential VM. Each `/tee/process` response also carries a quote when the CVM can reach the dStack agent. Otherwise the `attestation.tdxQuote` field is `null` and `attestation.tdxQuoteError` says why. You never have to take Solrouter's word for it: fetch the quote, verify Intel's signature chain, and confirm on-chain that your inference has a receipt. ## Live attestation endpoints [#live-attestation-endpoints] These two endpoints hand you the raw material for verification. Query them any time to retrieve the current attestation data. ### Get the TEE public key [#get-the-tee-public-key] ```bash GET https://api.solrouter.com/tee/public-key ``` This returns the X25519 public key currently active inside the Confidential VM. The SDK uses this key to encrypt prompts client-side. The enclave generates it at boot, so only the enclave holds the matching private key. The key changes on every CVM boot. Nobody on the host, including Solrouter, can decrypt traffic sealed to it. ### Get the TDX attestation quote [#get-the-tdx-attestation-quote] ```bash GET https://api.solrouter.com/tee/attestation ``` This returns the Intel TDX attestation quote, the hardware-signed proof that ties everything together. The response includes `teePublicKey`, `teePublicKeySha256`, `reportDataHex`, and `tdxQuote`. Its `report_data` is `sha256(teePublicKey)`, which binds the public key you fetched above to the enclave that produced the quote. Verify the quote and you confirm two things: 1. The host CPU is a genuine Intel TDX-capable processor. 2. The public key was generated inside that specific enclave instance. Confirming that the enclave runs the published Solrouter code needs reference measurements. Those are not published yet (see below). ### Two report\_data formulas [#two-report_data-formulas] Solrouter produces two kinds of quote. They pin different data, so check the right formula for the quote you hold. | Quote | `report_data` | | ------------------------------------------------ | ------------------------------------------------ | | `GET /tee/attestation` | `sha256(X25519 public key)` | | `attestation.tdxQuote` in a `/tee/process` reply | `sha256(X25519 public key ‖ ed25519 public key)` | The ed25519 key is a signing key the enclave also generates at boot. The enclave uses it to sign the encryption proof that goes on-chain. ## What you can verify [#what-you-can-verify] Attestation only matters if you can check the claims independently. Here is what the quote lets you prove on your own today, and what it does not yet. * **Intel root chain**: the TDX quote is signed by an Intel-issued key. Verifying the signature chain confirms the hardware is a genuine TDX CPU, not a simulated or spoofed environment. Live. * **Public key binding**: `report_data` commits to the enclave's public key. So you can confirm the key you encrypted to belongs to this enclave instance. An interceptor cannot substitute its own key. Live. * **Code measurements**: the `tdxQuote` field is the dStack guest agent's response, passed through unparsed. Solrouter does not publish a parser or reference values for the measurements inside it. The product repository is private. Reference measurements: Soon. Advanced users can verify the full Intel TDX quote chain independently using Intel's DCAP (Data Center Attestation Primitives) libraries or a third-party TEE verification service. The quote Solrouter returns is a standard TDX quote, no proprietary format. ## On-chain attestation anchor [#on-chain-attestation-anchor] Off-chain verification proves the enclave is genuine, but it lives in a response you have to trust Solrouter to keep. For a record nobody can quietly edit later, Solrouter anchors attestation data to Solana. Cluster: the commit code targets mainnet; the program deployment on mainnet has not been re-measured. The Solrouter attestation program is deployed at: ``` ATMRatMtsKX4bHax7U4FRdhbE4mjU4NKpDZGqZqAhBKb ``` Each inference sent through `POST /tee/process` gets a **Light Protocol compressed account** on Solana. Solrouter's deployer wallet commits it after the CVM signs the proof. This is automatic; you do not publish anything. The account address is derived from the seeds `attestation_v2` and `sha256(encryptedPrompt)`, so anyone who holds the ciphertext can derive the same address. The `/tee/process` response reports it under `onchainAttestation` with `address`, `signature`, and `explorerUrl`. If the commit fails, the inference still succeeds and `onchainAttestation` is `null`. To read a receipt back, use the public attestation endpoints. They read from the Solana ledger through a Photon indexer. ```bash # By the commit transaction signature from onchainAttestation.signature GET https://api.solrouter.com/attestation/by-tx/:sig # By sha256 of the encrypted prompt GET https://api.solrouter.com/attestation/by-hash/:hash # By the compressed account address from onchainAttestation.address GET https://api.solrouter.com/attestation/:address # Derive hash and address from a ciphertext you hold POST https://api.solrouter.com/attestation/derive { "encryptedPrompt": "..." } ``` The `umbra_attestation` MCP tool is a different thing. It returns the settlement record of a private-swap session from `GET /agents/v1/attestations/:sessionId`. It does not read inference receipts. ## Verify it yourself [#verify-it-yourself] The full check, from the live key through the Intel DCAP signature chain to the on-chain anchor, runs in your browser on [Check a reply yourself](/docs/verify). Auditors will find the by-hand steps there too. # Encryption (/docs/how-it-works/encryption) When you send a prompt to an AI provider, you normally trust that provider to read it, store it, and not misuse it. Solrouter removes that trust requirement for its own backend. Your prompt is encrypted on your own device before it leaves, and Solrouter's backend never holds the key to read it. Solrouter encrypts your prompts and responses with Arcium's `RescueCipher` cipher and `X25519` key exchange. X25519 lets two parties agree on a shared secret without sending it. The ciphertext travels through Solrouter's backend untouched. It is decrypted only inside a hardware-isolated Intel TDX Confidential VM, a TEE. A TEE (Trusted Execution Environment) is hardware that isolates code and data from the machine's owner. Solrouter's backend is a blind relay: it routes encrypted blobs it cannot read, and the private key that could decrypt them never leaves the enclave. ## Encryption components [#encryption-components] Here are the three building blocks that make the guarantee work, and what each one does. ### Client-side encryption [#client-side-encryption] The first line of defense is simple: encrypt before you transmit. The SDK encrypts your prompt in the browser or in your server process before it sends anything. * **`RescueCipher`**: Arcium's field-element symmetric cipher. Arcium chose it for compatibility with MPC, FHE, and ZK computation, so the same encrypted payload can be processed under any of those paradigms as Arcium's network matures. * **`X25519` key exchange**: your SDK session generates an ephemeral (single-use, per-session) X25519 keypair. The SDK derives the shared secret from your ephemeral private key and the TEE public key. The SDK fetches that key from `GET /tee/public-key`. It does not fetch or verify the attestation quote. Verification is a manual step; see [Attestation](/docs/how-it-works/attestation). * **TEE-generated keypair**: the TEE's own X25519 keypair is generated inside the Confidential VM at boot time. The private key never leaves the enclave, not even to Solrouter's own infrastructure. ### Inference isolation [#inference-isolation] Your data has to be decrypted somewhere to run the model. The question is *where*, and who can see it. With Solrouter, decryption happens inside an attested enclave. The model runs on a Nosana GPU node that the enclave calls. * **Intel TDX Confidential VM**: a hardware-enforced TEE. Memory is encrypted by the CPU and inaccessible to the host OS, hypervisor, and any Solrouter process running outside the enclave. * **Where plaintext exists**: the enclave decrypts your prompt, then calls the model on a Nosana GPU node (HTTPS per the documented node URL, not re-verified). The prompt and reply exist in plaintext in that node's memory during inference. The node runs outside the TDX enclave. The node operator could read the prompt at that moment; Solrouter does not control that hardware. The request is not linked to your identity on the node. When the reply returns to the enclave, it is encrypted with your session's ephemeral key before it leaves. * **No backend access**: no Solrouter employee, server process, or privileged operator on the backend can read your prompt or response. The hardware enforces this. ### Transport [#transport] Encryption only helps if there is no gap where plaintext leaks in transit between you and the enclave. There is none. * Your prompt and the reply are encrypted end-to-end between your client and the enclave. The request metadata (API key, model id, `chatId`, and any `systemPrompt`) reaches the backend in plaintext. * The Solrouter backend is a **blind relay**. It forwards encrypted blobs without being able to decrypt them. It never has the keys. The full request path, hop by hop, is on [How It Works](/docs/how-it-works/request-flow). ## Why RescueCipher? [#why-rescuecipher] You might wonder why Solrouter does not use a familiar cipher like AES. The answer is about where your data can go next. Most symmetric ciphers (AES-GCM, ChaCha20) are designed for classical computation. They are efficient on CPUs and GPUs but are not naturally compatible with the algebraic structures that MPC, FHE, and ZK proofs operate over. RescueCipher is a **field-element cipher**. It operates natively over the same finite-field arithmetic that MPC, FHE, and ZK systems use. That gives you three things: * The same encrypted payload you send today can, in principle, be processed directly under MPC or FHE computation without re-encryption. * As Arcium's MXE (Multiparty eXecution Environment) network ships support for more cryptographic compute primitives, Solrouter's encryption layer does not need to change. * You get a smooth upgrade path: stronger cryptographic compute guarantees over time, zero migration work on your side. This is why Arcium chose RescueCipher as the cipher for its MXE substrate, and why Solrouter uses it today, even before full MPC/FHE inference is live. ## What encryption does NOT cover (yet) [#what-encryption-does-not-cover-yet] Privacy claims in this space are often inflated, so here is the honest line on what Solrouter does and does not do today. Solrouter is **not** running pure FHE (Fully Homomorphic Encryption) inference today, and no production system does. LLM-scale FHE inference is many orders of magnitude away from viable latency. Anyone claiming "FHE LLM inference" in production is overclaiming. What Solrouter offers today is **client-side encryption + hardware TEE isolation for decryption**, which is a real and meaningful guarantee. Arcium's MXE is a hybrid of MPC + FHE + ZK primitives, and RescueCipher is designed to work with all three. As Arcium's network matures, more of the inference pipeline will move from TEE-isolated plaintext into cryptographic compute: MPC first, then FHE/ZK where they are practical. The client encryption layer stays unchanged throughout. To be precise about what is and is not guaranteed today: | Property | Today | | ------------------------------------------ | ------------------------------------------------------------- | | Client-side encryption before transmission | Live: RescueCipher + X25519 | | Plaintext hidden from Solrouter backend | Live: decryption happens only inside the Intel TDX enclave | | Plaintext hidden from the Nosana GPU node | No: the model runs on plaintext on that node during inference | | Intel-signed TDX quote you can fetch | Live: `GET /tee/attestation` | | Published reference measurements | Soon: the repository is private, no reference values yet | | MPC-based inference | Soon: Arcium MXE roadmap | | Full FHE inference | Not in production anywhere today | ## Encryption options in the SDK [#encryption-options-in-the-sdk] Encryption is on by default in `@solrouter/sdk`. You do not have to do anything to get it. You can turn it off for a plaintext path, but you give up every privacy guarantee on this page when you do. The plaintext path sends your prompt to the same self-hosted Nosana models. It does not unlock any other model. ```typescript // Default: fully encrypted (recommended) const response = await client.chat('Your prompt', { encrypted: true }); ``` ```typescript // Plaintext path: no encryption guarantees const response = await client.chat('Your prompt', { encrypted: false }); ``` When you set `encrypted: false`, your prompt and response travel in plaintext through Solrouter's infrastructure. Use the plaintext path only for non-sensitive workloads. # Architecture (/docs/how-it-works) Private swaps are Soon. Every other part of the map is Live. Solrouter has a handful of moving parts. This map shows all of them at once: what each part holds, what it can see, and where the code lives. Each page in this section zooms into one piece. It gets technical, and every claim points at the code. Click a node to open its details. Drag to pan. Pinch to zoom. **In words** * Chat app (solrouter.com/chat): holds your wallet session and, in Maximum Privacy Mode, encrypts each prompt in the browser. In the default mode it sends plaintext to the backend. * `@solrouter/sdk`: holds your API key and encrypts by default. Sends the ciphertext bundle plus the API key, model id, and chat id in plaintext. * `@solrouter/mcp-server` (your machine): holds `SOLROUTER_API_KEY`, `SOLROUTER_API_URL`, and `BRAVE_API_KEY`. Four tools use the encrypted path for the model step. Search and market lookups go to third parties in plaintext. * REST and x402 clients: send whatever they build. `POST /api/v1/chat/completions` and `/tee/process` require `encryptedPrompt`; `/agent` accepts plaintext or `encryptedPrompt`. * Solrouter backend (behind api.solrouter.com): checks the key, bills, runs the x402 paywall, relays ciphertext to the enclave, and commits receipts with its deployer wallet. It holds no decryption key on the encrypted path. * Intel TDX enclave on Phala dStack: generates an X25519 sealing key and an ed25519 signing key at boot, decrypts with RescueCipher, requests TDX quotes from the tappd agent, runs a 5-tool allowlist for encrypted agent mode, and hosts SearXNG in the same enclave. * Nosana GPU node, one per model: runs the open-weight model in Ollama and sees the prompt and reply during inference. Solrouter does not control that hardware. * Solana: holds one Light Protocol compressed receipt per private inference, under program `ATMRatMtsKX4bHax7U4FRdhbE4mjU4NKpDZGqZqAhBKb`. * Umbra mixer plus Jupiter (Soon): the private-swap path. The backend orchestrates it; no mainnet run is confirmed. * x402 facilitator: the live manifest advertises Coinbase. Which facilitator settles a payment is set on the server and is not visible from outside. The agent never talks to the facilitator. ## Zoom in [#zoom-in] # Models (/docs/how-it-works/models) The model table carries a Status column: gemma4:31b is Soon and qwen3:8b is Archived. In privacy mode Solrouter answers with self-hosted, open-weight models only. It never calls a proprietary API. That choice is what makes the privacy claim hold. Every model runs on its own [Nosana](https://nosana.io) GPU node. Your encrypted request travels from your device to an Intel TDX enclave (a TEE, a trusted execution environment: a hardware-isolated virtual machine). The enclave decrypts it and calls the model on the Nosana node (HTTPS per the documented node URL, not re-verified). No proprietary model API is in this path. ## Available models [#available-models] Each row is one model with its ids and its status on 2026-08-26. | Model | Catalog id | SDK id | Status | | ------------ | ------------- | ------------- | -------- | | GPT-OSS 20B | `gpt-oss:20b` | `gpt-oss-20b` | Live | | Qwen 3.8 27B | `qwen3.8:27b` | none yet | Live | | Gemma 4 31B | `gemma4:31b` | none | Soon | | Qwen 3 8B | `qwen3:8b` | `qwen3-8b` | Archived | `gpt-oss:20b`: default in the chat app and the SDK. Context 8192 tokens. `qwen3.8:27b`: listed as "Uncensored" in the chat picker. In SDK 1.1.0 it needs a type cast (see below). `gemma4:31b`: listed by `GET /api/v1/models` with a 262144-token context. The enclave has no endpoint for it yet, so the encrypted path is not confirmed. `qwen3:8b`: retired node. The chat app folds this id to `qwen3.8:27b`. The REST API returns catalog ids with a `nosana:` prefix, for example `nosana:gpt-oss:20b`. The backend accepts both forms. All of these models are open-weight. Their weights are public, so anyone can inspect what runs on your prompt. ## Choosing a model [#choosing-a-model] Leave the model out and the SDK defaults to `gpt-oss-20b`. To pick one, pass `model` to `client.chat()`: ```typescript const response = await client.chat('Your prompt here', { model: 'gpt-oss-20b', // the only typed live model in SDK 1.1.0 }); ``` The SDK type lists `gpt-oss-20b` and `qwen3-8b`. The second maps to the retired `qwen3:8b` node, so do not use it. Any other string passes through unchanged, so `nosana:qwen3.8:27b` works with a type cast. A typed alias needs a new SDK release (Soon). The chat app lets you pick `qwen3.8:27b` today. Call `list_models` on the MCP server to see the models `GET /api/v1/models` returns, with the price per million tokens. Today that list holds `gpt-oss:20b` and `gemma4:31b`, so it differs from the chat picker. ## Node warm-up [#node-warm-up] A Nosana node goes idle when nobody uses it. The first request after idle time can fail with the error "Nosana GPU node is warming up". The error is retryable. Wait a short time and send the request again. ## Why self-hosted models? [#why-self-hosted-models] End-to-end privacy only holds if your prompt never reaches a proprietary API. If Solrouter handed your decrypted prompt to OpenAI or Anthropic, that provider would see your plaintext. Client-side encryption and TEE isolation would then buy you nothing. Running only open-weight models on Nosana nodes closes that gap. It means: * No proprietary model provider ever sees your query, your documents, or your reply. * Solrouter's backend never sees your plaintext. It relays ciphertext only. * The model weights are public, so anyone can inspect what runs on your prompt. ## Where plaintext exists [#where-plaintext-exists] The enclave decrypts your prompt and then calls the model on a Nosana GPU node (HTTPS per the documented node URL, not re-verified) with `POST /v1/chat/completions` on that node. The node runs Ollama outside the TDX enclave. So your prompt and the reply exist in plaintext in that node's memory during inference. The node operator could read the prompt during inference. Solrouter does not control that hardware. What holds: Solrouter's backend never sees plaintext, and the request is not linked to your identity on the node. There are no proprietary model APIs in the Solrouter privacy pipeline: no OpenAI, no Anthropic, no Google. `{ encrypted: false }` does not unlock one. It sends your prompt in plaintext to the same self-hosted Nosana models, without TEE isolation. The SDK cannot reach any proprietary model. # On-chain proof (/docs/how-it-works/proof) [Attestation](/docs/how-it-works/attestation) proves the enclave is genuine. The **encryption proof** ties *your specific request* to that enclave. Inside the enclave, a key that exists nowhere else signs the hash of your exact ciphertext, and the receipt goes to Solana. The backend relays it and cannot change it without breaking the signature. Each receipt is a **Light Protocol compressed account**: about 0.000005 SOL each, roughly 400 times cheaper than a normal Solana PDA. That cost gap is why a proof per message is viable. ## What the record stores [#what-the-record-stores] A v2 attestation lives under the Solrouter program `ATMRatMtsKX4bHax7U4FRdhbE4mjU4NKpDZGqZqAhBKb`. Its address is deterministic: `deriveAddressV2(["attestation_v2", sha256(ciphertext)])`, so a bare ciphertext hash is enough to find it. Alongside `model`, `provider`, `timestamp`, `backend_saw_plaintext`, and `tee_processed`, it stores the proof: | Field | Meaning | | --------------------------------- | ------------------------------------------------------------------------- | | `client_pubkey` | Your ephemeral X25519 key from the request | | `tee_pubkey` | The enclave's X25519 sealing key your ciphertext was sealed to | | `nonce` | The RescueCipher nonce | | `enclave_pubkey` | The enclave's ed25519 signing key, bound inside the per-request TDX quote | | `enclave_sig_r` / `enclave_sig_s` | The two halves of the ed25519 signature | | `tdx_quote_hash` | `sha256` of the TDX quote that attests `enclave_pubkey` | Inside the enclave, the signature covers this exact byte tuple: ``` "SOLR-ATTEST-v2" ‖ sha256(ciphertext) // 32 your encrypted prompt ‖ tee_pubkey // 32 the sealing key ‖ nonce // 16 ‖ client_pubkey // 32 your request key ‖ len(model) ‖ model ‖ len(provider) ‖ provider ``` A `POST /tee/process` reply returns this record under `onchainAttestation` (`address`, `signature`, `explorerUrl`), or `null` if the commit failed. The SDK exposes `privacyAttestationId` only; call the REST route for the full object. ## Why a forged receipt is detectable [#why-a-forged-receipt-is-detectable] The ed25519 signing key is generated inside the enclave at boot and never leaves it. The backend pays for the Solana transaction with its deployer wallet, so it could commit a record carrying any key and signature. The on-chain program stores those fields without checking them. The safeguard is the per-request TDX quote: its `report_data` equals `sha256(tee_pubkey ‖ enclave_pubkey)`, so `enclave_pubkey` is pinned to attested hardware. A verifier who checks that binding can tell a real enclave key from a forged one. A passing signature check proves the key in the record signed your exact ciphertext, and that the record is on-chain. The per-request quote check proves that key belongs to an attested TDX enclave. The deepest level, Intel's full DCAP chain on the raw quote, needs the live quote from `GET /tee/attestation`. ## Check one yourself [#check-one-yourself] Paste a lock link, address, transaction, or ciphertext hash into the verifier on [Check a reply yourself](/docs/verify), or read a receipt directly through the [proof-lookup endpoints](/docs/how-it-works/attestation#on-chain-attestation-anchor). # The TEE request flow (/docs/how-it-works/request-flow) This page follows one `client.chat()` call through the SDK, the Solrouter backend, the Intel TDX enclave, the Nosana GPU node, and back. Use the step list to walk the map. Each step names its payload and source file. **In words** 1. `GET /tee/public-key` returns `{publicKey, publicKeySha256, algorithm, teeType}`. The SDK caches it for the life of the process. 2. The SDK makes an ephemeral X25519 keypair and derives the shared secret with the enclave key. 3. RescueCipher encrypts the prompt in packed-31 form. The bundle is `{ciphertext, nonce, publicKey, version: '2.0-packed31'}`. 4. `POST /tee/process` with a Bearer key. What leaves the machine: the ciphertext bundle plus, in plaintext, the API key, model id, `chatId`, and the optional `systemPrompt`, `useRAG`, `ragCollection`, `useLiveSearch`. 5. The backend forwards `{encryptedPrompt, model, privacyAttestationId}` unchanged. 6. The CVM derives the shared secret with its X25519 private key and decrypts. 7. The CVM calls the configured Nosana endpoint at `/v1/chat/completions` with the plaintext prompt. HTTPS per the documented node URL, not re-verified. 8. The CVM encrypts the reply to your key, signs the `SOLR-ATTEST-v2` tuple, and requests a tappd quote with `report_data = sha256(x25519 || ed25519)`. 9. The backend commits the compressed receipt and returns `{success, encryptedResponse, attestation, encryptionProof, requestId, metadata, backendRole: 'BLIND_RELAY', onchainAttestation, privacyProof}`. 10. The SDK decrypts `encryptedResponse` with the session private key. ## The short picture [#the-short-picture] **In words** * Your device encrypts. The backend relays ciphertext. The enclave decrypts. The Nosana node runs the model in plaintext. The reply returns encrypted. ## Key custody [#key-custody] * Your device: an ephemeral X25519 private key per session. Never sent. * Solrouter backend: no key on this path. * Enclave: an X25519 sealing key and an ed25519 signing key, generated at boot and never exported. * Nosana node: no key. It receives plaintext from the enclave. # What is a TEE? (/docs/how-it-works/what-is-a-tee) ## The question [#the-question] You type a prompt. A computer somewhere works on it. Who can read the prompt while that happens? With a normal AI service, the answer is "the company that runs the server". Their staff can read it. Their logs can store it. A TEE changes that answer for one part of the path. TEE is short for Trusted Execution Environment. In plain words: a sealed part of a computer. The program inside can work on your data. The people who own the computer cannot look in. See the [glossary](/docs/glossary) for the short form of every term on this page. ## The sealed room [#the-sealed-room] **In words** * A data center holds many computers. One of them runs Solrouter's private inference program. * That program lives in a sealed room called a Confidential VM. A VM is a virtual machine: one computer pretending to be a separate, smaller computer. * The room has one locked slot. Only data sealed to the room's public key can go in. Your device seals your prompt to that key before it leaves your machine. * The room has one window. Through it, anyone can read a signed note that says which program is running inside. That note is the attestation. * The operator of the data center stands outside. The CPU encrypts everything in the room's memory, so the operator cannot open the door. Where the analogy breaks: a real sealed room keeps everything inside. A TEE does not. The program inside can still send data out to other computers. The next sections say where Solrouter's program does that. ## Two layers [#two-layers] A TEE gives you two separate promises. ### Layer 1: isolation [#layer-1-isolation] The CPU chip encrypts the memory of the Confidential VM. The cloud operator, the host operating system, and any other program on the same machine see only scrambled bytes. This is why Solrouter can say its own backend and its cloud host never see your prompt in plain text. ### Layer 2: attestation [#layer-2-attestation] A sealed room could still hold the wrong program, one that copies your prompt somewhere. Attestation closes that gap. The chip signs a short note. The note says: "This exact program is running in this room right now." Anyone can fetch the note and check the signature against Intel's public records. Solrouter also puts a fingerprint of the room's public key inside the note. When you check the note, you also confirm the key belongs to this room. An impostor cannot fake that. Think of a tamper-evident seal on a package. The seal shows the package was not opened. It does not tell you what is inside. Attestation proves which program runs. It does not by itself prove the program is a good one. For that you need to compare the note against known reference values. Solrouter does not publish those values yet. Reference measurements: Soon. ## What a TEE does not do [#what-a-tee-does-not-do] A TEE stops outsiders from looking in. It does not stop the program inside from talking out. Solrouter's program inside the enclave decrypts your prompt. It then sends the plain-text prompt to a Nosana GPU node, a separate computer that runs the language model. That node is outside the sealed room. The node can see your prompt and the reply during inference. Solrouter does not control that hardware. The request is not linked to your identity on the node. Solrouter's own deployment file states the same limit. It claims "Solrouter never sees your prompt or searches" and adds "NOT no one sees them". Solrouter's backend and its cloud host cannot read your prompt. The Nosana GPU node that runs the model can, while it works on it. The page [What is private here](/docs/use/what-is-private) has the full table of who can see what. ## How Solrouter uses one [#how-solrouter-uses-one] Solrouter runs its enclave as an Intel TDX Confidential VM on Phala Cloud. TDX is Intel's name for this kind of sealed VM. Phala Cloud is the hosting service that provides the TDX machines. When the enclave boots, it makes a fresh key pair inside the sealed room. The private half never leaves. The public half is published at `GET https://api.solrouter.com/tee/public-key`. That reply names the enclave type as `INTEL-TDX-PHALA`. The key changes on every reboot, so a copied key from last week is useless. The signed note comes from `GET https://api.solrouter.com/tee/attestation`. Solrouter's program asks the Phala guest agent inside the same Confidential VM for it, then passes it to you unchanged. ## Check it yourself [#check-it-yourself] You do not have to take Solrouter's word for any of this. # Chat App (/docs/use/chat-app) Most AI chat tools want your email, store your conversations in plaintext, and lock you into a single model. Solrouter Chat at [solrouter.com/chat](https://solrouter.com/chat) works differently. You never create an account. You can turn on Maximum Privacy Mode to encrypt each prompt before it leaves your browser. You connect a Solana wallet, top up a prepaid balance, and pick one of two open-weight models. You also get file attachments and a RAG knowledge base. Encryption is a toggle. It is off by default. ## Two privacy modes [#two-privacy-modes] Encryption is a toggle, off by default. Here is what each mode does with your prompt and your history. | | Persistent (default) | Maximum Privacy (on) | | --------------------- | ----------------------------------------------------- | -------------------------------------- | | Prompt to the backend | plaintext | encrypted in your browser | | Opened where | backend, then the Nosana node | only the enclave, then the Nosana node | | Chat history | stored, encrypted at rest under a key Solrouter holds | not stored, lost on refresh | | Best for | everyday chats | sensitive prompts | For exactly who can read what, see [What is private here](/docs/use/what-is-private). ## Features [#features] Turn on Maximum Privacy Mode to encrypt each prompt before it leaves your browser. Off by default. Send images and documents with your question. Attachments are not encrypted, only the text prompt is. Upload files and ask questions grounded in them. Stored chunks are not encrypted at rest. Image and video generation is Archived. It is disabled in the chat app. ## Getting started [#getting-started] Four steps take you from a blank browser tab to your first message. ### Open the chat app [#open-the-chat-app] Go to [solrouter.com/chat](https://solrouter.com/chat) in your browser. ### Connect your Solana wallet [#connect-your-solana-wallet] Click **Connect Wallet** and approve the request in your wallet. Your wallet is your identity here. No email address or personal information is required. ### Top up your balance [#top-up-your-balance] Add credits in **USDC** or **`$ROUTER`**. You pay per call from this prepaid balance, so you only spend on what you use. ### Select a model and start chatting [#select-a-model-and-start-chatting] Pick a model from the selector and send your first message. To encrypt the prompt in your browser, turn on **Maximum Privacy Mode** first. It is off by default, and history is not kept while it is on. If the GPU node was idle, the first reply can say "Nosana GPU node is warming up". Wait a moment and send the message again. ## Supported models [#supported-models] The chat model picker lists two self-hosted open-weight models on the Nosana GPU network: `gpt-oss:20b` (Default, Live) and `qwen3.8:27b` (Uncensored, Live). No proprietary model is reachable in the chat app. Maximum Privacy Mode works with both models. For model ids and their status, see the [Supported Models](/docs/how-it-works/models) page. In Maximum Privacy Mode, Solrouter's backend cannot read your prompt. Plaintext exists on your device, inside the Intel TDX enclave, and on the Nosana GPU node that runs the model during inference. Solrouter does not control that node's hardware. # Pricing (/docs/use/pricing) Prepaid billing and x402 are Live. Buyback and burn is Soon: the configured ratios are not published. Most AI platforms make you commit before you build: a monthly subscription, a credit card on file, an email to verify. Solrouter does none of that. You pay per API call from a balance you fund yourself, so you only ever spend what you use. The setup is short. Connect a Solana wallet at [solrouter.com/sdk](https://solrouter.com/sdk), fund your balance with USDC or `$ROUTER`, generate an API key, and start building. Every product (the Privacy SDK, Agent Privacy API, MCP server, and chat app) draws from that same prepaid balance. You pay per call from your prepaid balance. `GET /payments/pricing` returns the live rate table. ## Feature status [#feature-status] | Feature | Status | | ------------------------------------------------------------ | ------ | | Prepaid balance in USDC or `$ROUTER` | Live | | Per-token metering (tables below) | Live | | x402 keyless payment on `POST /api/v1/x402/chat/completions` | Live | | `$ROUTER` buyback and burn | Soon | ## Payment methods [#payment-methods] You can fund your balance two ways. The difference is what each one means for you and for the token. Pay with USDC from your Solana wallet. As a stablecoin pegged to the dollar, its value stays put, so you carry no price risk between top-ups. Top up at any time from [solrouter.com/sdk](https://solrouter.com/sdk). Pay with the native `$ROUTER` token. Fees paid in `$ROUTER` feed the buyback-and-burn mechanism. See [$ROUTER buyback and burn](#router-buyback-and-burn) below. ## Per-call rates [#per-call-rates] Two rate tables exist in code. Table A bills the chat app and `POST /agent`. Table B bills the REST route `POST /api/v1/chat/completions`. `GET /payments/pricing` is the live source for table A. **Table A: chat and `/agent` billing.** Rates are USD per 1M tokens, before a 20% margin. The balance is held as app tokens at 1,000 app tokens per USD. Each billed direction has a floor of 10 app tokens ($0.01), so a normal inference costs at least $0.02. Charges settle in `$ROUTER` first and fall back to USDC. | Model | Input | Output | | ------------- | ----- | ------ | | `gpt-oss:20b` | $0.10 | $0.20 | | `qwen3.8:27b` | $0.15 | $0.30 | | `gemma4:31b` | $0.15 | $0.30 | **Table B: `POST /api/v1/chat/completions`.** Rates are USD per 1M tokens. No margin and no floor apply. Token counts are estimated from character counts (about 4 characters per token). The cost is debited from your USDC balance only. The route answers 402 when your USDC balance is zero, even if you hold `$ROUTER`. | Model | Input | Output | | ------------- | ----- | ------ | | `gpt-oss:20b` | $0.15 | $0.30 | | `qwen3.8:27b` | $0.15 | $0.30 | | `gemma4:31b` | $0.15 | $0.30 | The two tables disagree on `gpt-oss:20b`. Unifying them is a backend follow-up. x402 route prices come from the manifest at `https://api.solrouter.com/.well-known/x402`. ## x402 keyless payments [#x402-keyless-payments] An autonomous agent often has no human around to sign up for an account or manage an API key. x402 solves that: it lets an agent pay for each call on its own, with no registration at all. x402 is a standard for HTTP-native micropayments (payments built into the web request itself), settled in USDC on Solana mainnet. The live manifest advertises Coinbase (`api.cdp.coinbase.com/x402`) as the facilitator. The manifest shows `X402_FACILITATOR_URL` when it is set, or a built-in default when it is not. It does not show which facilitator settles payments. Any x402-aware agent can discover the payment manifest and start paying immediately. * **Discovery:** `https://api.solrouter.com/.well-known/x402`, the x402 paywall manifest listing available endpoints and pricing. * **Endpoint:** `POST /api/v1/x402/chat/completions`. Arcium-encrypted prompt in, encrypted response out. * **Price:** $0.005 per call. Solrouter's server verifies and settles the USDC payment through its facilitator and then returns the reply. * **Best for:** autonomous agents that self-fund their own inference costs without a human managing API keys. Going keyless costs you no privacy. The x402 path runs the same end-to-end encrypted inference as the API-key path: your prompt is still encrypted with Arcium's RescueCipher before it reaches Solrouter's backend. The paywall charges $0.005 per call. The response body of this endpoint currently reports `paid.amount: 0.02`. This is a backend follow-up; the manifest price is the one charged. ## Managing your balance [#managing-your-balance] You can read your current balance straight from the SDK, so an agent or app can check funds before it spends and top up when it runs low. ```typescript const { balance, balanceFormatted } = await client.getBalance(); console.log(`Balance: ${balanceFormatted}`); ``` To add funds, visit [solrouter.com/sdk](https://solrouter.com/sdk) and connect your Solana wallet. Deposit USDC or `$ROUTER`. Deposit minimum: not determined. ## $ROUTER buyback and burn [#router-buyback-and-burn] Status: Soon. The mechanism exists in code. The ratios are runtime settings, and the worker skips its run while they are zero. * A buyback worker reads the USDC inflow for each window and buys `$ROUTER` with a configured share of it (`BURN_USDC_BPS`). * A configured share of the `$ROUTER` bought back is burned (`BURN_OUTPUT_TOKEN_BPS`). The rest stays in treasury. * A configured share of fees paid directly in `$ROUTER` is burned on receipt (`BURN_TOKEN_BPS`). The rest stays in treasury. Configured ratios: not published. `GET /payments/buyback/log` returns the recent buyback worker ticks, including skipped ones. See [/docs/token](/docs/token) for the token supply schedule and vesting details. # What is private here (/docs/use/what-is-private) Each row states its own Live or Soon status. ## The question [#the-question] "If I type something private into Solrouter, who can read it?" With encryption on, Solrouter's own servers cannot read your prompt or the reply. The prompt is opened only inside a sealed computer (a TEE) and on the rented GPU machine that runs the AI model. Your attached files, your knowledge base, and your saved chat history do not get that protection, and the tables below show exactly where each one is readable. Encryption is a toggle in the chat app. It is off by default. The Privacy SDK encrypts by default. The page [Chat app](/docs/use/chat-app) explains the toggle. This page explains what each setting exposes. ## Who is who [#who-is-who] The matrix shows five parties. You are the sixth: your own device always reads your own words. Here is each party in plain words. * **You.** Your browser, or the program that uses the SDK. * **Network observer.** Anyone who watches the connection between you and Solrouter. For example, your internet provider or the owner of a public Wi-Fi. * **Solrouter backend.** Solrouter's own servers. They check your login, take payment, store your history, and pass messages along. * **CVM cloud host (Phala).** The company that owns the physical machine where the sealed computer runs. The sealed computer is a Confidential Virtual Machine (CVM). The processor encrypts its memory, so the machine owner cannot read it. See [What is a TEE?](/docs/how-it-works/what-is-a-tee). * **Nosana GPU host.** The operator of the graphics-card machine that runs the AI model. Solrouter rents it from the Nosana network. Solrouter does not control that hardware. * **Solana observer.** Anyone who reads the public Solana blockchain. Solrouter posts a receipt there for each encrypted request. Each cell says what that party can see. The legend under the matrix explains every word. ## Who can see what [#who-can-see-what] Rows here describe the encrypted path: the Privacy SDK with its default settings, or the chat app with Maximum Privacy Mode on. Amber cells mark the only places a party can read your words. Every row is Live. **Notes** * **Nosana GPU host, readable.** The model runs there in plaintext for the length of one request. Solrouter rents the machine and does not control it. The request is not linked to your identity. * **Chat history, at rest.** Persistent mode stores each message encrypted with AES-256-GCM under a key the backend holds. That protects against a stolen database copy, not against Solrouter. On this path the backend also reads the prompt in plaintext on the way in. * **Knowledge-base documents.** Files are split and embedded on the server as plain text, not encrypted at rest. Whether the deployed app isolates collections per user is not determined. * **Attached files.** The encrypted path sends the text prompt only. Attachments travel on the default path below. * **Web searches** is the encrypted agent path: a REST call to `/agent` with `encryptedPrompt`, Live for REST and Soon for the SDK. Search runs through SearXNG inside the enclave, which then queries public engines. Those engines receive the search text. * The enclave logs the first 50 characters of each reply. Who can read that log is not determined. ## Default chat (toggle off) [#default-chat-toggle-off] This is the chat app with the toggle off, the SDK with `encrypted: false`, and guest chat. There is no client-side encryption, so the backend and the model node read your words. Only the rows that change from the matrix above are shown. Status: Live. On this path attachments and knowledge-base files reach the backend and the model node in plaintext. Documents upload to Cloudflare R2 through a short-lived link, and the backend extracts their text. Live search and the `web_search` tool use Brave, with DuckDuckGo and Wikipedia as fallbacks, so those services receive the search text. ## Where your words are readable [#where-your-words-are-readable] The strip below follows one encrypted request from your device to the Solana receipt. Green zones hold your words in readable form. Grey zones hold only ciphertext or a hash. The amber zone is the rented GPU machine. **In words** * Your device: your words are readable here. Your browser or program scrambles them before they leave. * Network: ciphertext only. A watcher sees size and timing. * Solrouter backend: ciphertext only. It checks your login, bills you, and passes the blob along. * TDX CVM: your words are readable here, inside memory that the processor encrypts. The machine owner cannot open it. * Nosana GPU node: your words are readable here while the model runs. Solrouter does not control this machine. The request is not linked to you. * Solana: hash only. A receipt proves a request happened and names the model. It does not hold your words. Solrouter does not run fully homomorphic encryption (FHE) inference. FHE means a computer works on scrambled data without ever unscrambling it. No production system runs AI models of this size under FHE in 2026. The compute cost is many orders of magnitude away from usable speed. Anyone who claims "FHE LLM inference" in production is overclaiming. What Solrouter provides is encryption on your device, a hardware-isolated CVM that unscrambles the prompt, a model that runs on a rented Nosana GPU node, and a receipt on Solana for each encrypted request. That is a real and checkable guarantee. It is not FHE, and we will not claim otherwise. ## What we keep [#what-we-keep] Retention periods are not published. Each row states what is stored, in what form, who holds the key, and how to remove it. Rows are Live unless marked.
| What | Where | Format | Key holder | How to delete | Retention | | ---------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- | --------------------------------------------------- | | Chat rows (`enc:v1:`) | Solrouter's database, tables `chat_messages` and `chats` (message text, chat title, search and knowledge-base context, pitch-deck cards) | Scrambled with AES-256-GCM, stored as text with the prefix `enc:v1:` | Solrouter backend, from `CHAT_CONTENT_KEK` or `WALLET_VAULT_KEK` in the server settings | Deleting a chat in the app marks it archived. The rows stay in the database. A hard delete path: not determined | not published | | Memory envelope (`umem:v1:`) | Solrouter's database, table `user_memory`, one row per user | Scrambled in your browser with AES-256-GCM, stored as text with the prefix `umem:v1:`. Solrouter cannot read it | You. The key comes from your wallet signature and is never stored | "Forget all" in the app removes the row (`DELETE /memory`) | not published | | Knowledge-base chunks | Files on the backend server disk, one JSON file per collection under `data/vectors/` | Plain text chunks plus embedding vectors. Not encrypted | none | Delete the whole collection (`DELETE /rag/collections/:name`). Delete of one document: not determined | not published | | Uploaded files (R2) | A Cloudflare R2 bucket, key `documents/` plus a timestamp and a random id | The original file. Not encrypted by Solrouter | none | not determined. The code has upload and download, no delete | not published | | Usage log | Solrouter's database, table `api_usage` | Plain rows: key id, user id, model, token counts, cost, request id, time. No prompt text | none | not determined | not published | | Swap session rows (Soon) | Solrouter's database, table `agent_swap_sessions` | Plain rows: mode, state, tokens, amount, destination address, transaction ids, payer id, webhook URL. The one-shot signer key is scrambled and set to null when the swap ends | Solrouter backend, `WALLET_VAULT_KEK`, for the signer key only | not determined. The API has read and webhook routes, no delete | not published. Pending sessions expire after 7 days | | Guest per-IP counter | Backend process memory, not a database | Your internet address, a message count, and a reset time | none | No route. The entry resets 24 hours after first use and vanishes when the server restarts | not published |
Guest chat sends your prompt in plaintext to the backend and then to the model node. It stores no chat rows. ## Short answers [#short-answers] * "Can Solrouter read my prompt?" With the toggle on, or with the SDK default, no. With the toggle off, yes. * "Can Solrouter read my history?" In Persistent mode, yes. It holds the key. In Maximum Privacy mode there is no history. * "Can Solrouter read my memory?" No. Only your wallet can unlock it. * "Can Solrouter read my uploaded documents?" Yes. They are stored as plain text and plain files. * "Can anyone else read my prompt?" The operator of the Nosana GPU node could, while the model runs. No one else. * "Is my wallet address public?" It is not on the Solana receipt. Solrouter's backend knows it. ## Next [#next] Where the Privacy Mode toggle is and what each mode keeps. Paste the lock link from a reply and see the receipt check pass. The sealed-room picture and where it breaks. Every term on this page in one line each. # GET /tee/public-key (/docs/api-reference/tee/public-key) This endpoint returns the X25519 public key that is active inside the Solrouter Intel TDX enclave (a TEE, a trusted execution environment: a hardware-isolated virtual machine). Before you send a prompt, your client encrypts it to this key with Arcium's RescueCipher. Only the enclave can decrypt it. The Solrouter backend receives an opaque ciphertext blob and relays it to the enclave. It cannot read the contents. ## Endpoint [#endpoint] ``` GET https://api.solrouter.com/tee/public-key ``` No authentication is required for this endpoint. The backend proxies the call to the enclave and returns the enclave's body unchanged. ## Response [#response] | Field | Type | Description | | --------------- | ------ | -------------------------------------------------------------------------------------------------------------------- | | publicKey | string | The enclave's current X25519 public key, base64 encoded. Use it as the recipient key for client-side encryption. | | publicKeySha256 | string | Hex sha256 of the raw 32-byte public key. `GET /tee/attestation` pins this same digest in the quote's `report_data`. | | algorithm | string | Always `x25519` (lower case). | | teeType | string | Always `INTEL-TDX-PHALA`. | This response has no top-level `success` field. ## Example [#example] ```bash curl "https://api.solrouter.com/tee/public-key" ``` Example response (shape checked against the live endpoint on 2026-08-26; values shortened): ```json { "publicKey": "base64...=", "publicKeySha256": "hex...", "algorithm": "x25519", "teeType": "INTEL-TDX-PHALA" } ``` ## Errors [#errors] When the backend cannot reach the enclave, it answers with status 502 and this body: ```json { "error": "tee_unreachable", "message": "...", "teeEndpoint": "..." } ``` ## When to use this [#when-to-use-this] In most cases you do not need to call this endpoint yourself. * **`@solrouter/sdk`**: the SDK fetches the key once per process and caches it. You never handle the key yourself. * **`@solrouter/agent-tools`** (Soon): the package is not on npm yet. * **Custom client**: if you write your own encryption layer (for example in a language with no Solrouter SDK), fetch this endpoint first. Then use `publicKey` as the X25519 recipient key in your RescueCipher key-exchange flow. ## Key lifetime and caching [#key-lifetime-and-caching] The enclave generates a new X25519 keypair on every CVM boot. The SDK fetches the key once per process and keeps it until you call `clearSession()`. A long-lived process can hold a stale key after the enclave restarts. When a request fails with `tee_unreachable`, or a reply fails to decrypt, call `clearSession()` and retry. The next request fetches the current key. Custom clients should do the same: cache the key, and fetch it again after a failure. The X25519 keypair is generated inside the Confidential VM at boot. The private key never leaves the enclave: not to the Solrouter backend, not to any host process, and not to Solrouter staff. You can check this claim yourself. `GET /tee/attestation` returns a TDX quote whose `report_data` equals `sha256(publicKey)`. See the [attestation guide](/docs/how-it-works/attestation). # Provision a managed Umbra wallet (Mode A) (/docs/api-reference/agent-privacy/managed-wallets/agents/v1/wallets/post) {/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} # Quote a private swap (/docs/api-reference/agent-privacy/swaps/agents/v1/quote/get) {/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} # Pool depth for a denomination bucket (/docs/api-reference/agent-privacy/swaps/agents/v1/anonymity-set/get) {/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} # On-chain attestation for a settled session (/docs/api-reference/agent-privacy/sessions/agents/v1/attestations/sessionid/get) {/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} # Poll a swap session's state (/docs/api-reference/agent-privacy/sessions/agents/v1/sessions/id/get) {/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} # Begin a one-shot ephemeral-wallet private swap (Mode B) (/docs/api-reference/agent-privacy/swaps/agents/v1/swaps/oneshot/post) {/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} # Cryptographically private AI inference (Arcium-encrypted, x402-paywalled) (/docs/api-reference/agent-privacy/inference/api/v1/x402/chat/completions/post) {/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} # Wallet operation audit log (/docs/api-reference/agent-privacy/managed-wallets/agents/v1/wallets/id/audit/get) {/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} # Build an unsigned funding tx for the agent to sign (/docs/api-reference/agent-privacy/managed-wallets/agents/v1/wallets/id/fund-intent/post) {/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} # Run a private swap from a managed wallet (Mode A) (/docs/api-reference/agent-privacy/managed-wallets/agents/v1/wallets/id/swap/post) {/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} # Encrypted-balance snapshot for given mints (/docs/api-reference/agent-privacy/private-balance/agents/v1/wallets/id/balance/get) {/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} # Encrypt a public balance into an encrypted balance (/docs/api-reference/agent-privacy/private-balance/agents/v1/wallets/id/encrypt/post) {/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} # Shield + unlink: mixer round-trip to a fresh address (/docs/api-reference/agent-privacy/private-balance/agents/v1/wallets/id/shield/post) {/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} # Withdraw from encrypted balance to a public address (/docs/api-reference/agent-privacy/private-balance/agents/v1/wallets/id/withdraw/post) {/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} # Submit fundingTxSig and start the swap orchestrator (/docs/api-reference/agent-privacy/swaps/agents/v1/swaps/oneshot/id/execute/post) {/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}