Skip to content

binpatch

Every binary update re-downloads the whole file. Patch only what moved — Electron apps, CLIs, agents, anything that's a single-file artifact.
Measured on getsentry/cli 0.29.0 to 0.39.0. The typical (median) patch is 4.0% the size of the full gzipped binary — 1.32 MB versus 31.38 MB, saving 96% per update. Range across 8 release pairs: 0.9% (small fixes) to 8.1% (big features).

Bandwidth

Median 96% fewer bytes per update across 8 real getsentry/cli releases. A 31 MB gzipped full download becomes a 1.3 MB patch on a typical release — and small bug-fix releases go as low as 0.9%.

Wall time

On slow links the savings are dramatic. At 5 Mbps the full download takes ~53 s; the patch download + apply takes ~4 s. At 25 Mbps it’s ~11 s vs ~2 s.

User patience

The fastest update is the one that finishes before the user opens Twitter. Patch downloads feel instantaneous on any link.

The classic case: a mycli update command downloads and applies the next version. Especially good fits:

  • Bun Bun (bun build --compile) — embed a JS/TS entry into a standalone executable.
  • Deno Deno (deno compile) — Deno’s equivalent.
  • Node.js Node SEA & Fossilize — freeze a Node runtime (or V8 snapshot) with your app’s prepended scripts. node --experimental-sea-config + node --build for SEA.
  • pkg pkg — ship your Node.js project as one self-contained binary. No runtime install, no npm, just run.

Powers self-updates in production for shipped binaries you may already be using — including Sentry’s own getsentry/cli.

A patch is useless without both:

  1. Generate — produce the patch from old → new in CI, and publish it somewhere your users can find it.
  2. Apply — discover the right patch(es) for the user’s installed version, download them, and reconstruct the new binary safely (integrity checks, size caps, progress).

And if the user is several versions behind, they don’t get a single patch — they get a chain of patches. binpatch chains them automatically, downloads them in parallel, applies each hop in order, verifies the cumulative SHA-256, and falls back to a full download if any hop is missing or malformed.

Most projects hand-roll one half and skip the other. binpatch gives you both, as a small MIT-licensed TypeScript library plus a drop-in GitHub Action. See the GitHub Action page for how an end-to-end update flows through the system.

Terminal window
npm install binpatch

Then both: generate patches from CI with the GitHub Action (so nightly builds push to GHCR and stable releases push to GitHub Releases), and wire resolveAndApply into your binary’s update command to discover and apply them.

import { resolveAndApply } from "binpatch";
const result = await resolveAndApply({
currentVersion: "1.2.0",
targetVersion: "1.3.4",
source: ghcrSource({ repo: "myorg/mycli" }),
});
// result.destPath now holds the verified 1.3.4 binary; download was ~1 MB.