On this page

On this page

Gateway

Operator scopes

Operator scopes gate what a Gateway client can do after it authenticates. They are a control-plane guardrail inside one trusted Gateway operator domain, not hostile multi-tenant isolation. For strong separation between people, teams, or machines, run separate Gateways under separate OS users or hosts.

Related: Security, Gateway protocol, Gateway pairing, Devices CLI.

Connection roles

Every Gateway WebSocket client connects with one role:

  • operator: control-plane clients such as CLI, Control UI, automation, and trusted helper processes.
  • node: capability hosts (macOS, iOS, Android, headless) that expose commands through node.invoke.

Operator RPC methods require the operator role. Node-originated methods require the node role.

Scope levels

Scope Meaning
operator.read Read-only status, lists, catalog, logs, session reads, retained audit and execution-identity diagnostics, and other non-mutating calls.
operator.sessions.read Read visible sessions, history, and session metadata without general Gateway read access.
operator.sessions.write Read visible sessions, organize owned sessions, and start or continue the authenticated person's own work.
operator.write Mutating operator actions: sending messages, invoking tools, updating talk/voice settings, node command relay. Also satisfies operator.read.
operator.admin Administrative access. Satisfies every operator.* scope. Required for config mutation, updates, native hooks, reserved namespaces, and high-risk approvals.
operator.pairing Device and node pairing management: list, approve, reject, remove, rotate, revoke.
operator.approvals Exec and plugin approval APIs.
operator.questions Listing, reading, answering, and resolving interactive questions.
operator.talk Creating, steering, and closing Talk sessions without general Gateway write access. operator.write also satisfies this scope.
operator.talk.secrets Reading Talk configuration with secrets included.

Personal profile editing is a narrowly self-scoped exception to read-only behavior: operator.read or operator.sessions.read (including their implied write/admin scopes) allows an authenticated person to change their own display name, avatar, and credit preference. Avatar downloads under session-only scopes are restricted to that person's profile. Editing someone else's profile, assigning roles, linking identity aliases, and merging profiles still require operator.admin. These edits do not grant general write access.

Personal GitHub connection management is a narrowly self-scoped exception to read-only behavior: users.github.* requires operator.read plus the exact authenticated durable profile. That person can connect, poll, cancel, reconnect, or disconnect only their own account. These methods do not expose team secrets, mutate shared configuration, or grant OpenClaw write/admin scopes. System and per-agent GitHub changes remain operator.admin.

Session-scoped readers can read shared GitHub publication options and receipts for sessions they can view through sessions.github.options and sessions.github.status. For these narrow callers, the shared publication option is available only when the session has a current managed worktree or repository workspace with a supported GitHub remote. Positive worktree target discovery reuses verified Git metadata for up to 15 seconds; worktree ownership, registry changes, and session access are checked on every request. Publication always resolves the current Git remote again. Unavailable targets do not hide existing shared receipts. Reopen the chat or reconnect after the managed workspace changes to refresh its options. Personal account discovery and personal receipts still require operator.read and the authenticated owner. Identity, role, access grant, connection, and session visibility are rechecked before returning awaited reads.

With operator.sessions.write, a requester can publish ordinary changes from sessions they created through the shared GitHub account. Workflow definition changes require the original requester's current full operator.write authority. Personal publication also requires operator.write. Every publication still requires current session authorization. See GitHub connections.

Unknown future operator.* scopes require an exact match unless the caller already holds operator.admin.

operator.sessions.write includes operator.sessions.read. Broad operator.read also includes session reads, and operator.write includes both session scopes. The session scopes do not grant general diagnostics, configuration changes, Gateway-wide tool invocation, or publication outside the own-session shared-account path described above.

Session readers can browse visible conversations and receive their updates. They can inspect a visible session's effective tool names with tools.effective. The session-only response includes core summaries and preserves policy-filtered GitHub action descriptions. It omits other runtime descriptions, configuration diagnostics, and MCP connection notices. Session visibility and requester authority are checked before returning the preview; general Gateway read access still receives the full diagnostic response. The Control UI can copy visible history as Markdown. Session writers can rename, pin, archive, or restore their own existing conversations. Other people's visible sessions remain read-only under these grants, including shared sessions. Session grants never authorize deletion or changes to an existing session's sharing and visibility. Archiving a session does not grant permission to delete it.

Session readers can review changes with sessions.diff for conversations they can currently read when the session has an admitted project checkout. Unmanaged private sandbox file workspaces return not_git; their Files browser remains available. Existing sharing, agent, and sandbox access rules still apply; no general Gateway read grant is needed.

Session readers can use sessions.files.list, sessions.files.get, and sessions.files.assets for files within the workspace admitted to both the visible session and the caller. A caller whose role requires a sandbox cannot read an unsandboxed host workspace through another person's visible session, including an administrator's managed worktree. This does not require write access: ordinary read-only callers retain their admitted in-root reads, and shared sandbox sessions retain their own private workspace. Reads outside that root also require the session's file tools to allow Gateway-host access and the caller to have current permission to start a turn there, including ownership, sharing-role, agent, and sandbox checks. Such previews are read-only. canvas.document.preview also accepts session read access: it returns the caller's HTML and isolated sandbox location without reading session data. For saved inline widgets, canvas.document.view also accepts session read access with sessionKey: the Gateway verifies a successful saved show_widget result for that document in a conversation the caller can currently read. Inaccessible conversations and unbound documents remain denied. Broad operator.read callers retain document-ID-only access.

In the Control UI, session writers can use New Session, send messages in their own conversations, fork from a persisted user message in an owned settled conversation, use Advanced → Fork conversation on their own conversation, and stop their own active runs. Whole-conversation forks preserve the saved conversation history; while the source is working, they copy through its last completed reply. A whole-conversation fork of a Draft stays Draft unless the creation request explicitly selects another audience. If new Draft creation is disabled, creating that Draft copy is refused; the existing Draft remains available for continued work. Forking does not grant access to another creator's conversation or change the source's workspace and isolation rules. Their model, effort, fast-mode, and non-full permission choices use the same session grant and the Gateway's allowed model catalog. Full permission mode, sandbox changes, and changes to an existing session's context window still require administrator access. Reconnecting rechecks current permission before replaying a Stop for its original run.

Own-work methods, including message sending, ordinary session creation, recovery, and forks, accept operator.sessions.write where their parameters do not require administrator access. Session ownership, current authority, agent access, and runtime and sandbox requirements still apply.

Session readers can read visible dashboards and receive their updates and focus commands. Session writers can arrange tabs and put widgets on their own dashboards without general Gateway read or write access. Core report, progress, and website widgets are discoverable with session-read access; plugin-specific scopes and widget approvals remain unchanged. Canvas imports require a saved widget receipt in the dashboard conversation, with source access retained through the write. Broad readers keep their existing document-ID-only access.

Session readers can also use artifacts.list, artifacts.get, and artifacts.download for artifacts in a conversation they can currently read. Both session-key and run-ID queries retain the selected session's visibility, identity, and lifecycle checks. An artifact ID alone does not grant access to another conversation's files; reads and downloads recheck access before returning content.

Hidden native sub-agents started through sessions_spawn use the initiating person's session-write authority for the exact owned child and its private parent completion. The child retains that person's current authority, model policy, and required sandbox. Direct agent RPC calls keep their existing broader scope requirements; copying a child session or run identifier does not grant launch permission.

RPCs, events, and background tools use the same scope rules. A continuation with operator.write can read its GitHub identity and session state without another interactive message. Session access and execution-lifetime checks still apply.

question.* also accepts operator.sessions.write for ordinary questions bound to the caller's own admitted run and owned session. The Gateway records that binding from trusted run authority, never from caller-supplied session or run identifiers. The same ownership check filters question events and recovery reads. Session visibility or membership alone does not grant this access. operator.sessions.read alone cannot answer questions. Sessionless questions, secret prompts, and other privileged question workflows retain their existing operator.questions and administrative checks.

Named operator roles

Team Gateways can bind authenticated durable profiles to named operator roles. Each role controls access to other people's sessions, agents available for session creation and agent runs, a maximum set of operator scopes, and whether newly created sessions require sandboxing. It can also require an access policy supplied by a plugin.

json5
{  gateway: {    roles: {      default: "guest",      assignments: {        byGithubLogin: { octocat: "maintainer" },      },      definitions: {        maintainer: {          sessions: { others: "write" },          agents: ["roboclaw"],          scopes: ["operator.read", "operator.write", "operator.approvals"],        },        guest: {          sessions: { others: "view" },          agents: ["roboclaw"],          scopes: ["operator.sessions.read", "operator.sessions.write"],          sandbox: "required",        },      },    },  },}

Use the administrator-scoped users.setRole Gateway method with { profileId, role } to assign a configured role. Set role: null to clear an assignment. Assignment changes immediately invalidate and close that profile's active Gateway connections. Reconnecting applies the current role and scope ceiling. A committed change still retires the previous access if returning the result fails. An authorized self-downgrade receives its response before its connection closes.

The Control UI distinguishes a role ceiling from limited browser pairing. When the authenticated role does not allow administrator access, its limited-access notice explains that device approval cannot grant it and does not offer Request admin. Ask a Gateway administrator to change the role if more access is needed, then reconnect. A role that permits administrator access can still request an upgrade for a pairing-limited browser; approval and RPC authorization continue to check the current role.

The optional hello-ok.auth.roleAllowsAdmin field reports this ceiling for presentation. Clients can ignore it; it does not grant access or change RPC authorization.

Use gateway.roles.assignments.byGithubLogin to declare assignments before a person's first login or share the same mapping across Gateways. Keys are GitHub logins, matched case-insensitively after trimming; malformed logins and duplicate normalized logins are rejected. Every mapped role must exist in definitions. A valid explicit users.setRole assignment wins, followed by the mapping for the profile's cached verified primary GitHub identity, then gateway.roles.default. Profiles without a cached GitHub identity never match this mapping. Clearing an explicit assignment with role: null exposes the configured mapping or default again.

users.list keeps role as the explicit assignment and reports effectiveRole with roleSource ("assigned", "githubLogin", or "default"). gateway.roles.default is required whenever roles are configured and must name an existing definition. Omitting gateway.roles entirely leaves solo and shared-secret deployments unchanged.

Set a role's optional accessPolicyPlugin to the exact plugin ID when that plugin must confirm the person's current access. For example, the Visitor Access plugin requires accessPolicyPlugin: "visitor-access" on its restricted default role. A denied connection receives the OPERATOR_ACCESS_DENIED connect error detail, and the Control UI explains that the account has no access. The requirement belongs to Gateway configuration and remains enforced when the plugin or its manifest is missing, disabled, broken, or still starting. A loaded plugin must return current authority for the person; another plugin's policy cannot satisfy the requirement. Configuration validation permits an unavailable plugin reference so the Gateway can still start for repair. Independent staff roles without this binding and the Gateway owner retain their existing access. Restore the required plugin to admit the bound role. Removing or changing the binding applies through the same live role-policy update described below.

With live configuration reload enabled, edits to gateway.roles (including GitHub login assignments) and gateway.auth.identityScopes apply without restarting the Gateway. Existing Gateway clients reconnect to receive the current scope ceiling, except for changes confined to model policies as described below and identity-scope edits that leave an operator WebSocket login’s resolved grant set unchanged. Editing another login or reordering the same scopes preserves that connection, its accepted runs, and queued inputs. Changing its own resolved grants revokes retained and delegated work; restoring the grant does not revive the original authority. Clients without verified identities, node connections, HTTP requests, and plugin auth cookies do not consume identity-scope grants and are unaffected by these edits. Pending handshakes and mutations recheck the policy before acquiring authority; already-admitted runs retain their normal completion and cancellation lifecycle, including cancellation when their original access-policy grant expires or is revoked.

When roles are configured, identity-authenticated operator connections do not receive reusable device or bootstrap tokens: those tokens are not bound to a person and could bypass the role ceiling. Device-token or bootstrap-token authentication without a verified user identity is rejected for operator Gateway connections and HTTP requests. Reconnect through the trusted proxy or another supported verified identity, such as Tailscale. Node connections, shared-secret/password access, and Gateways without role configuration retain their existing behavior.

For sessions created by other people, sessions.others supports these values:

  • "none": hides foreign sessions from lists and targeted access, filters session-level usage to visible sessions, and denies Gateway-wide usage.cost because its aggregate can include hidden sessions.
  • "view": allows reading but does not allow mutation, even when a session is otherwise shared.
  • "suggest": allows viewing and the existing suggestion flow.
  • "write": allows participation in foreign sessions. Draft and incognito restrictions remain in force.

A person always owns their own sessions. Explicit session membership can raise "view" or "suggest" access for a specific session, and connections already holding operator.admin retain their administrative session access.

Set agents: "*" to allow session creation and agent runs on every agent, list agent IDs to allow only those agents, or use an empty array to disallow both. The allowlist also applies when a run targets an already-existing session. Agent discovery and the Control UI agent picker show only agents allowed by the caller's role. An empty allowlist returns an empty agent roster; the Gateway's session-routing ownership remains unchanged. Discovery does not grant or revoke access to existing sessions: the role's separate session-read policy still governs which sessions the caller can read. The Control UI waits for a live agent roster after loading or reconnecting, so an older browser roster cannot restore agents excluded by the current role.

Access program grants

For role-governed people, Standard Access needs no additional grant. Blue and Red each require an explicit entry in the role's accessPrograms.cyber list:

json5
// Inside gateway.roles.definitions.maintaineraccessPrograms: { cyber: ["daybreak_blue", "daybreak_red"] },

Omitting the field or setting cyber: [] permits neither Blue nor Red. Granting Red does not grant Blue, or vice versa. The person's effective role supplies these grants through the same explicit assignment, GitHub mapping, and default-role rules described above; there is no separate per-person Access assignment.

These grants are independent of modelPolicy: an unrestricted model list, model wildcards, the old Daybreak model aliases, or operator.admin do not grant Access to a named role. The selected model still needs model permission. The exact provider account and runtime must also advertise the program; provider metadata is capability evidence, never an RBAC grant. Catalog choices show only the intersection, and direct mutations and execution enforce it server-side.

With live reload enabled, changing these grants retires the role's existing source authority. Queued, retained, and recovered work cannot reuse a removed grant, and restoring it does not revive revoked authority. New admission is required. An unset-selection cyber fallback can retry with Blue only when both the role and provider permit Blue; an explicit Standard selection never escalates.

Existing roles are not automatically granted Blue or Red during upgrade. Administrators must explicitly add the intended grants. System/shared-secret use and deployments without gateway.roles keep their existing non-RBAC behavior and provider capability checks. This is not a bypass available to a role-governed person.

Native runtimes must support exact host-owned dispatch enforcement when a named role restricts either models or Access programs. An unrestricted model list alone does not disable Access enforcement. Incompatible runtimes fail before inference.

Model policy

Set a role's optional modelPolicy to limit the models used by its requests:

json5
// Inside gateway.roles.definitions.guestmodelPolicy: {  sourceAgent: "shared-agent",  deny: ["provider/restricted-*"],}

With no allow override, the policy follows that agent's configured primary and fallback models in order. Omitting sourceAgent uses the configured system or default agent, or the sole agent. A multi-agent Gateway without such an owner must name a source agent. This reads the existing agent configuration; it does not copy a model list or grant access to every installed model.

Use allow to replace the permitted set centrally, and deny to exclude models from either source. An empty allow: [] denies all models. Both lists accept exact provider/model references, aliases from the source agent's model settings, and trailing wildcards such as provider/*, provider/family/*, or provider/restricted-*. Other wildcard placements are rejected. Exclusions match resolved identities, so aliases cannot bypass them and a family prefix also excludes newly configured family members.

The role policy is an additional ceiling. Existing agent rules still govern manual model selection. Default can use the first permitted source model that the target agent already allows manually or through its configured primary and fallback chain. Automatic retries filter that chain before preparing providers; an empty permitted result returns an error instead of widening access.

With config reload enabled, edits confined to existing roles' modelPolicy settings apply when the config transaction commits, without restarting the Gateway or reconnecting its clients. Other role changes hot-apply and reconnect clients with current authority, including when combined with a model-policy edit. Disabling config reload leaves the current policy active until config application resumes.

The original role ceiling follows queued work and child runs. Accepted work must satisfy both its original model ceiling and the current policy. Removing one source model cancels its active model requests and blocks later calls using it, while still-permitted sibling models and unrelated work retain their authority. New requests use the updated source choices. Direct model requests, title previews, and user-invoked model completion or decision tools apply the same policy. Interactive plugin runtime attempts must certify exact model-policy enforcement before they can execute restricted requests, regardless of who supplies their credentials. Currently the built-in OpenClaw runtime supports these attempts; uncertified plugin runtimes, including Codex, refuse them with a compatible-runtime error. Adding a policy also cancels uncertified work that started without one, including retained work after its foreground turn finishes. An outer selected model does not establish which model a native runtime actually uses. Isolated prompt-only completions keep their separate exact-route contract and bind the selected model through completion and cleanup.

Bounded automatic metadata, operator-configured inbound media preprocessing, and host-owned execution approval keep their existing service authority. Omitting modelPolicy preserves the role's existing model access, and shared-secret System access is unchanged.

Activity recaps use the owning agent's utility route under the session's assigned human owner's model policy, or its authenticated profile creator's policy when there is no assigned human owner. Historical profile aliases resolve to the current canonical profile before policy is bound. The Gateway checks committed policy before provider requests, including retries and fallbacks; denied or revoked work is skipped with a logged reason and retains the previous recap. Requesting a refresh still requires session write access and never substitutes the viewer's model permissions for the session owner's. Assigning a task to an agent or the system does not remove its authenticated creator's policy. Sessions without an assigned human owner or authenticated profile creator retain Gateway service authority. Cron, subagent, and incognito sessions do not generate recaps. Disabling the owning agent's utility routing stops all its recap generation. See Activity tab.

Native Codex staff work without a model policy has a limited attribution case when qualified native hooks are disabled or unavailable: previously accepted unrestricted input mixed with other work may continue under the receiver's valid authority after its sender loses authorization or becomes restricted. Direct and otherwise unambiguously bound work still observes revocation. See the native model-policy boundary. Visitor Access requires an explicit model policy; its Codex runs require the qualified integration.

The optional sandbox policy defaults to "inherit", which keeps the agent's configured sandbox mode. Set sandbox: "required" to sandbox every new session created by an authenticated person with that role, even when the agent's sandbox mode is "off". The example lets maintainers use host execution on roboclaw while guest-created sessions on the same agent remain sandboxed.

Required sandboxes are isolated per authenticated session creator, not merely per agent or per session. Different guests using the same agent receive separate sandbox environments and workspaces. Multiple sessions created by the same guest reuse that guest's environment and workspace. This per-guest boundary applies regardless of the configured sandbox scope. If the agent configures workspaceAccess: "rw", OpenClaw reduces access to "ro" for role-required sessions and logs an agent/sandbox warning, preventing the shared agent workspace from becoming a writable bridge between guests. Maintainer sessions and other sessions without a role-required sandbox keep their configured scope and workspace access.

The Gateway records the authenticated creator and their sandbox requirement together before a new session first runs, including chat, the OpenAI-compatible HTTP endpoints, Talk, recovery, forks, checkpoint branches, cron, outbound messages, and spawned children. Delegated child work inherits a required parent's original creator and sandbox policy, even after role changes. Recovery and branching requested by another person use that person's own role rather than the source session's policy.

Required creation provenance is immutable. Role changes, sharing, participation, sessions.patch, whole-entry replacement, legacy imports, and normalized-key repair cannot remove or replace an existing required stamp. Blocked persisted overwrites emit a session-sqlite warning. Inspect them with openclaw logs --follow. Existing unstamped sessions and new sessions whose creator does not require sandboxing retain their existing behavior.

A person whose role requires sandboxing cannot start a run in an existing host-execution session, even when explicitly invited. Required sessions fail if their sandbox backend is unavailable or provisioning fails. They never fall back to the Gateway or a node. /elevated, exec host overrides, and configured host targets cannot bypass this restriction.

By default, the agent's managed GitHub identity is not injected into sandboxed execution: GH_CONFIG_DIR is absent, and GH_TOKEN and GITHUB_TOKEN are blanked. An administrator can set agents.entries.<id>.tools.github.allowInSandbox: true to expose that agent's managed identity to its own Docker or Podman sandbox, including role-required sandboxes isolated per creator. The profile is mounted read-only at /openclaw/github; sandboxed commands receive the managed token and Git author. Effective "shared" scope refuses this injection and logs a warning naming the agent. openclaw security audit warns for each opted-in agent. See GitHub identity for the credential boundary and backend requirements.

A required sandbox contains the session's exec and file tools. It does not contain these paths, so configure them before admitting people you do not trust with the Gateway host:

  • Code Mode under the default node executor runs cells in the Gateway process. Use the QuickJS executor.
  • CLI backends such as Claude CLI own their native tools and sandboxing on the Gateway host. Exclude them with the role's modelPolicy.
  • worker-turn cloud workers receive the agent's shared GitHub identity regardless of allowInSandbox. Keep restricted people off worker profiles.
  • sessions.others: "view" exposes complete shared transcripts, including tool output, from every agent. Keep sensitive work in draft sessions.
  • Gateway-brokered GitHub publication uses the shared account. See Workflow and CI changes for what pull request workflows can run.

The role's scopes list caps scopes granted through connection auth, identity grants, pairing, scope upgrades, and authenticated trusted-proxy HTTP requests. The ceiling uses the normal scope implications: operator.admin permits every operator scope, and operator.write permits operator.read and operator.talk. It only filters existing grants. It cannot add scopes the connection did not already receive. The ceiling intersects capabilities, including those implied by a broader grant. A write grant narrowed to a read-only role retains operator.read; an admin-only grant narrowed to a write role retains operator.write. The role cannot grant capabilities that the original credential did not allow, and an empty grant or role remains empty.

Session reads and organization use the last successfully applied role configuration. A rejected configuration reload leaves those permissions unchanged.

This includes plugin HTTP requests and WebSocket upgrades: without a scope header, ordinary Gateway-authenticated plugin routes start with only operator.write, then apply the role ceiling. A read-only role therefore retains operator.read on that path, while an empty role receives no runtime scopes. Control UI plugin grants carry the authenticated profile inside a signed cookie. Plugin HTTP requests reapply the profile's current role ceiling and reject grants without a matching durable identity when roles are enabled. Include operator.admin explicitly only when that role should retain administrative connection authority.

Principal-bound remote ingress uses a separate plugin-approved ceiling intersected with the bound person's current role. Read + write uses operator.read and operator.write; Full uses operator.admin, whose implications are still limited by the role. Thus Full for a read-only role yields read access, while an empty role or grant remains empty. The same exact effective scopes apply to the remote UI and grant-bound Gateway RPC: neither identity grants nor an admin plugin caller adds authority. Role or profile changes fence existing delegated work. Ingress never issues a reusable device token, and device scope upgrades cannot widen its grant ceiling.

An administrator-attested channel identity link also lets a sender inherit channel-owner authority from their effective role's operator.admin scope. This does not require an additional identity-scope grant. When roles are absent, channel ownership uses a matching administrative identity-scope grant instead. Connection scope grants and ceilings are unchanged.

Named roles apply to authenticated durable profiles and their attested channel identities. They organize collaboration within one trusted Gateway domain and do not replace separate Gateways when hostile-tenant isolation is required. Diagnostic audit methods, including audit.run.inspect, remain shared-domain operator.read surfaces and are not filtered by session role. Likewise, operator.write still authorizes Gateway-wide operations such as tool invocation, ordinary node command relay, and other write-scoped control-plane actions. Session restrictions do not turn that scope into a per-person isolation boundary. Use separate Gateways when mutually untrusted people must not share diagnostics or control-plane write authority.

Identity scope grants

gateway.auth.identityScopes grants operator scopes to verified user identities from trusted-proxy auth or Tailscale WhoIs:

json5
{  gateway: {    auth: {      identityScopes: {        "admin@example.com": ["operator.admin"],        "operator@example.com": ["operator.read", "operator.write"],      },    },  },}

The key is the verified proxy identity or Tailscale WhoIs login. Email keys match case-insensitively. Non-email identities match exactly. Config validation rejects scope names outside the closed set above.

Connection authority is resolved in this order:

  1. For trusted-proxy Control UI connections, x-openclaw-scopes first caps device enrollment or upgrade requests. Device authorization then establishes the persistent scopes. A device-less session contributes no self-declared scopes.
  2. OpenClaw unions a matching server-side identity grant with those scopes.
  3. OpenClaw applies x-openclaw-scopes to the final union as the session cap. An absent header means no cap. A present-but-empty header yields no scopes. The intersection honors scope implications: an operator.write grant with an operator.read cap yields only operator.read.
  4. If the authenticated profile has an effective named operator role, OpenClaw intersects the result with that role's configured scope ceiling.

The result is used for both hello.auth.scopes and Gateway method authorization. Identity grants are session-only: they do not create or modify pairing records or request a device scope upgrade. Token, password, and no-auth connections carry no verified identity and receive no grant. Identity grants apply only to operator-role connections. node-role connections never receive them.

Method scope is only the first gate

Each Gateway RPC has a least-privilege method scope that decides whether a request reaches its handler. Params-aware methods derive that scope before dispatch so authorization failures have one standard structured response:

  • agent needs operator.write for ordinary turns and operator.admin for /new or /reset session lifecycle commands.
  • node.invoke needs operator.write for ordinary relay commands and operator.admin when relaying browser.proxy, browser.proxy.upload.v1, fs.listDir, or terminal.upload to a node.
  • The top-level fs.listDir RPC needs operator.write for Gateway-host requests and operator.admin when nodeId targets a node. Its handler limits non-admin Gateway-host browsing to configured agent workspaces.
  • plugins.sessionAction requires every scope declared in the selected action's requiredScopes. Omitted or empty lists default to operator.write. operator.write satisfies operator.read and operator.talk. Other scopes require an exact match, or operator.admin.
  • sessions.create accepts operator.sessions.write for ordinary own-session creation, including a projectId, or the broader operator.write scope. Existing device folders (deviceFolder: { deviceId, path }) require the broader operator.write scope, a connected session host advertising folder support, and node-local consent through nodeHost.workerRuns.folders. They do not grant arbitrary node execution, but the selected folder becomes the session's workspace root. If you rely on tools.fs.workspaceOnly or exec restrictions to contain operator.write sessions, also set nodeHost.workerRuns.folders on the device. Incognito sessions and any execNode request require operator.admin. For non-admin callers, the handler limits cwd to configured agent workspaces. projectId cannot be combined with cwd or execNode.
  • environments.list needs operator.read for plain inventory and operator.write when runtimeId requests runtime-specific command eligibility. Session placement methods derive their scope from the requested target before schema validation: sessions.dispatch needs operator.write for explicit deviceId, autoDevice, or profileId. Cloud profile dispatch additionally requires an administrator or a current role’s workerProfiles grant for the session’s verified creator. Setup remains administrator-only. A target-less cloudWorkers.projectProfiles lookup requires operator.admin. sessions.move needs operator.write for Gateway or device targets and operator.admin for profile targets. abandonSource: true remains operator.write but is schema-valid only with a Gateway target and runtime-valid only for an exact offline device source. sessions.reclaim remains operator.write. Malformed dispatch params or a malformed move target use operator.write so the handler can return the precise schema error. All three methods retain session ownership, participation, and commit-time revalidation fences. operator.read alone cannot start, stop, or move a session. Cloud profile setup and configuration, pairing and Connect machine, raw environments.create or environments.destroy, incognito sessions, direct execNode execution, and arbitrary host or node paths remain operator.admin.
  • worktrees.branches needs operator.write. Its handler limits non-admin callers to workspace-contained paths or registered-project roots. Other host paths require operator.admin.
  • talk.config needs operator.read. includeSecrets: true also needs operator.talk.secrets.
  • talk.client.*, talk.session.*, talk.speak, and talk.mode need operator.talk (or the compatible broader operator.write).
  • sessions.patch and sessions.patchMany accept operator.sessions.write for organization fields, model, agentRuntime, thinking level, fast mode, and non-full permission modes on owned sessions. Metadata patches require an existing owned session; they cannot claim an unused key. Broad operator.write retains its existing session access. Full permission mode, sandbox changes, native runtime consent, and privileged or unknown fields require operator.admin. Runtime availability and sandbox checks still apply. Persisting a selected model as the configured agent default is admin-only.
  • sessions.delete requires operator.write for an archived-only request with the supported fields, and operator.admin otherwise. Neither session scope authorizes deletion.
  • session.reactions.list accepts operator.sessions.read or broader read access and requires current session visibility. session.reactions.set requires operator.write, an identified author, and permission to send to or suggest in the session. A view-only operator role cannot react.

Project RPCs use these scopes:

Method Required scope and additional gate
projects.list operator.sessions.read or broader read access; only callers satisfying operator.write receive repoRoot and originUrl.
projects.add operator.write and the controlPlaneWrite method flag.
projects.register, projects.remove operator.admin.
projects.searchRemote operator.read.

Some handlers then apply stricter checks based on the concrete thing being approved or mutated:

  • device.pair.approve is reachable with operator.pairing, but approving an operator device can only mint or preserve scopes the caller already holds.
  • node.pair.approve is reachable with operator.pairing, then derives extra approval scopes from the pending node's declared command list.
  • chat.send is a write-scoped method, but the /config set and /config unset chat commands require operator.admin on top of that, regardless of the caller's chat-send scope.

This lets lower-scope operators perform low-risk pairing actions without making all pairing approval admin-only.

Session mutation RPCs are authorized by their negotiated operator scopes, independent of the connecting client's client.id or client.mode. Client identity can still affect connection and device-auth policy, but it neither grants nor removes session mutation authority.

audit.run.inspect intentionally uses operator.read. Every client with that scope in a Gateway operator domain may receive the retained execution-identity context, including bounded pseudonymized references and secret-redacted display labels. operator.read is not a per-person or hostile multi-tenant privacy boundary. Operators who must keep this data separate need separate Gateway trust domains.

Device pairing approvals

Device pairing records are the durable source of approved roles and scopes. An already-paired device does not get broader access silently: a reconnect that asks for a broader role or broader scopes creates a new pending upgrade request.

A connected limited Control UI with broad read access can file that same pending request through Inbox > System > Limited access > Request admin without attempting a broader reconnect. The request is bound to the signed device identity on the live connection. Approval still comes from device.pair.approve and therefore requires operator.pairing plus authority for every requested scope. After approval rotates the operator token, the Gateway returns the new token only to that device's live waiter. The browser stores it before reconnecting. Canceling the wait or disconnecting before approval falls back to the ordinary pairing repair flow on the next connection.

A role with only operator.admin permits the Control UI's full operator scope request. Approval is still required. The role ceiling does not grant device scopes on its own.

Requests outside the authenticated person's assigned role ceiling are denied, not queued for device approval. The Gateway checks the current role again after approval, before returning the token, so a role demotion during the wait still blocks an out-of-role result. The Control UI shows the denial and administrator guidance without Retry. An administrator must change the role first.

The explicit exception is the administrator-capable Control UI owner profile issued directly on the Gateway host by openclaw dashboard or graphical onboarding. Its short-lived, single-use bootstrap can approve the exact closed scope set for a fresh browser or upgrade an existing limited credential only when it binds to that same signed browser keypair. Generic Control UI and Telegram handoffs, mobile setup profiles, shared credentials, locality, and caller-selected scopes do not receive this exception.

Approving a device request:

  • A request with no operator role does not need operator scope approval.
  • A request for a non-operator device role (for example node) requires operator.admin, even though device.pair.approve itself only needs operator.pairing.
  • A request for operator.read, operator.write, operator.approvals, operator.sessions.read, operator.sessions.write, operator.questions, operator.pairing, operator.talk, or operator.talk.secrets requires the caller to already hold that scope, or operator.admin.
  • A request for operator.admin requires operator.admin.
  • A repair request with no explicit scopes can inherit the existing operator token's scopes. If that token is admin-scoped, approval still requires operator.admin.

Non-admin shared-secret and trusted-proxy sessions can only approve operator-device requests within their own declared operator scopes. Approving non-operator roles is admin-only even when those sessions can otherwise use operator.pairing.

For paired-device token sessions, management is self-scoped unless the caller has operator.admin: a non-admin caller sees only its own pairing entries, and can approve, reject, rotate, revoke, or remove only its own device entry.

Node pairing approvals

node.pair.* capability approvals are stored on the paired device record in the shared SQLite pairing store. Gateways migrate any remaining entries from the retired standalone nodes/paired.json store into those records once at startup. See Gateway pairing for details.

node.pair.approve derives extra required scopes from the pending request's command list:

Declared commands Required scopes
none operator.pairing
ordinary node commands operator.pairing + operator.write
system.run, system.run.prepare, system.which, browser.proxy, browser.proxy.upload.v1, fs.listDir, or system.execApprovals.get/set operator.pairing + operator.admin

Here, fs.listDir is the node command declared for relay through node.invoke, not the top-level Gateway RPC described above.

Approving a node declaration records its command surface. For computer.act, the node advertises that surface only after Computer Control is enabled locally. Once the pairing update is approved, invoking it through node.invoke requires write scope but not admin scope for each action. Commands classified as dangerous or privacy-heavy still require a persistent gateway.nodes.commands.allow entry in addition to pairing.

Node pairing establishes identity and trust. It does not replace a node's own system.run exec approval policy.

Shared-secret auth

Shared gateway token/password auth is treated as trusted operator access for that Gateway. OpenAI-compatible HTTP surfaces, /tools/invoke, and HTTP session-history endpoints restore the full default operator scope set for shared-secret bearer auth, even if a caller sends narrower declared scopes.

Identity-bearing modes, such as trusted proxy auth or private-ingress none, can still honor explicit declared scopes. Use separate Gateways for real trust boundary separation.

  • Trusted proxy auth — how a trusted proxy supplies the operator identity these scopes attach to
  • Gateway protocol — the methods these scopes authorize
  • Cloud Workers — worker dispatch, whose environment and session calls are authorized against these scopes
Was this useful?