Skip to content

Robot Agent Services

The robot agent lets the tablet identify a robot and inspect its health without touching the control path. It is a small read-only service that runs alongside ROS, and it never imports ROS, never acquires a control lease, and never publishes actuator commands. Control traffic stays on rosbridge port 9090.

Two systemd services make up the agent:

  • hausbots-robot-agent — the HTTP service on port 8080. It answers with the robot’s identity and a health snapshot.
  • hausbots-robot-health — a read-only ROS adapter. It subscribes to /hb2/app/status, writes /run/hausbots/health.json, and is the only part that talks to ROS at all.

The agent exists so the tablet can discover robots on the switch, show diagnostics while ROS is down or faulted, and gate launch on fresh readiness. It is not a second control channel: commands and telemetry stay on rosbridge.

All three endpoints are GET only, and every response carries Cache-Control: no-store:

Endpoint Purpose
/api/v1/identity Stable robot identity used for discovery and defaults
/api/v1/health Diagnostic snapshot used to verify readiness
/healthz Liveness: 200 with ok while serving, 503 during shutdown

Identity and health return JSON. Identity looks like this:

{
"schemaVersion": 1,
"protocolVersion": "1.0.0",
"robotId": "hb2-23",
"displayName": "Wall Crawler 23",
"model": "HB2",
"capabilities": ["drive", "fan", "static-camera", "mira", "pundit", "lifter"],
"rosbridgePort": 9090,
"isSimulator": false
}

Health is a diagnostic snapshot. A fully ready robot:

{
"schemaVersion": 1,
"status": "ready",
"components": { "controller": "ready", "rosbridge": "ready" },
"uptimeSeconds": 1234,
"observedAt": "2026-09-22T08:00:00.000Z"
}

The same robot with the ROS side down:

{
"schemaVersion": 1,
"status": "degraded",
"components": { "controller": "unknown", "rosbridge": "offline" },
"uptimeSeconds": 1234,
"observedAt": "2026-09-22T08:00:00.000Z"
}

The adapter writes /run/hausbots/health.json from /hb2/app/status at 10 Hz. The agent reads that file and treats a snapshot older than five seconds — or more than two seconds in the future — as stale, reporting controller: unknown. It adds a bounded loopback TCP probe as rosbridge. Overall ready requires a fresh snapshot with controller: ready and rosbridge: ready; anything else is degraded.

observedAt is the robot’s own clock and is never compared against the tablet’s clock.

The tablet scans port 8080 across the switch subnet. For each candidate it validates the identity response, fetches health, and probes rosbridge itself. It launches only a fully verified candidate: health ready with controller ready. A robot with a valid identity but degraded health stays visible in the list for diagnostics — it just cannot be launched. Health is diagnostic only and never grants a control lease.

The agent ships as one tarball from this site. See Downloads for sizes and checksums.

File Purpose
hausbots-robot-agent-linux-arm64 The service binary
install-robot-agent.sh Installer (verifies ROS prerequisites, installs files, creates the hausbots user, enables services)
robot-agent.example.json Configuration template
hausbots-robot-agent.service HTTP service unit
hausbots-robot-health.service ROS health adapter unit
ros/health_snapshot.py Adapter script

Run these checks on the robot first. A fresh shell needs ROS sourced so rospack and rosmsg are on PATH:

Terminal window
source /opt/ros/noetic/setup.bash
rospack find hb2_api_bridge
rosmsg show hb2_api_bridge/TabletStatus

Both must succeed. The installer refuses to run when either is missing. If a check fails, stop and contact HausBots: the robot is missing the bridge package this agent is paired with, and installing the agent will not fix that.

The offline path is the normal one for a robot on an isolated switch. Download and verify the bundle on a workstation:

Terminal window
curl -fLO https://hausbots-docs.celeriscode.com/downloads/robot-agent/hausbots-robot-agent-linux-arm64.tar.gz
curl -fLO https://hausbots-docs.celeriscode.com/downloads/robot-agent/hausbots-robot-agent-linux-arm64.tar.gz.sha256
sha256sum -c hausbots-robot-agent-linux-arm64.tar.gz.sha256 # expect: OK
scp hausbots-robot-agent-linux-arm64.tar.gz robot@10.0.0.23:/tmp/

Then, on the robot:

Terminal window
tar -xzf /tmp/hausbots-robot-agent-linux-arm64.tar.gz -C /tmp
cd /tmp/hausbots-robot-agent-v*-linux-arm64
sudo ./install-robot-agent.sh

The directory name carries the bundle version; v* matches whatever version you downloaded.

The installer verifies hb2_api_bridge and hb2_api_bridge/TabletStatus, installs /usr/local/bin/hausbots-robot-agent, /usr/local/libexec/hausbots-health-snapshot.py, both systemd units, and /etc/hausbots/robot-agent.json, creates the hausbots system user, and enables and starts both services. It preserves an existing configuration unless --replace-config is passed.

If the robot has internet access, fetch and run the same tarball directly on the robot:

Terminal window
curl -fLO https://hausbots-docs.celeriscode.com/downloads/robot-agent/hausbots-robot-agent-linux-arm64.tar.gz
curl -fLO https://hausbots-docs.celeriscode.com/downloads/robot-agent/hausbots-robot-agent-linux-arm64.tar.gz.sha256
sha256sum -c hausbots-robot-agent-linux-arm64.tar.gz.sha256 # expect: OK
tar -xzf hausbots-robot-agent-linux-arm64.tar.gz
cd hausbots-robot-agent-v*-linux-arm64
sudo ./install-robot-agent.sh

Edit /etc/hausbots/robot-agent.json. Every robot on a switch needs its own robotId and displayName; never reuse an id, and never run a simulator on a robot’s address.

Field Meaning
identity.schemaVersion Must be 1
identity.protocolVersion Semver whose major must be 1 (e.g. 1.0.0)
identity.robotId Stable id used in discovery (e.g. hb2-23)
identity.displayName Human name shown on the tablet (e.g. Wall Crawler 23)
identity.model Robot model string (e.g. HB2)
identity.capabilities Non-empty strings; the tablet acts on static-camera (standard camera and LED), pundit (PTZ camera and controls), mira, lifter; conventional drive and fan describe the base robot; unknown values are ignored
identity.rosbridgePort Rosbridge port, 9090
identity.isSimulator false for a real robot
listenAddress / httpPort HTTP bind, 0.0.0.0 / 8080
rosbridgeHost / rosbridgePort Health probe target, 127.0.0.1 / 9090
healthSnapshotPath /run/hausbots/health.json

The installed template in full:

{
"identity": {
"schemaVersion": 1,
"protocolVersion": "1.0.0",
"robotId": "hb2-23",
"displayName": "Wall Crawler 23",
"model": "HB2",
"capabilities": ["drive", "fan", "static-camera", "mira", "pundit", "lifter"],
"rosbridgePort": 9090,
"isSimulator": false
},
"listenAddress": "0.0.0.0",
"httpPort": 8080,
"rosbridgeHost": "127.0.0.1",
"rosbridgePort": 9090,
"healthSnapshotPath": "/run/hausbots/health.json"
}

Apply the change:

Terminal window
sudo systemctl restart hausbots-robot-agent

The health unit orders itself after roscore.service, rosbridge.service, and robot_launch.service. If the robot’s bridge runs under different unit names, override the dependencies with a drop-in, which survives installer updates:

Terminal window
sudo systemctl edit hausbots-robot-health.service

In the editor, add (replacing your-bridge.service with the actual unit name):

[Unit]
Wants=
After=
Wants=roscore.service your-bridge.service
After=roscore.service your-bridge.service

Then apply it:

Terminal window
sudo systemctl daemon-reload
sudo systemctl restart hausbots-robot-health

The empty Wants=/After= lines reset the inherited lists, which is why they come before the new values.

The agent itself only needs rosbridge reachable on 127.0.0.1:9090.

From another machine on the switch:

Terminal window
curl http://10.0.0.23:8080/api/v1/identity
curl http://10.0.0.23:8080/api/v1/health
curl http://10.0.0.23:8080/healthz # expect: ok

On the robot:

Terminal window
systemctl status hausbots-robot-agent.service hausbots-robot-health.service
journalctl -u hausbots-robot-agent -n 20 --no-pager
journalctl -u hausbots-robot-health -n 20 --no-pager

Identity should print the configured values. Health should show ready with both components ready once ROS is up; healthz prints ok. Both services should be active, and the journal tails should be free of repeating errors.

Download the new bundle, verify its checksum, and re-run the installer on the robot. The existing configuration is preserved, so identity and network settings survive the update. Unit-file edits do not survive updates — the installer re-copies both units. Dependency overrides made with systemctl edit are drop-ins and do survive.

Terminal window
sudo systemctl disable --now hausbots-robot-agent.service hausbots-robot-health.service
sudo rm -f /lib/systemd/system/hausbots-robot-agent.service /lib/systemd/system/hausbots-robot-health.service
sudo rm -f /usr/local/bin/hausbots-robot-agent /usr/local/libexec/hausbots-health-snapshot.py
sudo rm -f /run/hausbots/health.json
sudo systemctl daemon-reload

Keep /etc/hausbots/robot-agent.json if you plan to reinstall; remove it and the hausbots system user only when the robot will not run the agent again.

Symptom Fix
Identity returns 404 Wrong path; only the three endpoints above exist.
Agent exits with code 2 /etc/hausbots/robot-agent.json failed validation; read the logged field error and fix the config.
Health stays degraded, controller unknown The adapter has no fresh /hb2/app/status; check systemctl status hausbots-robot-health and that the bridge publishes status.
Health shows rosbridge offline Nothing is listening on 127.0.0.1:9090; start the rosbridge service.
Health shows degraded, controller unknown after a fault clears The adapter writes 10 Hz; refresh once the bridge reports a fresh status.
Port 8080 already in use ss -ltnp | grep 8080; stop the conflicting process or change httpPort (and the tablet scan setting).
Services do not start after reboot systemctl is-enabled hausbots-robot-agent hausbots-robot-health; re-run the installer if disabled.