Compatibility
The version field
Section titled “The version field”Byte 4 of every archive is a u32 format version. It is 1 today.
A reader accepts exactly its own version and rejects anything else with
UnsupportedVersion. There is no forward compatibility and no partial reading:
$ zpack list future.zpakzpack: cannot open archive 'future.zpak': UnsupportedVersionThat is deliberate. An archive format where an old reader silently skips fields it does not understand is a format where an old reader silently extracts the wrong thing. Refusing outright means the failure is loud, immediate, and actionable.
What version 1 promises
Section titled “What version 1 promises”While the format version stays 1:
- The header stays 12 bytes:
ZPAK,u32version,u32entry count - An entry record stays
path_len u16,path,offset u64,size u64,stored_size u64,hash u64,method u8, in that order - Integers stay little-endian
methodvalues0and1keep meaning store and raw deflate- Hashing stays
XxHash64with seed0 - Entries stay sorted by path
- Paths stay UTF-8, relative,
/-separated
An archive written by any v1 zpack is readable by any other v1 zpack, in either direction, on any platform.
What would bump it
Section titled “What would bump it”Anything that changes how existing bytes are interpreted:
| Change | Why it bumps |
|---|---|
A new method value |
An old reader would reject it as UnsupportedMethod mid-index, having already read part of a file it cannot use |
| A different hash function | Same field, different meaning |
| A new field in the entry record | Shifts every following field |
| Big-endian, or a different integer width | Same bytes, different values |
| Unsorted entries | Breaks reproducibility, and the contiguous-parent assumption in unpack |
Adding a compression method is the most likely reason it will ever happen.
What would not bump it
Section titled “What would not bump it”Anything that changes what zpack writes without changing what a v1 reader can read:
- A different deflate level, or a smarter store-versus-deflate heuristic
- Packing a different set of files by default
- New CLI commands, new library functions, new output formats
- Changes to the manifest shape, which is a reporting format and not part of the archive
An archive is compatible if a v1 reader produces the original bytes from it. How those bytes got there is not part of the contract.
Asset id stability
Section titled “Asset id stability”An asset id is XxHash64 of the path under
id_seed = 0x5a7061636b496473. Neither the function nor the seed changes within
a format version, so:
- Handles generated by
zpack idsstay valid across repacks - They stay valid across zpack versions
- A generated
assets.zigonly needs regenerating when the set of paths changes, not when the archive is rebuilt
The seed is distinct from hash_seed so a path handle and a content digest can
never be confused for one another.
zpack’s own version
Section titled “zpack’s own version”The CLI version - 0.0.2 today - is independent of the format version. It comes
from build.zig.zon at compile time, so the binary cannot disagree with the
package that produced it, and the release workflow refuses to publish a tag
whose name disagrees with that field.
While zpack is pre-1.0, the library API may change between releases. The archive format is versioned separately and does not move with it.
To check what a binary supports:
$ zpack --versionzpack 0.0.2And what an archive claims:
$ zpack manifest game.zpak | head -2.{ .format_version = 1,Zig version
Section titled “Zig version”zpack targets Zig 0.16.0 and uses the std.Io interfaces introduced there.
It does not build on 0.15 or earlier. This is a build-time requirement only -
archives are not affected by which Zig produced the binary.