Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
b2cbef8
refactor(core/vendor): decode committed archives from one in-memory b…
mikolalysenko Sep 23, 2026
4c814f6
feat(core/vendor): verify a committed artifact for reuse against the …
mikolalysenko Sep 23, 2026
cefd430
fix(core/vendor): reuse the committed npm tarball before acquiring a …
mikolalysenko Sep 23, 2026
2394e4d
fix(core/vendor): re-wire the committed pypi wheel after a relock
mikolalysenko Sep 23, 2026
18144cc
fix(core/vendor): let an in-sync golang re-run pass service + offline
mikolalysenko Sep 23, 2026
5872257
fix(core/api): retry transient vendor-service failures and trip a bre…
mikolalysenko Sep 23, 2026
2c46e54
test(cli): pin vendor re-run idempotence across a service outage end …
mikolalysenko Sep 23, 2026
d18032f
test(core/vendor): cover the bun.lock forged-ledger and new-uuid reus…
mikolalysenko Sep 23, 2026
cf4f8f9
fix(core/vendor): reuse only a canonical committed archive
mikolalysenko Sep 23, 2026
c6f98b8
fix(core/vendor): bind a reused wheel's name to the package and previ…
mikolalysenko Sep 23, 2026
02a7a17
fix(core/vendor): let an npm dry run see the committed-tarball reuse
mikolalysenko Sep 23, 2026
fffa818
fix(core/vendor): derive yarn berry's checksum from the verified tarb…
mikolalysenko Sep 23, 2026
6dc66bf
fix(core/api): bound each vendor-service attempt and type the body-re…
mikolalysenko Sep 23, 2026
c876468
test(core/vendor): disable vendor-service retries in non-retry test c…
mikolalysenko Sep 23, 2026
8651115
docs(changelog): note canonical-archive reuse, dry-run parity and att…
mikolalysenko Sep 23, 2026
8141a23
test(cli): make the bun.lockb outage re-run tell reuse from a local r…
mikolalysenko Sep 23, 2026
d6b41c2
test(scripts): require the reuse event when the pdm relock re-scan re…
mikolalysenko Sep 23, 2026
81bc4d3
fix(vendor): golang dry-run of an in-sync module under --offline no l…
mikolalysenko Sep 24, 2026
fdc2527
Merge origin/main (#249 integrity hardening) into fix/vendor-service-…
mikolalysenko Sep 24, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,26 @@ into the new version's section — see docs/releasing.md.

### Fixed

- **A vendoring-service outage no longer re-vendors packages.** An npm
re-run (every lock flavor, `bun.lockb` included) re-acquired its tarball
from whichever source answered — the service's prebuilt, or a local pack
with different bytes — so an outage or its recovery rewrote the lock's
integrity and the committed tarball and reported `applied`. A re-run now
keeps the committed artifact whenever the vendor ledger vouches for it
(uuid-bound path, no symlink, sha256 + size equal to the ledger, a
canonical archive an installer extracts exactly as decoded, every
patched file verified from the same bytes) and is `already_vendored`
with no service request, in every `--vendor-source` mode (including
`service` + `--offline`, as cargo and composer already did; golang now
matches). A pypi re-scan after a relock re-wires the committed wheel
instead of pinning a new sha, the PDM partial-relock guard holds
whichever source built the wheel, a wiring failure no longer deletes a
committed wheel, and a missing prebuilt wheel during an outage now says
to wait for the service. `--dry-run` previews the same reuse, so it no
longer predicts a `service` + `--offline` refusal the real run does not
have. Transient service failures (network, timeouts, 429, 5xx) are
retried with backoff, each attempt is time-bounded, and after two
consecutive exhausted fetches the run stops calling the service.
- **Terminal output is clean on every command.** Progress lines no longer
leave stale text behind (`scan` printed e.g. `Found 7 patches for 1
packagesatch 7/7)`) or run into warnings printed while they are active.
Expand Down
16 changes: 16 additions & 0 deletions crates/socket-patch-cli/CLI_CONTRACT.md
Original file line number Diff line number Diff line change
Expand Up @@ -481,6 +481,21 @@ per service outcome:
| `--offline` | local build | refuse (`vendor_service_offline_conflict`) |
| no API client configured (library callers of the vendor engine; the CLI always configures one) | local build | refuse (`vendor_prebuilt_required`) |

`--vendor-source` governs ACQUISITION, not reuse: a re-run whose committed artifact the ledger
vouches for (npm tarball / pypi wheel: path under this patch uuid, no symlink, whole-file sha256 and
size equal to the ledger, every afterHash verified from the same bytes; the dir-shaped ecosystems:
the wired copy's afterHashes) keeps it in every mode — no service request, no local build, no
rewrite — whichever source built it. So a service outage (or its recovery) never re-vendors an
already-vendored package: the re-run is `already_vendored`, including under `service` +
`--offline`, and `build` does not rebuild a service-built artifact (delete the uuid dir to force
a rebuild). The ledger records no provenance, so `service` cannot tell a locally built committed
artifact from a prebuilt one; it keeps what verifies. A lock that drifted off a verified committed
artifact (a relock, a hand revert) is re-wired to those exact bytes (pypi re-scans report the
Verbose `vendor_artifact_reused`). Service round trips are retried on transport errors and
429/500/502/503/504 (3 attempts, exponential backoff with jitter, `Retry-After` honored, 4s cap);
after 2 consecutive exhausted fetches the rest of the run skips the service (`auto` builds
locally, `service` refuses).

**golang service leg staging (v5.0)**: the module zip is downloaded, extracted and `h1:`-verified in a `<copy>.socket-stage` sibling and swapped into place only afterwards; a failed re-download of a WIRED, present copy keeps the copy and its `replace` directive (previously both were torn down), while a missing copy still drops the dangling directive.

Coverage today: **npm** (all lock flavors), **pypi** (wheel — sdist falls back / refuses), **cargo**
Expand Down Expand Up @@ -1125,6 +1140,7 @@ Every `--json` invocation emits a single JSON object that follows the **unified
| `vendor_fetch_unverifiable` | `skipped` (warning) | vendor: the lockfile records no usable integrity for the missing package; nothing was fetched (fail-closed) and the `package_not_installed` skip follows. |
| `vendor_artifact_missing` | `skipped` (warning) / `failed` | vendor: the committed artifact is gone — the registry resolution is recovered from the ledger and the artifact rebuilt (warning); repair `--offline` with no local source surfaces it as the per-entry failure instead. |
| `vendor_artifact_corrupt` | `failed` | repair `--offline`: the committed artifact fails verification (member afterHashes or the ledger's whole-file sha256) and no local source can rebuild it. Online repairs rebuild instead. |
| `vendor_artifact_reused` | `skipped` (verbose note) | vendor / scan `--vendor` (pypi): the wiring was dropped by a relock but the committed wheel the ledger vouches for verified, so it was re-wired as-is — no service download, no rebuild; the lock pins the first run's sha again. |
| `vendor_artifact_rebuilt` | `skipped` (warning) | vendor / scan `--vendor`: a wired-but-missing/stale artifact was rebuilt in place. The lockfiles are untouched, except that nuget re-pins `packages.lock.json` to the rebuilt bytes. gem/maven/nuget: the package's event is `applied` (also for a rebuild from the patch service), and the ledger entry's artifact fingerprint (gem `fileInventory`, maven/nuget `sha256` + `size`, and the nuget lock pin) is refreshed to the rebuilt bytes, and its wiring records are kept unchanged, so `--revert` still restores the pre-vendor files. A rebuild whose ledger has no entry for the package, or only one from another patch uuid, records none. cargo/composer/gem rebuilds honour `--vendor-source` like a fresh vendor (`service` downloads the prebuilt artifact and refuses when it cannot). Other ecosystems leave the ledger entry untouched. (Under `repair` the `rebuilt` event carries this signal.) |
| `vendor_artifact_rebuild_failed` | `failed` | repair: the rebuild ran but the result failed verification against the recorded fingerprint (e.g. an edited state.json sha); the unverifiable artifact was removed. |
| `vendor_artifact_unrepairable` | `failed` | repair: no verifiable pristine source exists (not installed + lockfile rewired + no recoverable ledger fragment), the wheel is platform-locked with no installed copy, or the ledger entry itself cannot be trusted. |
Expand Down
10 changes: 8 additions & 2 deletions crates/socket-patch-cli/src/commands/vendor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -368,8 +368,9 @@ enum AdvisoryTier {

fn advisory_tier(code: &str) -> AdvisoryTier {
match code {
// Every successful service vendor emits this, one per package.
"vendor_prebuilt_downloaded" => AdvisoryTier::Verbose,
// Every successful service vendor emits this, one per package; a
// relock re-scan that re-wires the committed wheel emits the other.
"vendor_prebuilt_downloaded" | "vendor_artifact_reused" => AdvisoryTier::Verbose,
// The run did what was asked; these explain how.
"vendor_fetched_missing"
| "vendor_would_revert_redirect"
Expand Down Expand Up @@ -4208,6 +4209,11 @@ mod ui_format_tests {
),
Some("Note: vendored x from the service".to_string())
);
assert_eq!(format_advisory("vendor_artifact_reused", "r", false), None);
assert_eq!(
format_advisory("vendor_artifact_reused", "re-wired x", true),
Some("Note: re-wired x".to_string())
);
assert_eq!(
format_advisory("vendor_fetched_missing", "fetched", false),
Some("Note: fetched".to_string())
Expand Down
Loading
Loading