# Iguana: Zig downloader and version manager in 64 lines of code

**URL:** <https://ziggit.dev/t/iguana-zig-downloader-and-version-manager-in-64-lines-of-code/16310>\
**Category:** Showcase\
**Tags:** version-manager\
**Created:** [June 23, 2026, 12:08pm UTC](https://ziggit.dev/t/iguana-zig-downloader-and-version-manager-in-64-lines-of-code/16310 "2026-06-23T12:08:01Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![mslapek](https://ziggit.dev/user_avatar/ziggit.dev/mslapek/32/9281_2.png) [@mslapek](https://ziggit.dev/u/mslapek)\
**Post date:** [June 23, 2026, 12:08pm UTC](https://ziggit.dev/t/iguana-zig-downloader-and-version-manager-in-64-lines-of-code/16310/1 "2026-06-23T12:08:01Z")

</div>

# iguana:

* * *

**Zig downloader and version manager in 64 lines of code**

[www.codeberg.org/mslapek/iguana](http://www.codeberg.org/mslapek/iguana)

It allows easy installation of various Zig versions, to use them directly from the shell:

```zig
$ iguana install 0.16.0
$ iguana install 0.15.2

$ zig version # will print 0.16.0, by default takes the newest installed version

$ export ZIG_RELEASE=0.15.2
$ zig version # will print 0.15.2
$ zig zen

```

The distinguishing feature - _auditable_, the self-contained script consists only of 64 lines of POSIX shell script:

```zig
$ wc -l iguana
      64 iguana

```

Recently there’s a lot of discussion about chain supply attacks. This prompted me to make that script and experiment how “lean” we can get our software (and the pain points resulting from that).

Explicitly `README.md` asks you to audit the script, with the philosophy “don’t trust me”.

### Stuff I learned

- It turns out that the `tar` shell command isn’t in the POSIX standard, instead there’s a `pax` tool. However, I’ve decided to go with `tar` in my script, because it’s more popular (battle tested!). The homepage of GNU paxutils feels abandoned (last update in 2011).

- Making a documentation of this script was boring, however it displayed to me, what could be improved in it - to remove stuff from `README.md` (less is more). For instance:

### Design decisions

To make the script lean, I’ve made a few compromises:

- crude error messages, no build-in documentation,
- user has to modify parameters in the script (at least it encourages to take a look at that),
- limited argument verification, for instance you can pass an excessive number of parameters, like `iguana install 0.16.0 foo bar`.

The repo has some smoke tests in pytest - so I can test the script in various environments easily with `make test`. However, it isn’t TDD - had I accidentally omitted `minisign` invocation in the script, the current tests wouldn’t catch that.

### Supported Zig versions

All published Zig releases (or at least those that have a similar tarball structure as Zig 0.16.0).

It should work on all Unix-like OSes satisfying the requirements from README.md.

---

<div class="post-metadata">

**Author:** ![matklad](https://ziggit.dev/user_avatar/ziggit.dev/matklad/32/2462_2.png) [@matklad](https://ziggit.dev/u/matklad)\
**Post date:** [June 23, 2026, 12:28pm UTC](https://ziggit.dev/t/iguana-zig-downloader-and-version-manager-in-64-lines-of-code/16310/2 "2026-06-23T12:28:01Z")

</div>

Like this! We also have a version of similar thing in TigerBeetle, which should be good enough to copy-paste:

> <https://github.com/tigerbeetle/tigerbeetle/blob/main/zig/download.ps1>

- Its a local per-project install, rather than global install
- Checks hashes, rather than signatures
- Hash-checking is also used to implement download caching (I believe iguana does the same, just wanted to call this out as a positive important-for-ci thing)
- work on windows as well.

---

<div class="post-metadata">

**Author:** ![mslapek](https://ziggit.dev/user_avatar/ziggit.dev/mslapek/32/9281_2.png) [@mslapek](https://ziggit.dev/u/mslapek)\
**Post date:** [June 24, 2026, 7:47am UTC](https://ziggit.dev/t/iguana-zig-downloader-and-version-manager-in-64-lines-of-code/16310/3 "2026-06-24T07:47:34Z")

</div>

The script from Tigerbeetle looks cool.

The trick with a Powershell comment in `./zig/download.ps1` is something new for me - a script which runs both under Windows and UNIX.

### Hashes vs signatures

- Yeah, your script with sha256sum is indeed better for CI - because we want to confirm that the downloaded tar is the same as on our development machines. Even in the case of private keys leak (or just a bug in the release software), automation will not start autonomously consuming tampered software.

- However, if a dev wants to experiment with various Zig versions (“browse around”), they would need to add manually the reference to the sha256 sum to the script. In that case, iguana’s approach is more attractive.

### Per project vs per UNIX user install

Per project install is less brittle because it only puts stuff in the project’s dir. And removal of that dir cleans up all artifacts, instead of `~/.magic` or `~/.cache/magic` floating in the home dir.

On the other hand, iguana assumes that Zig 0.16.0 is the same Zig 0.16.0 for all projects used by the user - so it’s only once unpacked in `~/.iguana/ver/0.16.0`.

To reduce chances of corrupting Zig’s installation (for instance, in an editor, by browsing stdlib files), iguana removes write permissions on installed Zig:

```zig
chmod -R a-w "ver/$ZIG_RELEASE"

```

Assuming you don’t use `:w!` in vi. 😛

### Mirrors

I’ve noticed that Tigerbeetle’s `zig/download.sh` uses such URLs:

```zig
${ZIG_MIRROR}/0.14.1/zig-aarch64-linux-0.14.1.tar.xz

```

Unfortunately, it doesn’t work with mirrors from [https://ziglang.org/download/community-mirrors.txt](https://ziglang.org/download/community-mirrors.txt).

For instance, `https://zigmirror.com` has tarballs directly under the root, not in the `0.14.1` directory:

```zig
${ZIG_MIRROR}/zig-aarch64-linux-0.14.1.tar.xz

```

I was myself surprised by that.

Iguana out of the box uses mirrors instead of `https://ziglang.org/download`, because due to human nature - people will forget to set the mirror and strain `ziglang.org`.

---

<div class="post-metadata">

**Author:** ![lukeflo](https://ziggit.dev/user_avatar/ziggit.dev/lukeflo/32/7514_2.png) [@lukeflo](https://ziggit.dev/u/lukeflo)\
**Post date:** [June 30, 2026, 6:38am UTC](https://ziggit.dev/t/iguana-zig-downloader-and-version-manager-in-64-lines-of-code/16310/4 "2026-06-30T06:38:25Z")

</div>

I really like that cool project. Its dead simple, but works (and despite writing shell scripts for years, I learned a new and easy way of argument parsing from it 😉). I’m about to switch to it and leave `anyzig` behind.

Maybe an idea for a little enhancement: If you want to install the current dev version, you need to search for the (unintuitive) version number and copy it to your shell. E.g. `iguana install 0.17.0-dev.1099+7db2ef610`. While this works, it might be more convenient to be able to just type `iguana install master`.

Edit: In my fork, I added a `iguana install repo` argument which mimics `anyzig`’s behaviour of installing the version specified in `build.zig.zon`. Its just one (longer) additional line of code. Was just for fun, but maybe its an interesting option: [additional line](https://codeberg.org/lukeflo/iguana/src/commit/1b1ba2511d98aeaf667825397f0b16b85f27d40e/iguana#L43)

---

<div class="post-metadata">

**Author:** ![pancelor](https://ziggit.dev/user_avatar/ziggit.dev/pancelor/32/9684_2.png) [@pancelor](https://ziggit.dev/u/pancelor)\
**Post date:** [July 1, 2026, 1:27am UTC](https://ziggit.dev/t/iguana-zig-downloader-and-version-manager-in-64-lines-of-code/16310/5 "2026-07-01T01:27:15Z")

</div>

Neat! Nice and small. Btw I noticed a missing part of the verification: [https://codeberg.org/mslapek/iguana/issues/2](https://codeberg.org/mslapek/iguana/issues/2)  
tl;dr:

> To prevent downgrade attacks, a “file” field in the [trusted comment](https://jedisct1.github.io/minisign/#:~:text=Trusted%20comments) is provided that must be verified to match the name of the requested tarball. The reference implementation, `minisign`, will verify the trusted comment but does **not** look for a “file” field, so this verification step must be implemented manually.

> **[Community Mirrors
      ⚡
      Zig Programming Language](https://ziglang.org/download/community-mirrors/)**

---

<div class="post-metadata">

**Author:** ![giluis](https://ziggit.dev/user_avatar/ziggit.dev/giluis/32/5886_2.png) [@giluis](https://ziggit.dev/u/giluis)\
**Post date:** [July 1, 2026, 3:12pm UTC](https://ziggit.dev/t/iguana-zig-downloader-and-version-manager-in-64-lines-of-code/16310/6 "2026-07-01T15:12:30Z")

</div>

learned a lot from this post, thanks!  
I love this sort of practical learnings / tips  
Never really thought of lean software as a means to more transparent software, makes total sense

---

<div class="post-metadata">

**Author:** ![mslapek](https://ziggit.dev/user_avatar/ziggit.dev/mslapek/32/9281_2.png) [@mslapek](https://ziggit.dev/u/mslapek)\
**Post date:** [October 5, 2026, 11:31am UTC](https://ziggit.dev/t/iguana-zig-downloader-and-version-manager-in-64-lines-of-code/16310/7 "2026-10-05T11:31:38Z")

</div>

### New Zig

Iguana works out of the box with the fresh Zig 0.17.0 - without any change to the Iguana’s code itself - so we can say that the code is “antifragile”.

```zig
$ zig version
0.16.0
$ iguana install 0.17.0
$ zig version
0.17.0

```

### Features

Thank you for your insightful comments folks.

Relating to your replies (sorry for my late reply!):

The idea of the project is to provide a solid, minimalistic base, so others could:

- customize it - like @lukeflo did, interesting idea for people switching between repos.
- eyeball it for bugs - like @pancelor did, thanks! Fixed in the ticket you’ve created.

Hence, the Iguana will itself err on the side of lack of features.
