Command Palette

Search for a command to run…

ZarynZaryn
Packages·5 min read

Zig packages

Publish a Zig package and get the exact hash zig fetch will compute, check a package before publishing, use any Zaryn repository as a dependency, and fix build.zig.zon errors. Every step explained.

Zig doesn't have a central package registry. A dependency is simply **a web address of an archive plus its hash**, written in your project's `build.zig.zon`. `zig build` downloads the archive, computes its hash, and stops if it doesn't match. That design is safe, but it leaves you to host the archive somewhere and work out the hash. Zaryn does both: - **A registry for Zig.** Upload your package once; Zaryn gives you its address and the hash, worked out exactly the way `zig fetch` does it, so you can paste them straight into `build.zig.zon`. - **A check before you publish.** Zaryn reads your `build.zig.zon` the way zig does and tells you, in plain words, what zig would refuse (a missing fingerprint, a name that isn't allowed, files left out of the package). - **Any repository is a dependency.** Every commit of a Zaryn repository is downloadable as an archive with a fixed hash. - **Mirrors.** Dependencies from GitHub, Codeberg and others (archives, `.zip` files and `git+https` repositories), and the Zig compiler itself, can come through Zaryn; `zev mirror zig` does it for a whole project: see [Mirrors](/docs/en/package-mirrors#zig-dependencies). ## What a Zig package needs A folder with a `build.zig` (how to build it) and a `build.zig.zon` (what it is). `zig init` creates both. A `build.zig.zon` looks like this: ```zig .{ .name = .greet, // the package name: letters, digits and _ .version = "0.1.0", // raise it for every release .fingerprint = 0x3610a6865d7ac0a2, // made by zig init; never change it .minimum_zig_version = "0.15.1", // oldest Zig that builds it .dependencies = .{}, // other packages it uses .paths = .{ "build.zig", "build.zig.zon", "src" }, // what belongs to the package } ``` - **`.name`** starts with a dot (`.greet`, not `"greet"`) since Zig 0.14. - **`.fingerprint`** is your package's permanent ID. If you copy someone's package to start your own, Zaryn's check tells you the new value to use. - **`.paths`** lists the files and folders that are part of the package. Everything else in the archive (notes, test data, pictures) is left out and doesn't change the hash. ## Pack it From the folder above your package (here the package is in `greet/`): ```bash tar czf greet-0.1.0.tar.gz greet ``` This makes one compressed file of the folder. `.tar.xz`, `.tar.zst` and plain `.tar` work too. ## Check it (optional, recommended) On [Packages](/packages), choose **Zig**, then **Check a package before publishing** and pick the file. Or from a terminal: ```bash curl --data-binary @greet-0.1.0.tar.gz -H "Authorization: Bearer $ZARYN_TOKEN" \ https://api.zaryn.xyz/v1/packages/zig/-/check ``` You get the hash, how many files are in the package, and every problem zig would stop at, for example: ```text invalid fingerprint 0x1234 for the name .greet: if this is a new or forked package, use 0x3610a6865d7ac0a2 ``` Nothing is stored. ## Publish Create a token on [Packages](/packages) (**Create a publish token**), then: ```bash curl -T greet-0.1.0.tar.gz -H "Authorization: Bearer $ZARYN_TOKEN" \ https://api.zaryn.xyz/v1/packages/zig/ ``` - `-T` uploads the file. - The name and version come from `build.zig.zon`, so there's nothing else to type. - To publish for an organization you own or administer, add `?owner=<organization>` to the address. Zaryn answers with everything someone needs to use it: ```json { "name": "greet", "version": "0.1.0", "hash": "greet-0.1.0-osB6XSsBAACcv0dHgf4SJ_AaruNKCheoTxH_lgjFjt-i", "url": "https://api.zaryn.xyz/v1/packages/zig/greet/greet-0.1.0.tar.gz", "zon_entry": ".greet = .{ .url = \"…\", .hash = \"…\" }," } ``` A published version can't be replaced: for a fix, raise `.version` and publish again. ## Use it in another project In the other project's folder: ```bash zig fetch --save https://api.zaryn.xyz/v1/packages/zig/greet/greet-0.1.0.tar.gz ``` `zig fetch --save` downloads the package, checks it, and adds it to `build.zig.zon` with its hash. The package's page on Zaryn shows the same entry, ready to copy, if you'd rather paste it yourself. Then use it in `build.zig`: ```zig const greet = b.dependency("greet", .{}); exe.root_module.addImport("greet", greet.module("lib")); ``` `"lib"` is the module name the package's own `build.zig` gives with `b.addModule("lib", …)`; the package's README should say which names it offers. ## Use a Zaryn repository as a dependency Every commit of a repository can be downloaded as a `.tar.gz`: ```text https://api.zaryn.xyz/v1/repos/<owner>/<repo>/branches/<branch>/archive/<commit>.tar.gz ``` The same commit always gives the same bytes, so its hash never changes. The simplest way to get the address and hash is to ask Zaryn: ```bash curl https://api.zaryn.xyz/v1/repos/<owner>/<repo>/branches/main/zig-package/HEAD ``` The answer pins the newest commit (never "HEAD", which moves) and includes a ready `zig fetch --save …` line and `.dependencies` entry, plus anything in that commit's `build.zig.zon` zig would refuse. On the repository's page, a repository with a `build.zig.zon` also has a **zig** tab next to the clone commands that shows the same. The repository must be public for `zig fetch` to download it, since zig can't send a token. ## Private Zig packages Packages can be made private on their page (Premium). Because `zig fetch` can't send a token, download a private package with curl, unpack it inside your project, and point to the folder: ```bash curl -LO -H "Authorization: Bearer $ZARYN_TOKEN" \ https://api.zaryn.xyz/v1/packages/zig/greet/greet-0.1.0.tar.gz mkdir -p deps && tar xzf greet-0.1.0.tar.gz -C deps ``` ```zig .dependencies = .{ .greet = .{ .path = "deps/greet" }, }, ``` ## When something goes wrong | You see | What it means | Fix | |---|---|---| | "missing top-level 'fingerprint' field; suggested value: 0x…" | `build.zig.zon` has no fingerprint | add `.fingerprint = 0x…` with the suggested value | | "invalid fingerprint … use 0x…" | the fingerprint doesn't belong to this name (often a copied package) | use the value given | | "'name' must be an enum literal" | `.name = "greet"` is the old style | write `.name = .greet` | | "missing top-level 'paths' field" | zig needs to know which files belong to the package | add `.paths = .{ "build.zig", "build.zig.zon", "src" }` | | "there is no build.zig.zon at the top of the archive" | the archive holds a folder of folders, or no manifest | pack the package folder itself | | "this package name belongs to someone else" | another account published that name | rename `.name` (and make a new fingerprint) | | zig says `hash mismatch` | the archive at that address changed since the hash was written | for Zaryn addresses this can't happen; for other sites, see [Mirrors](/docs/en/package-mirrors#zig-dependencies) | ## How the hash works zig doesn't hash the archive file; it hashes **what's inside**. It unpacks the archive, keeps only the files under `.paths`, hashes each file together with its path, and combines those hashes in name order. The result is written as `name-version-` followed by 44 characters that also encode the fingerprint and the package's size. That's why re-packing the same files with a different tool, or adding a notes file outside `.paths`, gives the same hash. Zaryn uses the same rules (checked against `zig fetch` on real packages), so the hash it shows is the hash zig will compute.