WIP: feat(tsconnect): expose service advertisement to JS #9

Draft
codinget wants to merge 1 commits from feat/tsconnect-service-advertisement into webnet
Owner

Summary

  • Adds SetExplicitServices([]tailcfg.Service) to LocalBackend — a browser-safe alternative to the OS portlist scanner that bypasses the ShouldUploadServices gate when services are declared explicitly.
  • Exposes a setServices(services) method on the WASM IPN object, accepting {proto, port, description?} entries.
  • Includes a services field on every node in the netmap JSON (self and peers), populated from Hostinfo.Services with internal peerapi entries stripped (those are already in peerAPIURL).

Design notes

The ShouldUploadServices hook is normally set by the portlist extension via OS-level port scanning, which can't run in a browser. Rather than registering a hook (which panics if set twice), the fix adds an explicitServices field to LocalBackend. hostInfoWithServicesLocked only clears hi.Services when that slice is empty, leaving all non-WASM callers unaffected.

Test plan

  • GOOS=js GOARCH=wasm go vet ./cmd/tsconnect/wasm/ — passes
  • go build tailscale.com/ipn/ipnlocal — passes
  • Manual: call ipn.setServices([{proto:"tcp",port:8080}]) after Running, verify via localAPI GET /localapi/v0/status that the node reports the service, and that a second peer sees it in its netmap.
## Summary - Adds `SetExplicitServices([]tailcfg.Service)` to `LocalBackend` — a browser-safe alternative to the OS portlist scanner that bypasses the `ShouldUploadServices` gate when services are declared explicitly. - Exposes a `setServices(services)` method on the WASM IPN object, accepting `{proto, port, description?}` entries. - Includes a `services` field on every node in the netmap JSON (self and peers), populated from `Hostinfo.Services` with internal peerapi entries stripped (those are already in `peerAPIURL`). ## Design notes The `ShouldUploadServices` hook is normally set by the `portlist` extension via OS-level port scanning, which can't run in a browser. Rather than registering a hook (which panics if set twice), the fix adds an `explicitServices` field to `LocalBackend`. `hostInfoWithServicesLocked` only clears `hi.Services` when that slice is empty, leaving all non-WASM callers unaffected. ## Test plan - `GOOS=js GOARCH=wasm go vet ./cmd/tsconnect/wasm/` — passes - `go build tailscale.com/ipn/ipnlocal` — passes - Manual: call `ipn.setServices([{proto:"tcp",port:8080}])` after `Running`, verify via `localAPI GET /localapi/v0/status` that the node reports the service, and that a second peer sees it in its netmap.
codinget added 1 commit 2026-06-09 00:57:46 +02:00
Add SetExplicitServices on LocalBackend so the browser WASM node can
declare TCP/UDP services that get uploaded to the control server and
distributed to all peers in the netmap — without the OS port-scanner
(portlist extension) that cannot run in a browser.

The ShouldUploadServices gate in hostInfoWithServicesLocked is bypassed
when services were set explicitly, leaving all other callers unaffected.

On the JS side, a new setServices(services) method accepts an array of
{proto, port, description?} objects.  The netmap JSON now includes a
services field on every node (self and peers), populated from
Hostinfo.Services with internal peerapi entries stripped (they are
already reflected in peerAPIURL).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This pull request is marked as a work in progress.
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin feat/tsconnect-service-advertisement:feat/tsconnect-service-advertisement
git checkout feat/tsconnect-service-advertisement
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: webnet/tailscale#9