Orrery docs. Run a router, or run a node.
Orrery is a peer-to-peer AI inference network. A router on your computer sends each request straight to a provider's machine and pays it through a Solana payment channel. Nothing sits in between, and the program takes no fee.
01Quickstart
- Install the router from npm:
npm install -g orrery-node(Node 22 or newer). - Fund a Solana wallet with a few USDC and a little SOL.
- Run
orrery router --wallet <keypair.json> --rpc <your RPC>. - Point any OpenAI-compatible tool at
http://127.0.0.1:7171/v1with any API key, and add:cheap,:fast,:trustedor:privateto the model name when you care.
That is the whole setup. There is no account, no sign-up and no API key to buy: your wallet is the account, and each answer is paid to the machine that served it.
02Install
The router and the provider node are one npm package, orrery-node, which installs a single command, orrery. It needs Node.js 22 or newer and runs on macOS, Linux and Windows (WSL).
node --version # v22 or newer npm install -g orrery-node orrery help
Or run it once without installing:
npx -y orrery-node router --wallet ~/.config/solana/id.json
Everything the router keeps lives in ~/.orrery (set ORRERY_HOME to move it): the session key that signs your running totals and the list of your open channels. Updating is npm install -g orrery-node@latest; uninstalling is npm uninstall -g orrery-node after you close your channels.
03Fund a wallet
The router pays from an ordinary Solana wallet file (the JSON keypair the Solana CLI writes). Use a dedicated wallet with only what you plan to spend.
solana-keygen new -o ~/.config/solana/orrery.json # or use a wallet file you already have solana address -k ~/.config/solana/orrery.json # send USDC and a little SOL here
| Needs | How much | Why |
|---|---|---|
| USDC | $2 per provider you use | each new channel locks a deposit (--deposit, default 2). What you do not spend comes back when the channel closes. |
| SOL | about 0.05 | transaction fees, and the rent for each channel's accounts, which is returned when the channel closes |
| RPC | one URL | the router reads the provider list with a filtered getProgramAccounts call; most paid RPCs allow it, the public endpoint is rate limited |
Settlement is in mainnet USDC (EPjF…m3CY). Only this wallet signs to open, top up or close a channel; every answer in between is signed by a session key on your computer.
04Run the router
orrery router \ --wallet ~/.config/solana/orrery.json \ --rpc https://your-rpc.example.com \ --daily-cap 5
Orrery router on http://127.0.0.1:7171/v1 · 3 providers · route auto · wallet 7xKXtg… session key 9WzDXw…AWWM signs vouchers; console at http://127.0.0.1:7171/
The router listens on 127.0.0.1 only, so nothing outside your computer can reach it. Its console at http://127.0.0.1:7171/ shows every request, the provider that answered, the tokens and what it cost, plus your providers and channels.
| Flag | Default | What it does |
|---|---|---|
--wallet | none | wallet file that opens and tops up channels. Without it the router is read-only. Env ORRERY_WALLET. |
--rpc | public mainnet | Solana RPC URL. Env ORRERY_RPC_URL. |
--route | auto | default route: auto, price, speed, reputation, privacy |
--deposit | 2 | USDC locked when a channel opens |
--top-up-below | 0.25 | add to a channel when what is left falls under this |
--daily-cap | 5 | stop spending for the day after this many USDC |
--min-reputation | 0.2 | the auto route ignores providers below this score (0 to 1) |
--port | 7171 | local port |
--refresh | 60 | seconds between re-reading the provider list from the chain |
--enclave-images | none | comma-separated enclave measurements you trust for :private |
05Connect your tools
The router speaks the OpenAI Chat Completions API (/v1/chat/completions, streaming or not, and /v1/models). Anything that lets you set a base URL works unchanged. The API key can be any string.
| Tool | Where to set it |
|---|---|
| Cursor | Settings → Models → OpenAI API key: any string, Override OpenAI Base URL: http://127.0.0.1:7171/v1, then add a model such as llama-3.3-70b:cheap |
| Continue | a model with provider: openai, apiBase: http://127.0.0.1:7171/v1, model: deepseek-v3 |
| Aider | OPENAI_API_BASE=http://127.0.0.1:7171/v1 aider --model openai/llama-3.3-70b:cheap |
| Open WebUI | Admin → Connections → OpenAI API: URL http://127.0.0.1:7171/v1, any key |
| LangChain | ChatOpenAI(base_url="http://127.0.0.1:7171/v1", api_key="unused", model="qwen3-235b") |
from openai import OpenAI
client = OpenAI(base_url="http://127.0.0.1:7171/v1", api_key="unused")
reply = client.chat.completions.create(
model="llama-3.3-70b:cheap",
messages=[{"role": "user", "content": "Hello from the network"}],
)
print(reply.choices[0].message.content)import OpenAI from 'openai';
const client = new OpenAI({ baseURL: 'http://127.0.0.1:7171/v1', apiKey: 'unused' });
const stream = await client.chat.completions.create({
model: 'deepseek-v3:fast',
stream: true,
messages: [{ role: 'user', content: 'Explain payment channels in two lines' }],
});
for await (const chunk of stream) process.stdout.write(chunk.choices[0]?.delta?.content ?? '');curl http://127.0.0.1:7171/v1/chat/completions \
-H 'content-type: application/json' \
-d '{"model":"gpt-oss-120b:cheap","messages":[{"role":"user","content":"hi"}]}' -iEvery answer carries three headers so you can see who served it and what you paid:
x-orrery-provider: 5Tq7… (the provider's on-chain listing) x-orrery-route: price x-orrery-cost: 0.000009 (USDC, signed into your channel)
The Anthropic Messages API (/v1/messages) is not served yet. Tools that only speak it cannot use the router today.
06Models and routes
Model names are the ones providers list. orrery models prints every model on the network with its cheapest price and how many providers serve it; orrery providers prints every listing with what the chain says about it.
$ orrery models llama-3.3-70b 2 providers from $0.12/M in · $0.30/M out deepseek-v3 1 providers from $0.27/M in · $1.10/M out gpt-oss-120b 1 providers from $0.030/M in · $0.11/M out
Add one word after the model name, or send the header x-orrery-route:
| Suffix | Route | Picks |
|---|---|---|
(none) | auto | a blend of expected cost, first-token time and reputation, among providers above your reputation floor |
:cheap | price | lowest expected cost for this request |
:fast | speed | fastest first token this router has measured |
:trusted | reputation | highest reputation: USDC settled on chain, number of redemptions, age, bonded $OREY, and this router's own success rate |
:private | privacy | only providers whose enclave this router verified; the request is sealed |
The router tries the top three providers in order, so a slow or failing one is skipped without your tool noticing. Pin one provider with the header x-orrery-provider: <first characters of its address>.
07How you pay
The first request to a provider opens a payment channel: your wallet moves a deposit into an account only the channel controls. From then on the session key on your computer signs a running total after every answer, an 88-byte voucher: orrery/voucher/1, the channel, the total, and a hash chain over every answer's usage.
- The router prices every answer itself from the provider's published price list and pays the smaller of its number and the provider's.
- A provider serves the next request only once the previous one is paid, so it is never owed more than one answer, and you never pay for an answer you did not get.
- The provider redeems the highest total whenever it likes. The program pays the difference to the provider's own token account and nowhere else.
- When what is left in a channel falls under
--top-up-below, the router asks your wallet to add another deposit.--daily-capstops all spending for the day.
08Your channels
Closing takes two steps so a provider cannot be cheated out of its last answer: you start the close, the provider gets a grace period (one hour) to redeem what you signed, then everything left comes back to you. A provider can also release a channel at any time, which returns the rest at once.
$ orrery channels 5HURjz… provider CakCxW… deposit $2.00 you signed $0.000136 redeemed $0.000136 open $ orrery close 5HURjz… --wallet ~/.config/solana/orrery.json closing 5HURjz…; the provider has 3600s to redeem, then run: orrery close 5HURjz… --wallet … --finish $ orrery close 5HURjz… --wallet ~/.config/solana/orrery.json --finish closed 5HURjz…; $1.999864 and the account rent are back in your wallet
09Run a provider
Put a provider node in front of any OpenAI-compatible server: vLLM, SGLang, Ollama, llama.cpp, or API capacity you hold. Routers anywhere can then pay you per answer. There is no application, no stake to post and nobody who can delist you.
- Serve a model locally with an OpenAI-compatible server, or have an API key.
- Make it reachable at a public HTTPS URL (a reverse proxy, a tunnel, or a host with a public IP). Routers call
/v1/manifest,/v1/chat/completionsand/orrery/voucheron it. - Write
provider.jsonwith your prices (next section). - Register with your wallet, then start the node.
orrery provider register --config provider.json --wallet ~/.config/solana/provider-wallet.json orrery provider show --config provider.json # what the chain now says, including your node key orrery provider start --config provider.json
Registering lists you on chain with your endpoint and the sha-256 of your price list; routers ignore a price list that does not match it. Your wallet stays cold: the node runs on its own key (created at ~/.orrery/provider/node.json), which can redeem and post enclave claims but never touches what you have earned. Send the node key about 0.01 SOL for redemption fees. Redemptions go straight to your wallet's USDC account. To change prices or the URL, edit the file and run register again; it updates the listing.
vLLM "baseUrl": "http://127.0.0.1:8000/v1" SGLang "baseUrl": "http://127.0.0.1:30000/v1" Ollama "baseUrl": "http://127.0.0.1:11434/v1" llama.cpp "baseUrl": "http://127.0.0.1:8080/v1" OpenRouter "baseUrl": "https://openrouter.ai/api/v1", "apiKeyEnv": "OPENROUTER_API_KEY"
Keep it running with any process manager. A systemd unit:
[Unit] Description=Orrery provider node After=network-online.target [Service] ExecStart=/usr/bin/env orrery provider start --config /etc/orrery/provider.json Environment=OPENROUTER_API_KEY=... Restart=always User=orrery [Install] WantedBy=multi-user.target
Only standard chat fields (messages, sampling settings, tools, response format) reach your upstream. Fields that would let a caller spend your key on something your price list does not cover, such as fallback model lists, plugins or provider routing, are dropped, and so is n.
10provider.json
{
"name": "My GPU",
"region": "eu-west",
"listen": 7272,
"publicUrl": "https://gpu.example.org",
"rpcUrl": "https://your-rpc.example.com",
"settleMint": "EPjFWJd5Wm8eUi4wSPdf8qJxQAXnmSeKEr5mVGYLm3CY",
"wallet": "~/.config/solana/provider-wallet.json",
"upstream": { "baseUrl": "http://127.0.0.1:8000/v1" },
"models": [
{ "id": "llama-3.3-70b", "upstreamModel": "meta-llama/Llama-3.3-70B-Instruct",
"context": 131072, "maxOutput": 8192,
"price": { "input": 120000, "output": 300000 } }
],
"redeem": { "everySecs": 300, "minUnits": 50000 }
}| Field | Meaning |
|---|---|
name, region | shown to routers and in orrery providers |
listen, host | local port (default 7272, or PORT) and bind address (default 0.0.0.0) |
publicUrl | the HTTPS address routers use, up to 120 characters, written on chain |
rpcUrl, settleMint | Solana RPC and the settlement mint (mainnet USDC) |
wallet or authority | the wallet that owns the listing and receives redemptions (a file, or just its address) |
nodeKey or nodeKeyEnv | the node's own key: a file path, or an environment variable holding the JSON key |
dataDir | where the node keeps vouchers until it redeems them; keep it on a disk that survives restarts |
upstream.baseUrl | your OpenAI-compatible server |
upstream.apiKey / apiKeyEnv | the upstream key, inline or (better) from an environment variable |
upstream.headers, upstream.body | extra headers, and fixed fields you add to every upstream call yourself |
models[].id | the name routers ask for |
models[].upstreamModel | the name your upstream knows it by, if different |
models[].price | USDC base units per million tokens: 120000 is $0.12 |
models[].maxOutput, context | caps per request; the router reserves at most maxOutput tokens of payment |
redeem.everySecs, minUnits | how often to cash vouchers, and the smallest amount worth a transaction |
11The private route
A provider running inside a hardware enclave (Intel TDX, AMD SEV-SNP, or TDX with NVIDIA confidential computing) posts a claim on chain: the enclave type, the measured image, the hash of its attestation report, and the key prompts are encrypted to. Claims expire within 30 days.
Before sealing anything, the router fetches the report and checks that it hashes to the on-chain claim, names the same key, has not expired, and booted an image on your allow list (--enclave-images). Checking the hardware vendor's signature needs an external verifier; without one the router does not treat an enclave as verified. Development reports are labeled and refused unless you pass --allow-dev-enclaves.
12$OREY
The token launches on Meteora. In the program it has one role: providers can bond it behind their listing, and routers count the bond as 10% of reputation. A bond is never slashed; unbonding returns it after seven days, only to the provider that posted it.
13The program
Program 7UHevY47LgbJv9hz4iymecR8FURy27jX55KYb1sxytZS on Solana.
- Anyone can list a provider. Nobody, including the admin, can delist one.
- Money in a channel goes only to its provider, with your signed voucher, or back to you.
- There is no fee field and no third recipient.
- The admin can tune the close grace period (10 minutes to 7 days), the unbonding delay (1 hour to 30 days) and the minimum deposit, and set the bond mint once.
14What can go wrong
| If | The most you lose |
|---|---|
| a provider stops answering | nothing: the router moves to the next provider, and it signs only after an answer has arrived |
| a provider overstates its token counts | at most what the router reserved for that one answer (the listed price for maxOutput tokens); it pays the smaller of its own calculation and the provider's, never more than the reservation |
| your session key leaks | what is left in your open channels, which only their providers can redeem; close them |
| your router misbehaves | at most --daily-cap per day |
| a provider's node key leaks | nothing of the provider's: redemptions only ever go to the listing wallet |
15Troubleshooting
| You see | Do this |
|---|---|
No models listed yet. or 0 providers | your RPC refuses getProgramAccounts; pass an RPC that allows it with --rpc |
no wallet configured | start the router with --wallet <keypair.json> |
no provider on the network serves … | check the name with orrery models |
daily spending cap reached | raise --daily-cap or wait for tomorrow (UTC) |
all providers failed | see orrery providers; try another route or model |
| a channel will not open | the wallet needs USDC for the deposit and a little SOL for fees |
SyntaxError or Unsupported engine | upgrade to Node.js 22 or newer |
| port 7171 in use | --port 7172 and use that base URL |
16Status
- Built and tested: the program (13 tests), the router and provider node (13 tests), and a full local rehearsal of the program, three providers and a router (14 of 14 checks).
- Package:
orrery-nodeon npm. Source: github.com/orrery-node/orrery (MIT). - Not yet: the Anthropic Messages API, a built-in hardware attestation verifier.