BASLTION

Docs

How this actually works.

Basltion is four things: a body in a physics engine, a trainer on an RTX 4090, a feed of published files, and a contract that will record them. This page says what each one is, what it is not, and how to check the numbers without taking this page's word for any of them.

01

The body

A Unitree G1 humanoid, described by files nobody here drew. The same description trains on the GPU and runs in your browser.

The robot is the unitree_g1 model from MuJoCo Menagerie, pinned to one commit. The scene it stands in, the actuator gains and the reward terms come from MuJoCo Playground's G1 joystick task, pinned to one commit as well. Pinning matters: every generation records the hash of every source file its scene reads, so a run can be reproduced against exactly these files and not against whatever the upstream repository looks like later.

Model

unitree_g1, MuJoCo Menagerie at commit 8161bba2

Scene

scene_mjx_feetonly_flat_terrain.xml or scene_mjx_feetonly_rough_terrain.xml, one per stage, MuJoCo Playground at commit 4057c147

Physics

MuJoCo 3.13.0, the same version in Python and as WebAssembly in this browser

Degrees of freedom

29 hinge joints on a floating base: nq 36, nv 35, 29 position actuators

Control rate

50 Hz, one control step every 0.02 s

Physics rate

500 Hz, ten substeps of 0.002 s inside each control step

Action

motor target = default pose + action x 0.5, default pose = the knees_bent keyframe

Episode

20 s, 1000 control steps

Joints

six in each leg, three in the waist, seven in each arm

The policy never sets a torque. It moves the target of a position servo on each joint, 29 numbers every 20 milliseconds, and MuJoCo works out the force from that joint's own limit. Those limits are what a seat changes later, which is why a seat can affect a gait without touching the policy.

A fall is not a matter of opinion either. An episode ends when the torso up vector drops below the horizon, when the feet contact the wrong body, when the state stops being finite, or when the pelvis goes below 0.35 m. Whichever held first is recorded by name with the frame it happened on.

The browser gets the same model with two differences, both written into the web bundle's own manifest. The visual meshes are decimated so the download stays small, which was checked against the original meshes for identical masses, inertias and a two second rollout. And a weld is added between the pelvis and a fixed anchor: that is the gantry the robot hangs from on the Body page. The weld is switched off for replay and for live mode, and it does not exist in the model that trains.

02

The trainer

PPO on an RTX 4090, evaluated somewhere else, published as files.

Training is MuJoCo Playground's G1 joystick environment with brax PPO, on flat ground or on rough ground depending on the stage. Thousands of copies of the robot step in parallel on one card while a three layer network learns to keep the body upright and to follow a velocity command.

Environment

G1JoystickFlatTerrain or G1JoystickRoughTerrain, MuJoCo Playground, one per stage

Algorithm

PPO, brax 0.14.2, policy and value networks 512 / 256 / 128

Parallel environments

8192

Hardware

one RTX 4090, plus CPU workers for evaluation

A generation

10,000,000 environment steps, then an evaluation, then a publish

Evaluation

100 episodes of 20 s in plain C MuJoCo, observation noise zero, fixed seeds

Published per generation

manifest, config, three recorded episodes, and the policy itself

What a generation is

A generation is not a unit of time. It is a fixed amount of experience: ten million environment steps. When those are done the checkpoint is evaluated away from the training code, in the plain C build of MuJoCo on the same scene, with the noise turned off and the same seeds every time. The numbers that come out of that evaluation are what gets published and what the ledger will record. In practice one lands every few minutes, so the number on the console goes up while you read this.

What a stage is

A stage is a target with a pass rule, not a phase on a roadmap. S0 Stand asks for survival at a zero command. S1 Walk asks for survival and a velocity error under a ceiling at 0.8 m/s forward. S2 moves to rough ground, S3 adds turning and walking backwards, S4 raises the command to 1.8 m/s, S6 pushes the robot while it walks. A stage passes only when every one of its thresholds is met over its own episodes at its own command, stages pass in order, and a stage that has passed never goes back to pending. The live status of all seven is on the training console, read from the same file the trainer writes.

Why S5 cannot pass

S5 is getting up after a fall, and it cannot run at all. The pinned MuJoCo Playground has a get up environment for the Go1 quadruped and one for Spot, and none for the G1. There is no task to train against and nothing to evaluate, so the stage board carries supported: false for S5 and the trainer stops there instead of reporting a number it never measured. Writing that environment is real work and nobody here has done it.

03

The feed

Every number on this site comes out of these files. Nothing is typed into the page.

Each generation is a directory. Three small pointer files at the root say which generation is newest, which ones exist, and where the stages stand.

Per generation

  • manifest.json: stage, kind, the hash of the model sources, the evaluation block, one row per episode with its seed and how it ended, and the sha256 of every other file beside it.
  • config.json: every environment, PPO, control and torque number the generation was produced with, including the 29 torque scales.
  • rollout-0.bin, rollout-1.bin, rollout-2.bin: three recorded episodes as raw little endian float32, frames by 36 values, one frame per control step at 50 Hz, so 1001 frames for 20 s. This is what the replay on the Body page plays back.
  • policy.onnx: the network that was evaluated, which is also the file live mode runs in your browser. Absent for an untrained generation.

At the root

  • latest.json: the newest generation, when it was published, and whether the trainer is running, idle or paused, with its last heartbeat.
  • index.json: every published generation, newest first, with its headline numbers and the sha256 of its manifest.
  • stages.json: the seven stages with their criteria, thresholds, evaluation commands and current status.

Publishing is atomic

The files go up first, the manifest second, the index and the stage board third, and latest.json last. Each one is written to a temporary name in the same directory and renamed into place, so a reader never catches half a generation. If a publish is interrupted the next start finishes it, and a generation whose files do not match its stored manifest is left unreferenced and its number skipped.

Where it is hosted

The trainer publishes into a public bucket on Neon Object Storage, and a copy of the feed also ships with the site. The training console, the live strip on the home page and the simulation all read the bucket first and fall back to that copy when it does not answer, and each of them names the source it used. The bucket is public beta and has failed entire browser sessions in testing, which is why the fallback exists rather than an error message.

04

Check a generation yourself

Two commands. No account, no key, no trust in this page.

index.json carries the sha256 of each manifest's exact bytes, and that is the hash the ledger will record. The manifest in turn carries the sha256 of every file beside it. So one hash, checked from a shell, ties a chain entry to a set of files and to the numbers on this site.

Hash the newest manifest

curl -s https://br-raspy-dust-b4lq7s4t.storage.c-6.us-east-2.aws.neon.tech/feed/latest.json

# take "manifest" from that, then hash the exact bytes
curl -s https://br-raspy-dust-b4lq7s4t.storage.c-6.us-east-2.aws.neon.tech/feed/gen/000000/manifest.json | shasum -a 256

# and compare it with manifestSha256 for that generation
curl -s https://br-raspy-dust-b4lq7s4t.storage.c-6.us-east-2.aws.neon.tech/feed/index.json

Every JSON file is written with sorted keys, two space indent and a trailing newline, and nothing re-serializes it before hashing, so the bytes you download are the bytes that were hashed. The rollouts and the config work the same way against the files block inside the manifest. The training console prints the same command for whichever generation you are looking at, with the hash to compare against.

05

The ledger

Written, tested, not deployed. No address to show yet, and this page will carry it when there is one.

BasltionLedger is a Solidity contract for Robinhood Chain, chain id 4663. It is the permanent record of the training run: one entry per posted generation, one entry per stage passed, and nothing else. It holds no funds, has no upgrade path and no way to destroy itself.

What one entry records

  • The generation number and the stage it was trained on.
  • The sha256 of that generation's manifest bytes, the same hash you can compute yourself above.
  • Survival in basis points, mean distance in centimetres, and the number of falls.
  • The root of the seat table that was in force, so a single seat can be proved against the entry.
  • The block timestamp it was posted at.

Only the keeper key can write. Generation numbers must increase, stages pass in order and once each, and no entry can be edited or removed afterwards by anyone, including us. The owner's only power is rotating the keeper key, and giving up ownership gives up even that.

The contract and the keeper that feeds it are tested, sixty contract tests and eighty-three keeper tests, including a full run against a local node that posts generations out of a real feed. What has not happened is a deploy to the live chain. Until it does, the honest statement is the one the home page makes: the metrics are published files, and they are not on any chain yet.

When it is deployed, not every generation needs a transaction. The plan is an entry about once an hour plus every generation that passes a stage, which keeps the chronicle readable and the gas trivial on a chain with 0.1 second blocks.

06

The seats

Twenty-nine joints, and each one belongs to whoever has burned the most of the token into it. They open at launch, and nothing is claimable today.

The body has 29 actuated joints, so there are 29 seats. There is no token and no seat contract deployed, so no joint has an owner, nothing has been burned, and every generation published so far says seatsEnabled: false in its own config.

How a joint is claimed

Burn the token into the joint. Whoever has burned the most holds it, and burning more than its holder burned takes it from them. The tokens are destroyed either way: they are not held in escrow, and being outburned later loses the joint with nothing refunded. A claim is one transaction on Robinhood Chain, with an approval before it if the token needs one.

Which joint a seat number is

The joints are numbered in one fixed published order: waist yaw first, then the two knees, then hip pitch, hip roll and hip yaw, the ankles, waist pitch and roll, then shoulders, elbows and wrists, left before right. Seat one is the waist. That order is what the joint map shows; it lives in the interface contract, not in a page that can be quietly rewritten, and the resulting table is hashed into the chain entry for the generation it applied to.

What the torque budget does

Whoever holds a joint sets its torque budget, and it scales that joint's actuator force limit inside a fixed band, from 0.7 to 1.0 of its nominal range, for the next generation of training. Setting it is one more transaction. The floor exists so no seat can break the run, and the ceiling exists so no seat can buy a robot stronger than the real G1. A weakened joint is not a cosmetic label: the robot has to compensate with its gait, and that shows up in the replay and in the numbers.

The whole vector of 29 scales is written into the generation's config.json, the config is hashed in the manifest, and the manifest hash goes on chain. So the claim that a particular generation trained with a particular set of weakened joints is checkable after the fact, by anyone, with the same two commands as above.

Until launch the joint map shows the 29 joints, what each one is, and that they open at launch. With seats off, as they are today, every joint sits at 1.0.

07

What is not built yet

The short list of things this site does not have, so nobody has to guess.

  • The ledger is not deployed. There is no contract address and no transaction, and no metric shown anywhere on this site is on a chain yet.
  • There is no token and no seat contract deployed, so no joint can be claimed and nothing can be burned.
  • The stages past the one on the console have not been reached. Each one has to be trained and passed on its own evaluation, in order.
  • S5, getting up, has no environment in the pinned Playground, so it cannot be trained or evaluated at all until somebody writes one.
  • Recorded episodes are kept for the newest fifty generations plus every generation that passed a stage, and pruned after that. Manifests, configs and policies are never pruned, so an old generation stays verifiable against the chain even when its recording is gone.
  • Shared control of the rig, where seat holders move one robot together, is a design and not code.
  • The robot's own wallet and what it does with creator rewards is a design and not code. Nothing is flowing today.

08

What this is built on

Open source, with the licences and the exact commits. None of these projects endorse this one.

MuJoCo 3.13.0

Physics. Google DeepMind, Apache 2.0. The same version runs in Python on the GPU box and as WebAssembly in this browser.

MuJoCo Menagerie, unitree_g1

The robot description. Copyright Unitree Robotics, BSD 3-Clause. Pinned at commit 8161bba2.

MuJoCo Playground

The G1 joystick task: scene, rewards, observation and reset distribution. Google DeepMind, Apache 2.0. Pinned at commit 4057c147.

Brax and JAX

PPO and the accelerated training loop. Google, Apache 2.0.

ONNX Runtime Web 1.30.0

Runs a published policy in the browser. Microsoft, MIT.

three.js 0.186.0

Rendering. MIT.

Vite and TypeScript

Build and language for the simulation. MIT and Apache 2.0.

Foundry

Contract tests. MIT and Apache 2.0.

Golos Text and JetBrains Mono

Typefaces, SIL Open Font License.

The pins are exact commits, not version ranges. A copy of the same sources compiles the same model, and the manifest of every generation carries a hash over every source file its scene reads, so the model a generation trained on is identified rather than described.

The body, the training and the feed, in full.

thedocs