Zillowe FoundationZillowe Documentation

.zdelta Sidecars

Rebuild missing regions from bsdiff patches instead of fetching them.

Edit this page on GitLab

A zsync transfer recovers a target by finding blocks it already holds. Where the residue is absent, a range request closes the gap cheaply. Where the residue differs from what is already held rather than being absent, block matching still has to move all of it.

A .zdelta sidecar carries bsdiff patches for those regions, so the transfer pays for the difference instead.

The sidecar is additive. The .zsync control file is complete on its own, and a client that ignores the sidecar — including every other zsync implementation — still works.

Generating one

zyncmake image.bin -u https://example.com/image.bin --delta image.bin.old
image.zsync: 977 blocks of 2048 bytes, 7014 bytes of control file
image.bin.old: 1 regions differenced against image.bin
image.zdelta: 19861 bytes of sidecar

--delta may be repeated, so a client holding any one of several earlier versions can benefit.

On a 2 MB target with 200 KB rewritten in place, the same transfer fetches 204800 bytes without a sidecar and nothing at all with one:

fetched 204800 bytes in 1 requests; 0 bytes rebuilt from the sidecar
fetched 0 bytes in 0 requests; 204800 bytes rebuilt from the sidecar

Format

A .zdelta is a zstd-compressed tar holding a meta.json descriptor plus one bsdiff patch per region — the container shape that zbsdiff's zdelta feature defines.

{
  "format": "zsidecar.v1",
  "target_sha1": "…",
  "target_length": 2000000,
  "regions": [
    {
      "from_block": 244,
      "to_block": 344,
      "entry": "region-000000000244-000000000344.bsdiff"
    }
  ]
}

zbsdiff defines the container and carries meta.json without interpreting it, so the descriptor schema belongs to whoever writes it. zsidecar.v1 is this project's; other projects reuse the same container under their own ids — Zoi writes zoidelta.v1 for its package and registry deltas. A reader that checked only the container would otherwise accept the wrong schema.

target_sha1 and target_length say which target the patches rebuild. A sidecar describing anything else is refused outright: applying it would produce a file that is not the one asked for.

Regions are keyed by block span

Each region names an exact [from_block, to_block) span, and a patch applies only to that span. An approximate match is not enough — a patch built for a wider or narrower span describes different bytes, and stitching the wrong span in is precisely the corruption the checksums exist to prevent.

The span also has to be the one the client actually needs. Both sides run the same matcher over the same control file and the same base, so the missing blocks are the same set on each.

It is never authoritative

A sidecar is a hint, not a source of truth. Before a rebuilt region is accepted it is checked against the control file's own block checksums, and a patch is discarded if it fails to apply or produces anything other than the right bytes. The region is then fetched normally.

A wrong or hostile sidecar therefore costs bandwidth rather than correctness.

Region size limit

A bsdiff patch is produced from two buffers and applied to a third, so a region has to fit in memory three times over. Past --max-region the sidecar declines to carry the region, and the client fetches it as usual:

zyncmake image.bin --delta image.bin.old --max-region 8388608

The default is 64 MiB. A region that is too large is left out entirely rather than truncated — a partial patch would rebuild part of a region and leave the rest as though it had been fetched.

The base must be available

A patch describes a difference, so applying one needs the base's own bytes. Those are in the local copy passed with -i, not in the assembly's temporary file, which by construction holds only the blocks that were recovered and nothing at all where the residue is.

That is why supplying -i matters even when the sidecar covers everything the sidecar covers.

In the library

use std::path::Path;
use zync::delta::{self, DeltaIndex};

fn build(
    control: &zync::ControlFile,
    base: &Path,
    target: &Path,
) -> Result<(), zync::DeltaError> {
    let regions = delta::build(control, base, target, u64::MAX)?;
    let index =
        DeltaIndex::new(control.sha1.clone(), Some(control.length), regions);
    index.write(Path::new("image.zdelta"))
}

Read one back and hand it to the assembly:

use std::path::Path;
use zync::{Assembly, DeltaIndex};

fn attach(assembly: Assembly) -> Result<Assembly, zync::DeltaError> {
    let sidecar = DeltaIndex::read(Path::new("image.zdelta"))?;
    Ok(assembly.with_sidecar(sidecar))
}

DeltaIndex::describes reports whether a sidecar matches a control file, and patch_for returns the patch covering an exact block span if there is one.

See the format reference for what the sidecar rides alongside.


A software organization

2026 © All Rights Reserved.

  • All the content is available under CC BY-SA 4.0, expect where otherwise stated.

Last updated on