malt is a drop-in Homebrew alternative for macOS, built in Zig 0.16. It reuses every formula, bottle, cask, tap, and Brewfile from the existing Homebrew ecosystem, installs to its own prefix (/opt/malt), ships as a ~3 MB single binary, and starts in ~3 ms.
Why it exists
Two observations, briefly:
-
post_installis where most alternative Homebrew clients quietly give up. A surprising number of formulas don’t really finish installing the moment their files hit disk - they ship apost_installblock (symlinks, man pages, config files, service registration) that the binary expects at runtime. Skip it and the package extracts fine but isn’t actually usable. The native interpreter section below covers how malt handles this without spawning Ruby in the common case. -
A package manager is also infrastructure on your machine. It runs as your user, writes to a privileged-ish prefix, downloads code from the internet, and patches Mach-O headers. malt is built around that posture rather than around “smaller is better.”
A native Zig interpreter for post_install
When a formula defines post_install, malt tries the native interpreter first. It evaluates the Ruby subset these blocks actually use: Pathname ops, FileUtils, string interpolation, inreplace, Dir.glob, if/unless, the common iterator/array methods, Formula["name"] cross-lookup, ENV, %w[] arrays, boolean operators. It only activates for formulas that define the method. Source for homebrew-core formulas is fetched on demand if the tap isn’t cloned locally.
Every mutating filesystem op - write, rm, chmod, symlink - is validated against the formula’s Cellar prefix and the malt prefix. Paths containing .. or escaping via symlinks are rejected immediately. When the interpreter hits a construct it doesn’t support, the situation isn’t silent: the user is directed to --use-system-ruby, which delegates to a sandboxed Ruby subprocess scoped to that formula’s cellar, with a scrubbed environment, RLIMIT caps, and terminal escape sequences filtered from child output.
So packages like node, openssl, fontconfig, and docbook are fully configured by the time the install returns - and the absence of post_install support is reported, not silently ignored.
The install pipeline
Every install runs a single 9-step protocol under one advisory lock: acquire the lock, run pre-flight (deps, disk space, link conflicts), stream-download from the GHCR CDN with in-flight SHA256, extract to tmp/ and verify the hash against the manifest, atomic-rename into a content-addressable store/ keyed by SHA256, materialize into Cellar/ via APFS clonefile(), patch Mach-O load commands and ad-hoc codesign on arm64, create the symlink tree under bin//lib//etc., commit kegs/dependencies/links rows in a single SQLite transaction, and release the lock. Failure at any step triggers cleanup of that step only - no prior state is modified.
A few properties fall out of this:
- No unverified data ever touches the store. The hash is computed in-flight; a mismatch deletes the extracted tree before commit.
- Store entries are immutable. Mach-O patching always happens on the Cellar clone, never the store original; on failure, the Cellar copy is deleted and the store stays pristine for retry.
- Rollback is unlink → re-clone → DB update. Every previous bottle is still in the store, so reverting doesn’t re-download anything.
- Upgrades stage the new version fully before the old one is touched. On failure, old symlinks are restored.
- Read-only commands don’t acquire the lock.
list,info,searchnever block a concurrent install.
Benchmarks
The README has a benchmarks section with cold and warm install times for tree, wget, and ffmpeg. Each cell is the median of 5 rounds; BENCH_TRUE_COLD=1 wipes both the install prefix and the bottle download cache before every cold sample, so “cold” really means “no bottle anywhere on disk.”
The AI angle
malt is a study in human-directed AI implementation. Every line of Zig was written by AI (Claude + ruflo); the design, architecture, threat model, ADRs, and reviews were human.
The fragile thing about AI-generated code isn’t the first commit - it’s the tenth. malt has been refactored end-to-end more than once: the install protocol above, the post_install interpreter, the Mach-O patcher. Every round was steered by design notes, ADRs, and security review the model wouldn’t have written on its own.
The repo, the test suite (~78% line coverage via kcov), and the running tool are the evidence on offer.
Try it
curl -fsSL https://raw.githubusercontent.com/indaco/malt/main/scripts/install.sh | bash
mt install jq wget ripgrep
GitHub: GitHub - indaco/malt: A fast, drop-in Homebrew alternative for macOS. Warm installs in milliseconds. post_install scripts that actually run. · GitHub
Feedback - especially on the Zig and interpreter sides - is very welcome.