FleetlessFleetlessdocs
Concepts/Apps, End Users & Roles

Apps, End Users & Roles

How a robot’s exposed services become something a specific person, in a specific app, is or isn’t allowed to touch.

Org, users, groups, apps

A Fleetless account is always an org, and every person in it is one user in a single group. The org’s one Org Admins group is the only group that grants console access; its members carry a tier — Owner (everything, including org settings, deletion, and promoting another member to owner) or Developer (manage robots, apps, users, groups, assignments; no destructive org-level actions). There is no self-service path into an org — a person joins a group only by invitation from an existing org admin (or by the group’s OIDC provider; see below).

Within an org, an admin creates one or more apps. An app links to exactly one group, roles are defined per app, and OAuth clients and server keys are per app. A user reaches an app only through an explicit assignment that names a role — belonging to the app’s group is necessary but not sufficient.

One user pool, three claim kinds

Fleetless once kept developers and end users in two separate identity spaces. They are now one pool per org: a person is a single user row, and what they may do is decided by where they are placed (which group, which app assignments), not by which of two account types they hold. A token still carries a kindend_user, developer, or server_key — but that names the surface a session was minted for, not a separate account:

  • end_user — a session for a named app, backed by an assignment, with exactly one role in that app. This is who your app’s users are.
  • developer — a console session, available only to Org Admins.
  • server_key — the one non-person credential: a per-app secret with the app’s full permission set, for your own backend, automation or CI. Never handed to a client.

The same person can hold both a console session and app sessions, each on its own route (/api/auth/* for the console, /api/client/* for an app). An org admin may additionally sign into any app in the org through an impersonation step — as one of the app’s roles, or as a specific assigned user — with the session carrying an act claim naming the real admin. See Identity.

Getting a user into an app

An org admin assigns the user to the app and picks a role (the app’s default role is prefilled). The app must belong to the user’s group. A user can be assigned to more than one app; within each app they hold exactly one role. Org admins are never assigned — they always may impersonate into any app (above).

Users enter a group two ways now. By invitation through the console; or, if the group carries an OIDC provider, by just-in-time provisioning — a user your IdP vouches for is created into the group on first login, with the app assignments the provider’s JIT grants name. The old per-app self-registration — a public sign-up endpoint gated by an email-domain filter — has been removed with no successor (JIT is provisioning by your IdP, not strangers signing themselves up). An email collision is refused rather than merged. See Identity.

Roles are the only filter

Every app starts with two roles, observe and operate; a developer can define arbitrary custom roles. Permissions are a matrix of role × service, per robot, per slug. There is exactly one permission system: if a robot is assigned to an app, all of that robot’s services exist in the app — roles are the only filter. To hide a service app-wide, remove it from every role.

Roles also gate three app-level capabilities that aren’t slugs:

  • Action history — whether a role can see past job results, not just live ones.
  • Presence — whether a role can see other end users of the app and their online status.
  • assets — access to the robot’s asset store (URDF/meshes). This is its own right, deliberately not folded into “can see the robot,” because a mesh set reveals the machine’s physical shape and that’s a decision on its own.

Authentication: two doors, one token shape

  • client.auth.login() — your app collects email/password itself and calls the credentials endpoint directly.
  • The hosted login page — redirect the end user to a Fleetless-served page; they authenticate there and your app gets back the exact token shape login() would have produced. Use this when you want your app to never see a password. This is also what an MCP connection uses to log an end user in — see MCP. When the user’s group federates, the hosted page redirects out to the group’s own OIDC provider (Authorization Code + PKCE, S256 only) and returns through Fleetless — the app never sees a password or talks to the IdP directly. The per-app IdP configuration of earlier versions has been replaced by these per-group providers.

(Self-registration was a third door in earlier versions — an end user signing themselves up against an app. It has been removed; see “Getting a user into an app” above.)

Both doors converge on the same Fleetless token. Logout always ends the Fleetless session (all its refresh tokens). If the session came from a federated IdP login, it also attempts RP-initiated logout at that IdP — but that can fail for reasons outside Fleetless’s control (the IdP publishes no end_session_endpoint, which OIDC allows), in which case the IdP session survives and the SDK says so rather than claiming success. See idp_logout in the SDK Reference.

Server keys carry an app’s full permission set for backend code — rotatable, clearly marked as secret, never meant for a client.

Password lifecycle

The one user pool shares one password lifecycle, on the console (/api/auth/password/*) and app (/api/client/password/*) surfaces alike. Two different mechanisms, deliberately:

  • Changing a known password requires the current password, not just a valid session — a stolen session token alone can’t take over the account. Every other session is revoked on success; the session making the change survives.
  • Recovering a forgotten password is a two-step, unauthenticated flow (request → confirm) with a single-use, expiring link token. Spending it revokes every session, no exception.

Where to go next

  • Disconnect Behaviour for what a role’s bridge_state visibility is actually for.
  • The hosted-login recipe for the exact redirect flow, error shapes, and a verified end-to-end run.
  • The MCP chapter for how an AI tool authenticates as an end user through the same login page.