A decentralized GPU inference marketplace. Rent a GPU anywhere, run one docker command, start earning crypto. The control plane is a Next.js dashboard; GPU nodes authenticate with Nostr-signed HTTP requests — they never hold a database credential, run as an unprivileged user, and can operate outbound-only. Scales horizontally — every new provider on the network is real, additional capacity.
- Next.js 16 + React 19 web dashboard. Also ships as the Electron desktop app (same app, Electron wrapper).
- Public chat playground at
/chat— streams tokens via Server-Sent Events. Uses live P2P providers if any are online; otherwise falls back to NVIDIA NIM (build.nvidia.com) so the demo never breaks. - One-click GPU deploy at
/deploy— paste a RunPod API key, pick a GPU, the image boots and registers with your control plane automatically. infernetCLI — one binary per GPU server. 14 subcommands:init,login,register,update,remove,start,stop,status,stats,logs,payout,payments,gpu,firewall. Daemon has a local IPC socket for live queries + a public P2P TCP port 46337 (dual-stack IPv4/IPv6) for direct peer communication.- Supabase backend on the server — Postgres + Auth + Realtime. Self-hosted or cloud.
- Nostr-signed node API at
/api/v1/node/*— GPU nodes sign every request with their secp256k1 / BIP-340 keypair; the control plane verifies the signature before touching the DB. No service-role keys ever leave the server. - Privacy-preserving telemetry — heartbeats carry only coarse GPU capability (vendor + VRAM tier). No hostname, platform, CPU model, or RAM total is stored.
- Multi-coin payments via CoinPayPortal — BTC, BCH, ETH, SOL, POL, BNB, XRP, ADA, DOGE, plus USDT/USDC on ETH/Polygon/Solana/Base.
- GPU auto-detect — nvidia-smi, rocm-smi, Apple Silicon; stashed in
providers.specs.gpusfor control-plane job matching.
Design goals: a GPU node should be able to run as an unprivileged user, leak as little as possible about its host, and never hold a credential that could compromise the rest of the network.
- No database credential on nodes. The CLI holds a Nostr (secp256k1 / BIP-340 Schnorr) keypair and nothing else. The control plane is the only thing that talks to Supabase; nodes talk to the control plane over
/api/v1/node/*with a signed envelope on every request. - Signed-request envelope. Each call carries
X-Infernet-Auth: base64url({ v, pubkey, created_at, nonce, sig })wheresigis a Schnorr signature overMETHOD + path + created_at + nonce + sha256(body). 60-second replay window, per-process nonce cache, pubkey must matchpublic_keyon the target row. - Minimized telemetry. Heartbeats carry only coarse GPU capability:
{ vendor, vram_tier, model }per GPU. Hostname, platform, arch, CPU model, RAM total are discarded both client-side and at the server sanitizer. - Outbound-only mode. Pass
--no-advertisetoinit/register/startand the node never publishes an IP or port; it only pulls work via signed polls. Pairs naturally with Tor or a NAT. - Unprivileged install. Config lives at
~/.config/infernet/config.json(mode 0600). No/etc, no/var, no sudo, no privileged ports (default P2P port 46337, override with--p2p-port). - Auto-migration. Legacy configs that still carry
supabase.serviceRoleKeyare stripped silently on load — the field never gets re-saved to disk.
The control plane runs one of two ways:
- Self-hosted — run Supabase yourself via the Supabase CLI (
supabase start). Best for privacy and offline development. - Infernet cloud — point the CLI at the hosted control plane at
https://infernet.tech. Rent a GPU anywhere,infernet init, start earning.
Operators can point many GPU nodes at the same control plane — each node has its own Nostr identity and shows up as its own row in the dashboard.
┌────────────────────────────────────────────────┐
│ Next.js dashboard (self-hosted OR cloud) │
│ /chat · /deploy · /api/* · SSE streaming │
│ /api/v1/node/* — signature-verified endpoints│
└───────────────────────┬────────────────────────┘
│ (server-side only)
▼
┌────────────────────────────────────────────────┐
│ Supabase │
│ providers · clients · aggregators · jobs │
│ users · job_events · platform_wallets │
│ provider_payouts · payment_transactions │
└────────────────────────────────────────────────┘
▲
Nostr-signed HTTP │ anon / SSE
(BIP-340 Schnorr) │
┌────────────┐ ┌──────────┐ ┌─────────────────┐
│ infernet │… │ Expo app │ │ NVIDIA NIM │
│ CLI daemon │ │ │ │ (fallback only) │
│ P2P:46337 │ └──────────┘ └─────────────────┘
│ IPC sock │
└────────────┘
The GPU node holds a Nostr keypair (generated on infernet init); every
call to the control plane carries an X-Infernet-Auth envelope with a
Schnorr signature over method + path + timestamp + nonce + sha256(body).
The server enforces that the signing pubkey owns the target row. No DB
credential ever lives on the node.
Full detail: INFERNET-ARCHITECTURE.md.
- Node.js 18+, ESM only, pnpm workspaces monorepo (5 apps, 11 shared packages)
- Next.js 16.x (App Router), React 19, Tailwind CSS
- Supabase (
@supabase/supabase-js) - Vitest for tests
- Electron for the desktop shell
- React Native + Expo for mobile
- Docker + GitHub Actions CI/CD (multi-arch images:
linux/amd64+linux/arm64)
Self-hosted:
pnpm install
pnpm supabase:start
pnpm supabase:db:reset # applies all migrations in supabase/migrations/
pnpm dev # dashboard at http://localhost:3000Or point at Supabase cloud:
pnpm install
pnpm supabase:login
pnpm supabase:link
pnpm supabase:db:push # pushes migrations to the cloud project
pnpm devCopy sample.env → .env.local and fill in SUPABASE_URL, SUPABASE_SERVICE_ROLE_KEY, and the CoinPayPortal keys.
docker run --rm -it \
--gpus all \
-p 46337:46337 \
-e INFERNET_CONTROL_PLANE_URL=https://infernet.tech \
-e INFERNET_NODE_NAME=edge-01 \
ghcr.io/profullstack/infernet-provider:latestMulti-arch (linux/amd64 + linux/arm64). The image calls infernet init → infernet register → infernet start --foreground internally, so one command boots a provider and starts accepting jobs. No database credentials are passed in — the container generates its own Nostr keypair on first run and uses it to sign every request.
If you cloned the repo:
pnpm install
pnpm --filter @infernetprotocol/cli start -- help # any subcommandFull CLI surface: init, login, register, update, remove, start, stop, status, stats, logs, payout, payments, gpu, firewall. Config lives at ~/.config/infernet/config.json.
npm install (npm i -g @infernetprotocol/cli) and Homebrew (brew install infernet) are on the roadmap.
infernet init walks through:
- Control-plane URL (default
https://infernet.tech, or your self-hosted instance) - Node role (
provider/aggregator/client) - Human-readable name
- Nostr keypair — real secp256k1 / BIP-340 (auto-generated; bring your own with
--nostr-privkey) - P2P port (default 46337, TCP, dual-stack IPv6/IPv4) — pass
--no-advertiseto stay outbound-only - Auto-detected local address (override with
--address) - GPU detection via
nvidia-smi/rocm-smi/system_profiler - Firewall hint per your distro
Config lives at ~/.config/infernet/config.json (mode 0600). It contains the node's Nostr keypair and the control-plane URL — no database credentials, no service-role keys.
Node lifecycle:
init First-time setup (generates Nostr keypair)
login Re-point at a different control plane
register Announce this node (signed POST /api/v1/node/register)
update Re-push current state (signed; upsert)
remove Deregister and wipe local config (signed)
Daemon:
start Start daemon (detached; --foreground for supervisors)
stop Stop daemon (graceful via IPC, signal fallback)
status Control-plane row + live daemon snapshot
stats Live in-memory daemon stats via IPC
logs Show / tail the daemon log (-f for follow)
Diagnostics:
gpu Inspect local GPUs (list | json)
firewall Print firewall commands for the P2P port
Payments:
payout Manage payout coin/address (set, list)
payments Show recent payment transactions
infernet start detaches into the background (logs at ~/.config/infernet/daemon.log) and exposes a Unix-domain IPC socket at ~/.config/infernet/daemon.sock. The other CLI commands use that socket for live queries:
infernet statusfetches this node's row from the control plane (signed POST/api/v1/node/me) and merges it with the daemon's in-memory snapshot.infernet statsshows heartbeat counts, poll counts, active jobs, uptime, P2P connections.infernet stopsends a graceful shutdown command; falls back to SIGTERM via the PID file.infernet logs -ftails the log file.
Use infernet start --foreground under systemd / Docker / Kubernetes when a supervisor wants the process in the foreground.
Each provider/aggregator node binds TCP 46337 (dual-stack) for peer communication. Change with --p2p-port, or disable with --no-p2p. The node advertises its address:port to the control plane on every signed heartbeat so other nodes can discover it — unless it was started with --no-advertise, in which case nothing is published and the node operates outbound-only.
Need to open the port on your firewall? infernet firewall prints the exact commands for ufw / firewalld / nftables / iptables (Linux), pf (macOS), or netsh (Windows). We never touch firewall state automatically.
The CLI auto-detects GPUs on init, register, update, and daemon heartbeat:
- NVIDIA (CUDA) via
nvidia-smi— model, VRAM, driver, CUDA version, utilization, temperature, power. - AMD (ROCm) via
rocm-smi— same fields where available. - Apple Silicon via
system_profiler— model, unified memory. - Falls back to CPU-only if no GPU tooling is installed.
Detected GPUs feed the coarse capability payload that lands in providers.specs.gpus (jsonb): { vendor, vram_tier, model } per card, plus gpu_count. Enough to route jobs (e.g. "find me a provider in the 24-48gb tier") without broadcasting the machine's full fingerprint.
When the P2P network has no live providers, the chat playground transparently falls back to build.nvidia.com's OpenAI-compatible endpoint so the UX never breaks while the network is bootstrapping. Controlled by three env vars (all optional; leave blank to disable):
NVIDIA_NIM_API_KEY= # from https://build.nvidia.com/
NVIDIA_NIM_API_URL=https://integrate.api.nvidia.com/v1
NVIDIA_NIM_DEFAULT_MODEL=meta/llama-3.3-70b-instructSame SSE contract as the P2P path — tokens are mirrored into job_events so the audit trail is identical. The chat UI badges fallback responses "NVIDIA NIM (fallback)" for transparency. Adapter: packages/nim-adapter.
Consumers can pay for jobs and providers can be paid out in any of these coin/network combinations:
BTC, BCH, ETH, SOL, POL, BNB, XRP, ADA, DOGE; plus USDT on ETH/Polygon/Solana; plus USDC on ETH/Polygon/Solana/Base.
- Canonical list:
packages/config/payment-coins.js - Platform deposit addresses:
packages/config/deposit-addresses.js— also seeded into theplatform_walletstable. - Gateway: CoinPayPortal (
src/payments/coinpayportal.js). SetCOINPAYPORTAL_API_KEY,COINPAYPORTAL_WEBHOOK_SECRETin.env.local. - Invoice:
POST /api/payments/invoicewith{ jobId, coin }. - Webhook:
POST /api/payments/webhook— HMAC-verified, updatespayment_transactions+jobs.payment_status.
Provider earnings flow into the outbound direction of payment_transactions. Configure a payout wallet with infernet payout set <COIN> <ADDRESS>.
apps/
web/ Next.js dashboard + /chat + /deploy + REST/SSE API
cli/ The `infernet` binary (commands + daemon + IPC)
desktop/ Electron shell wrapping the Next.js app
mobile/ React Native + Expo app
daemon/ Placeholder for future standalone service daemons
packages/
config/ Payment-coin list + canonical deposit addresses
auth/ Nostr identity + signed-request envelope (BIP-340)
db/ Supabase-backed model layer (server-side only)
gpu/ GPU detection (NVIDIA / AMD / Apple Silicon)
inference/ Distributed inference coordinator / worker
payments/ CoinPayPortal gateway
logger/ Structured logger
sdk-js/ @infernetprotocol/sdk — REST + SSE client
api-schema/ OpenAPI 3.1 spec for the control-plane API
deploy-providers/ Cloud-GPU deploy adapters (RunPod today)
nim-adapter/ NVIDIA NIM fallback inference adapter
tooling/
docker/provider/ Dockerfile + entrypoint for ghcr.io/.../infernet-provider
dist/homebrew/ Homebrew formula + release-time updater
supabase/migrations/ SQL schema (applied via supabase CLI)
tests/ Vitest suite
GET /api/overview
GET /api/nodes
GET /api/jobs
GET /api/providers
GET /api/aggregators
GET /api/clients
GET /api/models
POST /api/chat Chat playground — picks P2P provider OR NIM fallback, returns streamUrl
GET /api/chat/stream/[jobId] SSE: tokens streamed live (job|meta|token|done|error)
POST /api/payments/invoice CoinPayPortal invoice mint
POST /api/payments/webhook CoinPayPortal webhook sink (HMAC-verified)
GET /api/deploy/runpod/gpu-types RunPod GPU catalog (proxied via user API key; not stored)
POST /api/deploy/runpod One-click launch an Infernet provider pod
Signature-verified node API (Nostr / BIP-340 Schnorr):
POST /api/v1/node/register Upsert a provider/aggregator/client row
POST /api/v1/node/heartbeat Refresh last_seen / status / address / port
POST /api/v1/node/jobs/poll Pull assigned jobs for this provider
POST /api/v1/node/jobs/[id]/complete Mark a job completed / failed + record payout row
POST /api/v1/node/jobs/[id]/events Batch-emit streaming tokens into job_events
POST /api/v1/node/remove Deregister this node
POST /api/v1/node/me Fetch this node's own row
POST /api/v1/node/payments/list List payment_transactions for this node
POST /api/v1/node/payouts/{list,set} Manage provider_payouts rows
Web/mobile routes are server-only — the Supabase service-role client is never imported into browser bundles. The /api/v1/node/* routes are the only ones nodes ever touch, and they verify a Nostr signature before any DB write. OpenAPI 3.1 spec ships as @infernetprotocol/api-schema (raw YAML at packages/api-schema/openapi.yaml).
@infernetprotocol/sdk(packages/sdk-js) — JS/TS SDK with anInfernetClientand an async-iteratorchat()helper for the SSE stream.@infernetprotocol/api-schema(packages/api-schema) — OpenAPI 3.1 spec; feed to any generator for Python/Go/Rust clients.@infernetprotocol/deploy-providers(packages/deploy-providers) — cloud-GPU adapters (RunPod today) powering the one-click/deploypage.
- Docker (LIVE) —
ghcr.io/profullstack/infernet-provider:0.1.0and:latest, multi-archlinux/amd64+linux/arm64. One command boots a provider; see the Quick start above. - npm (scaffolded, unblock pending) — 11 publishable
@infernetprotocol/*packages (cli,sdk,api-schema,deploy-providers,nim-adapter,auth,config,db,gpu,inference,logger,payments). Release workflow + dry-run publish are ready; waiting on an npm Automation token. - Homebrew (scaffolded) — formula + updater script at
tooling/dist/homebrew. Tap repo atprofullstack/homebrew-infernet. Activates as soon as npm ships.
Tag a v*.*.* and .github/workflows/release.yml builds the Docker image and creates a GitHub Release. See docs/RELEASING.md for the full flow.
The /deploy page (backed by POST /api/deploy/runpod) spins up an Infernet provider node on RunPod using a user-supplied API key. The server proxies the RunPod API call and immediately drops the key — nothing is persisted. The pod boots the infernet-provider image, generates a Nostr keypair, registers itself with the control plane over a signed request, and starts heartbeating.
See docs/CONTRIBUTING.md.
MIT. See LICENSE.