Skip to content

Commit 6d685a4

Browse files
refactor(cargo): patch crates in place; drop the [patch]-redirect backend + build guard (#107)
* refactor(cargo): patch crates in place; drop the [patch]-redirect backend + build guard Simplify the cargo apply path to patch crates in place (vendored or registry cache) like npm/pypi/gem, removing the project-local `[patch]`-redirect backend and the build-time `socket-patch-guard` setup wiring. Cargo now rolls back in place from before-blobs rather than dropping a redirect. Removed: - core: `cargo_setup` (discover/update), `patch/cargo_config`, `patch/cargo_redirect`, and `go_setup` (the build-time Go guard package + templates). - cli: cargo/go redirect dispatch in `apply`/`rollback`, cargo + Go guard wiring in `setup`, and the now-dead setup/redirect tests. Kept: - Go `replace`-redirect (`go_redirect`/`go_mod_edit`/`copy_tree`): the module cache is checksum-verified, so in-place patching fails `go.sum` at build time — Go still needs the project-local copy. - The `socket-patch-guard` crate and its build-integration test. `copy_tree` is now gated on `golang` only. Docs (README, CLI_CONTRACT, module headers) updated to drop the removed backends. Builds clean across feature combos (default / none / all / cargo-only) and the full suite passes (--no-fail-fast). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * ci: drop removed cargo-coexist suite + go-guard-template lint step The simplification deleted `tests/e2e_cargo_coexist.rs` and the `go_setup/templates/*.tmpl` files, but ci.yml still referenced them: the e2e matrix listed `e2e_cargo_coexist` (no such target → fail) and the lint-ecosystems job `cp`'d the now-absent Go guard templates for gofmt/vet (cp → fail). Remove both; keep `guard_build_integration`. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * refactor: remove the now-orphaned socket-patch-guard crate + stale references With cargo patching in place (no `[patch]`-redirect) and `setup` no longer wiring a build-time guard, the `socket-patch-guard` crate had no consumer: nothing in cli/core references it, `setup` never writes it, and its `.socket/cargo-patches/` + `SOCKET_PATCH_ROOT` machinery no longer exists. Remove it and every dangling reference. - Delete `crates/socket-patch-guard/` (crate, build.rs, README, SAME_TICK_HEAL_RND.md, same_tick_heal_experiment.rs) and drop it from the workspace members + Cargo.lock. - Delete `guard_build_integration.rs`; drop the `guard_build_integration` e2e matrix entry and the guard-template gofmt/vet step from ci.yml; drop the `cargo publish -p socket-patch-guard` step from release.yml. - CHANGELOG: rewrite the `[Unreleased]` cargo/guard entries to describe what actually ships — cargo in-place patching (default feature) and the Go `replace`-redirect backend — and drop the now-false "cargo apply now redirects" Changed bullet. - Drop the dead `SOCKET_PATCH_ROOT`/`SOCKET_PATCH_GUARD` env vars from test scrub lists (no longer read in src; `SOCKET_PATCH_BIN` kept — the gem Bundler plugin still uses it); repoint stale `setup_matrix_cargo` doc-comments to surviving sibling suites; refresh CLI_CONTRACT prose. Builds clean across feature combos; full suite passes (--no-fail-fast). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 4d5ba3d commit 6d685a4

49 files changed

Lines changed: 312 additions & 8966 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.github/workflows/ci.yml‎

Lines changed: 2 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,8 @@ jobs:
3939

4040
# Lint the out-of-workspace packaging artifacts for the ecosystems whose setup
4141
# / CLI-distribution we added: the RubyGems CLI launcher gem + the Bundler
42-
# plugin gem (Ruby), the Composer CLI launcher (PHP), and the generated Go
43-
# setup-guard templates. Ruby, PHP, Composer, and Go are all pre-installed on
44-
# the ubuntu-latest runner.
42+
# plugin gem (Ruby) and the Composer CLI launcher (PHP). Ruby, PHP, and
43+
# Composer are all pre-installed on the ubuntu-latest runner.
4544
lint-ecosystems:
4645
runs-on: ubuntu-latest
4746
steps:
@@ -63,19 +62,6 @@ jobs:
6362
php -l composer/socket-patch/bin/socket-patch
6463
( cd composer/socket-patch && composer validate --no-check-publish )
6564
66-
- name: Go — gofmt + vet the setup-guard templates
67-
run: |
68-
tmp="$(mktemp -d)"
69-
cp crates/socket-patch-core/src/go_setup/templates/guard.go.tmpl "$tmp/guard.go"
70-
cp crates/socket-patch-core/src/go_setup/templates/guard_test.go.tmpl "$tmp/guard_test.go"
71-
unformatted="$(gofmt -l "$tmp")"
72-
if [ -n "$unformatted" ]; then
73-
echo "::error::Go setup-guard templates are not gofmt-clean:"
74-
gofmt -d "$tmp"
75-
exit 1
76-
fi
77-
( cd "$tmp" && go mod init socketpatchguardlint >/dev/null && go vet ./... )
78-
7965
test:
8066
strategy:
8167
fail-fast: false
@@ -456,16 +442,6 @@ jobs:
456442
suite: e2e_composer
457443
- os: ubuntu-latest
458444
suite: e2e_nuget
459-
# Cargo project-local [patch]-redirect backend + fail-closed guard.
460-
# `guard_build_integration` is hermetic (a shell stub + `cargo build
461-
# --offline` against a zero-dep path dep), so it exercises the
462-
# build.rs-panic-aborts-a-real-build seam with no network.
463-
# `e2e_cargo_coexist`'s real-cargo proofs fetch a crate (cached) and
464-
# skip on fetch failure. Both suites are `#[cfg(unix)]`.
465-
- os: ubuntu-latest
466-
suite: guard_build_integration
467-
- os: ubuntu-latest
468-
suite: e2e_cargo_coexist
469445
# The live-API smoke suites (e2e_npm, e2e_pypi, e2e_gem,
470446
# e2e_scan) are intentionally NOT in the PR matrix — their
471447
# `#[ignore]`-gated tests hit the real public proxy at

‎.github/workflows/release.yml‎

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -247,17 +247,6 @@ jobs:
247247
env:
248248
CARGO_REGISTRY_TOKEN: ${{ steps.crates-io-auth.outputs.token }}
249249

250-
# socket-patch-guard is a standalone crate (no dependency on core/cli) that
251-
# `socket-patch setup` adds to a user's Cargo.toml as
252-
# `socket-patch-guard = "<major.minor>"`. It MUST be published on every
253-
# release or cargo setup writes an unresolvable dependency and the user's
254-
# `cargo build` fails. Its build.rs is a no-op when SOCKET_PATCH_ROOT is
255-
# unset (the case during publish verification), so this builds cleanly.
256-
- name: Publish socket-patch-guard
257-
run: cargo publish -p socket-patch-guard
258-
env:
259-
CARGO_REGISTRY_TOKEN: ${{ steps.crates-io-auth.outputs.token }}
260-
261250
- name: Wait for crates.io index update
262251
run: sleep 30
263252

‎CHANGELOG.md‎

Lines changed: 18 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -16,49 +16,24 @@ in this file — see `.github/workflows/release.yml` (`version` job).
1616

1717
### Added
1818

19-
- **Project-local cargo `[patch]`-redirect backend (local mode).** Patching a
20-
Rust dependency from the registry cache no longer mutates the shared
21-
`$CARGO_HOME` registry in place. Instead `apply` writes a project-local
22-
patched **copy** under `.socket/cargo-patches/<name>-<version>/` and a managed
23-
`[patch.crates-io]` entry (+ `[env] SOCKET_PATCH_ROOT`) into
24-
`.cargo/config.toml`, so patches are project-scoped and the registry stays
25-
pristine for sibling projects. `rollback` cleanly drops the entry + copy
26-
(leaving `setup` state — the guard dependency + `[env]` — intact).
27-
`apply --check` is a new read-only, lock-free, offline auditor that verifies
28-
the committed copies/config match the manifest **and** cross-checks
29-
`Cargo.lock` (flagging a patched dependency that silently resolved to an
30-
unpatched version); it exits non-zero on drift (for CI / GitHub-App use).
31-
Vendored crates (`vendor/`) and `--global` cargo keep the existing in-place
32-
`.cargo-checksum.json` rewrite path unchanged. **`cargo` is now a default
33-
feature** (alongside the always-on npm + PyPI support), so released binaries and
34-
a plain `cargo install socket-patch-cli` patch Rust dependencies and run the
35-
guard out of the box; `golang`/`maven`/`composer`/`nuget`/`deno` remain opt-in.
36-
A binary built `--no-default-features` (no cargo) now fails `apply --check`
37-
closed rather than reporting "in sync", so it can never make the guard pass
38-
vacuously.
39-
- **`socket-patch-guard` crate + `setup` cargo support.** `socket-patch setup`
40-
now also configures Rust projects: it adds a tiny `socket-patch-guard`
41-
dependency (a normal `[dependencies]` entry, not a `[build-dependencies]` one,
42-
so cargo always compiles it and runs its build script) to every workspace
43-
member and writes `[env] SOCKET_PATCH_ROOT`. The guard's build script runs `socket-patch apply --check`
44-
on every relevant `cargo build` and is **fail-closed**: if the committed
45-
patched copies are out of sync with `.socket/manifest.json` (a stale copy, or
46-
a patched dependency that resolved to an unpatched version), the build
47-
**fails** rather than silently compiling stale/unpatched sources — closing the
48-
CI footgun where a one-shot build could ship an unpatched binary. The fix is
49-
run-order-independent (it checks the static committed state, not when the
50-
build script happens to run). It is a single fail-closed mode with no
51-
`warn`/`off` escape: on drift it regenerates the copies then fails the build
52-
with a "re-run" message (the retry is clean), and an unrecoverable state or a
53-
missing `socket-patch` CLI also fails the build. In normal use the guard never
54-
fires, since changing a patch goes through `get`/`apply` (which regenerate the
55-
copies). The user's own `build.rs` is never touched. For CI, run
56-
`socket-patch apply --check --ecosystems cargo` as an explicit pipeline gate.
57-
`setup --check` / `setup --remove` cover the
58-
round-trip. *(A guarded repo requires `socket-patch` on the build machine —
59-
wire it into apps/workspaces you control, not a published library. Pre-GA:
60-
`socket-patch-guard` will be published to crates.io; airgapped users vendor
61-
it.)*
19+
- **Cargo support (`cargo` is now a default feature).** `apply` patches a Rust
20+
dependency **in place** wherever the crawler finds it — the project `vendor/`
21+
directory or the shared `$CARGO_HOME` registry cache — rewriting the crate's
22+
`.cargo-checksum.json` sidecar so `cargo build` accepts the modified files.
23+
`rollback` restores the original bytes from the `beforeHash` blobs, like
24+
npm/PyPI/gem. `cargo` ships on by default (alongside the always-on npm + PyPI
25+
+ Ruby gems support), so released binaries and a plain `cargo install
26+
socket-patch-cli` patch Rust dependencies out of the box;
27+
`maven`/`composer`/`nuget`/`deno` remain opt-in.
28+
- **Project-local Go `replace`-redirect backend (`golang`, default feature).**
29+
The Go module cache is shared, read-only and checksum-verified, so in-place
30+
patching would fail `go.sum` at build time. Instead `apply` writes a
31+
project-local patched **copy** under `.socket/go-patches/<module>@<version>/`
32+
and a managed `replace` directive in the project `go.mod`, so the patch is
33+
project-scoped and the cache stays pristine for sibling projects. `rollback`
34+
cleanly drops the `replace` directive + copy. `apply --check` is a read-only,
35+
lock-free, offline auditor that verifies the committed redirects match the
36+
manifest, exiting non-zero on drift (for CI / GitHub-App use).
6237
- **Inline OpenVEX generation on `apply` and `scan` via `--vex <path>`.** A
6338
single successful `apply`/`scan` can now both patch and emit the OpenVEX
6439
0.2.0 attestation, instead of requiring a separate `socket-patch vex` step.
@@ -71,16 +46,6 @@ in this file — see `.github/workflows/release.yml` (`version` job).
7146
command exit non-zero even when the apply/scan itself succeeded, surfacing a
7247
stable error code in the envelope.
7348

74-
### Changed
75-
76-
- **Local cargo `apply` now redirects instead of patching in place.** Registry
77-
crates patched by a previous (in-place) version leave a mutated shared
78-
registry + rewritten `.cargo-checksum.json` behind; the new local backend
79-
never touches the registry, so those stay dirty until cargo re-fetches.
80-
`apply` now prints a one-line **warning** when it detects such a crate
81-
(suppressed under `--offline`, so the build-time guard stays quiet) and points
82-
at restoring the pristine copy. No automatic registry cleanup is performed.
83-
8449
## [3.2.0] — 2026-05-29
8550

8651
A repo-wide correctness, security, and filesystem-safety hardening pass: every

‎Cargo.lock‎

Lines changed: 0 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎Cargo.toml‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
members = [
33
"crates/socket-patch-core",
44
"crates/socket-patch-cli",
5-
"crates/socket-patch-guard",
65
]
76
resolver = "2"
87

‎README.md‎

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -408,10 +408,9 @@ Configure your project so patches are **re-applied automatically after install**
408408

409409
- **npm / yarn / pnpm / bun** — writes a `postinstall` script into `package.json` so any install re-applies patches (pnpm: root package only).
410410
- **Python (pip / uv / poetry / pdm / hatch)** — Python has no universal post-install hook, so `setup` instead commits a **`socket-patch[hook]`** dependency (for classic Poetry, the equivalent `socket-patch = { extras = ["hook"] }`). Installing it lays down a startup `.pth` (shipped by the small `socket-patch-hook` wheel) that re-applies your committed `.socket/` patches the next time the interpreter runs. It is package-manager-agnostic (it rides the interpreter, not any one installer) and **fail-open** — a hook error can never break interpreter startup.
411-
- **Cargo** — adds a `socket-patch-guard` build dependency to each workspace member's `Cargo.toml` plus an `[env] SOCKET_PATCH_ROOT` in `.cargo/config.toml`. The guard's build script re-applies patches on every `cargo build` and is **fail-closed** — a build using stale/unpatched sources fails loudly. (Requires the `socket-patch` CLI on `PATH` at build time.)
412-
- **Go** — generates a committed `internal/socketpatchguard/` guard package plus a blank import in each `main` package. The guard re-applies patches and gates both `go test ./...` (CI) and every `go run` / binary launch via `init()` — **fail-closed**. Fully self-contained committed source. (Requires the `socket-patch` CLI on `PATH`.)
413411
- **Ruby gems (Bundler)** — adds a managed `plugin "socket-patch"` block to the `Gemfile` and commits an in-tree Bundler plugin under `.socket/bundler-plugin/`. It re-applies patches on every `bundle install` (cached *and* fresh). (Requires the `socket-patch` CLI on `PATH`.)
414412
- **Composer (PHP)** *(opt-in `composer` feature)* — appends `socket-patch apply` to `composer.json`'s `post-install-cmd` / `post-update-cmd` script events, so patches re-apply on every `composer install` / `composer update`. Only available in a build compiled with `--features composer`. (Requires the `socket-patch` CLI on `PATH`.)
413+
- **Cargo & Go** — *apply-only, no `setup` hook.* A one-click auto-repatch-on-build isn't possible for these, so `setup` skips them. Patch with `socket-patch apply` directly: **cargo** patches the crate in place (in `vendor/` or the registry cache, rewriting `.cargo-checksum.json` so `cargo build` accepts it); **go** writes a project-local patched copy under `.socket/go-patches/` plus a `go.mod` `replace` directive (the module cache is `go.sum`-verified, so in-place patching can't build). Commit `go.mod` + `.socket/go-patches/` so a clone builds the patched bytes. Declare them in `setup.manual` for VEX attestation.
415414
- **Apply-only ecosystems** (nuget · maven · deno) — no native install hook to wire, so `setup` reports `no_files`; patch them on demand with `socket-patch apply`.
416415

417416
**Usage:**
@@ -425,7 +424,7 @@ socket-patch setup --remove # revert what setup added
425424
| Flag | Description |
426425
|------|-------------|
427426
| `--check` | Read-only verification that every manifest is configured; exits non-zero if any still needs setup. Never writes (safe in CI). Conflicts with `--remove`. |
428-
| `--remove` | Revert the install hooks `setup` added (npm `package.json` scripts, the Python `socket-patch[hook]` dependency, the cargo `socket-patch-guard` dependency + `[env]`, the Go guard package + imports, and the gem Bundler plugin wiring). |
427+
| `--remove` | Revert the install hooks `setup` added (npm `package.json` scripts, the Python `socket-patch[hook]` dependency, and the gem Bundler plugin wiring). |
429428

430429
#### Disabling / opting out (Python hook)
431430

0 commit comments

Comments
 (0)