Commit Graph
100 Commits
Author SHA1 Message Date
codingetandClaude 99f0efc69c feat(vfs): share the optional-stripping helpers in conformance
withoutOptional and unsupportedOptional replace the local copies in the
fallback tests; the protocol packages need the same reduction to put their
servers' fallback paths under the suite.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-05 19:53:50 +00:00
codingetandCodex c562af8ec3 fix(vfs): reject overlapping copies
CI / format (pull_request) Successful in 2m45s
CI / lint (pull_request) Successful in 3m6s
CI / install (pull_request) Successful in 7m33s
CI / typetest (pull_request) Successful in 2m28s
CI / typecheck (pull_request) Successful in 2m46s
CI / node-tests (pull_request) Successful in 2m52s
CI / browser-tests (pull_request) Successful in 4m32s
Co-Authored-By: gpt-5.6-sol <noreply@openai.com>
2026-08-05 17:22:46 +00:00
codingetandClaude b07a3608e9 docs: record the statAndReaddir policy and the issues filed
CI / install (pull_request) Failing after 3m8s
CI / typecheck (pull_request) Skipped
CI / typetest (pull_request) Skipped
CI / node-tests (pull_request) Skipped
CI / browser-tests (pull_request) Skipped
CI / format (pull_request) Successful in 4m58s
CI / lint (pull_request) Successful in 5m9s
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-05 01:47:55 +00:00
codingetandClaude 016e506393 fix(vfs): let the statAndReaddir callsites fall back on unsupported
Every one of these callers needs the listing to answer the request, and the
stat plus readdir it falls back to are required operations, so there is no
reason to stop at an implementation that has the combined call but cannot serve
it for this path. The default policy stops there because for most operations a
fallback is materially different from the native one; for this one it is only
slower.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-05 01:44:37 +00:00
codingetandClaude b72ee0c9f1 test(vfs): cover the review's untested boundaries
CI / lint (pull_request) Successful in 2m38s
CI / format (pull_request) Successful in 2m43s
CI / install (pull_request) Successful in 8m55s
CI / typetest (pull_request) Successful in 2m40s
CI / typecheck (pull_request) Successful in 2m58s
CI / node-tests (pull_request) Successful in 3m9s
CI / browser-tests (pull_request) Successful in 4m52s
A stream that fails after the native call resolved is deliberately not retried,
and cancelling the fallback's output while a read is in flight must still
release the source. Both were argued from the code rather than demonstrated.
The root path overlaps every other path, so it is rejected at either end of a
copy; that followed from the guard without being stated.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-05 00:34:14 +00:00
codingetandClaude 4387ea9837 docs: record the vfs fallback work
CI / typecheck (pull_request) Canceled after 0s
CI / typetest (pull_request) Canceled after 0s
CI / node-tests (pull_request) Canceled after 0s
CI / browser-tests (pull_request) Canceled after 0s
CI / format (pull_request) Canceled after 1m58s
CI / lint (pull_request) Canceled after 2m4s
CI / install (pull_request) Canceled after 2m10s
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-05 00:13:37 +00:00
codingetandClaude cf485af184 test(vfs): cover shimmed and unsupported conformance variants
An implementation that answers unsupported at runtime was not expressible: the
capability flags only said present or absent, so the suite could not check that
such a rejection carries the right code and leaves the filesystem alone.

The fallbacks are then run against the same suite with the optional operations
first removed and then rejecting, which is what shows they can stand in for
them.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-05 00:12:56 +00:00
codingetandClaude a81437430c fix(vfs): honour empty ranges in the drive and ftp clients
Both clients failed the boundary behaviour the conformance suite now requires.
WebDAV answers a range at or past the end of a file with 416, which RFC 9110
defines as unsatisfiable and the client reported as an unexpected status; at
this level the window is simply empty. FTP carries no end bound on REST, so an
end below the start transferred from the start to the end of the file instead
of nothing.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-05 00:12:43 +00:00
codingetandClaude c8352a319e test(vfs): pin readFileRange boundary behaviour
Serving a byte range needs an empty window and an unanswerable request to be
distinguishable, so a start at or past the end of a file and an end below the
start return an empty stream rather than failing. The suite did not say so, and
NodeVFS passed both bounds to createReadStream, which rejects an inverted range
with a plain RangeError.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-04 20:00:38 +00:00
codingetandClaude 1660b1df2e refactor(vfs): share the statAndReaddir fallback
The optional operation arrived with three byte-identical copies of the same
fallback, one per call site.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-04 19:59:04 +00:00
codingetandClaude 4c17366172 feat(vfs): add fallbacks for optional operations
Callers of an optional AsyncVFS operation each had to decide what to do when
an implementation lacked it, so the same fallbacks were written repeatedly and
diverged. withFallbacks presents every optional operation as available, so a
consumer can call it without branching on method presence, and the operations
that cannot be built from the required ones say so with the unsupported code
rather than appearing absent.

The policy chooses when a fallback may stand in. Only an unsupported rejection
ever triggers one: any other failure means the operation was attempted, and
retrying it another way would hide that.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-04 19:57:24 +00:00
codingetandClaude 3270e373a0 feat(vfs): map native unsupported statuses onto the vfs code
Each client already had a native response meaning the operation does not exist
here, and each discarded it: SFTP OP_UNSUPPORTED and FTP 502 surfaced as bare
protocol errors, WebDAV 501 hit the unexpected-status branch, and SMB2
STATUS_NOT_SUPPORTED reached callers as forbidden, which claims a permission
problem the server never reported.

Without this a server emits the status and its own client throws the meaning
away, so no caller can act on it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-04 19:53:37 +00:00
codingetandClaude 9ae97a37ad feat(vfs): add an unsupported error code
Optional AsyncVFS operations could only be declared by method presence, which
is global to an instance and cannot express a backend that performs an
operation for some shares, paths or negotiated protocol versions and not
others. Those cases were forced onto forbidden, which claims the caller lacks
permission when the backend said the operation does not exist here.

The outbound wire mappings are exhaustive over VFSErrorCode, so each one names
its protocol's own unsupported response.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-04 19:50:48 +00:00
codingetandCodex 394d720aeb docs(vfs): qualify overwrite replacement
CI / lint (pull_request) Successful in 2m0s
CI / format (pull_request) Successful in 2m6s
CI / install (pull_request) Successful in 7m14s
CI / typetest (pull_request) Successful in 2m5s
CI / node-tests (pull_request) Successful in 2m33s
CI / typecheck (pull_request) Successful in 2m35s
CI / browser-tests (pull_request) Successful in 6m52s
Co-Authored-By: gpt-5.6-sol <noreply@openai.com>
2026-08-04 14:39:04 +00:00
codingetandCodex c9ff59bd9a docs(vfs): define overwrite replacement
CI / format (pull_request) Successful in 2m0s
CI / typecheck (pull_request) Canceled after 0s
CI / typetest (pull_request) Canceled after 0s
CI / node-tests (pull_request) Canceled after 0s
CI / browser-tests (pull_request) Canceled after 0s
CI / lint (pull_request) Canceled after 2m12s
CI / install (pull_request) Canceled after 2m28s
Co-Authored-By: gpt-5.6-sol <noreply@openai.com>
2026-08-04 14:36:59 +00:00
codingetandCodex e00d55d2af fix(vfs): preserve overwrite destinations
CI / lint (pull_request) Successful in 2m8s
CI / format (pull_request) Successful in 2m8s
CI / install (pull_request) Successful in 6m52s
CI / typetest (pull_request) Successful in 2m12s
CI / typecheck (pull_request) Successful in 2m43s
CI / node-tests (pull_request) Successful in 2m43s
CI / browser-tests (pull_request) Successful in 4m34s
Co-Authored-By: gpt-5.6-sol <noreply@openai.com>
2026-08-04 13:50:22 +00:00
codingetandCodex a82ddab3a2 fix(drive): preserve overwrite destination
CI / lint (pull_request) Successful in 2m8s
CI / format (pull_request) Successful in 2m11s
CI / typecheck (pull_request) Canceled after 0s
CI / typetest (pull_request) Canceled after 0s
CI / node-tests (pull_request) Canceled after 0s
CI / browser-tests (pull_request) Canceled after 0s
CI / install (pull_request) Canceled after 6m25s
Co-Authored-By: gpt-5.6-sol <noreply@openai.com>
2026-08-04 13:44:07 +00:00
codingetandCodex e1dc165e03 fix(vfs): address combined metadata review
CI / lint (pull_request) Successful in 2m34s
CI / format (pull_request) Successful in 2m37s
CI / typecheck (pull_request) Canceled after 0s
CI / typetest (pull_request) Canceled after 0s
CI / node-tests (pull_request) Canceled after 0s
CI / browser-tests (pull_request) Canceled after 0s
CI / install (pull_request) Canceled after 5m5s
Co-Authored-By: gpt-5.6-sol <noreply@openai.com>
2026-08-04 13:39:10 +00:00
codingetandCodex 93b2dff943 refactor(vfs): reuse combined directory metadata
CI / format (pull_request) Successful in 2m21s
CI / lint (pull_request) Successful in 2m35s
CI / install (pull_request) Successful in 9m0s
CI / typetest (pull_request) Successful in 2m35s
CI / node-tests (pull_request) Successful in 3m4s
CI / typecheck (pull_request) Successful in 3m13s
CI / browser-tests (pull_request) Successful in 4m46s
Co-Authored-By: gpt-5.6-sol <noreply@openai.com>
2026-08-04 00:51:42 +00:00
codingetandCodex e21ad5f24f feat(vfs): combine stat and directory listing
Co-Authored-By: gpt-5.6-sol <noreply@openai.com>
2026-08-04 00:49:30 +00:00
codingetandCodex dc613b5dfe refactor(taildrive): clarify discovery exports
CI / lint (pull_request) Successful in 2m47s
CI / format (pull_request) Successful in 3m0s
CI / install (pull_request) Successful in 9m49s
CI / typetest (pull_request) Successful in 2m41s
CI / typecheck (pull_request) Successful in 2m59s
CI / node-tests (pull_request) Successful in 3m13s
CI / browser-tests (pull_request) Successful in 5m0s
Co-Authored-By: gpt-5.6-sol <noreply@openai.com>
2026-08-03 23:08:54 +00:00
codingetandCodex a60f1b626e fix(taildrive): harden peer probes
CI / format (pull_request) Successful in 1m55s
CI / lint (pull_request) Successful in 2m4s
CI / install (pull_request) Successful in 5m52s
CI / typetest (pull_request) Successful in 2m7s
CI / node-tests (pull_request) Successful in 2m18s
CI / typecheck (pull_request) Successful in 2m20s
CI / browser-tests (pull_request) Successful in 3m47s
Co-Authored-By: gpt-5.6-sol <noreply@openai.com>
2026-08-03 01:27:54 +00:00
codingetandCodex a0ad4bf17b feat(taildrive): discover peers with shares
CI / lint (pull_request) Successful in 1m49s
CI / format (pull_request) Successful in 2m0s
CI / typecheck (pull_request) Canceled after 0s
CI / typetest (pull_request) Canceled after 0s
CI / node-tests (pull_request) Canceled after 0s
CI / browser-tests (pull_request) Canceled after 0s
CI / install (pull_request) Canceled after 5m12s
Co-Authored-By: gpt-5.6-sol <noreply@openai.com>
2026-08-03 01:23:10 +00:00
codingetandClaude ab987f50d0 test(tsconnect): assert drive peers are reported online
CI / format (pull_request) Successful in 2m25s
CI / lint (pull_request) Successful in 2m28s
CI / install (pull_request) Successful in 7m35s
CI / typetest (pull_request) Successful in 2m15s
CI / typecheck (pull_request) Successful in 2m31s
CI / node-tests (pull_request) Successful in 2m32s
CI / browser-tests (pull_request) Successful in 3m56s
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-02 01:39:46 +00:00
codingetandClaude a6b71597ca fix(tsconnect): filter offline and unreachable taildrive peers
CI / format (pull_request) Successful in 2m35s
CI / lint (pull_request) Successful in 2m37s
CI / typecheck (pull_request) Canceled after 0s
CI / typetest (pull_request) Canceled after 0s
CI / node-tests (pull_request) Canceled after 0s
CI / browser-tests (pull_request) Canceled after 0s
CI / install (pull_request) Canceled after 5m55s
listDrivePeers returned every peer holding PeerCapabilityTaildriveSharer,
which is an ACL grant usually given to a whole group or tag, so offline
peers and peers with no reachable peerAPI were included. Bump the
tailscale submodule to the fix and correct the TS doc comments, which
claimed the result was peers exposing a share.

The submodule bump also picks up the removal of the obsolete ssh, fetch
and setExitNodeEnabled wasm bridge APIs, already on the fork's webnet
branch and unused here.

Closes #143

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-02 01:34:07 +00:00
codinget f4131462af build(turbo): fix turbo cache with submodules
CI / format (pull_request) Successful in 2m39s
CI / lint (pull_request) Successful in 2m55s
CI / install (pull_request) Successful in 7m57s
CI / typetest (pull_request) Successful in 2m4s
CI / node-tests (pull_request) Successful in 2m40s
CI / typecheck (pull_request) Successful in 2m44s
CI / browser-tests (pull_request) Successful in 4m13s
2026-08-01 00:19:50 +00:00
codingetandCodex 1287baf203 ci: paginate CA updater PR discovery
CI / format (pull_request) Successful in 2m16s
CI / lint (pull_request) Successful in 2m36s
CI / install (pull_request) Successful in 9m51s
CI / typetest (pull_request) Successful in 2m40s
CI / typecheck (pull_request) Successful in 3m5s
CI / node-tests (pull_request) Successful in 3m17s
CI / browser-tests (pull_request) Successful in 4m59s
Co-Authored-By: gpt-5.6-sol <noreply@openai.com>
2026-07-31 20:52:07 +00:00
codingetandCodex 5ac5f08378 ci: schedule tsconnect CA bundle refresh
Co-Authored-By: gpt-5.6-sol <noreply@openai.com>
2026-07-31 20:52:07 +00:00
codingetandCodex d28697eeee fix(tsconnect): publish only used build outputs
CI / lint (pull_request) Successful in 2m49s
CI / format (pull_request) Successful in 2m54s
CI / install (pull_request) Successful in 7m37s
CI / typetest (pull_request) Successful in 2m2s
CI / node-tests (pull_request) Successful in 2m25s
CI / typecheck (pull_request) Successful in 2m35s
CI / browser-tests (pull_request) Successful in 3m55s
Co-Authored-By: gpt-5.6-sol <noreply@openai.com>
2026-07-31 22:35:18 +02:00
codingetandClaude 962ed20219 docs: drop the duplicated #147 disclosure entry
CI / format (pull_request) Successful in 2m43s
CI / lint (pull_request) Successful in 2m47s
CI / install (pull_request) Successful in 9m49s
CI / typecheck (pull_request) Successful in 2m46s
CI / typetest (pull_request) Successful in 2m43s
CI / node-tests (pull_request) Successful in 2m21s
CI / browser-tests (pull_request) Successful in 4m23s
The union merge driver kept both the pre- and post-rebase versions of the
tsconnect shutdown entry; the older one predates the dead-handle change.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 16:49:20 +00:00
codingetandClaude 279733fc01 test: run Go runtime shutdowns in-process again
CI / lint (pull_request) Successful in 3m58s
CI / format (pull_request) Successful in 2m55s
CI / install (pull_request) Successful in 13m11s
CI / typecheck (pull_request) Successful in 3m35s
CI / typetest (pull_request) Successful in 1m30s
CI / node-tests (pull_request) Successful in 1m34s
CI / browser-tests (pull_request) Successful in 3m59s
The child-process probes, the missing shutdown() in the tsconnect
integration teardown, and the *.probe.ts tsconfig excludes all existed
only to keep the late "Go program has already exited" errors away from
node:test. That bug is fixed, so drop the workarounds; the restored
in-process shutdowns are the regression test for it.

Refs #147, #152

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 00:39:07 +00:00
codingetandClaude 342b996828 fix(tsconnect): treat handles from an exited runtime as closed
CI / lint (pull_request) Successful in 2m34s
CI / format (pull_request) Successful in 2m34s
CI / install (pull_request) Successful in 7m2s
CI / typetest (pull_request) Successful in 1m59s
CI / node-tests (pull_request) Successful in 2m24s
CI / typecheck (pull_request) Successful in 2m31s
CI / browser-tests (pull_request) Successful in 3m53s
With _resume no longer throwing after exit, a call into a dead Go handle
resolves with undefined instead of failing — a read loop would spin on it
rather than break out. Conn, PacketConn and TCPListener now report closed
once their runtime has exited, so those calls raise ClosedError and
close() is a no-op.

Refs #147

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 00:38:58 +00:00
codingetandClaude 90f262e301 fix(tsconnect): drop the Go runtime's pending work on exit
CI / format (pull_request) Successful in 3m27s
CI / lint (pull_request) Successful in 3m27s
CI / install (pull_request) Successful in 11m17s
CI / typetest (pull_request) Successful in 2m11s
CI / node-tests (pull_request) Successful in 3m8s
CI / typecheck (pull_request) Successful in 3m24s
CI / browser-tests (pull_request) Successful in 5m56s
wasm_exec.js keeps the Go scheduler's setTimeout armed and every js.FuncOf
callback registered across program exit, and _resume throws "Go program has
already exited" when they fire. After a clean shutdown() that surfaces as
uncaught errors and unhandled rejections seconds later: a scheduler timeout,
DERP WebSocket close listeners, and a fetch continuation.

initIPN now clears the scheduled timeouts from runtime.wasmExit and makes
_resume a no-op once the runtime has exited.

Fixes #147

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-30 23:51:40 +00:00
codingetandCodex ad0d5259ab test(tsconnect-worker): isolate Go runtime shutdowns
CI / lint (pull_request) Successful in 2m24s
CI / format (pull_request) Successful in 2m32s
CI / install (pull_request) Successful in 7m2s
CI / typetest (pull_request) Successful in 1m54s
CI / node-tests (pull_request) Successful in 2m8s
CI / typecheck (pull_request) Successful in 2m19s
CI / browser-tests (pull_request) Successful in 3m37s
Co-Authored-By: gpt-5.6-sol <noreply@openai.com>
2026-07-30 00:38:22 +02:00
codinget afbc809240 chore: remove deprecated APIs: setExitNodeEnabled, ping(disco), fetch, ssh
CI / format (pull_request) Successful in 2m41s
CI / lint (pull_request) Successful in 2m46s
CI / install (pull_request) Successful in 10m45s
CI / typetest (pull_request) Successful in 2m49s
CI / typecheck (pull_request) Successful in 3m2s
CI / node-tests (pull_request) Successful in 3m10s
CI / browser-tests (pull_request) Successful in 6m13s
2026-07-29 21:38:42 +00:00
codinget 7f6a2bbc22 chore: switch to the rebased tailscale build
CI / lint (pull_request) Successful in 1m52s
CI / format (pull_request) Successful in 2m3s
CI / install (pull_request) Successful in 6m22s
CI / typetest (pull_request) Successful in 2m0s
CI / typecheck (pull_request) Successful in 2m15s
CI / node-tests (pull_request) Successful in 2m17s
CI / browser-tests (pull_request) Successful in 3m29s
2026-07-29 20:15:25 +00:00
codingetandClaude ca91c68717 docs: record tsconnect integration test coverage in AI_CHANGES
CI / format (pull_request) Successful in 1m58s
CI / lint (pull_request) Successful in 2m0s
CI / install (pull_request) Successful in 5m9s
CI / typetest (pull_request) Successful in 1m38s
CI / typecheck (pull_request) Successful in 1m54s
CI / node-tests (pull_request) Successful in 2m0s
CI / browser-tests (pull_request) Successful in 3m14s
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-28 23:51:26 +00:00
codingetandClaude fddeb7f5ba docs: document the new integration test env variables
CI / format (pull_request) Successful in 2m4s
CI / lint (pull_request) Successful in 2m9s
CI / install (pull_request) Successful in 5m28s
CI / typetest (pull_request) Successful in 1m46s
CI / node-tests (pull_request) Successful in 1m55s
CI / typecheck (pull_request) Successful in 2m5s
CI / browser-tests (pull_request) Successful in 3m19s
TSCONNECT_TEST_USER_AUTH_KEY and TSCONNECT_TEST_EXIT_NODE_ID gate the Taildrop
and exit node tests; both are optional and the suite is green without them.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-28 23:21:18 +00:00
codingetandClaude 8edecd479b test(tsconnect): cover UDP, ICMP, TLS, taildrop and shutdown against headscale
CI / lint (pull_request) Successful in 2m6s
CI / format (pull_request) Successful in 1m59s
CI / install (pull_request) Successful in 5m37s
CI / typetest (pull_request) Successful in 1m49s
CI / node-tests (pull_request) Successful in 2m4s
CI / typecheck (pull_request) Successful in 2m4s
CI / browser-tests (pull_request) Successful in 3m18s
The integration suite only exercised a fraction of IpnClient: reaching Running,
localAPI, service advertisement and a TCP listen/dial pair. Everything else was
either unit-tested against mocks (serveDrive, listDrivePeers, upgradeTls) or
only ever checked by hand.

Adds real two-node coverage for:

- UDP listen/writeTo/readFrom, both directions
- listenICMP lifecycle plus an echo request/reply round-trip
- listenTLS + dialTLS with a self-signed CA, the negative case where the cert
  does not chain to the supplied CA, and upgradeTls on a live TCP conn
- taildrop end-to-end: listFileTargets, sendFile, waitingFiles, openWaitingFile
  and deleteWaitingFile
- serveDrive/listDrivePeers over a real netmap
- suggestExitNode, and setExitNode gated on TSCONNECT_TEST_EXIT_NODE_ID

listenTLS works here because it takes the cert and key directly rather than
going through ACME, so it does not need a control plane that issues certs. Only
Funnel needs that, and it stays manual.

Taildrop requires both ends to be untagged: canPutFile wants IsSelfUntagged()
or an ACL-granted PeerCapabilityFileSharingSend, and the shared auth key
registers nodes as tag:test. Those tests run their own pair from
TSCONNECT_TEST_USER_AUTH_KEY and skip when it is unset.

shutdown() runs in a child process (src/shutdown.probe.ts): it exits the whole
Go runtime, and the runtime leaves scheduler timeouts behind that throw "Go
program has already exited" afterwards, which node:test attributes to whichever
hook created them. For the same reason the shared suite no longer calls
shutdown() in its teardown. See webnet/webnet#147.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-28 23:00:49 +00:00
codingetandClaude 0172018cc6 ci: bound the Node heap for CI jobs
CI / lint (pull_request) Successful in 1m23s
CI / format (pull_request) Successful in 1m26s
CI / install (pull_request) Successful in 4m58s
CI / typetest (pull_request) Successful in 1m40s
CI / node-tests (pull_request) Successful in 1m58s
CI / typecheck (pull_request) Successful in 1m59s
CI / browser-tests (pull_request) Successful in 3m9s
Closes #136

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-27 22:24:45 +00:00
codingetandClaude 1196469156 test(vfs): require reads to leave the file intact
The truncation this branch fixed was only caught incidentally, by the
cancellation test happening to re-read afterwards. Reading through a default
reader does not reproduce it either: the entry is detached only once a consumer
transfers the chunk's buffer, which is what postMessage to a worker does.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-27 22:58:45 +02:00
codingetandClaude 4ebe30ddc5 docs: record the conformance review round in AI_CHANGES
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-27 22:58:45 +02:00
codingetandClaude 5ca35f59ab fix: address conformance review findings
The suite gained no coverage of readFileRange's error contract when the
per-package tests for it were removed, and none of copy replacing an existing
directory, which NodeVFS merged into instead.

SMB2 copy could recurse without bound when the destination was inside the
source, and could not overwrite a destination of the other type.

The DAV client read every 405 as is-a-directory; RFC 4918 only gives it that
meaning for GET and PUT, so any other method now keeps a generic error.

The FTP server relayed the backing filesystem's prose as 550 text, leaving the
client to guess the code from wording that MemoryVFS never produced. It now
sends conventional wording per code, so the client no longer needs to accept
not-found in place of forbidden.

SFTP marks the VFS code it embeds in SSH_FX_FAILURE messages, so a third-party
server's prose can no longer be decoded as one of ours.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-27 22:58:45 +02:00
codingetandClaude 11f907cae5 docs: record the AsyncVFS conformance suite in AI_CHANGES
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-27 22:58:45 +02:00
codingetandClaude e573e22078 test(drive): run the AsyncVFS conformance suite
The suite caught four client bugs: copy() and move() defaulted to overwriting
the destination, delete() only checked for a non-empty directory when
recursive was explicitly false, readdir() of a file returned an empty listing
instead of rejecting, and setProps() merged rather than replaced.

The server reported is-a-directory as 409, which the client read back as
already-exists; RFC 4918 calls for 405 on a collection, which is what the GET
handler already returns.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-27 22:58:45 +02:00
codingetandClaude 8c6617dcb0 fix(vfs): copy file contents out of MemoryVFS on read
readFile enqueued the entry's own array, and a consumer reading through a byte
stream transfers the chunk's buffer, detaching it. Reading a file over WebDAV
and cancelling the stream truncated the stored file to zero bytes.

The conformance suite compared payloads with assert.deepEqual, so this surfaced
as node building a diff between a 128 KiB array and an empty one, which
exhausts memory and takes the process down before the failure is reported.
Compare byte ranges directly instead.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-27 22:58:45 +02:00
codingetandClaude 38a98c3155 test(smb2): run the AsyncVFS conformance suite
The suite caught two client bugs: delete() accepted the share root, and copy()
ignored opts.overwrite so it always clobbered the destination, and could not
copy directories.

The mock server also mis-called #respond on two missing-parent paths, ignored
CreateOptions.DIRECTORY_FILE / NON_DIRECTORY_FILE, and ignored ReplaceIfExists
on rename, so it never produced the errors a real server would.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-27 22:58:45 +02:00
codingetandClaude 43fbd976e8 test(sftp): run the AsyncVFS conformance suite
The suite caught two bugs. statusToVFSError guessed the VFS error code from
the request verb for SSH_FX_FAILURE replies, discarding the code that
vfsErrorToStatus embeds in the message, so most invalid-operation errors
surfaced as a bare SFTPError. Opening a directory for reading also succeeded,
deferring is-a-directory to the first READ rather than rejecting readFile().

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-27 22:58:45 +02:00
codingetandClaude e00be011f4 test(ftp): run the AsyncVFS conformance suite
The suite caught two client bugs: move() only checked for an existing
destination when { overwrite: false } was passed explicitly, so a default move
silently clobbered it, and it reported already-exists rather than
precondition-failed. replyToVFSError also discarded the server's explicit
"not a directory" / "is a directory" text, reporting not-found instead.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-27 22:58:45 +02:00
codingetandClaude 395752ad3f feat(vfs): add a reusable AsyncVFS conformance suite
Expose the suite from a test-only `@webnet/vfs/conformance` entry point and
adopt it for MemoryVFS and NodeVFS.

NodeVFS rejected neither reading a directory nor deleting the filesystem root,
both of which the suite requires; fix both.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-27 22:58:45 +02:00
codingetandCodex 8bfac8913c docs: record frontend review follow-up
CI / lint (pull_request) Successful in 1m45s
CI / format (pull_request) Successful in 1m48s
CI / install (pull_request) Successful in 5m44s
CI / typetest (pull_request) Successful in 1m36s
CI / typecheck (pull_request) Successful in 2m1s
CI / node-tests (pull_request) Successful in 2m2s
CI / browser-tests (pull_request) Successful in 3m20s
Co-Authored-By: gpt-5.6-terra <noreply@openai.com>
2026-07-26 19:06:19 +00:00
codingetandCodex f965429309 fix: preserve example HTTP route
CI / typecheck (pull_request) Canceled after 0s
CI / typetest (pull_request) Canceled after 0s
CI / node-tests (pull_request) Canceled after 0s
CI / browser-tests (pull_request) Canceled after 0s
CI / lint (pull_request) Canceled after 39s
CI / format (pull_request) Canceled after 39s
CI / install (pull_request) Canceled after 41s
Co-Authored-By: gpt-5.6-terra <noreply@openai.com>
2026-07-26 19:05:59 +00:00
codingetandCodex c34e948bd8 chore: align frontend target libraries
CI / lint (pull_request) Successful in 1m57s
CI / format (pull_request) Successful in 1m57s
CI / install (pull_request) Successful in 5m25s
CI / typetest (pull_request) Successful in 1m48s
CI / typecheck (pull_request) Successful in 2m3s
CI / node-tests (pull_request) Successful in 2m4s
CI / browser-tests (pull_request) Successful in 3m30s
Co-Authored-By: gpt-5.6-terra <noreply@openai.com>
2026-07-26 17:32:22 +00:00
codingetandCodex b252b02444 feat: typecheck frontend apps
CI / lint (pull_request) Successful in 1m52s
CI / format (pull_request) Successful in 2m5s
CI / install (pull_request) Successful in 7m31s
CI / typetest (pull_request) Successful in 1m29s
CI / node-tests (pull_request) Successful in 2m0s
CI / typecheck (pull_request) Successful in 2m1s
CI / browser-tests (pull_request) Successful in 3m21s
Co-Authored-By: gpt-5.6-terra <noreply@openai.com>
2026-07-26 17:06:21 +00:00
codingetandCodex 2cabbf50f1 test: tighten package-boundary checks
CI / format (pull_request) Successful in 1m58s
CI / lint (pull_request) Successful in 2m0s
CI / install (pull_request) Successful in 5m26s
CI / typetest (pull_request) Successful in 1m27s
CI / node-tests (pull_request) Successful in 1m48s
CI / typecheck (pull_request) Successful in 1m49s
CI / browser-tests (pull_request) Successful in 3m10s
Co-Authored-By: gpt-5.6-sol <noreply@openai.com>
2026-07-26 15:50:36 +00:00
codingetandCodex 668ae52dd0 fix: anchor package linting to repository
CI / lint (pull_request) Successful in 2m2s
CI / format (pull_request) Successful in 2m3s
CI / install (pull_request) Successful in 5m23s
CI / typecheck (pull_request) Successful in 1m26s
CI / typetest (pull_request) Successful in 1m38s
CI / node-tests (pull_request) Successful in 1m48s
CI / browser-tests (pull_request) Successful in 3m11s
Co-Authored-By: gpt-5.6-sol <noreply@openai.com>
2026-07-26 14:19:42 +00:00
codingetandCodex df27509467 fix: normalize relative package escapes
CI / format (pull_request) Successful in 1m47s
CI / lint (pull_request) Successful in 1m50s
CI / typecheck (pull_request) Canceled after 0s
CI / typetest (pull_request) Canceled after 0s
CI / node-tests (pull_request) Canceled after 0s
CI / browser-tests (pull_request) Canceled after 0s
CI / install (pull_request) Canceled after 2m20s
Co-Authored-By: gpt-5.6-sol <noreply@openai.com>
2026-07-26 14:17:33 +00:00
codingetandCodex 8451205d62 refactor: address package-boundary review
CI / format (pull_request) Successful in 1m38s
CI / lint (pull_request) Successful in 1m39s
CI / typecheck (pull_request) Canceled after 0s
CI / typetest (pull_request) Canceled after 0s
CI / node-tests (pull_request) Canceled after 0s
CI / browser-tests (pull_request) Canceled after 0s
CI / install (pull_request) Canceled after 4m25s
Co-Authored-By: gpt-5.6-sol <noreply@openai.com>
2026-07-26 14:13:21 +00:00
codingetandCodex 9ca6a76b5a test: keep shared coverage with its owner
Co-Authored-By: gpt-5.6-sol <noreply@openai.com>
2026-07-26 14:13:21 +00:00
codingetandCodex 717eeb997a fix: close package-boundary lint gaps
Co-Authored-By: gpt-5.6-sol <noreply@openai.com>
2026-07-26 14:13:21 +00:00
codingetandCodex d79b322871 test: preserve auth failure cleanup coverage
Co-Authored-By: gpt-5.6-sol <noreply@openai.com>
2026-07-26 14:13:21 +00:00
codingetandCodex 997f4849ab refactor: enforce package boundaries
CI / lint (pull_request) Successful in 1m42s
CI / format (pull_request) Successful in 1m45s
CI / install (pull_request) Successful in 5m12s
CI / typetest (pull_request) Successful in 1m27s
CI / node-tests (pull_request) Failing after 1m44s
CI / typecheck (pull_request) Successful in 1m52s
CI / browser-tests (pull_request) Successful in 3m7s
Co-Authored-By: gpt-5.6-sol <noreply@openai.com>
2026-07-26 13:12:56 +00:00
codingetandCodex 325edd94c1 fix(tsconnect-react): report initialization errors
CI / format (pull_request) Successful in 1m35s
CI / install (pull_request) Successful in 5m5s
CI / typetest (pull_request) Successful in 1m32s
CI / typecheck (pull_request) Successful in 1m44s
CI / node-tests (pull_request) Successful in 1m45s
CI / browser-tests (pull_request) Successful in 2m57s
CI / lint (pull_request) Successful in 1m34s
Co-Authored-By: gpt-5.6-sol <noreply@openai.com>
2026-07-26 01:04:34 +00:00
codinget 4515794d1a build(tailshare): fix wrong regex for css modules in webpack config
CI / typetest (pull_request) Successful in 1m24s
CI / format (pull_request) Successful in 1m27s
CI / typecheck (pull_request) Successful in 1m48s
CI / lint (pull_request) Successful in 1m36s
CI / install (pull_request) Successful in 4m59s
CI / node-tests (pull_request) Successful in 1m50s
CI / browser-tests (pull_request) Successful in 3m11s
2026-07-26 00:44:50 +00:00
codingetandCodex 3450e0f51b test(tsconnect-worker): remove async timing races
CI / typetest (pull_request) Successful in 1m34s
CI / typecheck (pull_request) Successful in 1m53s
CI / node-tests (pull_request) Successful in 1m54s
CI / browser-tests (pull_request) Successful in 3m9s
CI / format (pull_request) Successful in 1m35s
CI / lint (pull_request) Successful in 1m35s
CI / install (pull_request) Successful in 5m0s
Co-Authored-By: gpt-5.6-sol <noreply@openai.com>
2026-07-26 00:11:52 +00:00
codingetandClaude fffaba733c fix(tailshare): address second-round review findings
CI / install (pull_request) Successful in 5m2s
CI / typetest (pull_request) Successful in 1m18s
CI / typecheck (pull_request) Successful in 1m23s
CI / node-tests (pull_request) Successful in 1m30s
CI / browser-tests (pull_request) Successful in 2m45s
CI / format (pull_request) Successful in 1m35s
CI / lint (pull_request) Successful in 1m39s
- Freeze build-time options: configAtBuildRef is now snapshotted before
  useWorker/workerConfig/builderParams are derived, so toggling SharedWorker
  or fileOps after startup no longer switches the active connection path or
  silently restarts the main-thread IPN mid-session.
- Include useWorker and fileOps in requireRestart so changes to those fields
  show the restart prompt.
- Cross-tab restart guidance: when connectionMode is "worker", the restart
  line now tells the user to close all Tailshare tabs rather than just
  reloading the current one, since the SharedWorker ignores config from
  tabs that join after it has already started.
- Use workerClient.connectionMode (the actual mode from connectWithFallback,
  which reflects the fallback path) rather than re-deriving it from flags.
- Remove the duplicate AI_CHANGES.md entry for the SharedWorker PR.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-25 23:11:54 +00:00
codingetandClaude 6c0b8ec3b9 fix(tailshare): move configAtBuildRef to IpnProvider level
CI / typetest (pull_request) Successful in 1m32s
CI / typecheck (pull_request) Successful in 1m39s
CI / node-tests (pull_request) Successful in 1m41s
CI / browser-tests (pull_request) Successful in 3m4s
CI / lint (pull_request) Successful in 1m34s
CI / format (pull_request) Successful in 1m34s
CI / install (pull_request) Successful in 4m58s
configAtBuildRef was local to TailscaleConfig, so navigating away from
/tailscale and back would remount the component and reset the ref, making
the restart-required banner disappear even if the config had changed.

Moving configAtBuild into IpnPrepareContext keeps it alive for the
lifetime of the provider.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-25 22:26:58 +00:00
codingetandClaude b1c9c2eac6 chore: retrigger CI
CI / typecheck (pull_request) Successful in 1m36s
CI / node-tests (pull_request) Successful in 1m37s
CI / lint (pull_request) Successful in 1m28s
CI / format (pull_request) Successful in 1m28s
CI / browser-tests (pull_request) Successful in 2m54s
CI / install (pull_request) Successful in 4m51s
CI / typetest (pull_request) Successful in 1m25s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-25 21:34:56 +00:00
codingetandClaude 65a724516b chore: update AI_CHANGES.md and PR description
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-25 21:34:56 +00:00
codingetandClaude 03fc837428 refactor(tailshare): address feedback
- Extract useSharedWorkerAvailable() into @webnet/react
- Add explicit defaults (useWorker: true, fileOps: "memory") in parseConfig
  so consumers don't repeat them
- Restore markAutostart tri-state: undefined → latches to true on first
  Running, false → never autostart, true → autostart

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-25 21:34:56 +00:00
codingetandClaude dc415e78d5 fix(tsconnect-react): add skipLibCheck to fix @types/eslint-scope conflict
Same fix as @webnet/react: @types/eslint-scope and eslint built-in types
have conflicting Scope definitions that break tsc --noEmit.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-25 21:34:56 +00:00
codingetandClaude 794cdf074d fix(tailshare): address code review findings
- Remove markAutostart: autostart is now only triggered by the explicit
  checkbox, not silently latched on first connect
- Show spinner instead of disabled "Enable Tailscale" button while worker
  is connecting (ipn is null but willBuild is true)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-25 21:34:56 +00:00
codingetandClaude e82e8e272f fix: ci lock file, review findings, and tsconnect-react overload
- Restore missing turbo platform packages in package-lock.json (darwin-64,
  darwin-arm64, linux-arm64, windows-64, windows-arm64) so npm ci passes
- Fix building state in TailscaleStateButton to not require NoState
- Disable OPFS option in NativeSelect when SharedWorker unavailable
- Fix "Adresses" typo → "Addresses"
- Extend useBuildIpnWorker first overload to accept null (implementation
  already handles it; fixes TS error in IpnContext.tsx)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-25 21:34:56 +00:00
codingetandClaude f9e00909cc feat(tailshare): shared worker support, config consolidation, and file ops UI
- use @webnet/tsconnect-worker (useBuildIpnWorker) so multiple tabs share one
  IPN instance via SharedWorker, with automatic fallback to main-thread mode
- replace IPN concrete class with IpnClient interface throughout; context
  now provides IpnClientHandle (extends IpnClient) from the worker hook
- remove ipnPrepare Redux slice; replace with IpnPrepareContext reading/writing
  a single tailshare:config localStorage key as JSON (consolidates the former
  ipn#hostname, ipn#controlURL, ipn#authKey, ipn#exitNode, ipn#auto keys plus
  new useWorker and fileOps fields)
- add fileOps NativeSelect in config modal (memory/opfs); OPFS passes
  fileOps:true to WorkerConfig so received files persist across reloads; shows
  a warning if OPFS is selected without worker mode active
- add SharedWorker toggle in config modal (when available); current connection
  mode (worker/main-thread) displayed inline
- remove alreadyRunning multi-tab lock path (SharedWorker handles coordination)
- main-thread fallback uses InMemoryState to avoid cross-tab state conflicts
- workerAvailable detection uses useSyncExternalStore to avoid SSR mismatch
- delete local hooks/useLocalStorage.ts and hooks/useClient.ts, replaced by
  @webnet/react (added as dependency)
- CaCertProvider simplified to fetch on mount (no longer depends on ipnPrepare)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-25 21:34:56 +00:00
codinget 84437ed033 chore(docs): add notice of AI review of the tailshare implementation
CI / format (pull_request) Successful in 1m37s
CI / lint (pull_request) Successful in 1m40s
CI / install (pull_request) Successful in 4m57s
CI / typetest (pull_request) Successful in 1m42s
CI / typecheck (pull_request) Successful in 1m57s
CI / node-tests (pull_request) Successful in 1m58s
CI / browser-tests (pull_request) Successful in 3m19s
2026-07-25 20:58:34 +00:00
codinget a291b371f6 chore(tailshare): fix lint and typescript issues
CI / install (pull_request) Canceled after 3m2s
CI / lint (pull_request) Successful in 1m30s
CI / format (pull_request) Successful in 1m30s
CI / typecheck (pull_request) Canceled after 0s
CI / typetest (pull_request) Canceled after 0s
CI / node-tests (pull_request) Canceled after 0s
CI / browser-tests (pull_request) Canceled after 0s
2026-07-25 20:53:08 +00:00
codinget 120baafa82 chore(tailshare): remove unused deps 2026-07-25 20:43:07 +00:00
codinget dd9055276c fix(tailshare): properly use stableNodeID for exit nodes, not nodeKey
CI / lint (pull_request) Successful in 1m38s
CI / format (pull_request) Successful in 1m41s
CI / install (pull_request) Successful in 4m57s
CI / typetest (pull_request) Successful in 1m27s
CI / typecheck (pull_request) Successful in 1m40s
CI / node-tests (pull_request) Successful in 1m43s
CI / browser-tests (pull_request) Successful in 2m55s
2026-07-25 20:41:45 +00:00
codinget 21e1b4b666 refactor(tailshare): remove duplicate code that got extracted to other packages
CI / lint (pull_request) Successful in 1m27s
CI / format (pull_request) Successful in 1m29s
CI / typecheck (pull_request) Canceled after 0s
CI / typetest (pull_request) Canceled after 0s
CI / node-tests (pull_request) Canceled after 0s
CI / browser-tests (pull_request) Canceled after 0s
CI / install (pull_request) Canceled after 2m31s
2026-07-25 20:39:29 +00:00
codinget 5232539476 chore: fix npm package-lock deps for npm 11
CI / lint (pull_request) Successful in 1m15s
CI / format (pull_request) Successful in 1m24s
CI / install (pull_request) Successful in 4m38s
CI / typecheck (pull_request) Successful in 1m14s
CI / typetest (pull_request) Successful in 1m21s
CI / node-tests (pull_request) Successful in 1m28s
CI / browser-tests (pull_request) Successful in 2m46s
2026-07-25 17:57:04 +00:00
codinget 0a79ffdc2a fix(tailshare): streaming downloads where available 2026-07-25 17:56:02 +00:00
codinget 1caa24239b fix(tailshare): non-SI units 2026-07-25 17:56:02 +00:00
codinget f885ec50cd fix(tailshare): wrong key for OutgoingFileNotifier 2026-07-25 17:56:02 +00:00
codinget 9101ee93bd chore(tailshare): add todo 2026-07-25 17:56:02 +00:00
codinget 52dd50c4b8 feat(tailshare): remove taildrop setting (always enable), use real asterisks and flag authKey warn 2026-07-25 17:56:02 +00:00
codinget 3dbd09a2c4 fix(tailshare): drawer shouldn't default to open 2026-07-25 17:56:02 +00:00
codinget f0ef99c635 chore(tailshare): remove useless comments 2026-07-25 17:56:02 +00:00
codinget 77b6686e0d fix(tailshare): potential crash if a peer disconnects in the middle of a transfer 2026-07-25 17:56:02 +00:00
codinget 2f5290797e chore(tailshare): remove dead code 2026-07-25 17:56:02 +00:00
codinget 5c968c281e fix(tailshare): use oncancel 2026-07-25 17:56:02 +00:00
codinget 8d3aa16e23 fix(tailshare): fix broken import 2026-07-25 17:56:02 +00:00
codinget 5a94656194 fix(tailshare): use correct version of sendFile() after streaming switch 2026-07-25 17:56:02 +00:00
codinget 80ec3460f1 feat(tailshare): show tailnet and send files via taildrop 2026-07-25 17:56:02 +00:00
codinget f09ca3285d feat(tailshare): download incoming taildrop files 2026-07-25 17:56:02 +00:00
codinget 49580e3f48 feat(tailshare): block multiple instances across tabs 2026-07-25 17:56:02 +00:00
codinget 167387f81e feat(tailshare): incoming taildrop file notifier 2026-07-25 17:56:02 +00:00
codinget af264ff3fd fix(tailshare): use a useSyncExternalStore base impl of useLocalStorage 2026-07-25 17:56:02 +00:00
codinget e14b168d88 feat(tailshare): save exit node to localStorage 2026-07-25 17:56:02 +00:00
codinget a9d588d7fa feat(tailshare): save tailscale settings to localStorage 2026-07-25 17:56:02 +00:00
codinget 8dfb67c25d feat(tailshare): continue implementing tailscale integration 2026-07-25 17:56:02 +00:00