
If you're a part of the Bittensor community, please pay attention.
Bittensor's network layer handles authentication between validators and miners through axon/dendrite — it works for the standard pattern where validators directly query miner processes.
But when you want to build a web application on top of Bittensor — a dashboard, a submission portal, an API — you hit a gap. There's no standard way to authenticate a hotkey against an HTTP API. The wallet SDK gives you Keypair.sign() and Keypair.verify(). That's it. No session management, no challenge-response flow, no request signing middleware, no replay protection. There are no community packages where this exists.
We needed all of this for ORO. Miners submit agents through a CLI and monitor results on our dashboard. Validators authenticate to claim work and report scores. Admins manage the leaderboard. Every one of these interactions goes through our API, and every one needs to know which hotkey is making the request and whether they're authorized to do it.
So we built the auth stack from scratch around one principle: miners
should never think about authentication. The entire submission flow
is oro submit agent.py. The CLI discovers your Bittensor wallet on
disk, signs the request with SR25519, handles OAuth for inference
credits, and uploads your code. There's no manual key management,
headers to construct, or tokens to refresh.

Under the hood, every authenticated request carries a hotkey address, timestamp, unique nonce, and an SR25519 signature over all three. The backend verifies the signature, checks the timestamp, and stores the nonce in Redis with a matching TTL. If an attacker captures a signed request, they can't replay it — the nonce is burned on first use.
For the web dashboard we added a session layer on top. Miners connect their Polkadot wallet extension, sign a one-time challenge, and get a session token. The SDK injects that token on every subsequent request and refreshes it before expiry — the auth lifecycle is invisible.
Two auth modes sharing the same backend, providing the same security guarantees with different ergonomics:
- Per-request signing for CLI tools: Stateless, with every request independently verifiable.
- Session tokens for the browser: Stateful, managed invisibly by the SDK.
When we add a backend endpoint, the TypeScript client and Python CLI update automatically. Auth handling, type safety, retries, and error classification are all built in. If anyone wanted to build something on top of the ORO APIs, they could just use the SDK out of the box.
Local testing works the same way — one Docker Compose command spins up the same sandbox, scoring logic, and product search engine that validators use in production. If it works on your machine, it works on the network.
The broader point is that developer experience is a competitive advantage for subnets.
Every hour a miner spends debugging auth or figuring out how to submit is an hour they didn't spend improving their agent. We want miners thinking about climbing the leaderboard, not about Bittensor infrastructure. The less they notice the platform and the network, the more they focus on building better agents — which is the whole point.
We've open-sourced the auth components — the SR25519 middleware,
session management, nonce replay protection, and the SDK auth layer.
If you're a subnet owner or community member building a web dashboard,
a miner portal, or any HTTP API that needs to know "which hotkey is
calling me" — you shouldn't have to build this from scratch. We
already did. pip install bittensor-auth and you're up in minutes.
The network needs a standard for HTTP-level hotkey authentication. Every subnet that builds a web interface will face this exact problem. We'd rather see one battle-tested solution adopted broadly than 30 subnets each rolling their own with different security tradeoffs.
Let's make Bittensor the best place to build.