# Architecture

> The RosieOS processes, the hosts they run on, the sockets and ports between them, and how jog, moves and weld programs travel from a client to the drives.

URL: https://advancedmetalresearch.com/docs/concepts/architecture
Section: RosieOS docs / Concepts
Last updated: 2026-10-10

A RosieOS cell is a small set of processes around one controller. The real-time core, `rosie-rt-core`, owns the drives. The Go adapter `rt-control` is the only public way to command it. Every client, whether the offline programming app, a pendant, a motion server or your own code, goes through `rt-control` and needs its lease to move anything.

![RosieOS processes by host: the operator PC runs the OLP UI, OLP server and weld planner; the cell host runs the motion servers, rt-control, rosie-rt-core and the NATS publisher; the Steam Deck runs the pendant and a headless OLP server; the drives and a hardware E-stop sit below.](https://advancedmetalresearch.com/assets/docs/architecture-hosts.svg)

*Figure: Processes by host. Solid arrows carry commands, dashed ones observation only. The hardware E-stop removes drive power without any software.*

## Processes

| Process | Runs on | Role | Listens on |
|---|---|---|---|
| `rosie-rt-core` | Cell host | 1 kHz cyclic controller: EtherCAT master, CiA402 drive state, the motion start gate and the final output permit | Private native socket, `/run/rosie-rt-core/native/ipc.sock` when installed |
| `rosie-rt-core-sim` | Any Linux host | The same state machine over a simulated bus | Private native socket |
| `rt-control` | Cell host | The public control API: lease and fence, jog lane, trajectories and programs, events, telemetry | `control.sock` and `jog.sock` (Unix), plus an optional mutual-TLS listener |
| `rt-natspublisher` | Cell host | Republishes status and telemetry to NATS. It has no command authority. | None (reads `control.sock`) |
| `robot-v4-cartesiand` | Cell host, and inside OLP | Cartesian motion server: Cartesian jog and position moves on the rt-core jog lane. OLP also runs it in `--resolve-only` mode to turn Cartesian twists into joint velocities. | UDP intent listener (`--udp-listen`) |
| `joint_trajectory_daemon` | Cell host | Stores dense `.rdt` programs and plays them through `rt-control` | TCP `127.0.0.1:8797` |
| OLP server | Operator PC, or headless on the Deck | Offline programming backend: programs, planning broker, local simulator, machine session | HTTP `127.0.0.1:8794` |
| OLP UI | Browser | The programming and operating app | Vite on `:5189` |
| Weld planner | Operator PC with an NVIDIA GPU | Turns a `.weldplan` into verified joint trajectories and dense `.rdt` files | HTTP `:8796` |
| Pendant v5 | Steam Deck | Native Qt teach pendant. It runs OLP's TypeScript program logic and talks only to a headless OLP server on the Deck. | None |
| Pendant v4 | Steam Deck | Earlier native pendant, kept as a rollback. It talks to `rt-control` directly. | None |
| Virtual pendant | Operator PC | Browser skin of the v4 pendant plus a loopback bridge, for simulation only | UI `:51711`, bridge `127.0.0.1:51712` |

Ports, socket paths and environment variables are all listed in [Ports, sockets and environment](https://advancedmetalresearch.com/docs/reference/ports-and-environment).

## Public and private interfaces

- **Public: `rt-control`.** This is HTTP/JSON over the Unix socket `control.sock`, with jog datagrams on `jog.sock` in the same directory. With `--remote-listen` it also serves the same API over mutual TLS on TCP, plus a WebSocket jog lane. Deployed cells pin that listener to `127.0.0.1:8443`. The contract is `rt-core/protocol/application-v1.schema.json`, and the Go, C++ and TypeScript clients are generated from it. See [rt-control HTTP API](https://advancedmetalresearch.com/docs/apis/rt-control-http).
- **Private: native IPC.** `rt-control` talks to the core over a Unix socket plus shared-memory rings and a fast-control region. The layout is specified in `rt-core/protocol/control.json` and can change between releases. Never program against it.
- **Service APIs.** The OLP server, the weld planner and the dense daemon each have their own interface. They are clients of `rt-control`, not alternatives to it.

When installed with the host units, `rosie-rt-core` runs as user `rosie-rt` and `rt-control` as `rosie-ctl`. The public sockets live in `/run/rosie-rt-core/public/`, with symlinks at `/run/rosie-rt-core/control.sock` and `/run/rosie-rt-core/jog.sock`.

## How motion reaches the drives

There are three ways to move the robot. Each ends at the same motion start gate in the core. See the [safety model](https://advancedmetalresearch.com/docs/get-started/safety-model#motion-start-gate).

### Jog

A jog is a stream of velocity updates, each with its own deadline. The client opens a jog generation with `begin_jog`, then sends 224-byte datagrams on `jog.sock`, or WebSocket frames on a remote cell. When the updates stop, the core ramps the axis to a hold.

| Client | Path |
|---|---|
| OLP joint jog | OLP server → Go SDK jog session → `jog.sock` |
| OLP Cartesian jog | OLP server → `robot-v4-cartesiand --resolve-only` (twist to joint velocities) → the same jog session |
| Cartesian motion server | UDP intent packet → `robot-v4-cartesiand` → C++ client jog producer → `jog.sock` |
| Pendant v4 | mutual TLS → WebSocket jog on `/v1/jog` |
| Virtual pendant (simulation) | Browser → bridge on `:51712` → Go SDK jog session → `jog.sock` |

### Point-list moves

A client can upload a short list of timed points with `prepare_trajectory`. Positions are in rad or m and times in ns from the plan start. The client then starts it with `start_trajectory`. OLP uses this for its joint and Cartesian moves. `rtctl prepare` and `rtctl start` send the same operations from a file.

### Dense weld programs

A planned weld program travels as an immutable dense trajectory, a `.rdt` file.

1. The OLP server sends a `.weldplan` to the weld planner.
2. The planner plans each seam and each connecting move, runs the verifier, and writes a `.rdt` only if every segment passes.
3. OLP's **Load** fetches the `.rdt` by digest from the planner and checks it against the robot and the cell. It then uploads it to `rt-control` with `POST /v1/program` (`prepare_program`).
4. **Play** sends `start_program`. rt-core interpolates the samples in the cycle.

The dense daemon offers the same upload-then-play path for other clients: TCP ingest on `:8797`, with play and stop over NATS under a leader lease. Programs that arrive through it are not re-verified.

See [Motion paths and planning](https://advancedmetalresearch.com/docs/concepts/motion-and-planning) and [Weld planning and verification](https://advancedmetalresearch.com/docs/concepts/weld-planning-and-verification).

## Identity ties it together

Several digests keep every process agreeing on which robot it is talking to:

- The **robot description** (`robot_description/robots/<model>/`) has a SHA-256 identity over the files its manifest registers.
- A **machine config** pins that description, and `rtctl compile` turns it into an immutable `configuration_sha256`.
- `rt-control` is started with that digest and a **pair binding** (pair id and revision). Every `acquire` must present the same binding.
- The planner stamps the robot and cell identity into each `.rdt` header. OLP refuses to load a plan made against a different robot or cell.

See [Cells, machines and positioners](https://advancedmetalresearch.com/docs/concepts/cells-and-positioners) and [Robot description and coordinate frames](https://advancedmetalresearch.com/docs/concepts/robot-description-and-frames).

## Observation

Anyone with access to the socket can read state without taking control:

- `GET /v1/describe`, `GET /v1/status`
- events from `/v1/events`, or as Server-Sent Events from `/v1/events/stream`
- binary telemetry batches from `/v1/telemetry`

On a remote listener, a client certificate is still required. `rt-natspublisher` republishes status and telemetry to NATS subjects `robot/v4/rtcore.<host>.status` and `robot/v4/rtcore.<host>.telemetry.batch`, for displays and archiving. Those subjects carry no command authority.

## Maturity

| Status | Components |
|---|---|
| Production source | rt-core (core, `rt-control`, SDKs, `rtctl`), both motion servers, OLP dense execution and the local simulator, the weld planner, robot descriptions |
| Current, not qualified | Pendant v5 (not qualified on physical input or real motion). `reset_fault` is interim. |
| Simulation only | Virtual pendant |
| Experimental | `mujoco-sim/v1`, an independent MuJoCo model. It is not a controller stand-in. |
| Legacy, off by default | OLP connected execution over NATS (`--enable-connected-execution`), and older versioned trees |

## Sources

Written from these files in the RosieOS repository (https://github.com/advanced-metal-research/RosieOS):

- `rt-core/cmd/rt-control/main.go:24-66`
- `rt-core/host/rosie-rt-core.service:12-33`
- `rt-core/host/rosie-rt-control.service:12-27`
- `rt-core/host/rosie-rt-natspublisher.service:15-27`
- `rt-core/adapters/rosie/control/http.go:52-178`
- `rt-core/protocol/control.json (wire_contract)`
- `rt-core/protocol/application-v1.schema.json (capabilities`
- `rules.remote_jog_upgrade)`
- `rt-core/sdk/control/jog.go:23,87-130`
- `rt-core/tools/natspublisher/publisher/publisher.go:385`
- `rt-core/tools/rtctl/control.go:22-26,147-176`
- `motion-server/v1/src/robot_v4_cartesian_cli.hpp:236-339`
- `motion-server/joint-trajectory/v1/src/joint_trajectory_daemon.cpp:99-128,209-211`
- `motion-server/joint-trajectory/v1/config/joint_trajectory_daemon.config.json`
- `offline-programming/v1/main.go:257-300`
- `offline-programming/v1/internal/denseexec/joint_jog.go:81-83`
- `offline-programming/v1/internal/denseexec/joint_move.go:437-491`
- `offline-programming/v1/internal/denseexec/cartesian_process.go:49`
- `offline-programming/v1/internal/denseexec/rt_core.go:735-840`
- `offline-programming/v1/internal/denseexec/session.go:236-256`
- `offline-programming/v1/ui/vite.config.ts:3-14`
- `weld_planner/v1/python/weld_motion_planner/server/motion_planner_server.py:57,538-565`
- `weld_planner/v1/python/weldplan/native_admission.py:204-250`
- `steamdeck/real/v5/src/main.cpp:18-24`
- `steamdeck/virtual/bridge/cli.go:20-45`
- `steamdeck/virtual/bridge/rt_core.go:165-330`
- `steamdeck/virtual/v1/ui/vite.config.ts:108-120`
- `rt-core/tools/rtctl/command.go:25-45`
- `offline-programming/v1/internal/denseexec/cartesian_jog.go:15-120`
