Skip to content

fix(gateway): auto-approve scope upgrades for loopback clients#23708

Closed
widingmarcus-cyber wants to merge 1 commit intoopenclaw:mainfrom
widingmarcus-cyber:fix/loopback-subagent-scope-upgrade
Closed

fix(gateway): auto-approve scope upgrades for loopback clients#23708
widingmarcus-cyber wants to merge 1 commit intoopenclaw:mainfrom
widingmarcus-cyber:fix/loopback-subagent-scope-upgrade

Conversation

@widingmarcus-cyber
Copy link
Contributor

@widingmarcus-cyber widingmarcus-cyber commented Feb 22, 2026

Summary

Fixes subagent sessions failing with pairing required error when spawned on a loopback gateway.

Problem

When a device previously paired with a narrower scope (e.g. operator.read) reconnects from localhost requesting broader scopes (e.g. operator.admin), the gateway triggers the full pairing flow — requiring manual approval even though the client is local and trusted.

This breaks subagent sessions because sessions.patch requires operator.admin scope, but the initial gateway-client connection was paired with only operator.read. The scope upgrade triggers requirePairing("scope-upgrade") which rejects the connection with 1008: pairing required.

Root Cause

The silent auto-approve check in message-handler.ts only applied to not-paired reason:

silent: isLocalClient && reason === "not-paired",

Scope upgrades from local clients were treated identically to remote clients — requiring explicit manual approval.

Fix

Extend the silent auto-approve to include scope-upgrade for local clients:

silent: isLocalClient && (reason === "not-paired" || reason === "scope-upgrade"),

Role upgrades (e.g. operatornode) still require explicit approval regardless of client location, preserving the security boundary between different trust levels.

Testing

  • Updated existing requires pairing for scope upgrades test → auto-approves scope upgrades for local clients
  • Updated legacy metadata test to verify auto-approval works for devices missing scope metadata
  • All 36 auth tests pass

Fixes #23661

Greptile Summary

Extends silent auto-approval for loopback clients from just initial pairing (not-paired) to also include scope upgrades (scope-upgrade). This fixes subagent sessions that previously failed with "pairing required" errors when escalating from operator.read to operator.admin.

The change modifies the condition in message-handler.ts:641 from:

silent: isLocalClient && reason === "not-paired"

to:

silent: isLocalClient && (reason === "not-paired" || reason === "scope-upgrade")

Key security properties preserved:

  • Only applies to loopback clients verified by isLocalDirectRequest() (checks IP is loopback AND host is localhost/127.0.0.1/::1/ts.net AND no untrusted proxy headers)
  • Role upgrades (operatornode) still require manual approval regardless of client location
  • Scope validation still enforced through roleScopesAllow() before pairing

Tests updated to verify auto-approval works for both normal scope upgrades and legacy metadata (devices paired before scopes field existed).

Confidence Score: 5/5

  • Safe to merge - targeted fix with preserved security boundaries
  • The change is minimal (one line), well-tested (36 auth tests pass), and maintains strong security invariants. The isLocalClient check requires both loopback IP AND local hostname AND absence of untrusted proxy headers. Role upgrades still require manual approval, preserving the security boundary between operator and node roles. The fix directly addresses the documented issue without introducing new attack surface.
  • No files require special attention

Last reviewed commit: 41a0aff

When a device previously paired with a narrower scope (e.g. operator.read)
reconnects from localhost requesting broader scopes (e.g. operator.admin),
the gateway now auto-approves the upgrade via silent pairing — matching the
existing auto-approve behavior for initial pairing of local clients.

This fixes subagent sessions failing with 'pairing required' when spawned
on a loopback gateway. The subagent's callGateway uses sessions.patch which
requires operator.admin scope, but the gateway-client was initially paired
with only operator.read. On localhost this scope upgrade is safe and should
not require manual approval.

Role upgrades (e.g. operator → node) still require explicit approval
regardless of client location, preserving the security boundary between
different trust levels.

All 36 auth tests pass.

Fixes openclaw#23661
@steipete
Copy link
Contributor

Thanks for the fix and discussion here.

This has now landed on main in commit 9165bd7 (same core change + tests/changelog), so I’m closing this PR as superseded by the direct merge.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

gateway Gateway runtime size: XS

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: Subagent sessions fail with 'pairing required' error on loopback gateway

2 participants