Skip to content

Backend loopback client loses operator scope when the relay forwards from the host's own interface IP (agent RPC fails: missing scope: operator.write) #103636

Description

@andududu

Filed AI-assisted (Claude Code) with a human operator. Fix approach (A) below was validated on a live 2026.6.11 install.

Bug type

Gateway / authentication / operator scopes

Beta release blocker

No — but it silently stops the assistant from replying on any channel routed through the internal backend client.

Summary

On a host where the gateway's loopback relay forwards to the host's own interface/tailnet IP (not 127.0.0.1), the internal backend client (clientName: gateway-client, mode: backend, shared-token auth, no device identity) has its requested operator scopes cleared at the handshake. agent, message.action, and exec.approval.list then fail with missing scope: operator.write / operator.approvals, even though the paired operator device holds all operator.* scopes. The assistant receives inbound messages but can never run the agent turn to reply.

Steps to reproduce

  1. Run the gateway with gateway.auth.mode: token on a multi-homed host where the loopback relay forwards to the host's real interface IP (e.g. a Tailscale/utun address) rather than 127.0.0.1.
  2. Pair an operator device with full operator.* approvedScopes.
  3. Send an inbound message (e.g. Telegram) so the gateway dispatches the internal agent RPC via the backend loopback client.
  4. The call is rejected at the scope gate.

Expected behavior

The internal backend client, connecting over loopback with the valid gateway token (the gateway talking to itself on the same host), retains the operator scopes it requests, so agent / message.action / exec.approval.list succeed and the assistant replies.

Actual behavior

⇄ res ✗ agent 0ms errorCode=INVALID_REQUEST errorMessage=missing scope: operator.write
⇄ res ✗ exec.approval.list 0ms errorCode=INVALID_REQUEST errorMessage=missing scope: operator.approvals
[main-session-restart-recovery] recovered=0 failed=1 (GatewayClientRequestError: missing scope: operator.write)

Inbound message.action channel=telegram is accepted, but the agent turn that generates the reply is rejected → the assistant appears "up but never answers."

OpenClaw version

2026.6.11 (also reproduces on 2026.6.1)

Operating system

macOS 26.3 (arm64), Node 24.16.0

Install method

npm global (npm i -g openclaw)

Model

N/A — the rejection happens at the gateway scope gate (0ms, INVALID_REQUEST) before any model/provider dispatch. Reproduced with the default agent model.

Provider / routing chain

N/A — pre-dispatch auth-gate rejection.

Root cause (analysis)

The handshake in src/gateway/server/ws-connection/ clears requested operator scopes for a device-less token-auth connection unless it is classed as a trusted local backend self-pairing. That classification (isSharedSecretLoopbackLocalEquivalent) requires isLoopbackAddress(params.remoteAddress). Because the loopback relay forwards to the host's own interface IP, remoteAddress is the host's tailnet/interface address, not 127.0.0.1, so the predicate returns false → scopes cleared. Prior scope fixes (#17127, #17681) covered webchat and allowInsecureAuth but not this backend loopback path.

Proposed fix (deferring to maintainers/secops — this touches src/gateway/**/*auth*.ts)

  • (A) In isSharedSecretLoopbackLocalEquivalent, also treat a remoteAddress matching one of the host's own interface addresses (resolveLocalInterfaceAddressMatch) as loopback-equivalent. A remote peer presents its own source IP and can never match one of the host's interface IPs, so the surface stays "connection physically originated on this host + holds the valid gateway token." Validated locally.
  • (B) Alternatively, have the backend client present its device identity on loopback calls so it authenticates via the normal device-auth path (no boundary relaxation).

Related

#52625, #17408, #48007, #46716, #46014 — same class; closed as status/probe-only or covering only webchat/allowInsecureAuth.

Impact and severity

High for self-hosted, always-on setups where the loopback relay binds/forwards to the host's interface IP (common on Tailscale/multi-homed hosts): the assistant silently stops replying on all channels routed through the backend client, and internal session-recovery + approvals fail. Every OpenClaw update re-breaks it, since the only workaround is a local dist patch that updates overwrite.

Metadata

Metadata

Assignees

Labels

P1High-priority user-facing bug, regression, or broken workflow.clawsweeper:needs-maintainer-reviewClawSweeper marked this issue as needing maintainer review before automation.clawsweeper:needs-product-decisionClawSweeper marked this issue as needing a product or behavior decision.clawsweeper:needs-security-reviewClawSweeper marked this issue as needing security-sensitive review.clawsweeper:no-new-fix-prClawSweeper does not recommend queueing a new automated fix PR for this issue.clawsweeper:source-reproClawSweeper found a high-confidence source-level issue reproduction.impact:auth-providerAuth, provider routing, model choice, or SecretRef resolution may break.impact:message-lossChannel message delivery can be lost, duplicated, or misrouted.impact:securitySecurity boundary, credential, authz, sandbox, or sensitive-data risk.issue-rating: 🦞 diamond lobsterVery strong issue quality with high-confidence source-level or clear reproduction.maturity:stableIssue affects a taxonomy feature currently scored M4/M5.

Type

No type

Fields

Priority

None yet

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions