Skip to content

Silent rollback

Silent rollback is the design choice that lets Upwarden sit in front of a build pipeline without breaking it. A package manager that asks for “the latest 1.x” gets the latest safe 1.x — not a 403, not a stalled CI job, not an angry developer at 3am. The bad version is invisible to the resolver; the build resolves to the next-best safe version and proceeds.

Package managers do dependency resolution in two steps:

  1. Fetch the package’s manifest — the list of available versions.
  2. Resolve the user’s version range against that list and download a specific version.

Upwarden intercepts step 1. When the proxy returns the manifest, it has already stripped out any version Upwarden would have BLOCKED or QUARANTINED. The resolver in step 2 simply doesn’t see those versions — there’s nothing to fail on.

A request for ^1.2.0 walks the safe-version list and picks the highest; if 1.5.7 is non-safe but 1.5.6 is, the developer ends up on 1.5.6. No error surface. No npm install failure.

The 403 path only fires when a developer pins exactly to a non-safe version:

// package.json
{
"dependencies": {
"foo": "1.5.7" // exact pin to a BLOCKED version → 403
}
}

This is intentional: silent rollback protects builds that ask for “give me the safe one”; explicit pins to known-bad versions are a deliberate “I want this specific version” request and Upwarden refuses loudly.

A consequence worth knowing: a version that’s currently in cooldown (younger than VANGUARD_DEFAULT_MIN_AGE_DAYS) is also stripped from the manifest. A brand-new legitimate release won’t get picked up by a floating range until cooldown clears. Builds stay green; the new version simply doesn’t appear in the resolution set yet.

If a developer needs the new version immediately (security fix, dependency override), the operator can either lower cooldown for that package via the policy override surface, or the developer can pin exactly — which surfaces a temporary 403, but the operator can allowlist that version explicitly.

We considered it. The reason silent rollback won:

  • CI determinism. A build that asked for ^1.2.0 yesterday and got 1.5.6 should still get 1.5.6 today even if 1.5.7 published-and-got-flagged in between. Erroring the manifest would break every CI in the company because of one bad upstream release.
  • Floating-range semantics are already permissive. A developer who wrote ^1.2.0 is already telling the resolver “any compatible version is fine”. Upwarden is honouring that — just with a smaller “compatible” set than the unfiltered registry.
  • The 403 surface is reserved for “you asked specifically for the bad thing”. That’s where the operator/developer attention needs to land.

Silent rollback is not silent in the audit log. Every manifest request that strips a non-safe version writes a row showing what was filtered. Operators can query for “how many builds were saved from version X” — the answer is exactly the number of rollback-applied manifest rows.