feat(hosted): golang hosted redirect via fork-replace + committed go.sum pin (free tier) - #173
Open
Mikola Lysenko (mikolalysenko) wants to merge 5 commits into
Open
feat(hosted): golang hosted redirect via fork-replace + committed go.sum pin (free tier)#173Mikola Lysenko (mikolalysenko) wants to merge 5 commits into
Mikola Lysenko (mikolalysenko) wants to merge 5 commits into
Conversation
…sum pin (free tier) Overturns docs/design/golang-hosted-no-go.md for the free tier. The committable shape: a fork-style `replace <mod> <ver> => patch.socket.dev/gopatch/<uuid> <ver>-socketpatch.<n>` in go.mod plus the socket module's two h1: lines in go.sum (and the replaced original's lines pruned — the tidy-stable state). Day-2 machines need zero configuration: go consults the checksum database only for modules absent from go.sum, so the committed pair is the entire redirect — validated empirically per docs/design/golang-hosted.md and pinned by e2e_golang_hosted_build.rs (fresh caches, bogus-GOSUMDB tripwire, tidy byte-level no-op, tampered-hash SECURITY ERROR). - redirect/mod.rs: real rewrite_golang, gated per-dep on registry_override.kind == "goproxy"; fails closed (redirect_golang_* warnings, no partial writes) on missing hashes, out-of-namespace module path, require-version mismatch, or user replace conflict; references without the override keep redirect_golang_unsupported (paid tier). Schema: Integrity.goModH1 + identifiers.goModuleVersion (additive). - go_mod_edit.rs: ReplaceOwner::Hosted (RHS-prefix ownership under patch.socket.dev/gopatch/), module-target parse + hosted upsert with cross-owner in-place takeover; appended directives now gofmt-shaped (blank line before the stanza) so tidy stays a no-op. - go_sum_edit.rs (new): pure sorted upsert / exact-version prune / prefix removal; pruned lines ride the ledger `original` for revert. - scan/hosted.rs: go.mod + go.sum join REDIRECT_CANDIDATE_FILES; the redirected-confirmation matcher accepts the socket module path (a Go rewrite contains no artifact/index URL). - Golden fixture golang/gomod/basic pins the byte contract the depscan TS twin must match; prod e2e golang leg becomes a both-worlds shape guard. - docs: design doc (incl. depscan server requirements: gopatch artifact flavor + token-free GOPROXY routes + go-get meta + write-once invariant), ecosystems matrix, README, CLI contract, changelog. Server-side publication is the remaining half: production publishes no golang hosted modules yet, so CLI behavior is unchanged until depscan ships the goproxy override. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…rust per adversarial review Nine confirmed findings from the post-implementation review, all fixed: - Local apply now FAILS CLOSED on a Hosted-owned replace instead of silently taking it over (which would strand pruned go.sum lines and break every -mod=readonly build once the directive is later dropped); the error names the way out. Hosted->local stays one-way by design — hosted-takes-over-local reconciles go.sum in the same pass. - apply --check no longer reports MissingReplace drift for a module a hosted redirect legitimately took over (exemption now matches Vendor AND Hosted owners). - vendor takeover of a hosted replace is visible: the wiring record captures the hosted directive text as `original` (action Rewritten), a vendor_takeover warning explains the go.sum implications up front, and vendor --revert detects the hosted prior from the namespace prefix in `original` and names the recovery (`scan --mode hosted` re-run or `go mod tidy`). - Injection guard: every server-controlled token written into the line-oriented go.mod/go.sum (module paths, versions) is refused on whitespace/control characters (redirect_golang_unsafe_coords), and h1 hashes must be exactly `h1:` + 44-char base64. - A committed-but-inert socket pin (require bumped past the patch version) is now reconciled away — directive and its go.sum lines removed with `removed` edits — so the stale module path can no longer confirm the dep as redirected (ledger + VEX) while go links the unpatched version. - Hosted takeover of an existing socket directive records the replaced text in the ledger `original` with action `updated` (was: added, original None — the pre-redirect state was lost to revert). - e2e hardening: GOENV=off (empty env-var pins don't defeat `go env -w` config), a chmod-on-drop guard so read-only GOMODCACHE trees don't leak from the tempdir on assertion failure, and a positive tripwire control proving the bogus GOSUMDB actually fires when consulted (a missing go.sum line must fail with the invalid verifier error). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…to integrity The server carries the hosted-Go hash pair (goZipDirhashH1 + goModH1) on the goproxy override's IDENTIFIERS, not the tarball artifact's integrity — the tarball dirhashH1 must stay the original-path flavor that released CLIs verify vendor-mode downloads against (the h1 dirhash covers entry names, so the two flavors hash differently despite identical contents). scan --mode hosted now merges the pair into the normalized DepOverride.integrity (both-or-neither: a half pair still trips the rewriter's fail-closed integrity check), mirroring the yarnBerry10c0 merge. Schema: identifiers gain goZipDirhashH1/goModH1 (additive). Design doc's server-requirements section updated to the final contract. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Mikola Lysenko (mikolalysenko)
force-pushed
the
feat/golang-hosted-redirect
branch
from
August 13, 2026 23:42
0b18bd4 to
97060f6
Compare
The tidy-stable append writes ONE blank stanza separator before its single-line directive; removal left that blank behind, so a vendor/apply -> revert round-trip no longer restored go.mod byte-identical (caught by e2e_vendor_golang_build in CI — a suite the local sweep after the append change missed). Removal now drops the preceding blank when it is not separating the directive from real user content below (next line EOF or blank); a blank guarding user content survives. Byte-identical round-trip pinned for both the path-target and hosted module-target flavors, plus the user-content-below case. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Mikola Lysenko (mikolalysenko)
enabled auto-merge (squash)
August 14, 2026 14:31
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.
What
Makes
scan --mode hostedwork for Go (free tier), overturningdocs/design/golang-hosted-no-go.md. The committable shape:The load-bearing fact (validated empirically against go 1.26, with a bogus-
GOSUMDBtripwire): go consults the checksum database only for modules absent fromgo.sum— committed hashes are the committable per-module sumdb exemption the no-go doc said Go lacked. A grant-free, content-addressed module path keeps the build-once converter (one artifact per patch), and free-tier publicity makesproxy.golang.orgcaching a robustness win instead of a leak. Paid tier stays vendored, as documented (docs/design/golang-hosted.mdcarries the full validated-claims list and the server requirements).Changes
rewrite_golangis now a real rewriter (was: unconditionalredirect_golang_unsupported). Activates per-dep onregistry_override.kind == "goproxy"; fails closed (per-depredirect_golang_*warnings, zero partial writes) on missing/malformed hashes (h1:+ 44-char base64), a module path outsidepatch.socket.dev/gopatch/, whitespace/control chars in any token (go.mod/go.sum injection guard), a require-version mismatch (a version-pinned replace the graph doesn't select is SILENTLY inert — validated), or a user-authored replace conflict. References without the override keep the historical warning, so behavior is unchanged until the server publishes hosted Go modules (production has zero golang patches today; server half: SocketDev/depscan#24218).go_mod_edit:ReplaceOwner::Hosted(ownership = RHS module path under the socket namespace), module-target replace parsing/upsert, in-place cross-owner takeover, gofmt-shaped appends (tidy-stable). New purego_sum_edit: sorted upsert of the flavor's two lines + prune of the replaced original's lines (removed lines ride the ledgeroriginal).applyrefuses a Hosted-owned replace (taking it over would strand the pruned go.sum lines and brick later builds);apply --checkexempts hosted-owned modules from MissingReplace drift; vendor takeover of a hosted replace is ledger-recorded and revert warns to re-runscan --mode hostedorgo mod tidy; a committed-but-inert stale pin is reconciled away so it can't false-confirm ledger/VEX.scan --mode hostedwiring:go.mod/go.sumjoin the candidate files, the redirected-confirmation matcher accepts the socket module path (a Go rewrite contains no URL), and the server's identifiers-borne hash pair (goZipDirhashH1/goModH1) merges into the normalized integrity — the tarball'sdirhashH1stays the original-path flavor that released vendor-mode CLIs verify against.Testing
e2e_golang_hosted_build.rs: a fresh "day-2 machine" (empty caches, default-mod=readonly, bogusGOSUMDBthat fails loudly if ever consulted, hostile ambient env baked in as RED guards) builds the PATCHED module from the committedgo.mod+go.sumalone;go mod tidyis a byte-level no-op; a tampered hash dies with go's checksumSECURITY ERROR; a positive tripwire control proves the bogus sumdb actually fires when a line is missing.golang/gomod/basicpins the byte contract the depscan TS twin matches (its inline copy already passes there); ~30 unit tests across the editors/rewriter; full suites green.🤖 Generated with Claude Code
Note
Medium Risk
Changes committed
go.mod/go.sumand cross-mode Go replace ownership; mistakes could brick-mod=readonlybuilds or strand go.sum state, though fail-closed checks and extensive tests mitigate this. Production impact is gated on server publication of gopatch artifacts.Overview
Adds free-tier Go hosted mode for
scan --mode hostedwhen a patch reference includes agoproxyregistry override. The rewriter commits a fork-stylereplaceontopatch.socket.dev/gopatch/<uuid>plus the socket module’s twogo.sumh1:lines, prunes the replaced original’s lines for tidy-stable output, and fails closed (per-depredirect_golang_*warnings, no partial writes) on bad hashes, untrusted module paths, require/version mismatch, or user replace conflicts. References without the override still emitredirect_golang_unsupported.Implementation: real
rewrite_golang(replaces the unconditional no-go stub), newgo_sum_edit,ReplaceOwner::Hostedand hosted upsert ingo_mod_edit, wire fieldsgoModH1/goModuleVersionwith gopatch hash merge inscan/hosted.rs, and cross-mode rules (local apply refuses hosted-owned replaces; vendor takeover/revert warnings; stale pin reconciliation).Docs & tests:
golang-hosted.mdsupersedes the no-go doc for free tier; ecosystems matrix and production e2e updated. Golden fixturegolang/gomod/basic, large unit suite, ande2e_golang_hosted_build.rs(bogusGOSUMDB, tidy no-op, tamperSECURITY ERROR). No behavior change in production until the server publishes grant-freegopatchmodules.Reviewed by Cursor Bugbot for commit 0b18bd4. Configure here.