Skip to content

Subsystem Binding Manifests

Librux uses manifests to separate subsystem contracts from deployment wiring.

The rule is this.

subsystem manifest = role plus what this subsystem provides and requires
deployment manifest = how required interfaces are bound to provider instances
runtime registry = where the bound subsystem is currently reachable

Subsystem code should not hardcode the concrete subsystem name of another subsystem. It should use a logical requirement name, and the deployment manifest binds that requirement to a provider instance.

Subsystem Manifest

Every managed subsystem has a librux.subsystem manifest.

kind: librux.subsystem
version: 1

subsystem:
  name: arm_controller
  role: component

requires:
  joint_servo:
    contract: api.actuator.servo.v1
    cardinality: one
    required: true

provides:
  manipulator:
    contract: component.manipulator.v1

subsystem.role is required. Supported roles are app, component, gateway, and compound. provides declares interfaces implemented by the subsystem. requires declares logical dependencies used by the subsystem code. Contracts on public subsystem interfaces must refer to installed Librux spec dictionary contracts. Use component.* for component subsystems, api.* for gateway-facing API contracts, and capability.* for app-facing compound capabilities.

Private or tutorial-only wiring must be explicit.

provides:
  sim_debug:
    contract: tutorial.robot_cleaner.debug.v1
    visibility: internal

visibility defaults to public. Public interfaces are the compatibility surface seen by robot slots, capability implementations, and applications. Internal interfaces are allowed for mechanics tutorials, tests, and private wiring, but they do not claim product compatibility. A platform slot or capability implementation cannot be backed by an internal interface.

When a public interface contract is a canonical component.* id, the SDK expands the component into its conformance API contracts and validates the corresponding runtime registration against the installed spec dictionary. Capability contracts resolve to their backing api.capability.* definitions for endpoint validation. provides and requires decide binding direction. The contract spec defines the public compatibility surface. Do not repeat endpoint lists on public interfaces. Event, Control, Procedure, and Operation endpoints are part of the referenced contract. The endpoints field is only for explicit internal/tutorial wiring that is not claiming public product robot compatibility.

Runtime endpoint registration is closed-world. A subsystem may only register endpoints that come from its public Librux spec contracts or from explicit visibility: internal endpoint lists in its manifest. Do not create Event, Control, Procedure, or Operation endpoint names dynamically at runtime. Put dynamic identifiers such as request_id, operation_id, object_id, or task_id in the message payload.

Deployment Manifest

A deployment manifest binds a consumer requirement to a provider interface.

kind: librux.deployment
version: 1

subsystems:
  arm_controller:
    manifest: ./arm_controller/subsystem.yaml
  left_servo_gateway:
    manifest: ./left_servo_gateway/subsystem.yaml

bindings:
  arm_controller.joint_servo:
    to: left_servo_gateway.servo

For reusable packages, use instances:. Each key is the concrete runtime subsystem identity. The entry may point directly at a subsystem manifest or at a package directory containing package.yaml. By default, the package resolves subsystem.yaml; use subsystem_manifest only for a non-default relative path.

kind: librux.deployment
version: 1

instances:
  demo.left_manipulator:
    package: ./packages/manipulator_servo
    params:
      subsystem.name: demo.left_manipulator
      controller.robot_id: left_ps_ca
  demo.right_manipulator:
    package: ./packages/manipulator_servo
    params:
      subsystem.name: demo.right_manipulator
      controller.robot_id: right_ps_ca
  demo.isaac_sim_gateway:
    manifest: ./subsystem.yaml

bindings:
  demo.left_manipulator.actuator_servo_gateway:
    to: demo.isaac_sim_gateway.actuator_servo_gateway
  demo.right_manipulator.actuator_servo_gateway:
    to: demo.isaac_sim_gateway.actuator_servo_gateway

subsystems: validates that the manifest's declared subsystem.name matches the deployment key. instances: allows the same reusable manifest to appear under multiple runtime identities.

The binding key is always.

<consumer-subsystem>.<required-interface>

The target is always.

<provider-subsystem>.<provided-interface>

Subsystem names may contain dots. Binding references split at the rightmost dot, so demo.mobile_base_rc2_prototype.actuator_servo_gateway means subsystem demo.mobile_base_rc2_prototype and required interface actuator_servo_gateway.

The loader rejects missing subsystem manifests, missing provider interfaces, unsupported cardinality, duplicate subsystem or instance names, and contract mismatches before the subsystem becomes ready.

Deployments can also reference a robot manifest and application manifest. That layer validates robot slots, application-required capabilities, optional platform compatibility labels, and subsystem bindings before runtime reconciliation runs. See Robot/Application/Deployment Manifests.

Availability

Binding validation is structural. It does not require the provider process to already be running.

This distinction matters for cyclic or mutually dependent subsystems. A valid binding can be structurally configured while the route is still unresolved. In that case the subsystem can report a waiting or not-ready state instead of treating the deployment as invalid.

Use these state boundaries.

State Meaning
INVALID_CONFIG Manifest or binding structure is invalid
WAITING_BINDING Binding is valid but provider route is not available yet
BOUND Binding target is known and route resolution succeeded
READY Required bindings and local registrations are ready
DEGRADED A previously available runtime binding was lost

SDK Shape

Python and C++ subsystems parse a manifest at construction time.

from librux.wrapper import ComponentSubsystem


class ArmController(ComponentSubsystem):
    def __init__(self) -> None:
        super().__init__(
            name="arm_controller",
            manifest_path="subsystem.yaml",
            deployment_manifest_path="librux.deployment.yaml",
        )

    def move(self, params):
        target = self.binding_target("joint_servo")
        return self.call_procedure(
            target=target,
            procedure="set_position",
            params=params,
        )

Convenience APIs such as call_bound_procedure(...) and start_bound_operation(...) can resolve the target internally from the logical binding name.

C++ subsystems use the same manifest boundary. Pass SubsystemOptions::manifest_path directly, set LIBRUX_SUBSYSTEM_MANIFEST, or start the package through librux launch run or lbx deploy up. Launch and deployment bindings are passed to C++ through LIBRUX_BINDINGS.

Diagram Model

The manifest split enables three diagram levels.

flowchart LR A["arm_controller<br/>requires joint_servo"] --> B["left_servo_gateway<br/>provides servo"] B --> C["runtime route<br/>host/port/socket"]
  • Static interface diagrams use subsystem manifests only.
  • Deployment wiring diagrams add the deployment manifest.
  • Runtime diagrams add route and health state from the control backend.

The Web Console Runtime graph uses the live control status snapshot. When a subsystem SDK reports manifest and binding status, the graph can show running subsystems, provided interfaces, required interfaces, and binding edges in the same view as Event and timed-exchange endpoints.