FleetlessFleetlessdocs
Concepts/URDF & Meshes

URDF & Meshes

This is the asset store — the mechanism, not the rendering recipe built on top of it (see the end of this page for that).

An asset is an immutable file, not a service kind

An asset is an immutable file that belongs to one robot — not a service type like a datapoint or an action. Every asset has a UUID and is fetched by it. There are no org-level assets and no public fetch: every access is authenticated and role-checked (the assets right, see Apps, End Users & Roles).

One authorization mechanism: the Authorization header

Assets use exactly the same bearer token as every other API call — no token in the URL, no cookie, no signed URL. That’s a deliberate choice about the number of mechanisms, not ergonomics: a second credential would carry its own expiry, its own renewal, its own rotation, and every log entry would need to say which kind of token it was. Instead assets inherit token renewal, role checks, audit and rate limiting from the mechanism that already exists.

Consequence, stated explicitly: this is not a CDN. A shared cache must not store a response to a request carrying Authorization. Assets are immutable, so Cache-Control: private, immutable is correct and a browser’s own cache works fully — an edge cache in front does not. Signed URLs remain possible as a later, purely additive endpoint, precisely because assets are immutable and UUID-addressed.

URDF is an asset with a special role

The URDF is copied along with a robot’s configuration and is the entry point through which meshes are found.

Two ways an asset enters the store:

  • A connected bridge detects the URDF (default source /robot_description) and reports only that it’s available — nothing transfers until the developer explicitly clicks Sync in the console. This spends the org’s asset quota, which is why it’s a developer/console action rather than something your client app can trigger.
  • A developer manually uploads a zip (URDF + meshes).

Meshes (package:// URIs) are resolved by the bridge against its workspace and uploaded on request. Storage is content-addressed — identical meshes across robots cost one copy — and counts against the org quota. The console reports completeness (“2 meshes missing”).

The served URDF is re-serialized, not byte-identical

GET .../urdf parses the document and returns it re-emitted: XML comments and the original attribute/element order are not preserved. Everything semantics-bearing survives — namespaces, xacro: prefixes, attribute values, structure, unicode names, empty elements — but a license or attribution header written as a comment is gone after fetch, and a diff against the original file is not meaningful. If you need the original bytes, fetch the URDF asset directly by its UUID — the store keeps that copy untouched; rewriting only happens on the .../urdf route.

GET .../urdf also rewrites mesh package:// URIs into Fleetless asset URLs your client can fetch with the same bearer token.

Rendering is your app’s job, and the SDK carries the one piece you can’t build yourself

3D rendering (three.js + urdf-loader) is deliberately not a platform feature — it’s a documented recipe. But there’s one piece of glue nothing else can substitute for: neither a plain <img> tag nor urdf-loader’s default loaders can set an Authorization header, and every asset byte on Fleetless requires one. The SDK ships a mesh-loading callback (assets.createMeshLoader) and a fuller scene-preparation helper (assets.prepareUrdfScene, which also handles a <material>'s texture and an image a .dae references internally) so every app doesn’t write this fetch-with-header glue itself, slightly differently each time.

Joint states are an ordinary datapoint (typically a whole-topic subscription on /joint_states) — nothing special about them.

For the full step-by-step, worked code, and a verified browser run, including the traps found along the way (an unmapped mesh reference must refuse rather than silently fetch cross-origin; a .dae’s relative texture path needs a second normalized lookup), see the Live 3D recipe.