Skip to content

Write an rSkill and publish it to the Hugging Face Hub

An rSkill is OpenRAL's packaging format for a robot skill: a Hugging Face Hub repo containing an rskill.yaml manifest, a README.md, optional eval/<benchmark>.json results, and (usually) a pointer to model weights that live in an upstream repo. The manifest adds capability checking, license surfacing, latency budgets, and local-registry integration on top of raw weights — so installing a skill is as repeatable as installing a model.

This tutorial takes you from nothing to a published, installable rSkill. It uses only real commands; nothing here is a mock.

Prerequisites

just bootstrap && just sync   # toolchain + Python workspace (always `just sync`,
                              # never bare `uv sync` — see docs/contributing/toolchain.md)
openral doctor                # confirm the CLI and GPU/runtime

You'll also want a Hugging Face account and a write token (HF_TOKEN) if you intend to publish.

1. Scaffold the package

openral rskill new copies the canonical template in rskills/template/ and round-trips it through the schema, so a malformed scaffold fails immediately rather than on first load. There are three modes:

# (a) Most intuitive — introspect a published checkpoint and pre-fill
#     model_family / chunk_size / sensors_required / state_contract /
#     image_preprocessing.aliases / weights_uri from its config.json:
openral rskill new pi05-pick-cube --from-hf <owner>/<repo>

# (b) Family-aware defaults without Hub introspection:
openral rskill new pi05-pick-cube --family pi05 --embodiment-tag franka_panda

# (c) Interactive — prompts for owner / license / embodiment / family:
openral rskill new pi05-pick-cube

Valid --family values: act | smolvla | pi05 | xvla | diffusion. --license is one of apache-2.0 | mit | bsd | permissive_research | nvidia_non_commercial | proprietary | unknown. --embodiment-tag must be a canonical EmbodimentTag literal (e.g. so100_follower, franka_panda, aloha). This writes rskills/pi05-pick-cube/{rskill.yaml,README.md,eval/}.

2. Fill in the manifest

Open rskills/<id>/rskill.yaml. The fields that matter most for consumers (modeled on the in-tree rskills/pi05-libero-int8/rskill.yaml):

Field What it does
name <owner>/rskill-<model>-<robot>-<task>-<quant> — the Hub repo id (enforced; see Naming convention).
license License posture; drives commercial_use_allowed.
role s1 (fast policy), s2 (reasoner), or s0 (cerebellar).
kind vla for a learnable policy; detector kinds also exist.
embodiment_tags Must match a robot's RobotCapabilities.embodiment_tags.
sensors_required Modality + vla_feature_key + min resolution per camera.
actuators_required Each entry needs control_mode_semantics (e.g. mode: absolute).
runtime / quantization pytorch / onnx / tensorrt; dtype + min_vram_gb.
weights_uri hf://<owner>/<repo> — the rSkill does not copy weights.
chunk_size / n_action_steps Action-chunk size and replan cadence.
latency_budget.per_chunk_ms Contractual — enforced by sim-tier latency tests.
actions / objects / scenes Vocabulary the reasoner's LLM palette uses to pick the skill.

The full schema is openral_core.schemas.RSkillManifest. Strip the pi0.5-shaped knobs (image_preprocessing, state_contract, processors) if your policy is a simpler ACT/Diffusion model that doesn't need them.

For a normalized Cartesian controller, declare action_contract.cartesian_delta_scale as its per-axis physical range. Predictive safety applies clip(raw, -1, 1) * scale, while the unchanged raw command continues to the native controller. RoboSuite OSC_POSE uses [0.05, 0.05, 0.05, 0.5, 0.5, 0.5] (metres, then radians). Omit the field when the policy already emits physical deltas.

Naming convention

The name is enforced at publish time. Hyphens are ONLY the segment separators; every token uses underscores internally, so a name parses by a plain split("-"). Two shapes:

<owner>/rskill-<model>-<robot>-<task>-<quantization>   # weight-bearing kinds
<owner>/rskill-<model>-<robot>-<task>                  # ros_action / ros_service
<owner>/rskill-playbook-<name>                          # kind: playbook
Axis Vocabulary Example
<model> CANONICAL_MODEL_TOKENS — a versioned checkpoint token smolvla, gr00t_n17, lingbot_vla2, omdet_turbo
<robot> EmbodimentTag values incl. any + multi (>1 concrete robot) franka_panda, aloha_agilex, any, multi
<task> author-chosen, validated by shape ^[a-z0-9][a-z0-9_]*$ only libero_spatial, pen, pick_place_pen, locator
<quantization> {fp32, fp16, bf16, int8, nf4} (schema int4nf4); omitted for weightless ROS wrappers bf16, int8, nf4

For a VLA the <model> token must be consistent with model_family (e.g. a smolvla checkpoint can't be named pi05). A ROS-wrapper skill (ros_action/ros_service) has no weights, so it drops the quant segment entirely, e.g. OpenRAL/rskill-moveit-multi-eef_pose.

The <task> segment is yours to choose — it is validated by shape, not by equality against evaluated_tasks, so two skills that share a benchmark can still carry distinct task slugs (e.g. pen vs pick_place_pen). The other segments must use canonical tokens. Example: OpenRAL/rskill-smolvla-franka_panda-libero_spatial-bf16.

Validation is openral_core.schemas.repo_name_is_canonical; the publisher prints a suggested name (from expected_repo_name) on a mismatch. To migrate an existing manifest in one step, let the tool rewrite it — then refine the suggested <task> slug if you want a better one:

uv run python tools/rskill_publisher.py rskills/<id>/ --fix-name

Weight-mirror repos (an HF repo holding only weights, with no rskill.yaml) are out of publisher scope. Recommended non-enforced shape: <model>-<benchmark>-<quantization> (e.g. lingbot_vla-robotwin-nf4).

3. Write the README

The scaffolded README.md is full of <TODO> / TEMPLATE_ID markers. These are publish-blocking: the documentation validator (openral_cli._rskill_doc_validator.validate_rskill_docs) refuses to upload a README that still contains them. Fill in every section — what the skill does, the observation→action contract, how it was trained, supported robots, sensors required, and the license posture (keep commercial_use_allowed consistent with rskill.yaml::license).

4. Check it runs on your robot

openral detect                           # auto-provisions ./robot.yaml
openral rskill check pi05-pick-cube --robot robot.yaml

rskill check prints a per-section breakdown — embodiment, capability flags, GPU runtime, GPU dtype, sensors, actuators — and tells you whether the skill will run on the current host. Run openral rskill check with no id to walk every in-tree and installed rSkill.

5. (Optional) Produce reproducible eval results

If your skill ships eval/<benchmark>.json, the canonical producer is a sim run against a paired scene config:

openral sim run \
  --config scenes/<your-config>.yaml \
  --rskill rskills/pi05-pick-cube

Results validate against openral_core.SkillEvalResult. Paper-cited numbers you haven't reproduced locally are allowed with reproduced_locally: false plus a reproduction_cli so others can rerun them.

6. Publish to the Hub

Validate first (dry-run is the default — no upload):

python tools/rskill_publisher.py rskills/pi05-pick-cube/

This validates the manifest and the README/eval docs and prints a report. When it's clean, publish:

export HF_TOKEN=hf_...        # token with repo.write scope
python tools/rskill_publisher.py rskills/pi05-pick-cube/ --publish
# pin the exact upstream weights commit before uploading:
python tools/rskill_publisher.py rskills/pi05-pick-cube/ --publish --bump-revision

Two things to know:

  • Repos are created private. The publisher always creates the Hub repo as private; you flip it public on the Hub when you're ready. This is deliberate for license-restricted weights.
  • Provenance is not yet signed. Sigstore signing/verification is the planned control but is not implemented. Until it lands, rSkill.from_pretrained / from_yaml emit an rskill.unverified_provenance warning. Consumers can fail closed with OPENRAL_REQUIRE_SIGNED_SKILLS=1. *.pt weights are treated as untrusted pickle and need OPENRAL_ALLOW_UNSAFE_PICKLE=1 to load — prefer model.safetensors. Do not describe your skill as "signed" or "verified" until the control exists.

7. Install and use it

Anyone (including you, on another host) can now install it like a model:

openral rskill search pick-cube          # discover it on the OpenRAL Hub org
openral rskill install <owner>/rskill-pi05-pick-cube   # always org-qualified
openral rskill list                      # see it in the local registry

rskill install needs the full owner/name id — a bare name fails fast with an OpenRAL/… suggestion. Use rskill search [QUERY] [--kind/--role/--embodiment/--license] when you don't already know the id.

Run it by passing the bare rSkill reference to --rskill on the CLI — scene YAMLs reject a vla: block (_reject_legacy_vla_block raises ROSConfigError); policy always travels on the CLI (see the deploy tutorial).

See also