closed betaAccess comes in small rounds.
One small bridge process puts a ROS 2 robot on the internet. You pick, in a console, which topics, services, actions and cameras become API. Your app, your login page, your name on it — and an MCP server for every assistant that asks.
Fleetless is not a dashboard an agent has to click through. Everything the console does is a tool; everything the robot exposes is a tool. One host for you, one endpoint per app for the people your robot works for.
Paste it into Claude Code, Codex, Claude Desktop or anything else that speaks the Model Context Protocol. The client discovers everything from the bare host, registers itself and asks for no client id.
Read datapoints, call services and actions, watch jobs, take a camera snapshot — and every console operation: robots, exposure, apps, roles, users. Build the robot and the app from the same chat.
Switch on your app's own endpoint and hand it to your users. They sign in through your login page — Fleetless never shows them one — and get the same 14 robot tools, filtered by their role.
Your customer's operations lead asks their assistant whether the robot is charging, instead of writing to your support address.
Their identity stays in your app. Their role decides what the assistant may do; the tool list stays the same for everyone, and a refusal says why.
The bridge binds four kinds of source: ROS 2 image topics, RTSP, MJPEG and V4L2. Per camera you set resolution, frame rate and bitrate — which is also how you cap the robot's own uplink.
One uplink per camera, regardless of audience. The cloud fans out. Live over WebRTC: the first viewer starts the stream, the last one leaving stops it. Snapshots are always on, at your cadence, served from a cloud cache.
No recording. Fleetless hands you the pixels, not the archive.
Five kinds of thing can be exposed. Each gets a slug you choose, decoupled from the ROS name. Rename the topic; nobody's app breaks.
| kind | direction | what it does when things go wrong |
|---|---|---|
| Datapoint | robot → clients | One field or a whole topic; unit, range and scaling per field; max N Hz or on change; optional retention. Every value carries timestamp_ms: when the bridge captured it, never when your app received it. |
| Publisher | clients → robot | A timeout and a failsafe message the bridge publishes itself when clients go quiet — dropped link included. cmd_vel, 300 ms, zero-twist failsafe: a lost connection stops the robot instead of leaving it committed to its last command. |
| Service | request → response | Parameters validated in the cloud against rules you wrote: min/max, enum, pattern, required. An invalid value never reaches the bridge. |
| Action | a job with a lifecycle | Feedback, progress, result, cancel. A disconnect does not abort a running job; the result arrives on reconnect. A job lost to a bridge restart is reported as lost, not as still running. |
| Camera | robot → clients | Snapshot and live, as above. Bandwidth is set per camera, on the robot's side. |
Every exposed thing has the name you gave it. The robot can call it whatever it likes.
Every robot ships them. Build your offline state on the first one instead of inferring it from timeouts.
A command to a robot that is not there is rejected immediately. There is no queue that fires an hour later.
Your team lives in the console. The users of one app live in that app: each belongs to exactly one app, has exactly one role, and signs in through your UI — password or any number of OpenID Connect providers you configure — over a JSON auth API.
Permissions are a matrix of role × service, per robot, per slug. Every app starts with observe and operate and can define its own. Roles are the only filter. A per-app server key exists for your own backend.
| amr-04 · slug | observe | operate | shift_lead |
|---|---|---|---|
| battery_percentage | read | read | read |
| camera_front | snapshot | snapshot · live | snapshot · live |
| drive | — | publish | — |
| dock | — | start · cancel | start |
| set_mode | — | — | call |
@fleetless/sdk on npm. TypeScript, framework-agnostic, ESM and CJS. Same slugs, same roles, same validation as everything else on this page.
A recipe with a working demo: three.js + urdf-loader, loading your URDF and meshes from the authenticated asset store. Joint states are an ordinary datapoint.
A recipe on the client auth API. The page is yours; the tokens are our problem.
A recipe for switching on your app's endpoint and handing it out.
Ready-made three.js scenes for a robot app.
import { createClient } from "@fleetless/sdk"; const client = createClient({ apiUrl: "https://api.fleetless.dev", appIdentifier: "warehouse_dash", }); await client.auth.login("user@example.com", "correct-horse-battery"); const battery = await client.datapoints.get("robot-id", "battery_percentage"); console.log(battery.value, battery.timestamp_ms); client.datapoints.subscribe("robot-id", "battery_percentage", { onEvent(event) { console.log(event.value, event.timestamp_ms); }, });
Fleetless is in closed beta and access comes in small rounds. The list is the way in.
on the list · you@company.com