A Homebrew client in Zig. Warm installs in milliseconds. post_install scripts that actually run.
Note
Experimental project. All implementation code in malt was written by AI (Claude Code and ruflo). The design, architecture, and every merged change were directed and reviewed by a human. It's a hands-on look at how far human + AI pair-programming can go on a non-trivial systems project — and the tool actually works.
malt is a macOS package manager written in Zig that reuses Homebrew's formula, bottle, cask, and tap ecosystem — a client for the registry, not a fork. Single binary, ~3 MB, sub-millisecond cold start. Requires macOS 11+ on Apple Silicon or Intel.
Unlike other alternative clients, malt runs Homebrew post_install blocks natively via a built-in Zig interpreter, so packages like node, openssl, fontconfig, and docbook are fully configured at install time. On warm installs — the common case after day one — malt is the fastest tool measured on packages with dependencies. See Benchmarks.
Features · Install · Quick Start · Commands · Post-Install · Benchmarks
- Native post_install execution — a built-in Zig interpreter runs Homebrew
post_installscripts that every other tool skips, so packages actually work after install - System Ruby fallback —
--use-system-rubydelegates to any installed Ruby for the handful of scripts the interpreter doesn't cover - Isolated — installs to its own prefix, never touches Homebrew's files
- Deduplicated storage — identical files across versions are stored only once
- Parallel downloads — fetches multiple packages at the same time
- Brew fallback — hands off to Homebrew for anything it doesn't support
- Rollback — revert to a previous version of any package
- Ephemeral run —
malt runlaunches a formula without installing it permanently - Services —
malt servicesmanages long-running launchd processes,brew services-compatible - Bundles —
malt bundle installreads existingBrewfiles with no conversion - Path-sandboxed execution — the DSL interpreter validates all writes stay within the package prefix
- Safe under concurrency — multiple malt processes won't corrupt state
curl -fsSL https://raw.githubusercontent.com/indaco/malt/main/scripts/install.sh | bashDownloads the latest release, verifies the SHA256 checksum, installs the binary to /usr/local/bin/, and creates /opt/malt with proper ownership. Falls back to building from source if no release is available.
brew tap indaco/tap
brew install maltClone the repo and run the install script — it detects the local checkout and builds from source automatically:
git clone https://github.com/indaco/malt.git
cd malt
./scripts/install.shRequires Zig 0.15.x.
Note
zig build produces both malt and mt in zig-out/bin/. Both are identical — use whichever you prefer. All install methods (script, Homebrew, source) install both.
Tip
mt is a built-in alias for malt. Every command works with either name — use mt if you prefer fewer keystrokes.
Additional aliases: remove for uninstall, ls for list.
# Install a formula (resolves dependencies automatically)
malt install wget
# Install a cask (auto-detected)
malt install --cask firefox
# Install from a tap (inline — no separate tap step)
malt install user/tap/formula
# Install multiple packages (downloads in parallel)
malt install jq wget ripgrep
# List installed packages
malt list --versions
# Uninstall
malt uninstall wgetThe examples below use
mt(the shorter alias). All commands work identically withmalt.
Install formulas, casks, or tap formulas.
mt install <package> # auto-detect formula or cask
mt install <package>@<version> # versioned formula (e.g. openssl@3)
mt install --cask <app> # explicit cask
mt install --formula <name> # explicit formula
mt install <user>/<tap>/<formula> # inline tap (no separate tap step)
mt install <package> [<package> ...] # multiple packages| Flag | Description |
|---|---|
--cask |
Force cask installation |
--formula |
Force formula installation |
--dry-run |
Show what would be installed without installing |
--force |
Overwrite existing installations |
--use-system-ruby |
Execute post_install via system Ruby (fallback) |
--quiet, -q |
Suppress all output except errors |
--json |
Output result as JSON |
[!INFO] Post-install scripts run natively. malt includes a built-in interpreter that executes Homebrew
post_installblocks in Zig — no Ruby required. Packages likenode,openssl,fontconfig, anddocbookare fully configured at install time. For the small number of scripts the interpreter doesn't cover, add--use-system-rubyto delegate to any available Ruby, or usebrew installas a fallback. See Post-Install DSL Interpreter for details.
Remove installed packages.
mt uninstall <package>
mt uninstall --cask <app>
mt uninstall <package> --force # ignore dependents check| Flag | Description |
|---|---|
--force, -f |
Remove even if other packages depend on it |
--cask |
Force cask uninstall |
Checks for dependent packages before removing. If dependents exist, refuses unless --force is passed. For casks, checks if the application is running and refuses unless --force is passed. Store entries are preserved for mt purge --store-orphans.
Upgrade installed packages to latest versions.
mt upgrade <package> # upgrade a specific formula or cask
mt upgrade --cask # upgrade all outdated casks
mt upgrade --formula # upgrade all outdated formulas
mt upgrade --dry-run # show what would be upgraded| Flag | Description |
|---|---|
--cask |
Upgrade casks only |
--formula |
Upgrade formulas only |
--dry-run |
Preview without upgrading |
Formula upgrades install the new version, verify it, switch symlinks atomically, and only remove the old version after success. On failure, the old version is restored automatically.
Refresh the local formula/cask metadata cache.
mt updateInvalidates all entries in the API cache. The next install, search, or info command fetches fresh data from the Homebrew API.
List packages with newer versions available.
mt outdated
mt outdated --json
mt outdated --cask
mt outdated --formula| Flag | Description |
|---|---|
--json |
Output as JSON |
--formula |
Show outdated formulas only |
--cask |
Show outdated casks only |
--quiet, -q |
Suppress status messages |
Compares installed versions against the latest from the Homebrew API. Checks both formulas and casks by default.
wget (1.24.5) < 1.25.0
openssl@3 (3.3.2) < 3.4.1
List installed packages.
mt list
mt list --versions
mt list --cask
mt list --formula
mt list --pinned
mt list --jsonShow detailed information about a formula or cask.
mt info <package>
mt info <package> --json
mt info --cask <app>
mt info --formula <name>| Flag | Description |
|---|---|
--formula |
Show formula info only |
--cask |
Show cask info only |
--json |
Output as JSON |
Auto-detects whether the package is a formula or cask. For formulas, shows version, tap, cellar path, and pinned status. For casks, shows version, download URL, app path, and auto-update status.
Search formulas and casks by name.
mt search <query>
mt search <query> --formula
mt search <query> --cask
mt search <query> --jsonSystem health check.
mt doctor
mt doctor --post-install-status # check DSL support per installed formula| Check | Pass | Fail |
|---|---|---|
| SQLite integrity | PRAGMA integrity_check returns ok |
Error: database corrupt |
| Directory structure | All required directories exist under prefix | Warn: missing directory |
| Stale lock | No lock file, or lock PID is running | Warn: suggest removal |
| APFS volume | /opt/malt is on APFS |
Warn: clonefile unavailable |
| API reachable | HEAD to formulae.brew.sh returns 2xx |
Warn: offline |
| Orphaned store | All store entries referenced by a keg | Warn: suggest mt purge --store-orphans |
| Missing kegs | All DB keg paths exist on disk | Error: suggest reinstall |
| Broken symlinks | All symlinks in bin/, lib/ etc. resolve | Warn: suggest mt purge --housekeeping |
| Disk space | > 1 GB free on prefix volume | Warn: low disk space |
| Post-install DSL | All installed post_install formulae parseable | Warn: unsupported construct |
Exits with code 0 (all OK), 1 (warnings found), or 2 (errors found).
Unified housekeeping and full-wipe command. A scope flag selects what to remove — mt purge with no scope is an error.
# Housekeeping
mt purge --store-orphans # refcount-0 store blobs (was: mt gc)
mt purge --unused-deps # orphaned dep kegs (was: mt autoremove)
mt purge --cache=30 # cache files older than N days
mt purge --stale-casks # cache + Caskroom for uninstalled casks
mt purge --housekeeping # all four safe scopes at once
# Destructive (typed-confirm unless --yes)
mt purge --downloads # wipe {cache}/downloads entirely
mt purge --old-versions # remove non-latest Cellar versions
mt purge --wipe # nuclear: every malt artefact on disk
# Combine, preview, gate
mt purge --store-orphans --cache=7 --dry-run
mt purge --wipe --backup ~/snapshot.txt --remove-binary --yes| Scope | Removes | Confirm gate |
|---|---|---|
--store-orphans |
Refcount-0 blobs in {prefix}/store |
none |
--unused-deps |
Indirect-install kegs no other package needs | none |
--cache[=DAYS] |
Cache files older than DAYS (default 30) | none |
--downloads |
Entire {cache}/downloads directory |
type downloads |
--stale-casks |
Cask cache + Caskroom entries for uninstalled casks | none |
--old-versions |
Non-latest version directories in {prefix}/Cellar |
type old-versions |
--housekeeping |
= --store-orphans --unused-deps --cache --stale-casks |
none |
--wipe |
Every malt artefact on disk (mutually exclusive) | type purge |
| Shared flag | Description |
|---|---|
--dry-run, -n |
Preview every removal without touching disk |
--yes, -y |
Skip every typed-confirmation prompt |
--quiet, -q |
Suppress per-item output |
--backup, -b path |
Write a mt restore-compatible manifest before any deletion |
--wipe-only flag |
Description |
|---|---|
--keep-cache |
Preserve the cache directory (downloaded bottles stay on disk for reinstall) |
--remove-binary |
Also unlink /usr/local/bin/{mt,malt} (opt-in — they live outside prefix) |
--wipe cannot be combined with any other scope flag — it already supersedes them. For everything except --wipe, multiple scopes can be passed in a single invocation and run sequentially under one lock acquisition.
Acquires {prefix}/db/malt.lock before any destructive scope runs so concurrent malt processes cannot race; for --wipe, the lock is released before removing the db/ directory itself. Honours MALT_PREFIX and MALT_CACHE, so pointing those at a throwaway path is the safe way to test the command end-to-end.
Use mt uninstall <name> for per-package removal — mt purge deals exclusively with housekeeping artefacts and full uninstalls.
Manage taps explicitly. Taps are auto-resolved during install, so this is optional.
mt tap <user>/<repo> # register a tap
mt tap # list registered taps
mt untap <user>/<repo> # remove a tapImport an existing Homebrew installation.
mt migrate
mt migrate --dry-runScans the Homebrew Cellar, resolves each installed package via the API, and installs it through malt. Does not modify the Homebrew installation. Packages with post_install hooks are executed via the native DSL interpreter; unsupported scripts fall back to --use-system-ruby or are skipped with a report.
Dump the list of directly-installed formulas and casks to a plain-text file for later restoration on the same or another machine.
mt backup # writes malt-backup-<timestamp>.txt to cwd
mt backup --output my-setup.txt # custom path
mt backup -o - # write to stdout
mt backup --versions # pin each entry to its installed version| Flag | Description |
|---|---|
--output, -o |
Destination path (- for stdout) |
--versions |
Append @<version> to each entry for exact pinning |
--quiet, -q |
Suppress status messages |
Only directly-installed formulas are recorded — transitive dependencies are resolved again on restore. The file format is plain text, one entry per line (formula <name> or cask <token>), with # comments. It is safe to hand-edit before restoring.
Reinstall every entry in a backup file produced by mt backup.
mt restore my-setup.txt
mt restore my-setup.txt --dry-run # preview what would be installed
mt restore my-setup.txt --force # pass --force to the underlying installs| Flag | Description |
|---|---|
--dry-run |
Print the list of packages without installing |
--force |
Forward --force to mt install for each package |
--quiet, -q |
Suppress status messages |
Formulas and casks are batched into two mt install invocations, so dependency resolution, parallel downloads, and the atomic install protocol all apply. Lines prefixed with # and blank lines are ignored, and entries with a @<version> suffix are installed at that exact version.
Manage long-running background processes via launchd. Equivalent to brew services.
mt services list # show registered services + runtime state
mt services start postgresql@16 # bootstrap into the user launchd domain
mt services stop postgresql@16
mt services restart postgresql@16
mt services status postgresql@16 # combined DB + launchctl state
mt services logs postgresql@16 --tail 50 # last 50 lines of stdout
mt services logs postgresql@16 --stderr # read stderr instead| Subcommand | Description |
|---|---|
list |
Show every registered service with running / loaded / stopped |
start |
Generate and launchctl bootstrap the service into gui/<uid> |
stop |
launchctl bootout the service |
restart |
stop then start |
status |
Combined DB record + live launchctl list state |
logs |
Tail stdout.log (or stderr.log with --stderr); --tail N sets count |
Services are registered automatically when an installed formula carries a service block (e.g. postgresql@16, redis). State lives at {prefix}/var/malt/services/<name>/ (plist + log files) and in the SQLite services table. Currently macOS-only — Linux/Windows return OsNotSupported.
Group-install and export sets of packages. Drop-in for brew bundle: reads existing Brewfiles with no conversion.
mt bundle install # ./Brewfile or ./Maltfile.json
mt bundle install path/to/Brewfile # explicit file
mt bundle install --dry-run # print what would happen
mt bundle create # snapshot installed → ./Brewfile
mt bundle create --format json my.json # JSON output
mt bundle export # print current install to stdout
mt bundle export --format json my-bundle # named bundle, JSON
mt bundle list # registered bundles
mt bundle remove devtools # unregister (does not uninstall)
mt bundle import path/to/Brewfile # register without installing| Subcommand | Description |
|---|---|
install |
Install every member; idempotent — already-installed members are skipped |
create |
Write the currently-installed set to a bundle file |
list |
List bundles registered in the database |
remove |
Unregister a bundle (use --purge to also uninstall its members) |
export |
Print bundle (or current install) to stdout in brewfile or json format |
import |
Register a bundle definition without installing |
| Flag | Description |
|---|---|
--dry-run |
Print every member action without forking |
--format <fmt> |
brewfile (default) or json |
--from-installed |
(create) populate from currently-installed packages |
--purge |
(remove) also uninstall each member |
Bundlefile lookup order (when no path is given): ./Brewfile → ./Maltfile.json → ~/.config/malt/Brewfile → ~/.config/malt/Maltfile.json.
Brewfile compatibility: malt parses the standard directive set (tap, brew, cask, mas, vscode) including hash options (version:, restart_service:, link:) and Ruby symbols (restart_service: :changed). Conditionals (if OS.mac?) and do … end blocks are rejected with a clear error pointing to Maltfile.json for power-user cases.
Revert a formula to its previous version using the content-addressable store.
mt rollback <package>
mt rollback <package> --dry-runThe store retains all previously installed bottle versions. Rollback unlinks the current version, materializes the previous one from the store, and updates the database. No re-download needed.
Run a package binary without installing it.
mt run <package> -- <args...>
mt run jq -- --version
mt run ripgrep -- --helpDownloads the bottle to a temp directory, extracts the binary, executes it with the provided arguments, and cleans up. If the package is already installed, runs the installed binary directly.
Manage symlinks for installed kegs.
mt link <formula> # create prefix symlinks for a keg
mt link <formula> --overwrite # replace conflicting symlinks
mt unlink <formula> # remove symlinks (keg stays installed)| Flag | Description |
|---|---|
--overwrite, --force, -f |
Replace existing symlinks |
link scans for symlink conflicts before creating links. If conflicts are found, it reports them and aborts unless --overwrite is passed. unlink removes symlinks from bin/, lib/, etc. and the opt/ symlink, but leaves the keg installed in the Cellar.
Show the current version or self-update the binary.
mt version # show current version
mt version update # download and install latest
mt version update --check # check without installingupdate queries the GitHub releases API, downloads the correct binary for the current platform, and replaces the running binary in-place.
Generate a shell completion script for bash, zsh, or fish. The script is printed to stdout, so it can be eval'd immediately or redirected to a file for permanent install.
# Temporary (current shell only)
eval "$(malt completions bash)"
eval "$(malt completions zsh)" # run AFTER `compinit`
malt completions fish | source
# Permanent
malt completions bash > /usr/local/etc/bash_completion.d/malt
malt completions zsh > "${fpath[1]}/_malt"
malt completions fish > ~/.config/fish/completions/malt.fishCompletes subcommands (for both malt and mt), per-command flags, global flags, and the positional shell name for completions itself. Unknown shell names exit non-zero with an error.
| Flag | Description |
|---|---|
--verbose, -v |
Verbose output (all commands) |
--quiet, -q |
Suppress non-error output (all commands) |
--json |
JSON output (read commands) |
--dry-run |
Preview without executing (mutating commands) |
--help, -h |
Show help |
--version |
Show version |
malt installs to /opt/malt — its own prefix, fully isolated from Homebrew. The shorter path guarantees that Mach-O load command patching always has room to replace the original Homebrew path.
/opt/malt/
├── store/ # Content-addressable bottle storage (immutable, by SHA256)
├── Cellar/ # Installed kegs (APFS cloned from store/)
├── Caskroom/ # Installed cask applications
├── opt/ # Versioned formula symlinks
├── bin/ # Symlinks to keg binaries
├── lib/ # Symlinks to keg libraries
├── include/ # Symlinks to keg headers
├── share/ # Symlinks to keg shared data
├── tmp/ # In-progress downloads and extractions
├── cache/ # Cached API responses (TTL-based)
└── db/ # SQLite database + advisory lock
Bottles are stored by their SHA256 hash. The same bottle is never downloaded or extracted twice. Multiple installed kegs can reference the same store entry. Store entries are immutable — only mt purge --store-orphans removes them.
Kegs in Cellar/ are materialized from store/ via APFS clonefile(), which creates a copy-on-write clone at zero disk cost. On non-APFS volumes, a regular recursive copy is used as fallback.
Each bottle download is a single-pass pipeline:
Network (HTTPS from GHCR CDN)
├──-> SHA256 hasher (streaming — computed as chunks arrive)
└──-> gzip/zstd decompressor
└──-> tar extractor
└──-> filesystem write to tmp/
No intermediate archive file is written to disk. The SHA256 is verified against the Homebrew API manifest immediately after the stream completes. On mismatch, the extracted directory is deleted.
Homebrew bottles contain hardcoded paths like /opt/homebrew/Cellar/... in Mach-O load commands. Since malt uses its own prefix, these paths must be rewritten.
malt parses Mach-O headers using struct-aware parsing (not raw byte scanning), identifies all relevant load commands (LC_ID_DYLIB, LC_LOAD_DYLIB, LC_RPATH, etc.), and replaces paths in-place, padding the remaining space with null bytes. On arm64, every patched binary is ad-hoc codesigned via codesign --force --sign -.
Text files (.pc configs, shell scripts) containing @@HOMEBREW_PREFIX@@ or @@HOMEBREW_CELLAR@@ placeholders are also patched.
Patching is always performed on the Cellar copy, never the store original. If patching fails, the Cellar copy is deleted and the store entry remains pristine for retry.
Some packages aren't really installed the moment their files hit disk. They ship a post_install block that creates symlinks, sets up man pages, generates caches, or writes config files the binary expects to find at runtime. Skip that step and the package extracts fine but isn't usable. malt runs these scripts natively — no Ruby runtime required.
malt includes a native Zig interpreter for the Ruby subset used in post_install blocks. It adds under 80 KB to the binary and activates only for formulae that define the method.
- Parses and evaluates Ruby source in a sandboxed context bound to the formula's Cellar and prefix paths
- Covers Pathname ops, FileUtils, string interpolation,
inreplace,Dir.glob,if/unless,.each/.select/.map,Formula["name"]cross-lookup,ENVaccess,%w[]arrays,&&/||/!, and more - Enforces write boundaries — any filesystem mutation targeting a path outside the formula's Cellar prefix or the malt prefix is rejected outright
- Falls back cleanly — unsupported constructs are logged and the user is directed to
--use-system-ruby - Fetches source on demand — if the homebrew-core tap isn't cloned locally, the
.rbsource is fetched directly from GitHub
Execution cascade:
Formula has post_install?
|
yes --> Try native DSL interpreter
| |
| success --> done (package fully configured)
| |
| unsupported construct --> --use-system-ruby set?
| |
| yes --> delegate to Ruby subprocess
| no --> skip with clear message
|
no --> done (no post_install needed)
Every install follows a strict 9-step protocol. Failure at any step triggers cleanup of that step only — no prior state is modified.
- Acquire lock — exclusive advisory lock on
db/malt.lock - Pre-flight — resolve dependencies, check disk space, detect link conflicts
- Download — fetch bottles from GHCR CDN with streaming SHA256 verification
- Extract — decompress and untar to
tmp/ - Commit to store — atomic rename from
tmp/tostore/ - Materialize — APFS clonefile from
store/toCellar/, patch Mach-O, codesign - Link — create symlinks in
bin/,lib/, etc., record in DB - DB commit — insert into kegs, dependencies, links tables in a single transaction
- Release lock — clean up tmp files
- SHA256 verification — streaming hash computed during download, verified before extraction. No unverified data touches the store.
- Pre-flight checks — dependencies resolved, disk space verified, and link conflicts detected before any download begins.
- Link conflict detection — all target symlink paths scanned before creating any links. Conflicts abort the operation with a clear report.
- Atomic installs — the 9-step protocol uses
errdeferat every stage. Interrupted installs leave no partial state. - Concurrent access — an advisory file lock with a 30-second timeout prevents concurrent mutations. Read-only commands (
list,info,search) do not acquire the lock. - Upgrade rollback — new version is fully installed and verified before the old version is touched. On failure, old symlinks are restored.
- Store immutability — store entries are never modified after commit. Patching happens on the Cellar clone. Only
mt purge --store-orphansdeletes store entries. - DSL path sandboxing — the post_install interpreter validates every mutating filesystem operation (write, rm, chmod, symlink) against the formula's Cellar prefix and the malt prefix. Paths containing
..or resolving outside the sandbox via symlinks are rejected immediately.
| Variable | Description | Default |
|---|---|---|
MALT_PREFIX |
Override install prefix | /opt/malt |
MALT_CACHE |
Override cache directory | {prefix}/cache |
NO_COLOR |
Disable colored output | unset |
MALT_NO_EMOJI |
Disable emoji in output | unset |
HOMEBREW_GITHUB_API_TOKEN |
GitHub token for higher API rate limits | unset |
For commands not implemented by malt, malt checks if brew is installed and silently delegates the command to it.
If brew is not found, malt prints:
malt: '<cmd>' is not a malt command and brew was not found.
Install Homebrew: https://brew.sh
# Requires Zig 0.15.x
zig build # debug build
zig build -Doptimize=ReleaseSafe # release build (~3 MB)
zig build test # run tests
zig build universal # universal binary (arm64 + x86_64 via lipo)Install times on macOS 14 (Apple Silicon), comparing malt against other Homebrew-compatible package managers.
| Tool | Size |
|---|---|
| malt | 3.4 MB |
| nanobrew | 1.4 MB |
| zerobrew | 8.6 MB |
| bru | 1.8 MB |
| Package | malt | nanobrew | zerobrew | bru | Homebrew |
|---|---|---|---|---|---|
| tree (0 deps) | 0.945s | 0.460s | 2.119s | 0.991s‡ | 4.315s |
| wget (6 deps) | 4.959s | 6.913s | 6.537s | 0.358s‡ | 4.617s |
| ffmpeg (11 deps) | 4.191s | 3.737s | 8.299s | 3.926s‡ | 20.207s |
| Package | malt | nanobrew | zerobrew | bru |
|---|---|---|---|---|
| tree (0 deps) | 0.069s | 0.010s | 0.398s | 0.041s |
| wget (6 deps) | 0.024s | 0.585s | 0.692s | 0.074s |
| ffmpeg (11 deps) | 0.100s | 1.548s | 3.270s | 1.767s |
Note
Benchmarks on Apple Silicon (GitHub Actions macos-14), 2026-04-13. Auto-updated weekly via benchmark workflow.
Every package is installed cold exactly once per machine — the first time you type mt install ffmpeg on a fresh checkout. Everything after that — upgrades, reinstalls, dev-environment rebuilds (devbox, nix-style), CI cache restores, post-cleanup reinstalls — is a warm install against the existing store. In a realistic developer workflow the ratio is roughly 1 cold : 10+ warm over a machine's lifetime, so the warm row is where the minutes actually add up.
On that row, malt is the fastest tool measured across packages with dependencies — the common case (wget has 6 deps, ffmpeg has 11 — most useful packages do). Warm tree (0 deps) is within 1 ms of nanobrew, effectively tied. Cold installs are competitive but represent a one-time cost you pay per package, not an ongoing one.
Put plainly: the number that matters after your first day using malt is the warm row, and on that row malt is the fastest tool measured here.
malt trades a few ms against correctness and features that the lighter tools skip:
- SQLite state (~1.5 MB of the 3 MB binary) — ACID writes, reverse-dep queries (
mt uses openssl@3), linker conflict detection, atomic rollback. nanobrew and bru use a flatstate.json. - Native
post_installinterpreter — runs Homebrew post-install blocks in Zig, no Ruby subprocess. bru and zerobrew skip post_install entirely; nanobrew regex-scrapes a handful of patterns. - Ad-hoc codesign on arm64 (~15 ms/pkg) — every Mach-O patched to rewrite
/opt/homebrew→MALT_PREFIXis re-signed sodyldwill load it. - Global install lock + pre-link conflict check (~3 ms) —
flockondb/malt.lock+ a symlink-tree walk refusing to overwrite another keg's files.
Methodology. BENCH_TRUE_COLD=1 wipes each tool's prefix between cold and warm runs, so "cold" really means "no bottle in the store." See scripts/bench.sh. One caveat: bru keeps its download cache under ~/.bru/ and ~/Library/Caches/bru/, outside the wiped prefix, so bru's cold numbers reflect warm cache + materialise rather than a real network fetch. bru's warm row is apples-to-apples.
Contributions are welcome. Please open an issue to discuss before submitting large changes.
malt is licensed under the MIT License.
Third-party components and upstream projects — including Homebrew (BSD-2-Clause) and homebrew-core (BSD-2-Clause) — are acknowledged in the LICENSE file.
