Skip to main content
Pre-release

This release is a pre-release and may not be stable for production use.

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.py and python rt.py submit were removed on 2026-08-19. See docs/MIGRATION.md for the command map — your existing miner.yaml and state/round_N.json still 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.0 and bittensor needs numpy>=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, control.json URL

# 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, control.json, Docker, GPU, image, HF token, wallet balance
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 upload / announce Two of the submission steps, individually — for recovery, not routine use
openroboto burn Refuses, and says so. Paying needs the season confirmed against the backend and the uploaded layout judged, and this command can do neither — use openroboto submit
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; the miner-visible control.json schema and the read-only API contract.

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

Both pre-releases and stable releases go to PyPI, and the version number is what separates them:

git tag v0.1.0a2 && git push origin v0.1.0a2    # internal testing build
git tag v0.1.0   && git push origin v0.1.0      # stable

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 the pypi 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.

Testers install a pre-release by pinning it exactly — no extra index flags:

pip install openroboto==0.1.0a2

pip does not pick pre-releases when a stable release exists, so a miner running pip install openroboto will never land on one.

⚠️ One exception, while it lasts. openroboto has no stable release yet, and pip does select a pre-release when nothing stable is available. Until 0.1.0 ships, an alpha on PyPI is what a bare pip install openroboto gets. Either publish 0.1.0 early, or yank the alphas once testing is done — a yanked version is skipped by the resolver unless pinned exactly, and anyone who already installed it keeps working.

workflow_dispatch publishes to TestPyPI instead. That path exists to rehearse the pipeline itself — uv publish, the OIDC exchange, the approval gate — not to distribute test builds. It can never reach real PyPI.

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 0.1.0a3

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for openroboto 0.1.0a3
File Size Uploaded
openroboto-0.1.0a3.tar.gz 158.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for openroboto 0.1.0a3
File Interpreter ABI Platform
openroboto-0.1.0a3-py3-none-any.whl Python 3 none any Details

Total release size: 352.5 kB

Release files / openroboto-0.1.0a3.tar.gz

Download URL openroboto-0.1.0a3.tar.gz
Size 158.9 kB
Tags Source
SHA-256 checksum
How to use checksums
c7357a9aef6376046c21bef60214e74ffd3c3f56e2afde7a59453e934d756e38
BLAKE2b-256 checksum
How to use checksums
01bb371f894af95b13827b445c4e4ea36bb800ca9cdc9414773c507dadc42569
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-0.1.0a3-py3-none-any.whl

Download URL openroboto-0.1.0a3-py3-none-any.whl
Size 193.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
364bbab03369dc7665ed7ee5345868f6260d8e64d2ffd5b4fb3899d372adfd0e
BLAKE2b-256 checksum
How to use checksums
5f071ece3a049db3909d10ee3a93e6ead935b0a245e88a8c570ceb4914748f0a
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}
Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page