Advanced Subsystem Packages
The cleaner tutorial now runs robot subsystem packages as managed Librux packages.
run_sim_world.py only starts the provided simulator and browser frontend.
Early tutorial steps use librux launch run to start one subsystem package at a time;
the final application step uses librux deploy up to reconcile the full
robot.
This page explains the package boundary using the same cleaner subsystem package you launched in Step 3.
1. Package Boundary
A Librux package wraps one reusable subsystem.
subsystem package root
package.yaml
subsystem.yaml
subsystem implementation
config/
spec/
frontend/ optional static UI
For the cleaner tutorial, each subsystem folder is also a package root.
tutorials/subsystems/
component.mobile/
package.yaml
subsystem.yaml
mobile_controller.py
run.py
Reasonable subsystem packages include these examples.
| Subsystem package | Role |
|---|---|
gateway.device_gateway |
simulator-backed gateway subsystem package |
component.mobile |
mobile-base component subsystem package |
component.manipulation |
manipulator component subsystem package |
component.gripper |
gripper component subsystem package |
component.perception |
perception component subsystem package |
app.cleaner.teleop |
operator application package |
app.cleaner.radar |
perception-driven application package |
Deployment decides which installed package instance fills each robot slot.
2. Inspect A Managed Subsystem Package
Open the mobile subsystem package manifest.
nano tutorials/subsystems/component.mobile/package.yaml
The package manifest declares the following.
| Field | Meaning |
|---|---|
name |
reusable package identity |
entrypoint |
command, arguments, environment, and working directory |
parameters |
launch-time values allowed by the package |
resources |
CPU, network, device, CAN, or serial claims |
frontend |
optional static package UI served by the control backend |
contracts |
package-local spec roots |
Packages can include static browser UI. If the package also runs a local UI
backend, it must not open an unmanaged network listener. Declare
resources.sandbox.network: brokered, expose browser assets as static files,
and open any declared frontend.context listener through the resource broker.
resources:
sandbox:
network: brokered
frontend:
type: static
root: frontend/dist
index: index.html
context:
api: http://127.0.0.1:{{param.gui_port}}/
ws: ws://127.0.0.1:{{param.gui_port}}/ws
The Web Console serves the static files at /api/v1/packages/<package-id>/ui/.
Runtime opens a specific execution instance.
Build static package assets with relative URLs. This keeps scripts, styles, and
images below the package-scoped /api/v1 route without hardcoding a package id.
/api/v1/packages/<package-id>/ui/?instance=<instance-id>&context=/api/v1/packages/<package-id>/instances/<instance-id>/context
Frontend code should read the context endpoint and use the returned same-origin
api and ws proxy paths. It must not hardcode host ports. App code should
call the platform through Librux bindings and should not claim NIC, CAN,
serial, or device resources directly. Component packages may claim CPU and
brokered package-frontend networking, but hardware and fieldbus resources still
belong behind gateway packages.
Open the subsystem manifest next to it.
nano tutorials/subsystems/component.mobile/subsystem.yaml
The package entrypoint is the package-local run.py wrapper. The runtime looks
for subsystem.yaml by default, so most one-subsystem packages do not need a
subsystem_manifest field in package.yaml. Use subsystem_manifest only when
a package intentionally keeps the subsystem manifest at another relative path.
There is no separate packages/ wrapper directory in the cleaner tutorial.
3. Validate The Package
Use installed runtime tools.
. /opt/librux/env.sh
librux package validate tutorials/subsystems/component.mobile
Validation checks manifest schema, package name safety, entrypoint paths, optional static frontend paths, contract spec roots, and resource claim format.
4. Build A Runnable Package Directory
librux package build tutorials/subsystems/component.mobile \
--out /tmp/librux-robot-mobile.libruxpkg \
--force
The current build step validates and copies an already runnable package directory. It does not compile application source, vendor dependencies, sign artifacts, or create a system installer.
To upload through the Web Console, archive the package directory.
tar -czf /tmp/librux-robot-mobile.libruxpkg.tar.gz \
-C /tmp librux-robot-mobile.libruxpkg
5. Launch Through Librux
The installed resource service normally runs as a system service. If you are using a source-built runtime, start a local resource service first. With an installed release, launch the package.
librux launch run /tmp/librux-robot-mobile.libruxpkg \
--config /opt/librux/runtime/config.yaml \
--instance component.mobile \
--bind actuator_servo=gateway.device_gateway.actuator_servo
Expected result.
- launcher output starts with
Ran packageorLaunched package - the child process receives package environment variables
- the child process receives a resource lease
- package runtime files are written under the instance runtime directory
- detached stdout/stderr logs are written under
<instance-runtime-dir>/logs/stdout.logand<instance-runtime-dir>/logs/stderr.log
On an installed /opt/librux runtime, the default instance runtime directory is
under /opt/librux/var/run/packages/<instance-id>. This keeps installed
packages and tutorials read-only while still giving each launched instance a
writable state directory. In a source checkout without LIBRUX_PREFIX, the
fallback is package-local var/instances/<instance-id>.
If a detached process exits immediately, the launcher reports that early exit instead of returning a successful launch result. Check the stderr log path shown in the error for the package-side failure.
Add --json when you need the full machine-readable launch payload.
Launch a second instance with different parameters.
librux launch run /tmp/librux-robot-mobile.libruxpkg \
--instance component.mobile.test \
--set subsystem.name=component.mobile.test \
--bind actuator_servo=gateway.device_gateway.actuator_servo \
--detach
This is the same model a product cleaner robot would use to run multiple instances of a reusable controller package.
6. Web Console Management
The Web Console manages the same package contract.
- upload a
.tar.gzor.tgzpackage archive - validate and install it
- launch one or more runtime instances of the installed package from Runtime
- expose optional static package frontends under
/api/v1/packages/<package-id>/ui/ - open package frontends from concrete Runtime instances with instance context
- stop or restart individual runtime instances from Runtime
- uninstall managed packages when no runtime instances are running
Install registers the package once. Multiple execution instances are created at
launch time by choosing distinct instance ids. A package must use
{{instance.id}} or explicit parameters for runtime names if it is expected to
run more than once.
The Packages page accepts packages for gateway, component, compound, and
app subsystem roles. role: app still has the strictest policy: application
subsystems must call compound capabilities rather than claiming NIC, CAN,
serial, or device resources directly. Packages with browser UI should declare
static assets in frontend. If the frontend needs a package-specific REST or
WebSocket backend, declare frontend.context.api or frontend.context.ws as
localhost endpoint templates. The control backend exposes them to the browser
through instance-scoped same-origin proxy paths. The package backend must not
call bind() on a new TCP socket directly. Open the declared loopback listener
through the broker instead.
from librux.core import ResourceBrokerClient
listener = ResourceBrokerClient().open_frontend_tcp_listener(kind="api").socket
# pass listener to the HTTP server implementation for the app backend
Only packages launched through Librux receive leases, resource policy, runtime directories, and Web Console lifecycle management. Directly running a Python script is useful for development, but it is not a managed robot subsystem.
In practical terms.
python3 subsystem.py
-> can register and communicate as a subsystem
-> does not create a package lease or launcher-owned lifecycle
librux launch run package-dir
-> validates the package
-> starts through librux-resourced
-> creates a lease, runtime directory, and lifecycle handle
7. Cleaner Product Shape
The tutorial world uses the following.
run_sim_world.py -> simulator + static frontend
Single-subsystem execution uses the following.
subsystem package root -> librux launch run -> managed lease
Full robot execution uses the following.
deployment manifest -> package instances -> managed launch -> resource policy
For the cleaner, deployment manifests bind package instances into public robot slots.
mobile_base_controller -> installed mobile-base controller package instance
manipulator_controller -> installed manipulator controller package instance
gripper_controller -> installed gripper controller package instance
perception_controller -> installed perception controller package instance
actuator_gateway -> installed gateway package instance
digital_output_gateway -> installed gateway package instance
perception_gateway -> installed gateway package instance
application -> selected cleaner application package
Before packaging the cleaner as a product robot artifact, keep the same standard
contract instances and rerun librux deploy validate.
Continue to Advanced Teleop App Frontend.