Skip to content

fix: advertise route-aware LAN Control UI links - #98482

Merged
joshavant merged 4 commits into
mainfrom
fix/route-aware-lan-advertise
Jul 1, 2026
Merged

joshavant merged 4 commits into
mainfrom
fix/route-aware-lan-advertise

Conversation

@joshavant

Copy link
Copy Markdown
Collaborator

What Problem This Solves

Fixes an issue where users pairing a nearby device or copying Control UI links would be shown an unreachable LAN URL when the host has multiple private IPv4 interfaces and the first enumerated interface is not the default route.

This affects QR/setup-code pairing and the Control UI links printed by setup, configure, status, and daemon status surfaces. It is easiest to hit on Windows hosts with Hyper-V, WSL, VPN, or Parallels-style virtual adapters.

Related context: no exact GitHub issue matched the route-aware advertised-host bug. Older closed issues such as #19004, #18259, #11329, and #8823 covered sibling LAN/probe/bind behavior, but not the default-route advertised-host selection fixed here.

Why This Change Was Made

The fix separates remote-device display links from same-host probe links. Advertised LAN URLs now prefer the OS default-route interface, while local readiness probes continue to use loopback for bind=lan so same-host health checks do not accidentally exercise remote pairing behavior.

The default-route resolver is shared by setup-code pairing and Control UI display surfaces, with platform-specific route hints for Windows, macOS, and Linux. If route probing is unavailable, the behavior falls back to the existing interface-order LAN selection.

AI-assisted.

User Impact

Users with route-aware LAN environments should now see QR, setup-code, dashboard, configure, onboard, daemon status, and status --all links that point at the LAN address a nearby device can actually reach.

Existing local CLI and health-check behavior remains loopback-first, so the earlier local/remote pairing regressions should not come back.

Evidence

  • pnpm test src/infra/advertised-lan-host.test.ts src/pairing/setup-code.test.ts src/commands/onboard-helpers.test.ts src/commands/configure.wizard.test.ts src/wizard/setup.finalize.test.ts src/cli/daemon-cli/status.gather.test.ts src/cli/daemon-cli/status.print.test.ts src/commands/status-all/format.test.ts
  • node scripts/run-vitest.mjs src/infra/advertised-lan-host.test.ts src/pairing/setup-code.test.ts src/commands/onboard-helpers.test.ts src/commands/configure.wizard.test.ts src/wizard/setup.finalize.test.ts src/cli/daemon-cli/status.gather.test.ts src/cli/daemon-cli/status.print.test.ts src/commands/status-all/format.test.ts
  • $autoreview run before commit; no accepted/actionable findings remained.
  • Live E2E proof covered the Windows/Parallels route-aware pairing reproduction and sibling Control UI display surfaces.
  • Live regression proof covered local loopback probing/opening behavior so bind=lan same-host checks still do not depend on the advertised LAN URL.

@openclaw-barnacle openclaw-barnacle Bot added docs Improvements or additions to documentation gateway Gateway runtime cli CLI command changes scripts Repository scripts commands Command implementations extensions: device-pair size: L maintainer Maintainer-authored PR labels Jul 1, 2026
@joshavant
joshavant force-pushed the fix/route-aware-lan-advertise branch from 05a7260 to 0fd2d66 Compare July 1, 2026 07:32
@clawsweeper

clawsweeper Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs maintainer review before merge. Reviewed July 1, 2026, 4:59 AM ET / 08:59 UTC.

Summary
The PR adds route-aware LAN host selection for advertised Control UI and device-pairing links while keeping same-host probe paths on loopback.

PR surface: Source +371, Tests +494, Docs 0, Other 0. Total +865 across 35 files.

Reproducibility: yes. from source and PR discussion: current main uses first-private-interface LAN display links, while the contributor's live output shows a host where that differs from the OS default-route interface.

Review metrics: 1 noteworthy metric.

  • Public SDK exports: 1 added. The PR adds resolveAdvertisedLanHost to openclaw/plugin-sdk/gateway-runtime, which becomes a plugin compatibility contract.

Stored data model
Persistent data-model change detected: serialized state: src/pairing/setup-code.test.ts. Confirm migration or upgrade compatibility proof before merge.

Merge readiness
Overall: 馃悮 platinum hermit
Proof: 馃悮 platinum hermit
Patch quality: 馃悮 platinum hermit
Result: ready for maintainer review.

Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch.

Rank-up moves:

  • [P2] Have a maintainer explicitly accept or redirect the new gateway-runtime SDK helper before merge.

Risk before merge

  • [P1] Merging publishes resolveAdvertisedLanHost as a public gateway-runtime SDK helper, so maintainers should explicitly accept that compatibility contract before landing.

Maintainer options:

  1. Accept the narrow SDK seam (recommended)
    Maintainers can explicitly approve resolveAdvertisedLanHost as a supported gateway-runtime helper now that it is documented on the focused SDK subpath.
  2. Keep the helper private
    If maintainers do not want this as plugin API, rework the bundled device-pair call path so the route resolver stays internal to core-owned runtime code.
  3. Pause the PR
    If the SDK direction is not ready, pause this branch rather than landing a public helper whose long-term contract is unsettled.

Next step before merge

  • [P2] Manual maintainer review is needed because the PR has a protected maintainer label and adds a public plugin SDK helper.

Security
Cleared: The diff adds fixed OS route probes with bounded execution and no user-controlled shell command or secret-handling regression found.

Review details

Best possible solution:

Land the route-aware advertised-link behavior after maintainer acceptance of the narrow gateway-runtime SDK seam, keeping local probe behavior loopback-first.

Do we have a high-confidence way to reproduce the issue?

Yes from source and PR discussion: current main uses first-private-interface LAN display links, while the contributor's live output shows a host where that differs from the OS default-route interface.

Is this the best way to solve the issue?

Mostly yes: the route-aware resolver plus display/probe split is the right code shape, but the public SDK export remains a maintainer contract decision before merge.

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against 0ba41effeb93.

Label changes

Label changes:

  • add proof: sufficient: Contributor real behavior proof is sufficient. The PR discussion now contains copied live output showing the after-fix route-aware host selection, reachable LAN dashboard, loopback status probe, and auth refusal regression check.
  • add rating: 馃悮 platinum hermit: Overall readiness is 馃悮 platinum hermit; proof is 馃悮 platinum hermit and patch quality is 馃悮 platinum hermit.
  • add status: 馃憖 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (live_output): The PR discussion now contains copied live output showing the after-fix route-aware host selection, reachable LAN dashboard, loopback status probe, and auth refusal regression check.
  • remove rating: 馃Κ silver shellfish: Current PR rating is rating: 馃悮 platinum hermit, so this older rating label is no longer current.
  • remove status: 馃摚 needs proof: Current PR status label is status: 馃憖 ready for maintainer look.

Label justifications:

  • P2: The PR fixes a real but bounded LAN advertisement bug affecting setup, pairing, and status link display on multi-interface hosts.
  • merge-risk: 馃毃 compatibility: The diff adds a public plugin SDK helper, creating an API compatibility commitment that green CI cannot decide for maintainers.
  • rating: 馃悮 platinum hermit: Overall readiness is 馃悮 platinum hermit; proof is 馃悮 platinum hermit and patch quality is 馃悮 platinum hermit.
  • status: 馃憖 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (live_output): The PR discussion now contains copied live output showing the after-fix route-aware host selection, reachable LAN dashboard, loopback status probe, and auth refusal regression check.
  • proof: sufficient: Contributor real behavior proof is sufficient. The PR discussion now contains copied live output showing the after-fix route-aware host selection, reachable LAN dashboard, loopback status probe, and auth refusal regression check.
Evidence reviewed

PR surface:

Source +371, Tests +494, Docs 0, Other 0. Total +865 across 35 files.

View PR surface stats
Area Files Added Removed Net
Source 21 422 51 +371
Tests 11 500 6 +494
Docs 2 2 2 0
Config 0 0 0 0
Generated 0 0 0 0
Other 1 2 2 0
Total 35 926 61 +865

What I checked:

  • Repository policy read: Root and scoped AGENTS.md guidance for docs, extensions, plugin SDK, gateway, and scripts was read; the plugin SDK compatibility rules affected this review. (AGENTS.md:1, 0ba41effeb93)
  • Current main behavior: Current main resolves bind=lan Control UI links with the first best-effort LAN IPv4, so a multi-interface host can display the wrong reachable address. (src/gateway/control-ui-links.ts:31, 0ba41effeb93)
  • Route-aware implementation: The PR adds resolveAdvertisedLanHost, which filters private IPv4 candidates, reads platform default-route hints, and falls back to existing candidate order. (src/infra/advertised-lan-host.ts:230, 252abdaf8ccc)
  • Display/probe split: Advertised Control UI links may use the route-aware LAN host, while local readiness links force bind=lan back to loopback. (src/gateway/control-ui-links.ts:55, 252abdaf8ccc)
  • Device-pairing caller: Setup-code pairing now passes the resolved advertised LAN host into the existing gateway bind URL helper instead of re-picking the first private interface. (src/pairing/setup-code.ts:343, 252abdaf8ccc)
  • SDK boundary: The follow-up moved the helper out of plugin-sdk/core and into the narrower gateway-runtime public subpath, which is already exported as a package entrypoint. (src/plugin-sdk/gateway-runtime.ts:7, 252abdaf8ccc)

Likely related people:

  • yetval: Authored the current-main commit that introduced the central Control UI link, setup-code, and shared gateway bind URL surfaces now being changed. (role: recent area contributor; confidence: high; commits: 54e6afdc2955; files: src/gateway/control-ui-links.ts, src/pairing/setup-code.ts, src/shared/gateway-bind-url.ts)
  • vincentkoc: Merged the current-main PR that introduced the central files and behavior this PR builds on. (role: merger; confidence: medium; commits: 54e6afdc2955; files: src/gateway/control-ui-links.ts, src/pairing/setup-code.ts, src/shared/gateway-bind-url.ts)
What the crustacean ranks mean
  • 馃 challenger crab: rare, exceptional readiness with strong proof, clean implementation, and convincing validation.
  • 馃 diamond lobster: very strong readiness with only minor maintainer review expected.
  • 馃悮 platinum hermit: good normal PR, likely mergeable with ordinary maintainer review.
  • 馃 gold shrimp: useful signal, but proof or patch confidence is still limited.
  • 馃Κ silver shellfish: thin signal; proof, validation, or implementation needs work.
  • 馃 unranked krab: not merge-ready because proof is missing/unusable or there are serious correctness or safety concerns.
  • 馃寠 off-meta tidepool: rating does not apply to this item.

Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

How this review workflow works
  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

@clawsweeper clawsweeper Bot added rating: 馃Κ silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: 馃摚 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. P2 Normal backlog priority with limited blast radius. merge-risk: 馃毃 compatibility 馃毃 May break existing users, config, migrations, defaults, or upgrade paths. labels Jul 1, 2026
@joshavant

Copy link
Copy Markdown
Collaborator Author

Follow-up for the review feedback on this PR.

What changed in 252abdaf8c:

  • Moved resolveAdvertisedLanHost out of broad openclaw/plugin-sdk/core and into the existing focused openclaw/plugin-sdk/gateway-runtime subpath.
  • Kept extensions/device-pair/api.ts on public SDK imports, now sourcing this helper from gateway-runtime.
  • Updated the SDK docs rows and regenerated docs/.generated/plugin-sdk-api-baseline.sha256 for the intentional export-path move.

Inspectable live proof from current branch head 252abdaf8c on 2026-07-01, using an isolated .artifacts/pr98482-live-proof/state/openclaw.json config with gateway.bind=lan:

{
  "platform": "darwin",
  "routeHints": [{ "interfaceName": "en9" }],
  "candidates": [
    { "interfaceName": "en0", "address": "192.168.1.139", "order": 0 },
    { "interfaceName": "en9", "address": "192.168.1.234", "order": 1 },
    { "interfaceName": "bridge100", "address": "10.211.55.2", "order": 2 },
    { "interfaceName": "bridge101", "address": "10.37.129.2", "order": 3 }
  ],
  "selectedHost": "192.168.1.234",
  "advertisedLinks": {
    "httpUrl": "http://192.168.1.234:18789/",
    "wsUrl": "ws://192.168.1.234:18789"
  },
  "localProbeLinks": {
    "httpUrl": "http://127.0.0.1:18789/",
    "wsUrl": "ws://127.0.0.1:18789"
  }
}

That shows the prior first-private candidate would have been 192.168.1.139, while the route-aware advertised host is the default-route interface en9 / 192.168.1.234.

Live Gateway proof on non-default port 19842 with a throwaway token:

$ curl -i --max-time 8 http://192.168.1.234:19842/
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Content-Length: 10316

Live openclaw status against the same running temporary Gateway:

Dashboard            http://192.168.1.234:19842/
Gateway              local 路 ws://127.0.0.1:19842 (local loopback) 路 reachable 38ms 路 auth token

Security/regression check: openclaw gateway run --bind lan --auth none still refuses non-loopback LAN bind without auth:

Refusing to bind gateway to lan without auth.
Set gateway.auth.token/password (or OPENCLAW_GATEWAY_TOKEN/OPENCLAW_GATEWAY_PASSWORD) or pass --token/--password.

Validation run after the feedback fix:

.agents/skills/autoreview/scripts/autoreview --mode local
# autoreview clean: no accepted/actionable findings reported

node scripts/run-vitest.mjs src/infra/advertised-lan-host.test.ts src/commands/onboard-helpers.test.ts src/commands/configure.wizard.test.ts src/commands/status-all/format.test.ts src/commands/status.scan-overview.test.ts src/commands/status.scan.test.ts extensions/device-pair/index.test.ts --reporter=verbose
# 7 files / 135 tests passed across 4 Vitest shards

pnpm plugin-sdk:api:check
pnpm plugin-sdk:surface:check
pnpm docs:check-mdx
git diff --check
pnpm build

The earlier SDK export concern should now be addressed at the owner boundary, and the live proof is pasted here so the behavior evidence is inspectable from PR discussion.

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

馃馃Ч
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

@clawsweeper clawsweeper Bot added proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 馃悮 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 馃憖 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. and removed rating: 馃Κ silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: 馃摚 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. labels Jul 1, 2026
@joshavant
joshavant merged commit ba5244c into main Jul 1, 2026
117 of 127 checks passed
@joshavant
joshavant deleted the fix/route-aware-lan-advertise branch July 1, 2026 09:02
chenyangjun-xy pushed a commit to chenyangjun-xy/openclaw that referenced this pull request Jul 1, 2026
* Route LAN pairing URLs by default route

* Advertise route-aware LAN Control UI links

* Fix route-aware LAN test mocks

* Narrow advertised LAN SDK export
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request Jul 2, 2026
* Route LAN pairing URLs by default route

* Advertise route-aware LAN Control UI links

* Fix route-aware LAN test mocks

* Narrow advertised LAN SDK export
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cli CLI command changes commands Command implementations docs Improvements or additions to documentation extensions: device-pair gateway Gateway runtime maintainer Maintainer-authored PR merge-risk: 馃毃 compatibility 馃毃 May break existing users, config, migrations, defaults, or upgrade paths. P2 Normal backlog priority with limited blast radius. proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 馃悮 platinum hermit Good normal PR readiness with ordinary maintainer review expected. scripts Repository scripts size: L status: 馃憖 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant