Pools

Coding agents on a pool

A friend pool's spare GPUs can back Claude Code, Cursor, Cline, and Continue for free. Point the tool at NeuronPool's OpenAI-compatible /v1, authenticate with a pool-scoped API key, and (when the tool can send extra headers) set X-NeuronPool-Route: pool. Jobs stay inside the pool, never touch the ledger, and show up on the pool usage page.

Shared coordinates

FieldValue
Worker origin (until T66 custom domain)https://neuronpool.damnknee.workers.dev
OpenAI-compatible base URLhttps://neuronpool.damnknee.workers.dev/v1
Intended partner hostnamehttps://api.neuronpool.dev/v1
CatalogGET /v1/models
Chat (free pool path)POST /v1/chat/completions
AuthAuthorization: Bearer sk-neuronpool-…
Free pool (never billed)header X-NeuronPool-Route: pool
Pool dashboard/pools/<pool_id> (HTML)
Create a pool/dashboard/pools (HTML form) or POST /v1/pools
Mint a pool-scoped keypool page, or POST /v1/api-keys with pool_id

Until api.neuronpool.dev resolves, paste the Worker URL. Never commit a key. Max completion budget on the coordinator is 4096 tokens (DEFAULT_MAX_OUTPUT_TOKENS).

Recommended models

Pick a chat id the pool actually has resident (GET /v1/models). Defaults for coding-agent presets:

Catalog idWhy
qwen3-30b-a3bStronger coding / agent loop when a pool host has it loaded
gpt-oss-20bDefault chat preset; wide context; good fallback

Other chat ids (llama-3.1-8b-instruct, qwen2.5-7b-instruct, gemma-3-12b-it, llama-3.2-1b-instruct, …) work the same way. Continue @codebase / indexing uses nomic-embed-text on POST /v1/embeddings; that path honors the same pool-scoped key and X-NeuronPool-Route: pool as chat.

neuronpool-tiny-chat is a 128-token dogfood GGUF. Agent CLIs (Claude Code, Continue) send system/tool prompts larger than that — use a catalog id with at least a few thousand context tokens (llama-3.2-1b-instruct and up). The host omits runner-added logprobs when the request includes tools so Continue's default tool+stream call is accepted by llama.cpp.

Routing

How you callWhat happens
Pool-scoped key, no route headerDefault prefer-pool: try the pool first. Falls back to billed public only if that pool set allow_public_spend=1.
Header X-NeuronPool-Route: poolStrict pool. Never billed. No public fallback. Empty pool → no hosts (503).
Unscoped buyer key, no headerpublic (billed). Do not use this for "free on our GPUs."

A completed pool job returns X-NeuronPool-Pool: <pool_id> (and X-NeuronPool-Host as a pseudonym). That header is the proof the request stayed inside the pool.

POST /v1/messages (Anthropic Messages, T32) honors the same X-NeuronPool-Route / pool-scoped-key defaults as /v1/chat/completions. A completed pool job returns X-NeuronPool-Pool. Claude Code can use the Anthropic path as a free pool backend.

Get a pool-scoped key

You must be a member of the pool (owner, admin, or invitee). A non-member pool_id is 403 and no key is minted.

  1. Sign up / log in. Create a pool at /dashboard/pools (HTML form) or join via an invite link (/join/<code>). JSON POST /v1/pools still works.
  2. Attach an owned host from the pool page, or neuronpool pool join <code> then neuronpool serve.
  3. On the pool page, mint a pool-scoped key. The secret is shown once. The dashboard Create API key form stays unscoped (public default).
  4. Optional API equivalent (plaintext is returned once):
# Session cookie from a logged-in browser (DevTools → Application → Cookies).
# pool_id is the id from GET /v1/pools or the pool page URL.
curl -sS https://neuronpool.damnknee.workers.dev/v1/api-keys \
  -H "Cookie: $NEURONPOOL_SESSION" \
  -H "Content-Type: application/json" \
  -d '{"name":"coding-agents","pool_id":"pool_…"}'

Response includes key (sk-neuronpool-…), prefix, and pool_id. Store the secret in the tool's password / secrets field, not in git.

List existing keys (prefix + scoped pool, never the secret):

curl -sS https://neuronpool.damnknee.workers.dev/v1/api-keys \
  -H "Cookie: $NEURONPOOL_SESSION"

Smoke test (proves the pool path before any IDE)

export NEURONPOOL_API_KEY='sk-neuronpool-…'
export NP='https://neuronpool.damnknee.workers.dev/v1'

curl -sS "$NP/models" \
  -H "Authorization: Bearer $NEURONPOOL_API_KEY"

curl -sS -D - "$NP/chat/completions" \
  -H "Authorization: Bearer $NEURONPOOL_API_KEY" \
  -H "X-NeuronPool-Route: pool" \
  -H "Content-Type: application/json" \
  -d '{"model":"gpt-oss-20b","stream":false,"messages":[{"role":"user","content":"ping"}]}'

curl -sS -D - "$NP/embeddings" \
  -H "Authorization: Bearer $NEURONPOOL_API_KEY" \
  -H "X-NeuronPool-Route: pool" \
  -H "Content-Type: application/json" \
  -d '{"model":"nomic-embed-text","input":"ping"}'

Expect HTTP 200, catalog ids on /models, a choices[0].message.content reply on chat, an embeddings data[0].embedding array, and response header X-NeuronPool-Pool matching your pool on both. A 402 on this request means you hit the public path (wrong key or missing header). A 503 / no_hosts means no online pool host has that model — load it or pick an id from /v1/models. Fair-use 429 pool_quota_exceeded is limits.md.


Claude Code

Claude Code speaks the Anthropic Messages API (POST /v1/messages). NeuronPool implements that shape and honors X-NeuronPool-Route plus the pool-scoped key default (prefer-pool). A completed pool job returns X-NeuronPool-Pool.

Exact config (Anthropic path — free on a pool-scoped key)

ANTHROPIC_BASE_URL is the origin, not /v1. Claude Code appends /v1/messages.

export ANTHROPIC_BASE_URL="https://neuronpool.damnknee.workers.dev"
export ANTHROPIC_AUTH_TOKEN="$NEURONPOOL_API_KEY"
export ANTHROPIC_API_KEY=""
export ANTHROPIC_MODEL="gpt-oss-20b"
export ANTHROPIC_DEFAULT_SONNET_MODEL="qwen3-30b-a3b"
export ANTHROPIC_DEFAULT_HAIKU_MODEL="gpt-oss-20b"
export ANTHROPIC_CUSTOM_HEADERS="X-NeuronPool-Route: pool"

Persistent (~/.claude/settings.json):

{
  "env": {
    "ANTHROPIC_BASE_URL": "https://neuronpool.damnknee.workers.dev",
    "ANTHROPIC_AUTH_TOKEN": "sk-neuronpool-…",
    "ANTHROPIC_API_KEY": "",
    "ANTHROPIC_MODEL": "gpt-oss-20b",
    "ANTHROPIC_DEFAULT_SONNET_MODEL": "qwen3-30b-a3b",
    "ANTHROPIC_DEFAULT_HAIKU_MODEL": "gpt-oss-20b",
    "ANTHROPIC_CUSTOM_HEADERS": "X-NeuronPool-Route: pool"
  }
}

Then claude and send a one-line prompt. ANTHROPIC_CUSTOM_HEADERS is the strict-pool pin; a pool-scoped key without the header still prefers the pool and will not spend credits unless that pool set allow_public_spend=1.

FieldValue
Base URL (origin)https://neuronpool.damnknee.workers.dev
Keypool-scoped sk-neuronpool-… (ANTHROPIC_AUTH_TOKENAuthorization: Bearer …; x-api-key is also accepted)
Model idqwen3-30b-a3b or gpt-oss-20b
Free pool?Yes/v1/messages honors X-NeuronPool-Route and the pool-scoped key default.

Cursor

Cursor's BYOK path is OpenAI-compatible POST /v1/chat/completions. There is no extra-headers field in Settings → Models, so mint a pool-scoped key (default prefer-pool) and leave allow_public_spend off on the pool so a miss never spends credits.

Chat and Agent require a logged-in Cursor account. Settings can save the OpenAI key and Override Base URL while you are logged out, but Chat will not use them. A logged-out send fails with Authentication error / ERROR_NOT_LOGGED_IN to https://api2.cursor.sh, not a NeuronPool 4xx. Disposable *@agentmail.to signups are blocked. cursor-agent --endpoint talks to Cursor Cloud, not this BYOK override — it is not a substitute.

  1. Sign in to Cursor (a real account; not a disposable inbox).
  2. Cursor Settings → Models.
  3. Enable OpenAI API Key and paste the pool-scoped sk-neuronpool-….
  4. Enable Override OpenAI Base URL: https://neuronpool.damnknee.workers.dev/v1
  5. Add model → catalog id gpt-oss-20b (and qwen3-30b-a3b if a host has it). Use the API id, not a display name.
  6. Disable other provider keys if they steal the picker. Click Verify.
  7. In Chat / Agent, select that custom model and send ping.
FieldValue
Base URLhttps://neuronpool.damnknee.workers.dev/v1
Keypool-scoped sk-neuronpool-…
Model idgpt-oss-20b or qwen3-30b-a3b
Route headernot available in the UI — rely on the pool-scoped key default

Cursor Tab / Cloud Agents / Automations do not use this BYOK override; only local Chat / Agent. Agent mode posts to POST /v1/responses; that path is translated onto the same chat job pipeline (including pool routing). Ask / Chat use /v1/chat/completions.

Login gate (Cursor 3.19 desktop): Settings → Models accepts the OpenAI key, Override Base URL, and a custom catalog id before sign-in, but Chat / Agent replace Send with Log in. A Cursor account is required to complete a chat. Disposable inboxes (for example *@agentmail.to) are rejected at authenticator.cursor.sh with Access blocked, please contact support — use a real Cursor login, not a throwaway mailbox. The official cursor agent / agent CLI authenticates to https://api2.cursor.sh (Cursor-account key), not this OpenAI-compatible BYOK path — --endpoint + a NeuronPool key is rejected as invalid.


Cline

Cline Settings → API Provider → OpenAI Compatible. Full generic recipe: docs/integrations/clients.md (Cline). For a pool:

  1. Base URL: https://neuronpool.damnknee.workers.dev/v1
  2. API Key: pool-scoped sk-neuronpool-…
  3. Model: refresh from /v1/models, or type gpt-oss-20b / qwen3-30b-a3b.
  4. Custom Headers (API Configuration → Add Header), when the UI still exposes it:
HeaderValue
X-NeuronPool-Routepool

If a Cline build dropped Custom Headers, the pool-scoped key default (prefer-pool) is enough as long as the pool has not opted into public spend.

VS Code settings.json equivalent (key via Cline's secret field, not git):

{
  "cline.apiProvider": "openai-compatible",
  "cline.openAiBaseUrl": "https://neuronpool.damnknee.workers.dev/v1",
  "cline.openAiModelId": "gpt-oss-20b"
}

Then one Cline chat: Reply with the single word pong. Expect a streamed reply. Optional: capture response headers on the Worker (X-NeuronPool-Pool) via the smoke-test curl using the same key.

Cline CLI (headless)

The cline npm CLI is a different surface from the VS Code panel.

cline auth --provider openai-compatible \
  --apikey "$NEURONPOOL_API_KEY" \
  --modelid gpt-oss-20b \
  --baseurl https://neuronpool.damnknee.workers.dev/v1

--provider openai-compatible posts to /v1/chat/completions (same path as the VS Code OpenAI Compatible panel). --provider openai-native posts to /v1/responses (now implemented on this coordinator). Cline's native tools arrive as Responses functions ({type:"function", name}) plus extras such as image_generation; the translator wraps functions into OpenAI chat tools and drops non-function types so llama.cpp can parse them.

The CLI may open two chat requests at once. A host with max_concurrency=1 can 503 no_failover_host on the second while the first still completes on the pool (billable=0). Raise host concurrency, or use a single-stream VS Code chat. 1B-class models often invent a fake tool name that llama.cpp drops — the host retries once without tools. Cline's default system + tools prompt is ~8.3k tokens; start llama-server with -c 16384 or larger.


Continue

Continue uses provider: openai + apiBase (must include /v1). Copy into ~/.continue/config.yaml. Store the key in Continue secrets (${{ secrets.NEURONPOOL_API_KEY }}), not in the file.

name: NeuronPool pool
version: 1.0.0
schema: v1
models:
  - name: Pool gpt-oss-20b
    provider: openai
    model: gpt-oss-20b
    apiBase: https://neuronpool.damnknee.workers.dev/v1
    apiKey: ${{ secrets.NEURONPOOL_API_KEY }}
    roles: [chat, edit, apply]
    requestOptions:
      headers:
        X-NeuronPool-Route: pool
  - name: Pool qwen3-30b-a3b
    provider: openai
    model: qwen3-30b-a3b
    apiBase: https://neuronpool.damnknee.workers.dev/v1
    apiKey: ${{ secrets.NEURONPOOL_API_KEY }}
    roles: [chat, edit, apply]
    requestOptions:
      headers:
        X-NeuronPool-Route: pool
  - name: Pool nomic-embed-text
    provider: openai
    model: nomic-embed-text
    apiBase: https://neuronpool.damnknee.workers.dev/v1
    apiKey: ${{ secrets.NEURONPOOL_API_KEY }}
    roles: [embed]
    requestOptions:
      headers:
        X-NeuronPool-Route: pool

Legacy ~/.continue/config.json:

{
  "models": [
    {
      "title": "Pool gpt-oss-20b",
      "provider": "openai",
      "model": "gpt-oss-20b",
      "apiBase": "https://neuronpool.damnknee.workers.dev/v1",
      "apiKey": "sk-neuronpool-…",
      "requestOptions": {
        "headers": { "X-NeuronPool-Route": "pool" }
      }
    }
  ]
}

Reload the window, pick Pool gpt-oss-20b, send ping. @codebase / indexing uses Pool nomic-embed-text (POST /v1/embeddings) and stays on the pool when that model is resident. Paid unscoped Continue presets (no route header) are in clients/continue.config.yaml.

Continue CLI (headless)

The cn CLI (npx @continuedev/cli) uses the same OpenAI-compatible /v1/chat/completions path. Point --config at a yaml like the block above (local origin + pool-scoped key + X-NeuronPool-Route: pool):

npx --yes @continuedev/cli \
  --config ~/.continue/config.yaml \
  --readonly -p --silent \
  "Reply with the single word pong."