Repository navigation
v1.9.0: land #60, #84 and #87, fix the audit job, move to Go 1.26.9 - #93
Merged
Merged
Conversation
…d openUDPPaths wrapper These three branches were the only ones of the 32 that could not be merged directly: each conflicted in exactly one file against a sibling that had already landed, and the branches live on a fork this session cannot push to. Resolutions, each verified rather than guessed: - #87 (reverse QUIC DNS bounded by the dial timeout) conflicted with #71 in internal/utils/network/quic.go, in the import block alone. Both sides' imports are kept: strings, sync and sync/atomic. - #84 (TCP proxy setup bounded, retry backoff cancellable) conflicted with #66 in internal/utils/network/tcp_dialer.go. main's copy of that file was byte-identical to #66's, so #66 was its only other author, and #84's version is a superset of it: it keeps #66's cancellable retry timer and AfterFunc guard and adds the per-attempt deadline. #84's version is taken whole. - #60 (direct tunnel recovery) conflicted with #85 in internal/tunnel/limits/limits.go. main's copy is byte-identical to #85's, which already carries #60's ctx/cancel/Close plus the pacingDeadline work, so it supersedes #60's change to that file and is kept. #60's other ten files apply unchanged. Separately, #76 kept openUDPPaths as a context.Background() shim after moving its only caller to openUDPPathsContext. Nothing calls it, so staticcheck fails the audit job with U1000 — red on main since #76 landed, and clean on main before it. The shim is removed and its explanation moved onto the function that does the work. Verified on the result: gofmt clean, go build ./... and go vet ./... pass, staticcheck clean, and go test ./... plus go test -race ./... match main's baseline (internal/socks fails only because this container has no IPv6, which fails on plain main too). Co-authored-by: dr-hoseyn <95236112+dr-hoseyn@users.noreply.github.com> Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019wBoD8a2hetHgBWnfsRAuY
Bump the embedded version and regenerate VERSION so the cross-build job's guard matches, and record the release. The changelog entry covers everything in this release: the managed Naive HTTP/2, XHTTP/TLS and REALITY/Vision carriers with their paired setup links and the Connection Test that now proves a cover through the real helper pair; the hot-path allocation work on the Noise record layer, the L3 authenticated header, FEC scratch and the WebSocket relay, and traffic accounting that no longer takes a global lock; and the lifecycle fixes — DNS bounded by the dial timeout on every dial path, cancellation that is actually honoured, listeners and carriers that come back without a service restart, and UDP flows that expire when they should and not while they are still in use. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019wBoD8a2hetHgBWnfsRAuY
TestEveryDocumentSaysWhenItWasVerified requires every docs/*.md to name the running version, so bumping VERSION without this fails internal/app. It is the step the v1.8.5 release took in the same way (1bf6055 moved all of them at once). config-reference.md is generated, so it was regenerated with go run ./tools/configref rather than edited: only its stamp line changed, which confirms the body was already in sync with config/. Historical mentions of v1.8.5 — ADR status lines, troubleshooting notes about older builds, performance notes on what shipped then — are facts about that release and are left alone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019wBoD8a2hetHgBWnfsRAuY
… vulnerabilities Every CI run on main since the merges has failed one step, govulncheck in the audit job, and only that step: build, vet, the race suite, coverage and its floors, fuzzing, the soak, the cross-build and wire compatibility were green throughout. The scan finds twelve vulnerabilities in the Go 1.26.6 standard library that this code reaches — eight in net/http, and one each in net/textproto and crypto/tls, and two in html/template — all fixed in 1.26.9. The reachable paths run through the web panel, share-link decoding, the updater's checksum fetch and the Telegram client. golang.org/x/net moves from v0.59.0 to v0.60.0 too. Five of the same advisories name it; the scan reports its copy as imported but not called, so it was not failing the job, but it is the same fix and leaving it would keep the scan noisy. Static analysis sits after govulncheck in that job and has been skipped on every one of those runs. It would have failed as well once #76 landed, on the unused openUDPPaths wrapper removed earlier on this branch. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019wBoD8a2hetHgBWnfsRAuY
go.mod moved to 1.26.9, and the installer refuses to download a toolchain whose version does not match the checksums it carries, so the build-from-source fallback would have stopped on every machine that lacks a new enough Go. docs/releasing.md lists this pairing as a release step. The five values come from https://go.dev/dl/?mode=json&include=all, the source the table already cites, and were checked against it two ways: the same document's 1.26.6 entries match the values this replaces exactly, and each 1.26.9 archive was downloaded from dl.google.com and hashed locally, matching all five. The installer's own download_go then fetched and verified the amd64 and arm64 archives with the new table, and refused the amd64 archive when its checksum was altered. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019wBoD8a2hetHgBWnfsRAuY
CI run #316 failed the race step on main with every udp case of TestAKharejMadeFromTheSetupLinkCarriesTraffic — fifteen cases, three attempts each — saying the tunnel never carried traffic. In each, the Iran engine could not bind the UDP port it had been given ("something else on THIS server is already listening there"), for the tunnel port and in one case the forwarded port as well, while the kharej's pool dials to it were refused.
freeTestPort picked those ports by binding tcp 127.0.0.1:0, reading the number and closing it. That hands out ports from inside the kernel's ephemeral range and checks TCP only, though its own comment said the udp transport needs the number free on UDP too. Twenty-four pairs run at once, and every udp kharej keeps opening outgoing sockets whose source ports come from that same range, re-dialling every few seconds once a dial is refused; one of them can take a port the Iran engine has not bound yet. internal/testport documents this exact failure — "the udp tunnel never came up", seen only under -race and on CI — and was written after the same helper failed the same way in the end-to-end harness and the direct and layer-3 tests. This was the one copy left.
It did not reproduce at that commit run on its own here (the udp cases, twice, under -race), which fits: it needs the port churn of a full parallel run. internal/manage passes under -race with this change.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019wBoD8a2hetHgBWnfsRAuY
#92 lands in this release after this branch, so its user-visible change belongs in the same section. The toolchain move is what a user of the shipped binaries actually gets from fb1695a and 57eb3d7, so it gets a Security section, as v1.8.5's release did. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019wBoD8a2hetHgBWnfsRAuY
CI run #326 failed TestTunnelStatsCountTraffic: "dialler sent 0 packets / 0 bytes, want 1 / 24", for a packet the listener had already delivered. The dialler counts a packet only once the carrier has accepted the write — it must not count one that failed — so the far end can authenticate, decrypt and hand the packet on before the sending goroutine runs its two atomic adds. The test read the counter the moment the packet arrived. The listener side is not affected: it counts before handing the packet on, deliberately, so its figures are settled when across returns. And a lagging reading is this package's own contract — TestStatsAreNeverInternallyImpossible says a reading is allowed to be behind, only never impossible. Reproduced with twelve busy loops competing for four cores: 168 of 2000 runs failed with the CI message; 0 of 1000 failed without the contention. With this change the same stressed run passes 2000 of 2000. The test still requires exactly one packet and 24 bytes; it only waits, up to five seconds, for the dialler to have counted it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019wBoD8a2hetHgBWnfsRAuY
This was referenced Oct 9, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Finishes the v1.9.0 merge. 26 of the 32 open pull requests went onto
maindirectly; this carries the rest and turns CI green.Why CI on
mainis redEvery push run since the merges failed Known vulnerabilities (govulncheck). Build, vet, the race suite, coverage and all 14 floors, fuzzing, the soak, the cross-build and wire compatibility were green on 25 of those 26 runs.
The scan finds 12 vulnerabilities in the Go 1.26.6 standard library that this code reaches —
net/http×8,net/textproto,crypto/tls,html/template×2 — all fixed in 1.26.9. The paths run through the web panel, share-link decoding, the updater's checksum fetch and the Telegram client.Static analysis sits after it in the same job and was skipped on every one of those runs. It would have failed too: #76 left an unused
openUDPPathswrapper (staticcheck U1000).The 26th run, #316, also failed the race step: every udp case of
TestAKharejMadeFromTheSetupLinkCarriesTraffic. And this PR's own run #326 failedTestTunnelStatsCountTraffic. Both are test races with a found cause; see below.The
pull_requestruns on the contributor PRs (#282–#295) show as failed but have 0 jobs — they were fork workflows waiting for approval and were marked failed when the PRs closed. Nothing ran.What this changes
3d55ae0— #60, #84 and #87, and the #76 fix. These three were the only PRs that could not merge directly: each conflicted in exactly one file with a sibling that had already landed, and their branches are on a fork. Each resolution was checked rather than chosen:internal/utils/network/quic.gostrings,sync,sync/atomicinternal/utils/network/tcp_dialer.gomain's copy was byte-identical to #66's, so #66 was its only other author, and #84's version is a superset of it: it keeps #66's cancellable retry timer andAfterFuncguard and adds the per-attempt deadlineinternal/tunnel/limits/limits.gomain's versionmain's copy is byte-identical to #85's, which already has #60'sctx/cancel/Closeplus thepacingDeadlinework. #60's other ten files apply unchanged8c176e2—release: v1.9.0.internal/app.VersionandVERSION(viamake version), and a CHANGELOG section covering all 32 PRs;9305ca5adds #92 to it and a Security section for the toolchain move.1ac1366— docs stamps to v1.9.0.TestEveryDocumentSaysWhenItWasVerifiedrequires everydocs/*.mdto name the running version, so the bump alone failsinternal/app. The v1.8.5 release did the same (1bf6055).config-reference.mdis generated, so it was regenerated withgo run ./tools/configref; only its stamp line changed. Historical mentions of v1.8.5 are left alone.fb1695a— Go 1.26.9, andgolang.org/x/netv0.59.0 → v0.60.0 for the same advisories.57eb3d7—install.shpins Go 1.26.9 and its checksums. The installer refuses a toolchain whose version does not match the checksums it carries;docs/releasing.mdlists the pairing as a release step. The five values come fromhttps://go.dev/dl/?mode=json&include=all, and were checked two ways: that document's 1.26.6 entries match the values replaced exactly, and each 1.26.9 archive was downloaded fromdl.google.comand hashed locally, matching all five. The installer's owndownload_gothen fetched and verified the amd64 and arm64 archives with the new table, and refused the amd64 archive when its checksum was altered.41ceb73— setup-link test ports frominternal/testport(run #316).freeTestPortboundtcp 127.0.0.1:0, read the number and closed it: a port from inside the kernel's ephemeral range, checked on TCP only, though its own comment said the udp transport needs it free on UDP too. Twenty-four pairs run at once and every udp kharej keeps opening outgoing sockets whose source ports come from that range; one can take a port the Iran engine has not bound yet.internal/testportdocuments this exact failure ("the udp tunnel never came up", only under-raceand on CI) after the same helper failed the same way in three other packages. This was the last copy.713fc22—TestTunnelStatsCountTrafficwaits for the dialler's count (run #326). The dialler counts a packet only after the carrier accepts the write, so the far end can deliver it first; the test read the counter the instant the packet arrived. Reproduced with twelve busy loops on four cores: 168 of 2000 runs failed with the CI message, and 0 of 2000 with the change. It still requires exactly one packet and 24 bytes. The listener side already counts before delivering, andTestStatsAreNeverInternallyImpossiblestates the contract this relies on: a reading may lag, never be impossible.Verification
govulncheck, reproduced and fixed — the same binary under each toolchain, since govulncheck scans the standard library of the Go that runs it and CI installs exactly
go.mod's:main@ 88b2900Your code is affected by 12 vulnerabilities from the Go standard library, exit 3No vulnerabilities found, exit 0CI runs #323 and #324 on this branch were green across all seven jobs, govulncheck and staticcheck included.
Locally with Go 1.26.9:
gofmt,go vet,staticcheckclean; VERSION gate andgo.mod=GO_VERSION=GO_SHA_VERSION= 1.26.9;go testand-racepass (barinternal/socks, which needs IPv6 the test container lacks and fails identically onmainthere); coverage 55.0% with all 14 floors met; all 8 fuzz targets;make release.Before tagging v1.9.0
From
docs/releasing.md: L3 carriers on a real TUN against the previous release (BP_L3_LIVE=1 … TestL3CarriersOverARealTUN), which CI cannot do.Credit
dr-hoseyn wrote #60, #84 and #87 and is co-author on
3d55ae0. The 22 squash-merged PRs onmainare authored by them directly.🤖 Generated with Claude Code
https://claude.ai/code/session_019wBoD8a2hetHgBWnfsRAuY