Remove obsolete wasm bridge APIs #17

Merged
codinget merged 1 commits from remove-wasm-bridge-features into webnet 2026-07-31 01:28:51 +02:00
Owner

Removes obsolete APIs from the js/wasm bridge after their JavaScript-side removal in webnet/webnet#150.

  • remove the ssh and fetch bridge methods, including the direct golang.org/x/crypto/ssh dependency
  • remove setExitNodeEnabled; callers clear the exit node through setExitNode
  • stop accepting the non-functional disco ping type
  • leave core Tailscale behavior unchanged

Implements the remaining wasm bridge work from webnet/webnet#141, #144, and #145.

Checks:

  • GOOS=js GOARCH=wasm ./tool/go test -run=^$ -tags=netgo,omitidna,omitpemdecrypt,osusergo,tailscale_go,ts_omit_ace ./cmd/tsconnect/wasm
  • production wasm build with the same feature tags and release flags
  • verified golang.org/x/crypto/ssh is absent from go list -deps
  • raw stripped wasm shrank from 41,216,909 to 40,376,172 bytes (840,737 bytes, 2.0%)

AI disclosure: implemented and reviewed with Codex (gpt-5.6-sol).

Removes obsolete APIs from the js/wasm bridge after their JavaScript-side removal in webnet/webnet#150. - remove the `ssh` and `fetch` bridge methods, including the direct `golang.org/x/crypto/ssh` dependency - remove `setExitNodeEnabled`; callers clear the exit node through `setExitNode` - stop accepting the non-functional `disco` ping type - leave core Tailscale behavior unchanged Implements the remaining wasm bridge work from [webnet/webnet#141](https://gitea.codinget.me/webnet/webnet/issues/141), [#144](https://gitea.codinget.me/webnet/webnet/issues/144), and [#145](https://gitea.codinget.me/webnet/webnet/issues/145). Checks: - `GOOS=js GOARCH=wasm ./tool/go test -run=^$ -tags=netgo,omitidna,omitpemdecrypt,osusergo,tailscale_go,ts_omit_ace ./cmd/tsconnect/wasm` - production wasm build with the same feature tags and release flags - verified `golang.org/x/crypto/ssh` is absent from `go list -deps` - raw stripped wasm shrank from 41,216,909 to 40,376,172 bytes (840,737 bytes, 2.0%) AI disclosure: implemented and reviewed with Codex (`gpt-5.6-sol`).
codinget added 1 commit 2026-07-31 00:34:31 +02:00
codinget added the
Agent
gpt-5.6-sol
4
Agentic
labels 2026-07-31 00:34:59 +02:00
codinget marked the pull request as ready for review 2026-07-31 00:35:53 +02:00
codinget reviewed 2026-07-31 00:50:07 +02:00
codinget left a comment
Author
Owner

Review: Remove obsolete wasm bridge APIs

The Go change itself is clean and I could reproduce the claims. What is missing is the TypeScript half of cmd/tsconnect, which still speaks to the APIs this PR deletes.

Verified

  • GOOS=js GOARCH=wasm ./tool/go vet -tags=netgo,omitidna,omitpemdecrypt,osusergo,tailscale_go,ts_omit_ace ./cmd/tsconnect/wasm is clean at d94244830 — no orphaned imports (bytes, x/crypto/ssh were the only two that became unused; net/http, strings, ipnauth are still needed).
  • golang.org/x/crypto/ssh is gone from the wasm dependency graph: go list -deps matches 2 packages on webnet, 0 on this branch.
  • The ping narrowing matches the consumer: packages/tsconnect/src/types.ts already declares PingType = "TSMP" | "ICMP" | "peerapi" and packages/tsconnect/src/ipn.ts:428 already rejects "disco" before it reaches the bridge.
  • No stale references to ssh/fetch/setExitNodeEnabled remain in cmd/tsconnect/README.md, README.pkg.md, or the Go sources.

1. The TS side of cmd/tsconnect still calls the removed methods

cmd/tsconnect/src/types/wasm_js.d.ts:16 and :32 still declare ssh(...) and fetch(url) on the IPN interface. Because that declaration is what yarn lint (tsc --noEmit, run by build-pkg) checks against, nothing fails — the type contract just silently became wrong, and the callers below stay green:

  • cmd/tsconnect/src/lib/ssh.ts:56 calls ipn.ssh(def.hostname, def.username, {...}). After this PR that property is undefined, so runSSHSession throws TypeError: ipn.ssh is not a function on first use.
  • cmd/tsconnect/src/pkg/pkg.ts:40 re-exports runSSHSession from the published package entrypoint. build-pkg bundles src/pkg/pkg.ts (common.go:111) and generates pkg.d.ts from it, so the shipped package advertises and exports an API that is dead on arrival. This is not hypothetical for downstream: packages/tsconnect/build.sh in webnet/webnet runs build-pkg -pkgdir dist, and @webnet/tsconnect publishes files: ["dist/"].
  • Because runSSHSession stays in the entrypoint's export graph, esbuild keeps bundling xterm, xterm-addon-fit and xterm-addon-web-links into dist/pkg.js for code that can no longer run — which eats into the 2.0% wasm win the description reports.
  • The dev demo app (src/app/app.tsx:66, src/app/ssh.tsx) still renders an SSH tab that now breaks at runtime under go run . dev.

Suggested fix, smallest to largest:

  1. At minimum, drop the ssh and fetch declarations from wasm_js.d.ts. That makes tsc --noEmit surface every remaining caller instead of hiding them, so the build fails loudly rather than the package shipping broken exports.
  2. Then remove src/lib/ssh.ts, src/app/ssh.tsx and its use in app.tsx, the runSSHSession re-export in pkg.ts, and the three xterm* entries in cmd/tsconnect/package.json.

If keeping the upstream demo app intact is a deliberate goal for future rebases, that is a fine call to make — but then wasm_js.d.ts should still be corrected and the breakage noted in the PR description, because right now it reads as though the JS side is fully settled.

2. Nit: setExitNode("") is not equivalent to setExitNodeEnabled(false)

The description says "callers clear the exit node through setExitNode". That is true for the clearing effect, but SetUseExitNodeEnabled(false) also sets InternalExitNodePrior and clears AutoExitNode (ipn/ipnlocal/local.go:5130-5136), which is what makes a later re-enable possible. jsIPN.setExitNode only sets ExitNodeID. Since webnet/webnet dropped the toggle entirely rather than reimplementing it on top of setExitNode, nothing regresses — the wording just slightly oversells it as a drop-in replacement.


Once the wasm_js.d.ts declarations are dropped, this is good to go from my side.

Reviewed with Claude Opus 5.

## Review: Remove obsolete wasm bridge APIs The Go change itself is clean and I could reproduce the claims. What is missing is the TypeScript half of `cmd/tsconnect`, which still speaks to the APIs this PR deletes. ### Verified - `GOOS=js GOARCH=wasm ./tool/go vet -tags=netgo,omitidna,omitpemdecrypt,osusergo,tailscale_go,ts_omit_ace ./cmd/tsconnect/wasm` is clean at `d94244830` — no orphaned imports (`bytes`, `x/crypto/ssh` were the only two that became unused; `net/http`, `strings`, `ipnauth` are still needed). - `golang.org/x/crypto/ssh` is gone from the wasm dependency graph: `go list -deps` matches 2 packages on `webnet`, 0 on this branch. - The `ping` narrowing matches the consumer: `packages/tsconnect/src/types.ts` already declares `PingType = "TSMP" | "ICMP" | "peerapi"` and `packages/tsconnect/src/ipn.ts:428` already rejects `"disco"` before it reaches the bridge. - No stale references to `ssh`/`fetch`/`setExitNodeEnabled` remain in `cmd/tsconnect/README.md`, `README.pkg.md`, or the Go sources. ### 1. The TS side of `cmd/tsconnect` still calls the removed methods `cmd/tsconnect/src/types/wasm_js.d.ts:16` and `:32` still declare `ssh(...)` and `fetch(url)` on the `IPN` interface. Because that declaration is what `yarn lint` (`tsc --noEmit`, run by `build-pkg`) checks against, nothing fails — the type contract just silently became wrong, and the callers below stay green: - `cmd/tsconnect/src/lib/ssh.ts:56` calls `ipn.ssh(def.hostname, def.username, {...})`. After this PR that property is `undefined`, so `runSSHSession` throws `TypeError: ipn.ssh is not a function` on first use. - `cmd/tsconnect/src/pkg/pkg.ts:40` re-exports `runSSHSession` from the **published package entrypoint**. `build-pkg` bundles `src/pkg/pkg.ts` (`common.go:111`) and generates `pkg.d.ts` from it, so the shipped package advertises and exports an API that is dead on arrival. This is not hypothetical for downstream: `packages/tsconnect/build.sh` in webnet/webnet runs `build-pkg -pkgdir dist`, and `@webnet/tsconnect` publishes `files: ["dist/"]`. - Because `runSSHSession` stays in the entrypoint's export graph, esbuild keeps bundling `xterm`, `xterm-addon-fit` and `xterm-addon-web-links` into `dist/pkg.js` for code that can no longer run — which eats into the 2.0% wasm win the description reports. - The dev demo app (`src/app/app.tsx:66`, `src/app/ssh.tsx`) still renders an SSH tab that now breaks at runtime under `go run . dev`. Suggested fix, smallest to largest: 1. At minimum, drop the `ssh` and `fetch` declarations from `wasm_js.d.ts`. That makes `tsc --noEmit` surface every remaining caller instead of hiding them, so the build fails loudly rather than the package shipping broken exports. 2. Then remove `src/lib/ssh.ts`, `src/app/ssh.tsx` and its use in `app.tsx`, the `runSSHSession` re-export in `pkg.ts`, and the three `xterm*` entries in `cmd/tsconnect/package.json`. If keeping the upstream demo app intact is a deliberate goal for future rebases, that is a fine call to make — but then `wasm_js.d.ts` should still be corrected and the breakage noted in the PR description, because right now it reads as though the JS side is fully settled. ### 2. Nit: `setExitNode("")` is not equivalent to `setExitNodeEnabled(false)` The description says "callers clear the exit node through `setExitNode`". That is true for the clearing effect, but `SetUseExitNodeEnabled(false)` also sets `InternalExitNodePrior` and clears `AutoExitNode` (`ipn/ipnlocal/local.go:5130-5136`), which is what makes a later re-enable possible. `jsIPN.setExitNode` only sets `ExitNodeID`. Since webnet/webnet dropped the toggle entirely rather than reimplementing it on top of `setExitNode`, nothing regresses — the wording just slightly oversells it as a drop-in replacement. --- Once the `wasm_js.d.ts` declarations are dropped, this is good to go from my side. Reviewed with Claude Opus 5.
Author
Owner

Two corrections to my review above.

Withdrawing the setExitNodeEnabled nit. It holds in the general LocalBackend case but not here: the bridge only ever surfaces Prefs.ExitNodeID (wasm_js.go:557, via notifyExitNode), and never reads or emits InternalExitNodePrior or AutoExitNode. The only reader of InternalExitNodePrior was SetUseExitNodeEnabled itself, which this PR removes, and AutoExitNode was never settable through the bridge in the first place (jsIPN.setExitNode sets ExitNodeID only), so clearing it was already a no-op. Neither is observable from the API surface or from Tailscale behavior, so setExitNode("") is genuinely equivalent here and the description is accurate as written.

I overstated the downstream impact of the leftover TS. pkg-types writes to a hardcoded pkg/pkg.d.ts (cmd/tsconnect/package.json:19) rather than to -pkgdir, so the generated types land in cmd/tsconnect/pkg/pkg.d.ts and never reach the -pkgdir output. The built dist/ in webnet/webnet confirms it — there is no pkg.d.ts there, and nothing imports dist/pkg.js:

file bytes used by @webnet/tsconnect
main.wasm 37,169,651 yes
cacert.pem 226,168 yes
wasm_exec.js 16,992 yes (src/index.ts:1)
build-info.json 92 yes (staleness check)
pkg.js 288,385 no
pkg.js.map 1,194,628 no
pkg.css 9,979 no
pkg.css.map 16,992 no
package.json 240 no (stray @tailscale/connect manifest)
README.md 247 no

So it is purely npm package bloat: ~1.5 MB of unreferenced files in a 37.6 MB tarball. The broken runSSHSession is never loaded and never appears in the published type surface, and the bundled xterm lives in pkg.js, not main.wasm — my line about it eating into the 2.0% wasm win was wrong.

What remains from finding 1 is in-repo only, with no downstream consequence: src/types/wasm_js.d.ts now misdeclares ssh/fetch, which is what keeps yarn lint green over the now-broken src/lib/ssh.ts:56, and the go run . dev demo app's SSH tab fails at runtime. Still worth correcting so the type contract does not lie, but it does not block this PR.

Two corrections to my review above. **Withdrawing the `setExitNodeEnabled` nit.** It holds in the general `LocalBackend` case but not here: the bridge only ever surfaces `Prefs.ExitNodeID` (`wasm_js.go:557`, via `notifyExitNode`), and never reads or emits `InternalExitNodePrior` or `AutoExitNode`. The only reader of `InternalExitNodePrior` was `SetUseExitNodeEnabled` itself, which this PR removes, and `AutoExitNode` was never settable through the bridge in the first place (`jsIPN.setExitNode` sets `ExitNodeID` only), so clearing it was already a no-op. Neither is observable from the API surface or from Tailscale behavior, so `setExitNode("")` is genuinely equivalent here and the description is accurate as written. **I overstated the downstream impact of the leftover TS.** `pkg-types` writes to a hardcoded `pkg/pkg.d.ts` (`cmd/tsconnect/package.json:19`) rather than to `-pkgdir`, so the generated types land in `cmd/tsconnect/pkg/pkg.d.ts` and never reach the `-pkgdir` output. The built `dist/` in webnet/webnet confirms it — there is no `pkg.d.ts` there, and nothing imports `dist/pkg.js`: | file | bytes | used by `@webnet/tsconnect` | | --- | --- | --- | | `main.wasm` | 37,169,651 | yes | | `cacert.pem` | 226,168 | yes | | `wasm_exec.js` | 16,992 | yes (`src/index.ts:1`) | | `build-info.json` | 92 | yes (staleness check) | | `pkg.js` | 288,385 | no | | `pkg.js.map` | 1,194,628 | no | | `pkg.css` | 9,979 | no | | `pkg.css.map` | 16,992 | no | | `package.json` | 240 | no (stray `@tailscale/connect` manifest) | | `README.md` | 247 | no | So it is purely npm package bloat: ~1.5 MB of unreferenced files in a 37.6 MB tarball. The broken `runSSHSession` is never loaded and never appears in the published type surface, and the bundled `xterm` lives in `pkg.js`, not `main.wasm` — my line about it eating into the 2.0% wasm win was wrong. What remains from finding 1 is in-repo only, with no downstream consequence: `src/types/wasm_js.d.ts` now misdeclares `ssh`/`fetch`, which is what keeps `yarn lint` green over the now-broken `src/lib/ssh.ts:56`, and the `go run . dev` demo app's SSH tab fails at runtime. Still worth correcting so the type contract does not lie, but it does not block this PR.
Author
Owner

The lack of changes outside the wasm bridge itself is intentional, to keep this fork's patchset against upstream minimal, to simplify rebases whenever we need to catch up.

The fact that files that aren't useful to us are still ending up in @webnet/tsconnect's dist directory is preexisting, they've always been unused but now they're also incorrect and broken. Not blocking, but tracked in webnet/webnet#154 purely to remove dead weight and package bloat.

Final assessment: APPROVED.

The lack of changes outside the wasm bridge itself is intentional, to keep this fork's patchset against upstream minimal, to simplify rebases whenever we need to catch up. The fact that files that aren't useful to us are still ending up in @webnet/tsconnect's dist directory is preexisting, they've always been unused but now they're also incorrect and broken. Not blocking, but tracked in webnet/webnet#154 purely to remove dead weight and package bloat. Final assessment: APPROVED.
codinget merged commit d94244830b into webnet 2026-07-31 01:28:51 +02:00
codinget deleted branch remove-wasm-bridge-features 2026-07-31 01:28:51 +02:00
Sign in to join this conversation.