SubstraOSSUBSTRAOS
NODES 8JOBS/S 0.1TOK/S 667IN-FLIGHT 2MODELS 5BROWSER 4DESKTOP 3AVG LATENCY ~337MSFALLBACK ~13%USDC PAYOUTS LIVE
◆ Documentation

How SubstraOS works

A decentralized AI compute layer on Solana. People contribute GPU from a browser tab or native client; capacity is pooled and sold as AI inference; contributors earn. DePIN meets DeAI.

LIVE shipped & runningBUILDING built, gated / rolling outPLANNED on the roadmap

Overview

SubstraOS is a three-tier stack. Demand (apps) sits on top, a routing and gateway layer in the middle, and the supply (pooled GPUs) at the base. Products create demand; the substrate provides supply; verified work and earnings tie the two together.

AppsDEMAND
The playground, model hub, OpenAI-compatible developer API, and agents — the surfaces people use to run inference on the network.
Inference layerROUTING
Model registry, the scheduler/router that picks a worker by reputation and latency, an OpenAI-compatible gateway, and auditable job receipts.
GPU marketplaceSUPPLY
Browser and native worker nodes contribute compute into a shared pool; an off-chain ledger tracks verified work, reputation, and earnings.

Contribute compute

LIVE

Turn idle GPU into earnings. The browser path is live today:

1
Open a node. Visit the node page and your browser tab becomes a worker using WebGPU (via WebLLM). No install. A native desktop client (ollama) for larger models is on the roadmap.
2
Sign in with your Solana wallet. A wallet signature (SIWS) proves identity — no transaction, no funds required to contribute.
3
Serve jobs. The orchestrator dispatches inference requests to your node; you stream the answer back token by token.
4
Earn on verified work. Each verified job builds your leaderboard score, and paid buyer jobs accrue withdrawable USDC — never for idle uptime, only completed verified work.
HONEST FRAMING
The browser tier runs small models (≈2B–8B, quantized) and is intermittent — best on Chromium desktop. Heavier/throughput work is carried by native nodes and a rented fallback that guarantees the network always answers.

Use the network

Playground LIVE — chat with pooled models straight from the browser (wallet-gated). Requests hit the gateway, the router selects a worker, and tokens stream back; the fallback node always answers so you never see a dead network.

Developer API (OpenAI-compatible endpoint + keys + usage) is live. LIVE See the API reference sections below.

Model hub and Agents are built to the design and gated behind "coming soon" while they harden. BUILDING

API quickstart

LIVE

The gateway is OpenAI-compatible and responds with SSE (streaming) for every request -- there is no non-streaming JSON branch. When you use the raw openai SDK, always pass stream=True; a non-streaming call would expect a JSON body and fail to parse the event stream. If you want a single aggregated completion object, use the official SubstraOS SDKs (@substraos/sdk for JS/TS, substraos for Python), which read the stream and assemble one chat.completion for you. The gateway is centralized in v1; depending on the model you pick, inference runs on the pooled community network (people's GPUs) or a cloud-backed Pro upstream.

curl -N https://api.substraos.tech/v1/chat/completions \
  -H "Authorization: Bearer $SUBSTRA_KEY" \
  -H "Content-Type: application/json" \
  -H "Accept: text/event-stream" \
  -d '{"model":"qwen2.5-7b-instruct","messages":[{"role":"user","content":"Hello"}],"stream":true}'

Response shape. On HTTP 200 the wire is Content-Type: text/event-stream, with frames written as data: <json>\n\n in this order:

1. routing-meta (one frame)
   data: {"route":"browser|network|fallback|pro","worker_id":"w_..."|null,"request_id":"req_...","model":"..."}

2. chunk (one or more)
   data: {"id":"req_...","object":"chat.completion.chunk","model":"...","choices":[{"index":0,"delta":{"content":"..."},"finish_reason":null}]}

3. terminator
   data: [DONE]

Note that id is the request_id (not an OpenAI chatcmpl- id), and the SubstraOS SDKs transparently skip the routing-meta frame for you.

Official SDKs are publishing soon: @substraos/sdk (JS/TS) and substraos (Python). They are not on npm / PyPI yet -- install from the monorepo source for now. The streaming wire format is OpenAI-compatible, so the openai client works today with stream=True.

JS/TS — @substraos/sdk
import { SubstraOS } from "@substraos/sdk";
const client = new SubstraOS({ apiKey: "sk_sub_live_..." }); // baseURL defaults to https://api.substraos.tech
const stream = await client.chat.completions.create({
  model: "qwen2.5-7b-instruct",
  messages: [{ role: "user", content: "Hello" }],
  stream: true,
});
for await (const chunk of stream) process.stdout.write(chunk.choices?.[0]?.delta?.content ?? "");
PYTHON — substraos
from substraos import SubstraOS
client = SubstraOS(api_key="sk_sub_live_...")  # base_url defaults to https://api.substraos.tech
for chunk in client.chat.completions.create(
    model="qwen2.5-7b-instruct",
    messages=[{"role": "user", "content": "Hello"}],
    stream=True,
):
    print((chunk["choices"][0].get("delta") or {}).get("content", ""), end="")

API keys & auth

LIVE

Authentication is your Solana wallet, not a password. Sign in with your wallet (SIWS) to get a dashboard session, then mint an API key.

  • Sign-in flow (to obtain a session): POST /v1/auth/nonce { walletAddress } returns { nonce, message, expiresAt }; sign message with your wallet; POST /v1/auth/login { walletAddress, nonce, signature, message } returns { accessToken, expiresAt, userId, walletAddress }. (The dashboard does this for you.)
  • Create / rotate your key: POST /v1/auth/apikey (session-JWT-gated) returns { key, keyPrefix, id, createdAt }. You get one key per account -- creating a new one rotates and revokes the old one. The full secret sk_sub_live_... is shown once; store it now, it is never shown again.
  • List key metadata: GET /v1/auth/apikey returns { keys: [...] } (prefix + metadata only, never the secret).
  • Revoke: DELETE /v1/auth/apikey/:id returns { id, revoked: true }. Revocation is instant.

Send your key as Authorization: Bearer sk_sub_live_... on every request. Only POST /v1/chat/completions accepts the API key -- the key-management, usage, and other /v1/* routes require the dashboard session (JWT), not the key. Keys draw the same prepaid credit balance as the playground.

Models & tiers

LIVE

Community models run on pooled people's GPUs (browser/WebGPU contributors), with a rented fallback node that always answers -- expect small models and intermittent capacity. Pro models are cloud-backed (resold from an upstream provider) and billed cost-plus through the same credits. Fetch the live catalog any time at GET https://api.substraos.tech/v1/models (public, no auth; upstream provider ids are stripped from the response).

COMMUNITY MODELS
model id               tier       runtime           context   price (in + out combined)
qwen2.5-7b-instruct    community  browser / WebGPU  32,768    $10.00 / 1M tokens
qwen2.5-3b-instruct    community  browser / WebGPU   4,096    $10.00 / 1M tokens
Community pricing is a single blended rate of 1 credit per 1,000 tokens (input + output), i.e. $10 per 1M tokens -- there is no separate input/output rate.
PRO MODELS (CLOUD-BACKED)
model id                 tier  runtime       context    input $/1M   output $/1M
llama-3.3-70b-pro        pro   cloud-backed  131,072    $0.20        $0.64
llama-3.1-8b-pro         pro   cloud-backed  131,072    $0.04        $0.06
qwen-2.5-coder-32b-pro   pro   cloud-backed   32,768    $1.32        $2.00
Pro is cloud-backed and priced cost-plus (upstream cost x 2.0). Pro responses are capped at 2,048 output tokens, and the Pro tier can be temporarily paused when its daily upstream budget is hit (returns 503 pro_paused).

Image generation

LIVE

POST /v1/images/generations generates images on the Pro tier — cloud-backed upstream, not community GPU. It accepts the same auth as chat: a dev API key (sk_sub_…) or a session JWT. Plain JSON round-trip (no SSE): the response carries a base64 PNG in data[0].b64_json. Billing is prepaid credits (~3 cr per 1024×1024 image), debited from the key owner's balance.

CURL
curl https://api.substraos.tech/v1/images/generations \
  -H "Authorization: Bearer $SUBSTRA_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"flux-2-klein","prompt":"a red fox, studio light","size":"1024x1024"}'
JS/TS — @substraos/sdk
import { SubstraOS } from "@substraos/sdk";
const client = new SubstraOS({ apiKey: "sk_sub_live_..." });
const img = await client.images.generate({
  model: "flux-2-klein",
  prompt: "a red fox, studio light",
  size: "1024x1024",
});
const b64Png = img.data[0].b64_json; // base64 PNG
PYTHON — substraos
from substraos import SubstraOS
client = SubstraOS(api_key="sk_sub_live_...")
img = client.images.generate(model="flux-2-klein", prompt="a red fox, studio light", size="1024x1024")
b64_png = img["data"][0]["b64_json"]  # base64 PNG

Limits. 10 images / min per account, plus the shared per-key 60 requests / min. Errors use the standard envelope: 402 insufficient_credits (top up first), 422 invalid_body, 404 model_not_found, 400 not_an_image_model, 429 rate_limited, 503 pro_image_unavailable (Pro tier temporarily off), 503 pro_image_paused (daily image budget reached — retry later), 503 pro_image_failed (upstream error).

Limits & billing

LIVE

Billing is prepaid credits. 1 credit = $0.01 USD (deposits convert at credits = USDC x 100). A request first places a short hold for its estimated max cost, then settles to actual usage on completion. If your available balance can't cover the request estimate, the call returns 402 insufficient_credits before any work is scheduled. Top up credits in the dashboard.

Rate limit: each API key is capped at 60 requests per minute (fixed 60-second window). Exceeding it returns 429 rate_limited with a retry_after field (seconds) in the JSON body and a Retry-After response header. Session/playground requests (no API key) are not RPM-limited. The limiter fails closed -- if the backend rate-limit store is unavailable, requests return 429 with retry_after: 60.

Errors

LIVE

Errors use an OpenAI-style envelope. request_id is present on gateway/Pro responses; retry_after is present on 429: { "error": { "message": "...", "type": "...", "code": 400, "request_id": "req_...", "retry_after": 30 } }

400  invalid_request        malformed body or unknown/invalid field
401  invalid_auth           missing, invalid, or revoked key / session
402  insufficient_credits   not enough credits to cover the request -- top up
404  model_not_found        unknown or disabled model id
429  rate_limited           per-key RPM exceeded (see retry_after)
500  internal_error         unexpected server error -- retry
503  no_capacity            no worker available right now -- retry shortly
503  pro_unavailable        Pro tier disabled or not configured
503  pro_paused             Pro tier paused (daily budget reached)

The canonical type strings are exactly: invalid_request, invalid_auth, insufficient_credits, model_not_found, rate_limited, no_capacity, internal_error (plus the Pro-only pro_unavailable, pro_paused, pro_upstream_error).

IN-STREAM ERRORS
Once a response has started streaming, the HTTP status is already 200, so a worker failure mid-stream is delivered inside the stream, not as the envelope above. It arrives as a final data frame carrying only message + type (no code, no request_id), followed by [DONE]:
data: {"error":{"message":"no_capacity","type":"no_capacity"}}
data: [DONE]
A community request where no worker has the model loaded surfaces as type: "no_capacity"; a Pro upstream failure surfaces as type: "pro_upstream_error". Treat any in-stream error frame as a failed request and retry.

Compute tiers

Browser · WebGPULIVE
A browser tab running WebLLM. Small models, intermittent, Chromium-desktop first. The easy on-ramp — anyone can contribute with one click.
Desktop · native (ollama)PLANNED
A native client for machines with real GPUs — bigger models, higher throughput, persistent. Carries the quality/throughput tier.
Rented fallbackLIVE
A high-reputation cloud/rented-GPU node that guarantees liveness and sources ground truth for canary verification — so the network and demos never show dead.

Work verification

BUILDING

The central hard problem: there is no cheap cryptographic proof that a remote machine actually ran a specific LLM forward pass — inference is non-deterministic across hardware, so naive output-hashing fails for honest nodes, and TEEs/ZK are out of scope.

Instead SubstraOS uses a pragmatic layered rubric that makes cheating unprofitable and detectable rather than impossible:

  • Canary jobs — prompts with known-good answers, scored against ground truth.
  • Redundancy sampling — the same job to multiple nodes, compared by semantic agreement (embedding similarity, not string equality).
  • Timing & plausibility — empty or implausibly-timed completions are rejected outright.
  • Reputation — an EWMA score with asymmetric decay; new nodes start on probation and gate into higher-value work.
  • Rate limits & a trusted fallback oracle — bound abuse and provide a tiebreaker.

Semantic checks currently run in shadow mode while thresholds are calibrated on real honest-node output — they record and move reputation but don’t yet gate earnings; the robust timing/empty signals do. Collusion is the acknowledged residual weakness. Earnings always accrue on verified work, never uptime.

Earnings & payouts

LIVE

Two ledgers, one rule (verified work only). Points are your leaderboard score — a flat amount per verified job. Not money, not convertible, not a token. USDC is your real earnings — 75% of buyer spend on jobs your node served.

Provider USDC payouts are live. Paid verified work accrues a real USDC balance; you withdraw it to the wallet you signed in with, paid out in USDC on Solana mainnet from a $0.01 minimum. The off-chain ledger is the source of truth, and a withdrawal can never exceed what was verifiably earned.

ANTI-ABUSE
Earnings accrue only on completed verified jobs. New nodes start on probation; reputation gates higher-value work. The payout float is kept minimal by design, which bounds the worst case while verification continues to harden.

Architecture

Off-chain first. Postgres and Redis are the source of truth — nodes, jobs, reputation, the credit/earnings ledger, and receipts. The on-chain footprint is deliberately minimal: wallet sign-in signature verification and USDC payouts. No custom programs.

Solana via Alchemy. All RPC routes through the backend; the provider key is server-side only and never ships to the browser.

WHAT WE DON’T CLAIM
We say "decentralizing inference, centralized gateway in v1" — the gateway and scheduler run on a single coordinator today. It’s pooled people’s GPUs with auditable receipts, not datacenter GPU and not ZK-verified inference. The CPU-only coordinator never runs inference itself.

Roadmap

Each phase ships independently. Token integration stays additive and late.

P0
Brand & infrastructure
Domain, backend on the coordinator, database + RPC proxy, CI deploys.
LIVE
P1
MVP — “the network is alive”
Browser node + WebGPU inference, orchestrator routing, playground, the live network dashboard, wallet sign-in, off-chain points, and a fallback that keeps it answering.
LIVE
P2
Marketplace — earn for your GPU
Provider earnings, USDC payouts, and buyer credit settlement are live. Native node client is next.
BUILDING
P3
Developer API — build on it
OpenAI-compatible endpoint, API keys, usage dashboard, SDK, and docs.
LIVE
P4
Agents — live on the network
Deploy autonomous agents that run 24/7 on pooled compute, routed to native + fallback.
PLANNED
P5+
Upgrades
Image generation, token integration, compute-credit trading, pay-per-call selling, and verification hardening (canary/redundancy/reputation maturity, optional on-chain receipts).
PLANNED

Token

LIVE

The $SUBSTRAOS token is live on pump.fun — an external, founder-launched asset. The contract address:

Nothing in the product depends on the token. It is shown here as an informational contract address only — contribution today is leaderboard points plus live USDC payouts, and no flow requires the token. Any product integration (provider rewards, fee discounts, converting accumulated contribution points) is additive and still on the roadmap — launching the token does not wire any of it in. This is not financial advice.

FAQ

Do I need a GPU?
The browser node uses WebGPU, available on recent Chromium-based desktop browsers. A discrete GPU helps a lot; integrated graphics can run the smallest models.
Is my prompt/response data stored?
Jobs keep token counts and metadata only — no prompt or response text is persisted (data minimization).
Is it fully decentralized?
Inference is distributed across contributed nodes, but the gateway and scheduler are centralized in v1. We say so plainly.
How do payouts work?
Paid verified work builds a USDC balance you can withdraw to your connected wallet ($0.01 minimum), paid in USDC on Solana mainnet. A withdrawal never exceeds your verifiably-earned balance.
Status reflects the live build and changes as phases ship. Forward-looking items are plans, not commitments.