Audience
Verification level
Configuration only. The example resolves a real local Registry, verifies the
manifest and asset digests, and builds an immutable ExecutionPlan. It does not
start PyBullet or claim a native simulation pass.
What you will learn
One short use: reference travels through this exact chain:
use in run.yaml
-> nearest .fastsim/project.yaml
-> named Registry index
-> stable release 1.0.0
-> digest-verified component manifest
-> pybullet variant
-> digest-resolved URDF resource
-> immutable ExecutionPlan
Files
run.yamlasks for one reusable cube component.main.pycompiles the Run and asserts every important link in the chain.- Component manifest declares the component and its backend variants.
- Registry index maps the identity to a stable version and a digest-pinned manifest.
../.fastsim/project.yamlregisters that index and defines trusted asset roots.
Read the component manifest
The linked manifest contains:
schema: fastsim-component/1 # Component document contract.
id: object://fastsim/component-demo-cube # Stable, human-facing identity.
version: 1.0.0 # Immutable release version.
kind: object # Legal under scene.objects.
semantics:
category: primitive # Portable descriptive metadata.
variants:
pybullet: # Selected because run.yaml chooses PyBullet.
resources:
model: # Resource name inside this component.
uri: assets/cube.urdf # Relative to this manifest.
format: model/vnd.urdf+xml # Machine-readable asset format.
role: simulation # This resource builds the simulated entity.
sha256: 29d00308... # Expected bytes, checked before compilation succeeds.
The real manifest also provides MuJoCo and Isaac Lab variants. A component identity therefore stays unchanged when an application changes backend; its variant chooses the compatible representation.
The Registry index supplies two facts that do not belong in a Run:
stable: 1.0.0 # Version chosen when @version is omitted.
releases:
1.0.0:
manifest: object_cube.yaml # Location relative to the index.
sha256: df81158b... # Exact file-content integrity gate.
The index SHA-256 covers the manifest file bytes. The plan's
manifest_sha256 covers the validated, canonical manifest meaning, so those two
digests intentionally serve different layers and do not need to be equal.
Read run.yaml
schema: fastsim/2 # FastSim vNext Run schema.
name: component-loading-pipeline # Human-readable run name.
backend: pybullet # Select the pybullet manifest variant.
runtime:
physics_hz: 60 # Intended physics frequency.
control_hz: 30 # Intended control admission frequency.
seed: 101 # Deterministic run seed.
scenario:
scene:
objects:
cube: # Local alias: objects.cube.
use: object://fastsim/component-demo-cube
pose:
xyz_m: [0.0, 0.0, 0.5] # Position in world metres.
quat_xyzw: [0.0, 0.0, 0.0, 1.0] # Identity rotation, XYZW order.
cube is only this Run's alias. object://fastsim/component-demo-cube is the
portable catalog identity. The compiler rewrites the unversioned request to the
exact release object://fastsim/component-demo-cube@1.0.0 in the plan.
What enabled means
Every scene entity supports enabled: true or enabled: false. Omitting the field,
as this Run does, means true.
objects:
optional_cube:
use: object://fastsim/component-demo-cube
enabled: false
With enabled: false, FastSim still resolves and validates the component and keeps
the complete entity declaration in the Scenario, ExecutionPlan, and Run Lock. A
Scenario-reading plugin can therefore see that the entity was declared and disabled.
At Runtime, however, FastSim excludes it from the UniRoboSim physical projection:
- no native object, articulation, robot, environment, fluid, or sensor is created;
- it has no live physics/render/sensor state and is not a control target;
- it is absent from live planning geometry and cannot be a default robot or mount target that requires an enabled entity.
This is a compile-time scene switch, not a dynamic visibility or pause command. Changing it requires compiling a new Plan and starting a new application composition; an ordinary Runtime reset continues to use the existing Plan. It also does not skip Manifest/resource resolution during configuration compilation. Use it when one reusable Scenario needs optional entities; remove the entity entirely when even its component and resources should not be resolved.
Read main.py
Path(__file__).with_name("run.yaml")locates the sibling Run from any current working directory.load_project(RUN_FILE)walks upward to../.fastsim/project.yaml.project.compiler().compile_file(RUN_FILE)loads the index, verifies the manifest digest, selectspybullet, resolves the URDF, and freezes the plan.- The assertions prove the selected identity, version, manifest digest, variant, resource format, resource digest, and plan digest.
Only public fastsim.config API is used.
Run it
cd demo/components/01_loading_pipeline
fastsim config validate run.yaml --json
fastsim config expand run.yaml
fastsim config explain run.yaml scenario.scene.objects.cube.component.identity
fastsim config lock run.yaml --output run.lock.yaml
fastsim config verify-lock run.lock.yaml --json
python main.py
config explain queries the compiled effective plan. The authoring-only use field
has already been resolved there, so its effective path is component.identity; the
selected version and variant are available beside it under component.
What run.lock.yaml is
run.yaml is the human-written intent. It may select a Registry's stable component,
inherit Manifest defaults, and use resource URIs. fastsim config lock compiles that
intent and writes a generated snapshot of the exact result:
- the complete immutable
ExecutionPlan, including backend, runtime values, seed, entities, plugins, and the Plan digest; - each selected component's exact identity, version, backend Variant, and Manifest digest;
- every resolved resource location and its SHA-256 digest;
- a
lock_digestprotecting the canonical Plan and resource-location list.
fastsim config verify-lock checks the Lock structure and digests, then reads every
listed local resource and confirms that its bytes still match. Starting with
fastsim run run.yaml --lock run.lock.yaml also recompiles run.yaml and refuses to
start if the current effective Plan differs from the accepted Lock. A changed stable
release, edited pose, different backend Variant, or modified asset therefore fails
explicitly instead of silently changing an experiment.
A Run Lock does not freeze the native simulator, Provider package, GPU driver, operating system, or behavior of an external model/device service. Record those versions separately with production evidence.
Treat the Lock as generated evidence, not a second configuration to edit. Regenerate
it only after deliberately accepting a configuration change. This tutorial ignores
run.lock.yaml, so it is safe to delete after the exercise. For an accepted data run,
archive it with that Run's outputs and provenance; review local resource paths before
publishing it outside your environment.
Case 12 shows a multi-file component and verifies all of its locked resources.
Expected output
validate reports "ok": true. main.py prints a chain like:
use -> object://fastsim/component-demo-cube
release -> object://fastsim/component-demo-cube@1.0.0
variant -> pybullet
resource -> model (model/vnd.urdf+xml)
plan -> sha256:...
Digest prefixes may change when the manifest or effective Run changes; their full values remain 64 hexadecimal characters.
Common errors
PROJECT_CONFIG_NOT_FOUND: keep the Run inside this demo tree or pass its project explicitly. Copying onlyrun.yamlremoves its Registry context.COMPONENT_NOT_FOUND: compare theuseidentity withcatalog/index.yaml.MANIFEST_DIGEST_MISMATCH: the manifest changed without updating its release digest. Treat this as an integrity failure, not as a warning to bypass.RESOURCE_OUTSIDE_TRUSTED_ROOT: register the intended asset root in the project; do not broadly trust unrelated directories.
Next
Continue with 02 — Object component.