Skip to main content
Pioneer treats identity and access as gateway-owned state. A client does not decide what a role can do and does not carry a permanent gateway-wide token. The gateway authenticates a device session, evaluates the principal’s current access, and compiles the capabilities that a client may present. This page covers the implementation boundary. For the client workflow, see Account and collaboration. For wire types and method names, see Authentication and access API.

Identity vocabulary

These records have different lifetimes and responsibilities: The gateway, principal, device, and session snapshots returned by auth/me are separate objects. A client should keep their ids separate even when the UI shows them together.

Device activation

Device activation replaces the old shared superuser-token flow. The normal flow is:
  1. An already authorized operator runs pioneer device create on the gateway host.
  2. The gateway creates a pending device and session and returns a one-time activation code.
  3. The desktop or mobile client sends its installation descriptor and the code through the restricted activation transport.
  4. The gateway verifies the gateway id, consumes the pending activation, and returns an authenticated session grant.
  5. The client stores the refresh credential before switching the active connection to the new access credential.
The code uses eight symbols from a Crockford-style alphabet. It expires after 600 seconds by default and the gateway permits at most five failed activation attempts. The activation response contains the gateway, principal, device, and session snapshots as well as the access and refresh credentials. Activation can be represented by a pioneer://activate URI. The gateway base URL and gateway id are query parameters; the activation code is kept in the URI fragment. A development app scheme can be configured for non-production builds. Clients must verify the gateway id from the URI against the gateway returned during the exchange. The activation endpoint is deliberately not an ordinary authorized JSON-RPC method. auth/device/activate, auth/refresh, invite/preview, and invite/accept are accepted only by the pre-authenticated credential exchange transport.

Session lifecycle

Access credentials are short lived. The default access lifetime is 15 minutes. Refresh credentials live for 90 days by default and are rotated with a durable refresh request id. The access credential is an internal JWT representation, but clients use the session API and never need to mint or sign one themselves. Sessions have their own status and revocation reason. A user can log out the current session, revoke another session, or lose all sessions when the principal is suspended or removed. Refresh-token reuse and excessive activation failures also terminate the affected session. Session revocation is delivered to active clients through auth/session_revoked. A client should close the connection, discard unusable access state, and return to activation or refresh recovery. auth/access_expiring lets a client refresh before the access credential expires. The gateway redacts credentials in debug output and protocol snapshots. Raw access and refresh values should stay in the platform keystore on a client. Desktop gateway registry records contain credential references rather than raw values. See Secret storage for the gateway and client storage boundary.

Capability snapshots

Authorization is evaluated at the gateway and projected to clients through authorization/capabilities. The snapshot has a schema version, an authorization revision, the principal id, a stable role key, and server-owned role presentation. The snapshot has three scopes: The workspace projection also contains the operational resources the principal may use. Providers, provider models, CLI runtimes, CLI models, skills, and MCP servers are represented either by all or by exact stable ids. The client should filter selectors from this projection rather than guessing from a role name. Capability snapshots also carry server-enforced execution limits, agent permission presets, artifact attachment permission, and MCP invocation limits. These fields are useful for disabling unavailable controls, but the gateway repeats authorization and policy checks at every mutating boundary. Role presentation is display data. A client may show the role label and description, but it must not derive access from role_key, principal kind, or a locally maintained role enum. The gateway may return a future role presentation that an older client does not know.

Workspace and thread access

Every workspace and thread operation is checked against the authenticated principal, current membership, visibility, and the authorization revision captured by the request. Thread access is narrower than workspace access. A member can be allowed to read a workspace while lacking access to a private thread or to its participant list. When access changes, the gateway invalidates the affected projections and publishes access/changed, authorization/projection_changed, workspace/members_changed, member/changed, or thread/participants/changed as appropriate. Notifications are hints. After reconnect or a revision change, clients should reload the capability snapshot and the affected resource. Agent and task work does not bypass these checks. A detached task keeps the authorization facts needed for its run, but task admission, artifact access, child-thread access, delivery, and review are checked again against the stored context.

Invitations and membership

Invitations are scoped grants, not gateway passwords. An invitation has an inviter, a role presentation, one to 64 workspace grants, an expiry, and a gateway-bound credential. The default invitation lifetime is seven days. Credentials use the pinv1_ prefix and are returned in a protected app deep link when the client supports it. The accept flow is:
  1. The recipient previews the invitation through the restricted exchange transport.
  2. The client displays the gateway, inviter, role, workspaces, expiry, and transport security.
  3. The recipient submits a profile and installation descriptor.
  4. The gateway accepts the invitation, creates or links the principal and member records, creates a device session, and returns the granted workspace ids.
The gateway id in an invitation must match the gateway that receives the acceptance. TLS gateways use wss; loopback or explicitly configured plaintext endpoints use ws. The transport state is part of the preview so a client can show it before acceptance. Member suspension and removal are terminal access changes. Suspension revokes active sessions while retaining the member record for restoration. Removal revokes sessions, removes workspace membership, changes affected private-thread access, and invalidates related invitations. The gateway publishes the corresponding access and lifecycle events after the durable change commits.

Security boundaries in the code

The important boundaries are:
  • crates/gateway/src/auth authenticates credentials and owns session lifecycle.
  • crates/gateway/src/authorization resolves role, workspace, thread, agent, and resource access.
  • crates/gateway/src/message/auth_handlers.rs, invitation_handlers.rs, and member_handlers.rs implement JSON-RPC handlers.
  • crates/protocol/src/auth.rs, authorization.rs, invitation.rs, and member.rs define the public DTOs and validation rules.
  • crates/client stores the projected session and capability state used by both shells.
  • crates/client-ffi exposes the shared state to the React Native application.
The normal WebSocket transport carries an authenticated session. Restricted credential exchanges are separated from normal authorization so an unauthenticated caller cannot reach an ordinary handler with a fabricated role or session.