openroboto
The command-line tool for mining on OpenRoboto, a Bittensor mainnet subnet (netuid 80) that rewards improvements to vision-language-action models.
You fine-tune π₀.₅ on LIBERO, publish the checkpoint to Hugging Face, pay a small on-chain evaluation fee, and announce it. The subnet evaluates every submission in simulation with a seed nobody can predict, ranks the results, and pays emissions by rank.
Everything you type is this one package. There is nothing to clone.
pip install openroboto
Docs index · How the subnet works ·
Migrating from rt.py ·
Evaluation toolkit
Upgrading from a clone of this repository?
python miner.pyandpython rt.py submitwere removed on 2026-08-19. See docs/MIGRATION.md for the command map — your existingminer.yamlandstate/round_N.jsonstill work.
Requirements
- Linux, an NVIDIA GPU (24 GB VRAM minimum) and a recent driver
- Python 3.11
- Docker with the NVIDIA Container Toolkit — training runs in a container because
openpi needs
numpy<2.0and bittensor needsnumpy>=2.0; one interpreter cannot hold both - A registered Bittensor mainnet hotkey with enough TAO for the evaluation fee
- A Hugging Face account and a write token
Your first submission
# 1. Install and scaffold
pip install openroboto
openroboto init my-miner # a ready-to-use workspace: miner.yaml,
# train_strategy.py, README.md, .gitignore
cd my-miner
$EDITOR miner.yaml # hotkey_ss58, HF token + username
# 2. Check everything BEFORE anything costs money
openroboto doctor # GPU, Docker, HF permissions, balance, config
# 3. Build the training image, then train one round
openroboto build
openroboto train
# 4. Verify the checkpoint format — still free
openroboto check
# 5. Upload, pay the fee, announce on chain
openroboto submit
# 6. See what the subnet made of it
openroboto status
Steps 2 and 4 exist for one reason: burns are not refundable. The most expensive
mistake on this subnet is discovering after paying that the upload was not something
the evaluator can load — a bare LoRA adapter, or a checkpoint buried in a
subdirectory. doctor and check are free and catch both.
Full walkthrough: docs/MINER.md. Real-machine setup, systemd, custom strategies: docs/MINER_DEPLOY.md.
Commands
| Command | What it does |
|---|---|
openroboto init [DIR] [-s simple|example] [--validator] |
Create a working workspace: config, a training strategy to edit, a README with the exact next commands, and a .gitignore that keeps your wallet password out of git |
openroboto doctor |
Environment check: Python, config, the season's own spec, Docker, GPU, image, HF token, wallet balance against this season's fee |
openroboto build |
Build the training image from the build context shipped inside the package (no clone, no network). Refuses for a competition this client has no image for, rather than filling that competition's image name with the π0.5 one |
openroboto train [-s script.py] |
Run one round; your strategy script is mounted into the container |
openroboto check [PATH] |
Verify checkpoint layout with the rules the evaluator uses — no GPU, no network, no second repository |
openroboto submit [--force] |
All three, resumable from state/round_N.json |
openroboto status [--hotkey] |
Submission history and scanner rejection reasons (no API key needed) |
openroboto validator run |
External validator: read published weights, set them on chain |
openroboto --version |
CLI version and protocol package version |
⚠️ There is no openroboto merge, and none is planned. Exporting a full
checkpoint is part of training — it needs the model libraries, which cannot live
in the same interpreter as bittensor — so it belongs in your training script,
inside the training container. The bundled strategies leave that step blank on
purpose and say so; openroboto train tells you what the run actually produced, and
openroboto check gives the verdict before you pay.
⚠️ openroboto submit checks the layout too, and it is not optional. Between
the upload and the payment it reads the file listing of your HuggingFace
repository — the same listing the subnet reads after the fee — and stops
without paying anything if that listing would not earn a score. There is no flag
to skip it, because past that point a rejection is final and the TAO is not
refunded. If the listing cannot be fetched at all, it also stops: an answer we
could not get is not one this will spend your money on. Run openroboto check
first anyway — it is free, it runs before the multi-gigabyte upload, and it
checks two more rules that need the weight index on your disk.
Things that will cost you TAO if you skip them
The fee is published on the competition you are entering (params.fee) — 0.1 TAO
on the mainnet simulation season today, a different number on the real-hardware
one, which is why no single figure belongs in your config. Never hard-code it:
openroboto submit confirms it against the backend in the moment before paying,
and refuses to pay at all if it cannot, rather than reaching for an amount
that says how much but not which competition.
Do not run burn and announce as separate steps unless you are recovering. The
backend only accepts a submission whose burn is within 50 blocks (~10 minutes) of
the chain commitment; this stops a single fee being reused across submissions. Past
that window the submission is rejected and the fee is gone. openroboto submit runs
the three steps back-to-back so you stay inside it, and announce refuses to publish
once the window has closed instead of charging you a second fee for a doomed
submission.
Details: docs/PAYMENT.md.
Submission format
The evaluator accepts complete model checkpoints — an openpi JAX params/
directory or a PyTorch model.safetensors, plus
assets/physical-intelligence/libero/norm_stats.json. A bare LoRA adapter is rejected
by a CPU pre-check before any GPU time is spent.
Put them at the top of the directory you upload. The evaluator descends two
levels looking for the weights and stops; the LingBot exporter's own layout,
checkpoints/global_step_N/hf_ckpt/, is one level past that, so an unedited upload
of a training output is admitted and then never loaded.
Exact requirements: docs/SUBNET_OVERVIEW.md.
Verify your evaluation seed
Your seed is derived from three public values that did not exist when you submitted — the block hash that carried your commitment, the round number, and a drand beacon value. Nobody, including the subnet operator, can pick a seed for a specific miner. You can recompute it:
pip install openroboto-protocol
from openroboto_protocol.seed import derive_seed
seed = derive_seed(block_hash, round_num, drand_randomness)
The backend and this CLI import that exact function — not a copy of it. Formula, drand chain identifier and security assumptions: docs/SEED_GENERATION.md.
Docker: what runs where
Training always runs in a container — openpi needs numpy<2.0 and bittensor
needs numpy>=2.0, so one interpreter cannot hold both. You do not have to
arrange that:
openroboto build # builds the training image, once
openroboto train # starts it for you, data and strategy mounted in
The training image definition ships inside the package. There is nothing to clone, nothing to keep in sync, and no network needed to build it. You do need Docker on the host.
⚠️ There is exactly one image definition in the package and it installs
openpi (π0.5). Competitions on another base model have no image here yet, and
build / train say so and stop instead of running the π0.5 one under that
competition's name — train those your own way, then come back for openroboto check and openroboto submit, which both work on a checkpoint this CLI did
not produce.
Running the CLI in a container too (optional, needs a clone)
Separately, the Dockerfile and docker-compose.yml in this repository put
the CLI itself in a container, for people who would rather not install it into
their host Python. That is a repository-level convenience: openroboto init does
not write these files into your workspace, because a compose file that builds
the CLI image would need this repository's build context anyway.
git clone https://github.com/openroboto-ai/openroboto-cli && cd openroboto-cli
docker compose up train
docker compose run --rm train submit --config miner.yaml
There is deliberately no submit service — a compose service can be restarted, and
restarting a command that burns non-refundable TAO is not something to leave to a
restart policy.
⚠️ This compose file mounts the Docker socket so the containerised CLI can start
the training container. That grants host root to the container; run it only on
your own machine. Installing with pip avoids that entirely, which is why it is
the documented path.
Public trust boundary
Public: miner participation, local training, Hugging Face upload, burn and chain
announcement; chain commitment formats and weight-setting logic; evaluation rules,
baseline methodology, LIBERO tooling and seed derivation; and the read-only API
contract. (control.json is documented for external validators, who fetch
public_key from it — no miner command reads it.)
Not here: held-out task data, the scoring-service deployment, and subnet-owner operational tooling.
Seed derivation is public precisely because publishing it gives nothing away — the future block hash and drand value do not exist at submission time.
Development
For contributors. Skip this if you installed from PyPI.
git clone https://github.com/openroboto-ai/openroboto-cli
cd openroboto-cli
uv sync --locked
One repository is enough: openroboto-protocol is installed from PyPI at the exact
version pinned in pyproject.toml. To work against unreleased protocol changes,
override it in your environment only —
uv pip install -e ../openroboto-protocol — and do not commit a
[tool.uv.sources] path entry. A path source bypasses the version constraint,
which is how the pin once read ==1.0.0 while every local and CI run was actually
using 0.2.0.
--locked is deliberate: it fails when uv.lock no longer matches pyproject.toml
instead of silently resolving a different dependency tree. The interpreter is pinned to
Python 3.11 by .python-version — the version miners run.
bash scripts/lint.sh # mypy + ruff check + ruff format
uv run pytest -q # no GPU, no chain, no network
uv run coverage run --source=src -m pytest -q
uv run coverage report # fails below the threshold in pyproject.toml
uvx pre-commit install # optional: the same lint on every commit
.github/workflows/ci.yml runs these same commands — scripts/lint.sh is the single
definition of "lint", so local and CI cannot drift. CI also fails on any skipped test:
nothing here needs hardware or credentials, so a skip means a test was switched off.
Protocol constants (commitment encoding, seed derivation, shared vocabularies) come
from openroboto-protocol and are never copied in here.
.github/workflows/protocol-guards.yml enforces that and that the dependency is pinned
to an exact version — a floating range would let the miner side and the backend side
resolve different code, which is the one thing that package exists to prevent.
Releasing
git tag v1.0.1 && git push origin v1.0.1 # stable
git tag v1.1.0a1 && git push origin v1.1.0a1 # pre-release, for testing
The tag triggers the same gates every pull request runs, and the pypi
environment only accepts v* tags, so a stray branch build cannot reach the
index. It then waits for a reviewer to approve that environment: uploads cannot
be undone and a version number can never be reused, so that approval is the last
thing standing between a typo and every miner.
pip install openroboto resolves to the newest stable release. A tester
takes a pre-release by pinning it exactly (pip install openroboto==1.1.0a1);
no miner lands on one by accident.
Repository map
| Path | Purpose |
|---|---|
src/openroboto/ |
The package: commands/, chain/, huggingface/, payment/, config/, training/, templates/ |
src/openroboto/runner/ |
Training-image build context (Dockerfile + the in-container entry script), shipped in the wheel so openroboto build works offline |
docs/ |
Miner, validator and reproducibility documentation — index at docs/README.md |
tests/ |
Mirrors src/; needs no GPU, chain or network |
Dockerfile, docker-compose.yml |
Optional containerised way to run the CLI |
The old flat layout (rt.py, miner.py, payment.py, validator.py, miner/,
utils/, protocol/) was removed on 2026-08-19; the package replaces all of it. It
remains in git history, and docs/MIGRATION.md maps every old
command to its replacement.
Local configuration, runtime state, logs, caches and model weights are excluded by
.gitignore.
License
See LICENSE.
Metadata
Release files for openroboto 1.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| openroboto-1.1.1.tar.gz | 163.9 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| openroboto-1.1.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 362.6 kB
Release files / openroboto-1.1.1.tar.gz
| Download URL | openroboto-1.1.1.tar.gz |
|---|---|
| Size | 163.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
a02c479214c13ae6f84f899f5653ca91e4e810ba20fffe30633a73f43aa6fc76
|
|
BLAKE2b-256 checksum How to use checksums |
ec4e5a5dd37e0d95563eeed17012c08bf3220dabda5dcdefdee132ca98ebf1a6
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.11.18 {"installer":{"name":"uv","version":"0.11.18","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / openroboto-1.1.1-py3-none-any.whl
| Download URL | openroboto-1.1.1-py3-none-any.whl |
|---|---|
| Size | 198.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
22192cbf12b3c9fea21ca1611f5f897bb9b35e1589af8f3ee532f3a655ccdc14
|
|
BLAKE2b-256 checksum How to use checksums |
52c7e31e1fab638c1fd5b3fdfc13f1edd4a55857450c5117ce19a0d5faebf728
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.11.18 {"installer":{"name":"uv","version":"0.11.18","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|