Skip to content

fix: gateway-owned work fails through same-host relay#108423

Merged
joshavant merged 12 commits into
mainfrom
fix/issue-103636-gateway-owned-dispatch
Jul 16, 2026
Merged

fix: gateway-owned work fails through same-host relay#108423
joshavant merged 12 commits into
mainfrom
fix/issue-103636-gateway-owned-dispatch

Conversation

@joshavant

@joshavant joshavant commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Closes #103636

What Problem This Solves

Fixes an issue where Gateway-owned restart recovery and native approval work could fail when an operator's effective Gateway URL traversed a same-host relay. The relay correctly presented a non-loopback peer, but the internal backend client then lost the operator scope required to complete the workflow.

Why This Change Was Made

Gateway-owned work now uses least-privilege, instance-bound in-process principals instead of authenticating back into the same Gateway over WebSocket. This keeps host-interface peers remote, preserves the external authentication and device-pairing boundary, and makes internal authority independent of network topology.

The refactor also preserves request deadlines, approval client identity, canonical resolution publication, account isolation, replay/live-event deduplication, and terminal Gateway lifecycle cleanup. The plugin-facing approval adapter shape remains unchanged.

User Impact

Operators can use supported relay and proxy topologies without restart recovery or native approvals failing solely because an internal self-call returns through a host interface. External device-less host-interface clients still do not receive loopback-equivalent authority.

Evidence

  • Baseline live reproduction on 98e88ab1a6b7: direct loopback succeeded while the host-interface relay failed backend sessions.create and agent calls with missing scope: operator.write (run_70cf95e93287).
  • Exact-head main-session recovery proof on 377b26b4815: the external relayed write remained denied, while startup recovery completed in-process with recovered=1 failed=0, persisted the expected assistant marker, and opened no recovery self-WebSocket (run_c28accde9629).
  • Exact-head subagent recovery proof on 377b26b4815: a real persisted interrupted subagent bypassed its dead pre-restart wait, recovered and completed through the lifecycle-bound principal, produced its child and parent model turns, and opened no internal relay connection; the sole scope failure was the intentional external write probe (run_9780bb796fde).
  • Exact-head native approval matrix on 377b26b4815: all eight exec/plugin, live/replay, and resolve-after/resolve-during rows passed; every race row passed 9/9 with exactly-once delivery, callback, resolution, and finalization, and the configured relay saw no approval self-connection or scope/pairing failure (run_db7c259294b6).
  • Candidate native approval race soak on e1a16bab06ad: every delivery-race row passed 25/25 across three fresh Gateway processes, with exactly-once delivery/finalization and account isolation (run_b3cab0ad34ba, run_9559b7dfc1f3, run_8f8cfa1c0130).
  • Head 60cfa1656a34: 233 focused tests across the Gateway instance/startup runtime and subagent registry/recovery/announce-loop surfaces passed; fresh mandatory autoreview reported no actionable findings.
  • Exact-head Blacksmith Testbox tbx_01kxm1jqf8em1yjg5qs6hmyvvt: targeted pnpm check:changed passed, including core/core-test typechecks, lint, boundary, database-first, and import-cycle gates; pnpm build also passed (run).

AI-assisted: Yes (Codex).

@openclaw-barnacle openclaw-barnacle Bot added docs Improvements or additions to documentation gateway Gateway runtime agents Agent runtime and tooling size: XL maintainer Maintainer-authored PR labels Jul 15, 2026
@joshavant
joshavant marked this pull request as ready for review July 15, 2026 19:21
@joshavant joshavant self-assigned this Jul 15, 2026
@joshavant
joshavant force-pushed the fix/issue-103636-gateway-owned-dispatch branch 3 times, most recently from 0ddea61 to 676a51f Compare July 15, 2026 20:50
@openclaw-barnacle openclaw-barnacle Bot removed the docs Improvements or additions to documentation label Jul 15, 2026
@joshavant
joshavant force-pushed the fix/issue-103636-gateway-owned-dispatch branch from 60cfa16 to 50e6919 Compare July 16, 2026 02:35
@joshavant
joshavant force-pushed the fix/issue-103636-gateway-owned-dispatch branch from 50e6919 to 377b26b Compare July 16, 2026 03:25
@joshavant
joshavant merged commit 4b7f193 into main Jul 16, 2026
120 checks passed
@joshavant
joshavant deleted the fix/issue-103636-gateway-owned-dispatch branch July 16, 2026 03:51
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request Jul 16, 2026
* fix(gateway): dispatch owned runtime work in process

* fix(gateway): publish canonical approval resolutions internally

* fix(gateway): seal owned approval runtime lifecycle

* fix(gateway): keep instance runtime contracts acyclic

* fix(gateway): remove stale runtime type imports

* fix(agents): reconcile ambiguous recovery timeouts

* refactor(gateway): constrain internal approval methods

* fix(agents): route orphan recovery in process

* fix(agents): preserve aborted restores for recovery

* fix(agents): keep lifecycle waits in process

* test(agents): type lifecycle runtime mock

* test(agents): complete restored session fixture
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agents Agent runtime and tooling gateway Gateway runtime maintainer Maintainer-authored PR size: XL

Projects

None yet

Development

Successfully merging this pull request may close these issues.

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

1 participant