Commit Graph
39 Commits
Author SHA1 Message Date
codinget 7c67504fb6 feat(example-app): add SuggestExitNodeDebug 2026-05-10 14:53:02 +00:00
codingetandClaude 89c64e8c69 feat(tsconnect): add peerAPIURL to netmap types and localAPI wrapper
Add peerAPIURL field to IPNNetMapNode (shared by self and all peers).
Add localAPI(method, path, body?) method to IPN class and interface,
returning {status, body}. Bump tailscale submodule.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 01:23:25 +00:00
codingetandClaude 231b474d9c feat(tsconnect): add whoIs, queryDNS, ping, suggestExitNode TS wrappers
Add TypeScript types (WhoIsResponse, PingType, PingResult, DNSQueryResult,
ExitNodeSuggestion) and validated IPN class methods for the four new Go
bindings. Bump tailscale submodule.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 22:07:08 +00:00
codingetandClaude 2516be05d7 feat: add Funnel/HTTPS support (getCert, listenTLS, setFunnel)
Add TLSCertKeyPair type and TypeScript wrappers for getCert, listenTLS, and
setFunnel. Add Http example component that provisions a TLS cert, opens HTTP
and HTTPS listeners, serves "Hewwo, world!", and enables Tailscale Funnel on
port 443. Bump tailscale submodule to include the matching Go changes.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-06 11:19:59 +00:00
codinget 91e1b6afe8 feat(example-app): add caCerts and tailscale fetch 2026-04-18 23:44:53 +00:00
codinget ac658029a3 fix(example-app): add ts declarations 2026-04-18 23:42:33 +00:00
codingetandClaude 0665ff0acd feat: add turborepo for dependency-aware parallel builds
Replaces `npm run build --workspaces --if-present` with `turbo run build`,
which resolves build order from each package's package.json dependencies
and parallelises independent packages automatically.

- turbo.json: declares build task with `^build` topological dependency
- package.json: switches build script to turbo, adds packageManager field
  (required by turbo v2), adds turbo to devDependencies
- .gitignore: add .turbo/ cache directory
- tailscale: bump submodule for tsconfig types-pinning fix

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-18 20:15:43 +00:00
codingetandClaude da558c708b fix(tsconnect): bump submodule for skipLibCheck fix
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-18 19:52:37 +00:00
codinget 5d28032aa6 feat(example-app): login to tailscale, show peers, set exit node, use taildrop 2026-04-18 19:39:05 +00:00
codinget 1237206ec2 feat(tsconnect): add redux and react bindings 2026-04-18 19:37:46 +00:00
codinget 79e17bd2e3 feat(tsconnect): add boolean fileOps flag to IPN to signal availability 2026-04-18 19:37:03 +00:00
codingetandClaude fc3d134ee0 feat(example-app): bind dev server on all interfaces
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 19:52:50 +00:00
codingetandClaude 2edf7581f4 feat(example-app): enable CSS modules for SCSS imports
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 19:52:50 +00:00
codingetandClaude 67cb334928 feat(example-app): add webpack + TypeScript + React 19 + SCSS scaffold
Vanilla webpack 5 app with:
- babel-loader + @babel/preset-typescript for TS/TSX compilation
- @pmmmwh/react-refresh-webpack-plugin for React Fast Refresh in dev
- sass-loader + css-loader + style-loader for SCSS support
- HtmlWebpackPlugin templating
- Dev server on port 3000 (npm run dev), production build (npm run build)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 19:52:50 +00:00
codingetandClaude 42a95eb6d6 fix(tsconnect): lowercase name/size on IPNWaitingFile
Aligns the TypeScript type with the Go-side json tag fix so
waitingFiles() now returns {name, size} instead of {Name, Size}.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 19:41:30 +00:00
codingetandClaude 46bb960198 fix(tsconnect): bump tailscale submodule and expose done on incoming files
Picks up the notify-callback nil-Prefs panic fix and the restored
incoming-file progress notifications in taildrop.

Adds the done flag to IPNIncomingFile so consumers can distinguish the
final completion notification from in-progress updates.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-16 19:04:17 +00:00
codingetandClaude 4d44f90b26 feat(tsconnect): add notifyOutgoingFiles callback
Expose a new optional IPNRunOptions callback:

- notifyOutgoingFiles(files: IPNOutgoingFile[]): called with the full
  set of outgoing transfers whenever progress changes. Each entry carries
  id, peerID, name, started (Unix ms), declaredSize, sent bytes,
  finished, and succeeded. Entries remain present after completion so
  callers can observe success/failure before discarding them.

Also exports IPNOutgoingFile from the package index.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 23:01:38 +00:00
codingetandClaude 295593ccaf feat(tsconnect): add incoming file transfer notification callbacks
Expose two new optional callbacks on IPNRunOptions:

- notifyFilesWaiting(): called when a received file is staged and ready
  to retrieve via IPN.waitingFiles(). Use as a push alternative to
  polling.
- notifyIncomingFiles(files: IPNIncomingFile[]): called with the current
  snapshot of in-progress inbound transfers whenever progress updates.
  Empty array means all transfers have finished. IPNIncomingFile carries
  name, started (Unix ms timestamp), declaredSize (-1 if unknown), and
  received bytes, suitable for a progress bar.

Also exports IPNIncomingFile from the package index.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 22:58:56 +00:00
codinget 0b0c02e2f9 feat(test-app): expose helpers to window 2026-04-14 22:12:19 +00:00
codinget b1d6d0833b feat(tsconnect): add helpers and glue code 2026-04-14 22:11:58 +00:00
codingetandClaude 8cb151fa1c feat(tsconnect): add Taildrop and exit node JS bindings
Add TypeScript bindings for the Taildrop file transfer feature and exit
node selection to the @webnet/tsconnect package.

Taildrop:
- IPNConfig.fileOps: pluggable JS storage backend (IPNFileOps interface)
- IPN.listFileTargets(): list peers that can receive files
- IPN.sendFile(stableNodeID, filename, data): send a file to a peer
- IPN.waitingFiles(): list received files staged for pickup
- IPN.openWaitingFile(name): read a received file as Uint8Array
- IPN.deleteWaitingFile(name): delete a received file
- IPNFileOps void op callbacks simplified to cb(err?: string)
- IPNFileOps result op callbacks use cb(result: T | null, err?: string)

Exit node:
- IPNNetMapPeerNode.exitNodeOption, .stableNodeID
- IPNRunOptions.notifyExitNode callback
- IPN.setExitNode(stableNodeID): select or clear exit node
- IPN.setExitNodeEnabled(enabled): toggle most-recent exit node

Submodule also includes fixes for three Taildrop bugs: DirectFileMode
being incorrectly set for WASM (blocking WaitingFiles/OpenFile/Delete),
waitingFiles() returning null instead of [] when empty, and the
jsFileOps void callback convention change.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-13 22:49:34 +00:00
codingetandClaude 2f4fbec4c5 feat(tsconnect): expose exit node selection to JS
Wire up the exit node feature across the Go→JS bridge and the
TypeScript wrapper layer:

- Bump tailscale submodule to feat/tsconnect-exit-node (wasm bridge
  changes: exitNodeOption/stableNodeID on peers, notifyExitNode
  callback, setExitNode/setExitNodeEnabled methods).
- Add exitNodeOption and stableNodeID fields to IPNNetMapPeerNode.
- Add notifyExitNode to IPNCallbacks and IPNRunOptions.
- Add setExitNode/setExitNodeEnabled to the IPN interface and class.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-13 18:44:41 +00:00
codingetandClaude 760305bf76 docs: document @webnet/http package in README
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-12 22:49:26 +00:00
codingetandClaude 178d71178c build(http): compile to dist with declarations and inline sourcemaps
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-12 22:44:03 +00:00
codinget 4741a6f267 feat(http): implement http server library inspired by koa 2026-04-12 22:37:56 +00:00
codingetandClaude 3dffc32f7a chore(http): configure for Node.js and add node:test harness
- Switch to module/moduleResolution: NodeNext and verbatimModuleSyntax
- Drop DOM lib, add @types/node, bump target to ES2022
- Add test script (node --experimental-strip-types --test) and typecheck script
- Add mockTransport helper and smoke tests in connection.test.ts

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-12 19:18:31 +00:00
codingetandClaude 61ec60b46c feat(http): initialize @webnet/http package and wire into test-app
- Add packages/http with package.json, tsconfig.json, and empty index.ts
- Add @webnet/http as a dependency of test-app
- Expose http namespace on window for console testing

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-11 22:19:55 +00:00
codingetandClaude 7900e63b93 docs: add README
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-11 20:59:06 +00:00
codingetandClaude 341ece4f55 chore: add prettier, eslint, dpdm, lint-staged, commitlint; bump TS to 6
- Add Prettier with .prettierrc.json and format all source files
- Add ESLint v9 flat config with @eslint/js + typescript-eslint
- Add dpdm for circular dependency checking
- Add lint-staged (runs eslint + prettier on staged files)
- Add commitlint with conventional commits config
- Add husky pre-commit (lint-staged) and commit-msg (commitlint) hooks
- Bump TypeScript to ^6.0.2 in both packages
- Fix tsconnect moduleResolution: node → bundler (node10 deprecated in TS 6)
- Remove unused IPNConfig import and stale eslint-disable in ipn.ts

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-11 20:35:51 +00:00
codingetandClaude fe4058184d feat(test-app): wire up cacert.pem for manual dialTLS testing
Import @webnet/tsconnect/cacert.pem as a URL and expose cacertURL plus a
fetch-and-cache loadCACerts() helper on window. The example snippet in
index.html now shows how to pass the bundle into IPN.dialTLS for hosts that
the baked-in LE roots don't cover.

Vite's default server.fs.deny blocks *.{crt,pem} to guard TLS private keys;
the shipped bundle is a public trust store, so vite.config.ts overrides the
deny list to drop that pattern while keeping the .env guards. (The 403 error
reports this as an allow-list failure, which is misleading — both paths go
through the same handler in isFileLoadingAllowed.)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-11 16:45:22 +00:00
codingetandClaude 734d0bb391 feat(tsconnect): ship Mozilla CA bundle as a package export
Add the curl.se distribution of Mozilla's NSS trust store under ca/cacert.pem
(145 certs, 226 KB raw / 119 KB brotli) and expose it alongside main.wasm as
./cacert.pem, ./cacert.pem.br, and ./cacert.pem.gz subpath exports. build.sh
now copies the bundle into dist/ and precompresses it with gzip -9 and
brotli -q 11. scripts/update-ca-bundle.sh refreshes the committed PEM from
curl.se with SHA-256 verification; it's decoupled from the regular build so
the trust store can be bumped on its own cadence.

Consumers can hand the fetched PEM straight to IPN.dialTLS via
TLSDialOptions.caCerts to talk to hosts with non-LE certs that the baked-in
wasm roots don't cover.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-11 16:45:13 +00:00
codingetandClaude 0c7a73b07a feat(tsconnect): ship brotli-best precompressed wasm as a package export
Drop -fast-compression from the build so precompress uses brotli.BestCompression
instead of BestSpeed: main.wasm.br goes from 9.79 MB to 5.97 MB (-39%) with no
runtime change. Expose main.wasm.br and main.wasm.gz as subpath exports
alongside main.wasm so consumers can address the precompressed artifacts
directly; serving them with the right Content-Encoding is left to the reverse
proxy.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-11 16:01:41 +00:00
codinget fc3e46f9c0 feat(tsconnect): add class wrappers that validate before hitting Go
The raw objects returned from the wasm bridge are untyped bags of
js.FuncOf handlers. Calling them with the wrong types, on a closed
handle, or before the IPN is running, panics the Go runtime and
takes down the entire wasm instance for the lifetime of the page —
one bad call and the user has to reload.

Add src/ipn.ts with IPN / Conn / PacketConn / TCPListener classes
that wrap the raw handles and:

- Throw NotRunningError if an IPN method is called before run().
- Throw ClosedError if read/write/accept is called on a closed
  Conn/PacketConn/TCPListener, without dispatching into Go.
- Type-check string/Uint8Array arguments in JS before they reach
  js.CopyBytesToGo, which would otherwise crash.
- Range-check network literals for dial/listen/listenICMP.
- Expose addr / localAddr / remoteAddr / state / running / closed
  as getters rather than methods. Addresses are cached in the
  constructor so reading them never dispatches into Go, eliminating
  the "call .localAddr() on a closed conn" crash.
- Parse the netmap JSON inside notifyNetMap before handing it off,
  and make all run() callbacks optional (state is also a getter).

initIPN now returns a factory that produces IPN class instances
directly; index.ts re-exports the classes and hides the raw types,
so the public surface is the validated one.

The test-app inline example is updated to use getters (conn.remoteAddr,
ln.addr, ipn.state) instead of method calls.
2026-04-10 21:44:30 +00:00
codinget bdc4bcccee feat(tsconnect): add TCPListener and tighten dial/listen network types
Type the new TCP listening surface from the tailscale submodule:

- listen() is now overloaded: tcp/tcp4/tcp6 returns a TCPListener,
  udp/udp4/udp6 returns a PacketConn. The network parameter is a
  literal union instead of a bare string so callers get completion
  and typo protection.
- TCPListener extends AsyncIterable<Conn>, so consumers can write
  `for await (const conn of listener)`.
- dial() drops "udp" from its accepted networks. Connected UDP
  through a stream-shaped Conn was confusing; UDP belongs in
  listen(), and ICMP in listenICMP(). The Go side still accepts
  whatever string for callers who really want to bypass the type.

Bumps the TS lib/target to ES2018 for AsyncIterable, bumps the
tailscale submodule, and updates the test-app inline example to
demonstrate `for await` over a TCP listener.
2026-04-10 21:09:55 +00:00
codinget 08ddc31da1 feat(tsconnect): expose dialTLS and TLSDialOptions
Type the new ipn.dialTLS bridge from the tailscale submodule. The
default verification path uses the baked-in LetsEncrypt roots that
ship with the existing tsconnect wasm — sufficient for any tailnet
HTTPS endpoint provisioned via `tailscale cert`. Callers can supply
PEM roots, override SNI, or skip verification via TLSDialOptions.

Bumps the tailscale submodule and updates the test-app inline
example to demonstrate dialTLS.
2026-04-10 20:49:03 +00:00
codinget f445f03282 feat(test-app): add vite test app for manual browser testing
Adds a minimal @webnet/test-app package that boots a Vite dev server,
imports initIPN from @webnet/tsconnect and the bundled main.wasm as a
URL asset, and hangs both on window so you can drive the full IPN
lifecycle from devtools:

  const newIPN = await initIPN(wasmURL)
  const ipn = newIPN({ authKey, hostname })
  ipn.run({ ... })
  ipn.login()
  const conn = await ipn.dial("tcp", "peer:22")
  const pc = await ipn.listenICMP("icmp4")

The index.html doubles as usage documentation for the wrapper API.
2026-04-10 13:58:44 +00:00
codinget 7d0c67576f feat(tsconnect): expose Conn, PacketConn and ICMP types
Add TypeScript declarations for the new networking methods landed on
the tailscale submodule: ipn.dial (TCP/UDP), ipn.listen (UDP) and
ipn.listenICMP. Export the Conn and PacketConn interfaces that wrap
the underlying Go net.Conn / net.PacketConn bridges so callers can
type their handles.

Bumps the tailscale submodule to pick up the matching wasm changes
and the nethttpomithttp2 build tag fix.
2026-04-10 13:58:33 +00:00
codingetandClaude 2237d5a58d chore: point tailscale submodule to gitea mirror
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 23:35:48 +00:00
codingetandClaude b60522507f feat: initialize npm monorepo with tsconnect wasm package
Set up npm workspaces monorepo with the tailscale/tailscale repo as a
git submodule. Adds packages/tsconnect which builds the tsconnect wasm
via tailscale's Go toolchain and compiles a TypeScript wrapper that
initializes the wasm, captures newIPN from the global scope, removes it
to avoid pollution, and re-exports it with proper module-scoped types.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 23:19:25 +00:00