This guide is for miners who want to submit an agent to the subnet. The
bulk of the SDK (how to build an agent, typed request/response schemas,
provider abstraction, resume tokens, examples) lives in the
eirel SDK repo — this guide
covers the subnet-side flow: register, build, submit, monitor.
Miners submit containerized agents that the operator builds and deploys on subnet-owned runtime. Validators dispatch evaluation tasks to your deployment, score the responses locally, and publish weights that drive TAO emissions.
The only launch family is general_chat — multi-turn conversational
assistant across instant and thinking modes. Owner-routed tool
services are available to your agent: web search, URL fetch, a Python
sandbox for verifiable computation, and rag.retrieve over per-run
document corpora (used by rag_required tasks). Future families are on
the roadmap.
Within each family, weight allocation is winner-take-all. The single
highest-scoring miner of a run receives the family's full weight
allocation; every other miner in the same family gets exactly 0 emission
that run, regardless of how close their score was. To dethrone the
current winner, a challenger must beat the incumbent's re-scored value
by a fixed margin (0.05 on the 0–1 scale) — otherwise the incumbent
is retained for the next run. Tune your agent to win, not to place.
The "score" the leaderboard ranks on is a multiplicative composite:
score = grounded_gate × safety_gate × tool_attestation
× hallucination_knockout × cost_attestation
× efficiency × (outcome_score + pairwise_bonus)
Each gate is in [0, 1] and any one of them can zero the whole score,
so robustness matters as much as raw answer quality. The Knockout gates
that most often surprise new miners:
tool_attestation— for tasks where the task envelope marks a required tool (e.g.rag.retrieveforrag_required), failure to actually invoke that tool zeros the score. Tool calls are recorded on a server-side ledger; you can't fake them.hallucination_knockout— if your answer restates a value the task marked asmust_not_claim(e.g. a phone number the user never shared), you zero the task. Refuse cleanly when the requested fact isn't in the conversation history.grounded_correctness— answers are cross-checked against a three-oracle reconciler (OpenAI + Gemini + Grok via Responses API, reconciled by Chutes GLM). Confidently wrong answers score very low.cost_attestation— exceeding the per-run USD budget on the provider proxy gets the over-budget request rejected and the attestation factor reduced.
- Python >= 3.12 (for the
eirelSDK and CLI) - A Bittensor wallet you control (coldkey + hotkey)
- The operator's public
OWNER_API_URL - TAO on your coldkey to cover subnet registration only (submissions themselves are fee-free on this subnet — see Fees)
btcli wallet new_coldkey --wallet.name my-miner
btcli wallet new_hotkey --wallet.name my-miner --wallet.hotkey m1btcli subnet register \
--subtensor.network finney \
--netuid 36 \
--wallet.name my-miner \
--wallet.hotkey m1pip install eirelThe SDK ships a reference general_chat agent under
eirel/examples/graph_general_chat/ (single-pass baseline) and a
stronger eirel/examples/graph_dominant_chat/ (planner + tool fan-out +
memory introspection + sandbox compute) that you can fork. The canonical
authoring shape is the graph SDK (StateGraph + GraphAgent); the
legacy BaseAgent / MinerApp shape is still exported but graph-based
composition is preferred for new agents.
See the SDK README for:
StateGraphbuilder +GraphAgent(canonical authoring shape)MinerProviderConfig.from_env()+AgentProviderClientfor LLM routing- Built-in tools:
WebSearchTool,UrlFetchTool,SandboxTool,RagTool(the RAG client is required forrag_requiredtasks; pass themetadata.rag_corpus_idfrom the request envelope torag.retrieve) submission.yamlmanifest format (declares family, model, providers, resource requests)- Local testing with
eirel serve - Compliance preflight with
eirel compliance
Repo: https://github.com/rendixnetwork/eirel
The SDK CLI packages your source directory into a .tar.gz archive,
signs the request with your hotkey, and uploads to the operator's
/v1/submissions endpoint. Pass --skip-fee so the CLI doesn't try to
make the (no-longer-required) on-chain treasury transfer.
eirel submit \
--source-dir ./my-agent \
--owner-api-url https://api.eirel.ai \
--network finney \
--wallet-name my-miner \
--hotkey-name m1 \
--skip-feeOn success you get a submission_id and a deployment_id.
The operator's owner-api:
- Verifies the hotkey signature.
- Retires your previous deployment (if any) — you have one active deployment per family at a time.
- Builds your image and places a pod on subnet-owned runtime.
- Marks the deployment
deployed_for_evalonce healthy.
Subsequent runs pick it up automatically; validators start scoring against the new deployment at the next run boundary.
eirel status \
--owner-api-url https://api.eirel.ai \
--wallet-name my-miner \
--hotkey-name m1Shows the current submission state, progress of the in-flight build, and the last few scorecards once evaluation runs complete.
Submissions are free on this subnet. The on-chain treasury transfer
that used to gate /v1/submissions has been disabled — pass --skip-fee
to the SDK CLI and your submission goes through with no TAO transfer.
You still need TAO to register your hotkey on the subnet (btcli subnet register), but nothing beyond that for the submit flow itself.
To keep the eval queue clean and discourage spray-and-pray spam, every
hotkey can submit at most 2 submissions, lifetime. Both attempts
count even if the first one failed to build — there's no "free retry"
on a broken archive. The 3rd POST to /v1/submissions from the same
hotkey returns HTTP 429.
Plan accordingly:
- Run your agent locally with
eirel serveand exercise it against representative prompts before submitting. - Use
eirel complianceto catch manifest errors and missing capability declarations before they burn one of your two slots. - Save your first slot for a polished v1, not a smoke test.
A short list of constraints worth knowing before you architect your agent:
- One family at launch (
general_chat). Other families (deep_research,code_agent, ...) are on the roadmap but not yet scoring on chain. - Multi-turn history cap. The SDK's request schema enforces
history.max_length = 100. Multi-turn agentic-memory fixtures can exceed this; the validator's flattened history may end up over 100, in which case the request 400s at SDK validation. Defensive miners either truncate at their app boundary (override the/v1/agent/infer*routes — seegraph_dominant_chatfor an example) or accept that100-turn tasks may fail.
- One active deployment per family. A new submission retires your prior deployment for that family. Don't expect to A/B two builds simultaneously under one hotkey.
- Per-run USD budget. Your provider-proxy spend is capped per run
(
EIREL_RUN_BUDGET_USDon the operator side). Over-budget LLM calls are rejected at the proxy layer; persistent rejections dragcost_attestationdown. - Source becomes public after the run that scored it closes. During
the run, only you can download your own archive. Once the run is
marked
completed, every submission archive scored in that run is publicly downloadable + viewable from the leaderboard. Your competitors can study your code after a run ends — write for the expected post-run scrutiny. - Tasks are hidden until run time. Eval bundles are generated at
run open and never published. You can't pre-train against them. You
can analyze your per-task
EvalFeedbackrows after a run to understand which capabilities you missed (see below).
- Your agent is invoked via an HTTP proxy. Validators never talk to
your container directly — they hit the operator's
/runtime/{deployment_id}/v1/agent/inferpath, which routes to your pod. This means you don't need a public IP or axon. - Budget enforcement is at the proxy. Your LLM spend is capped per run at the provider-proxy layer. Requests that would push you over-budget are rejected.
- Latency is measured at the proxy. Your deployment's p50 latency feeds a penalty curve on the operator side. Large p50 deficits drag your official score down even if raw quality is high.
- Tool calls are server-attested. When you invoke a subnet tool
(web search, URL fetch, sandbox,
rag.retrieve), the orchestrator records the call on a server-side ledger. Validators scoretool_attestationfrom this ledger — claiming "I retrieved the document" in your response without actually callingrag.retrievezeros that factor forrag_requiredtasks.
After each run, you can fetch your per-task EvalFeedback rows from
owner-api directly with a hotkey-signed request:
curl -H "X-Eirel-Hotkey: <your-hotkey>" \
-H "Authorization: <signature>" \
"$OWNER_API_URL/v1/eval/feedback?run_id=run-N"The eirel CLI's status subcommand surfaces these alongside the
scorecard. Each row includes outcome (correct / partial / wrong /
hallucinated / refused / disputed), failure_mode, guidance,
composite_score, and any knockout_reasons that zeroed your score.
Rows are filtered to your hotkey by the signature — you can't read
another miner's feedback.
hotkey submission cap reached (lifetime limit: 2 submissions per hotkey) — you've used both of your lifetime submission slots from
this hotkey. There is no override. Register a new hotkey if you need
another two attempts, or wait for the cap policy to evolve.
Deployment stuck at build_failed. Your archive failed to build
(missing dep, bad submission.yaml, oversized image). Check the
deployment's health_details_json via eirel status for the build
log reason. Note: this still counts as one of your two lifetime
submissions.
Deployment reaches deployed_for_eval but validators score you 0.
Either your agent is returning empty responses, your citations are
being trace-gated, your provider_proxy credentials aren't propagating,
or you're failing one of the composite knockout gates (most commonly
tool_attestation for rag_required tasks — verify your agent
actually calls rag.retrieve for those). Exercise your agent locally
with eirel serve and hit it with a sample invocation before
re-submitting.
Multi-turn task fails with pod returned 400. The fixture flattened
to more than 100 history entries and the SDK rejected it. See
Limitations — the fix is to override
the /v1/agent/infer* route in your app and truncate history before
the SDK validates it.