Skip to content

fix(cli): normalize wildcard bind address in service registration URL - #59852

Open
Abioy wants to merge 2 commits into
anomalyco:devfrom
Abioy:fix-service-registration-url
Open

Abioy wants to merge 2 commits into
anomalyco:devfrom
Abioy:fix-service-registration-url

Conversation

@Abioy

@Abioy Abioy commented Oct 28, 2026 •

Copy link
Copy Markdown

When hostname is set to 0.0.0.0 (listen on all interfaces), the service registration file recorded http://0.0.0.0:port as the client connection URL. Since 0.0.0.0 is a bind wildcard rather than a routable address, client auto-discovery could fail to connect to the background service.

Normalize 0.0.0.0 to 127.0.0.1 and :: to ::1 in both the registration URL and the incumbent check probe URL, matching the existing behavior in the remote tunnel target construction.

Issue for this PR

Closes #49796

Type of change

  • Bug fix
  • New feature
  • Refactor / code improvement
  • Documentation

What does this PR do?

When hostname is set to 0.0.0.0 in ~/.config/opencode/service.json (listen on all interfaces), the service registration file at ~/.local/state/opencode/service.json recorded http://0.0.0.0:port as the client connection URL. Since 0.0.0.0 is a bind wildcard, not a routable destination address, the second opencode client's auto-discovery fails to connect to the running background service.

service-registration.ts used HttpServer.formatAddress(address) which passes the bind hostname directly into the URL. The remote tunnel target construction already normalizes 0.0.0.0 → 127.0.0.1, but the registration path missed this step.

Added formatClientAddress() to normalize 0.0.0.0 → 127.0.0.1 and :: → ::1 for the registration URL, and applied the same normalization in serviceURL() used by the findIncumbent startup probe.

How did you verify your code works?

Tested locally: with hostname: "0.0.0.0" and port: 14096 in ~/.config/opencode/service.json, the registration file now contains http://127.0.0.1:14096 instead of http://0.0.0.0:14096, and a second opencode terminal connects to the running service without issues.

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

Added by the opencode bot (AI-generated).

Related: #49796

Public API: registration file url is now loopback instead of 0.0.0.0/:: for wildcard binds (compatible with existing readers)

@github-actions github-actions Bot added needs:compliance This means the issue will auto-close after 2 hours. needs:issue labels Oct 28, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Thanks for your contribution!

This PR doesn't have a linked issue. All PRs must reference an existing issue.

Please:

  1. Open an issue describing the bug/feature (if one doesn't exist)
  2. Add Fixes #<number> or Closes #<number> to this PR description

See CONTRIBUTING.md for details.

@github-actions github-actions Bot removed needs:compliance This means the issue will auto-close after 2 hours. needs:issue labels Oct 28, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Thanks for updating your PR! It now meets our contributing guidelines. 👍

@opencodejr opencodejr Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As written, this breaks service registration for every service start, not just wildcard binds. formatClientAddress reads address.hostname and checks _tag === "UnixAddress", but Effect's NetAddress.SocketAddress is InetAddressV4 | InetAddressV6 | UnixPathAddress, and the IP is in address.address. So hostname is always undefined and hostname.includes throws before the registration file is written.

How I checked it:

  • tsc -b in packages/cli: 5 errors in service-registration.ts (TS2367 and TS2339); the base is clean.
  • bun test test/service.test.ts: 12 failures with TypeError: undefined is not an object (evaluating 'hostname.includes') from formatClientAddress; the base passes 28 with 0 failures.

Public API: the url in the registration file (~/.local/state/opencode/service.json) would change from the wildcard address to loopback for services bound to 0.0.0.0/::. No CLI, config or SDK changes.

Other concerns:

  • pair.ts treats a loopback registered URL as "only reachable locally". It then prints the SSH port-forward hint and "run opencode service set hostname 0.0.0.0". A service already bound to 0.0.0.0 would now register 127.0.0.1 and get that advice. Either keep the wildcard in the registration file and rewrite it on the client side, or adjust pair.ts in the same PR.
  • The same wildcard-to-loopback rewrite now lives in serviceURL, formatClientAddress, the tunnel target and desktop. A single shared helper would keep them consistent.

Related: #49796

@Abioy
Abioy force-pushed the fix-service-registration-url branch from 1be12cd to 289c6e4 Compare October 28, 2026 08:24

@opencodejr opencodejr Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The crash from the last review is fixed: formatClientAddress now uses NetAddress.isInetAddress/formatIp. The wildcard rewrite is now one shared normalizeLoopback helper, and opencode pair no longer suggests setting hostname 0.0.0.0 when it is already set.

How I checked it:

  • Called ServiceRegistration.register directly on both checkouts. On the base, 0.0.0.0/:: binds register http://0.0.0.0:… / http://[::]:…. With this PR they register http://127.0.0.1:… / http://[::1]:…, and other addresses are unchanged. I didn't reproduce the proxy case from #49796 end to end.
  • tsc -b in packages/cli is clean, and test/service.test.ts passes as on the base.

Public API: the url in the registration file ~/.local/state/opencode/service.json changes from the wildcard to loopback for wildcard binds. Readers in this repo are compatible.

One small change in behaviour, not blocking: with remote access on and the service bound to ::, the tunnel target is now [::1]:<port> instead of 127.0.0.1:<port>.

No other concerns.

@Abioy
Abioy force-pushed the fix-service-registration-url branch from 289c6e4 to d2f9958 Compare October 28, 2026 08:35

@opencodejr opencodejr Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new commit restores the base behaviour for the remote tunnel target: a service bound to :: targets 127.0.0.1:<port> again instead of [::1]:<port>, which was the small change I noted last time. tsc -b in packages/cli is clean and test/service.test.ts passes. Public API: no change from the last review (the registration file url is loopback for wildcard binds). No concerns.

@Abioy
Abioy force-pushed the fix-service-registration-url branch 2 times, most recently from 805c6d5 to 7f92614 Compare October 28, 2026 10:18
@Abioy
Abioy force-pushed the fix-service-registration-url branch from 7f92614 to f46d5d4 Compare October 28, 2026 10:18

@opencodejr opencodejr Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new push changes no code: its tree is identical to the commit I reviewed last time, so the previous checks still hold. Public API: unchanged since the last review (the registration file url is loopback for wildcard binds). No concerns.

@opencodejr opencodejr Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new push only merges dev. The change against dev is the same as in my last review.

I ran the #49796 reproduction from source against a proxy that returns 502 for 0.0.0.0:

  • On dev, the service registers http://0.0.0.0:<port>, service status prints stopped, and session list fails with "port already in use" after about 1300 probes through the proxy.
  • With this PR, it registers http://127.0.0.1:<port>, status and session list both work, and no requests reach the proxy.

tsc -b in packages/cli is clean and test/service.test.ts passes.

Public API: unchanged since the last review (the registration file url is loopback for wildcard binds). No concerns.

This branch has not been deployed

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

cli: service configured with hostname 0.0.0.0 is unreachable when an HTTP proxy is set

1 participant