Computes the .fingerprint value that Zig 0.16 expects in a package's build.zig.zon, and prints it on standard output.
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Jeffrey C. Ollie ea84534970
Document using the package as a library
The README mentioned the `zig_fingerprint` module only in passing, which was
not enough to actually depend on it. Cover adding the dependency, wiring the
module into a build, and the surface of the `Fingerprint` type, along with the
two things that trip people up: `format` needs the `{f}` specifier, and
`generate` takes an `std.Io` because Zig 0.16 moved randomness onto the I/O
interface.

The snippets were checked by building a dependent package against this one.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01XkhVPSDDeCM3L9QooPM7Z7
2026-08-25 18:15:13 -05:00
LICENSES Add a CLI that computes Zig 0.16 module fingerprints 2026-08-25 18:10:28 -05:00
src Add a CLI that computes Zig 0.16 module fingerprints 2026-08-25 18:10:28 -05:00
.gitignore Ignore the zig-pkg directory 2026-08-25 18:13:09 -05:00
build.zig Add a CLI that computes Zig 0.16 module fingerprints 2026-08-25 18:10:28 -05:00
build.zig.zon Add a CLI that computes Zig 0.16 module fingerprints 2026-08-25 18:10:28 -05:00
README.md Document using the package as a library 2026-08-25 18:15:13 -05:00

zig-fingerprint

Computes the .fingerprint value that Zig 0.16 expects in a package's build.zig.zon, and prints it on standard output.

zig init generates that field for you when a package is created, but there is no supported way to ask the toolchain for one on its own. This tool fills that gap: it is useful when you are writing a build.zig.zon by hand, generating one from a template, or repairing the field after renaming a package.

How a fingerprint is built

A fingerprint is a u64 made of two independent halves:

Bits Field Value
031 id Drawn at random once, when the package is created, and then never changed.
3263 checksum CRC-32 (ISO-HDLC, the variant used by gzip and zlib) of the package name.

Together with the name, the id is what gives a package a globally unique identity, which is how Zig can tell that two packages are versions of one another rather than unrelated. The checksum half is derived, so Zig recomputes it from the .name field and rejects the package when it disagrees. That check is what stops a fingerprint from being copied verbatim into a differently named package in an attempt to assume its identity.

Because only the id half is random, a fingerprint survives a rename intact: keep the id, recompute the checksum, and the package keeps its identity.

Building

$ zig build
$ zig build test

The executable lands in zig-out/bin/zig-fingerprint. zig build run -- <args> runs it without installing.

Usage

usage: zig-fingerprint [options] <name>

options:
  -i, --id <id>       Use <id> as the low 32 bits instead of drawing them
                      randomly. Preserves the identity of an existing
                      package whose name changed.
  -c, --check <fp>    Reuse the id of the existing fingerprint <fp> and exit
                      with status 1 if its checksum does not match <name>.
  -h, --help          Print this help and exit.

<name> is the .name field written without its leading dot. Numbers may be written in decimal or with a 0x, 0o or 0b prefix. Only the fingerprint is written to standard output; diagnostics go to standard error.

Generate a fingerprint for a new package:

$ zig-fingerprint my_package
0x5491cbb93f059bf2

Rename a package without losing its identity, by carrying the old id — the low eight hex digits of the old fingerprint — over to the new name:

$ zig-fingerprint --id 0x5cc769f2 zig_fingerprint
0x3ee809e95cc769f2

Check that a fingerprint already in a build.zig.zon belongs to the name beside it. The corrected fingerprint is printed either way, and the exit status is 1 when the two disagree:

$ zig-fingerprint --check 0xdeadbeef5cc769f2 zig_fingerprint
0x3ee809e95cc769f2
fingerprint 0xdeadbeef5cc769f2 does not belong to 'zig_fingerprint'; its checksum should be 0x3ee809e9

Using it as a library

The package exposes a zig_fingerprint module, so the same computation is available to your own build scripts and tools without shelling out to the executable.

Add it as a dependency, either from a URL or from a path relative to your build root:

$ zig fetch --save <url>

Then wire the module into whichever module needs it:

const zig_fingerprint = b.dependency("zig_fingerprint", .{
    .target = target,
    .optimize = optimize,
});
exe.root_module.addImport("zig_fingerprint", zig_fingerprint.module("zig_fingerprint"));

And use it:

const std = @import("std");
const Fingerprint = @import("zig_fingerprint").Fingerprint;

pub fn main(init: std.process.Init) !void {
    // A brand new identity for a package.
    const fresh: Fingerprint = .generate(init.io, "my_package");

    // The same identity carried over to a renamed package.
    const renamed: Fingerprint = .init("my_other_package", fresh.id);
    std.debug.assert(renamed.validate("my_other_package"));

    std.debug.print("{f}\n", .{renamed});
}

API

Fingerprint is a packed struct(u64) with two u32 fields, id (the low half) and checksum (the high half), so it can be bit-cast to and from the integer that appears in a manifest.

Declaration Purpose
init(name, id) Fingerprint The fingerprint for name, reusing an id you already have.
generate(io, name) Fingerprint The fingerprint for name, with a freshly drawn random id.
fromInt(u64) Fingerprint Reinterprets a manifest value, such as one you parsed out of a build.zig.zon.
int() u64 The value to write back into a manifest.
validate(name) bool Whether Zig would accept this fingerprint for a package named name.
format(writer) Renders as 0x followed by 16 hex digits, the way build.zig.zon spells it.
min_id, max_id The inclusive bounds Zig reserves for ids, 0x1 and 0xfffffffe.
checksumOf(name) u32 The checksum half on its own, for comparing against an existing fingerprint.

Two details are easy to trip over. format is a method rather than a default rendering, so it needs the {f} specifier — plain {} will not compile. And generate takes an std.Io because Zig 0.16 moved randomness onto the I/O interface; std.crypto.random no longer exists. If you have no Io at hand, draw the id yourself and call init instead.

License

MIT. This project follows the REUSE specification: every file carries its own SPDX headers and the license text lives in LICENSES/MIT.txt.