grew is what happens when you look at your package manager and think: "This could be so much simpler." Deterministic installs. Clean symlinks. A doctor that actually tells you what's wrong. No drama.
Formula + cask installs with SHA256 verification (no funny business)
Tap auto-install β automatically clones missing taps when you request user/repo/formula
Multi-hop binary delta updates β selfupdate uses bspatch to seamlessly apply sequences of intermediate patches to reach the latest version, saving bandwidth, with an automated CI patcher tool for releases and -U upgrade path verification
Dual-hash verification β self-updates and release assets are verified against both SHA256 and SHA512 to prevent single-algorithm collision attacks
Ed25519 bottle signing β cryptographic signatures on downloads, verified against a local trust store
Self-update health check β patched binaries are execution-tested in a sandbox before replacement, with multi-hop binary delta patching via bspatch
Signed tap verification β refuse or warn on unsigned git commits in tap repos (HOMEGREW_TAP_VERIFY)
Sandboxed source builds β macOS Seatbelt to keep your system safe
Sandboxed post-install scripts β keg is read-only, network denied, minimal env (Homebrew runs these unsandboxed)
Install snapshots β per-file SHA-256 manifests (.MANIFEST.json) recorded at install time for integrity verification
Installation receipts β stores build options, dependencies, and provenance metadata (INSTALL_RECEIPT.json) in the keg for future reference
Lockfile β pin exact versions, hashes, and dependency trees for reproducible environments
Deterministic linking with opt symlinks and dry-run support (look before you link)
Symlink conflict detection β ownership tracking prevents accidental overwrites when multiple formulas claim the same binary; version-family defense blocks incompatible family members (e.g., node@24 when node is already linked)
Comprehensive documentation β architecture guides, security deep-dives, and dependency resolver design in expanded technical documentation; contributor guides for understanding the codebase
Keg relocation β rewrites hardcoded library paths in bottles at install time via install_name_tool, so binaries just work without DYLD_LIBRARY_PATH hacks
Dependency resolver with an optional tree view (for the visually inclined)
Missing dependency checker β validates runtime dependencies are available on the system; useful for catching broken kegs or library mismatches
Description search β query by formula name, one-line description, or regex across both formulae and casks with flexible filtering
Doctor + audit β checks perms, HTTPS, broken links, snapshot integrity, stale kegs, and cask notarization
Hardened command execution β -- end-of-options on all external commands, shell-free namespace setup with positional parameters, XML-safe plist generation
Zip Slip protection β archive extraction validates symlink indirection to prevent writes outside the destination
Vulnerability scanning β queries OSV.dev for known CVEs via the integrated vuln-scan command
macOS Quarantine β automatically applies com.apple.quarantine attributes to downloaded apps and binaries, ensuring Gatekeeper protection is active
Structured logging via log/slog with CLI-friendly output (DEBUG/INFO/WARN/ERROR levels, -v/-d/-q flags). Debug logs include source file and line number context.
Colorful output β ANSI-colored output with automatic TTY detection for a polished, Homebrew-like aesthetic
Alias + shellenv helpers so your workflows stay snappy (i, rm, ls, up, ug, dr)
Download the latest release for your platform from the Releases page, extract it, and run setup:
# Download from GitHub Releases
tar -xzf grew_*.tar.gz
./grew setup
Prerequisites: Go 1.26+, git, and a dream.
git clone https://github.com/homegrew/grew.git
cd grew
make build # or: go generate ./pkg/... && go build -o grew
grew needs a home β a directory tree for the Cellar, symlinks, taps, and config. The setup command creates it and copies the binary into place:
./grew setup # macOS ARM β /opt/homegrew, Intel β /usr/local/homegrew
The system prefix isolates sandboxed builds from $HOME, preventing them from reaching ~/.ssh, ~/.gnupg, or other sensitive dotfiles. After setup, ownership is transferred to your user β no root needed at runtime.
Developer builds (make dev) can install to ~/.homegrew without root using ./grew setup --unsafe.
# bash (~/.bashrc) or zsh (~/.zshrc)
eval "$(grew shellenv)"
# fish (~/.config/fish/config.fish)
grew shellenv fish | source
grew i jq # 'i' is an alias for 'install'
grew install --cask firefox # going big
That's it. No dark rituals. No 47-step setup guide.
grew i jq nmap # install multiple formulas (alias 'i')
grew install --force jq # force reinstall even if already installed
grew install -s ldns # build from source, like a purist
grew install --force-bottle jq # pour a bottle (current or newest macOS), never build
grew install --cask firefox # going big
grew link jq # stitch it in
grew deps --tree jq # what hath jq wrought
grew up # stay fresh (alias for update)
grew ug # upgrade all (alias for upgrade)
grew version # what are we running
grew autoremove --dry-run # see which orphaned dependencies would be removed
grew autoremove # clean up unused dependencies
grew rm --force jq # uninstall even if not installed (alias 'rm')
grew cleanup -n # peek before you sweep
grew cleanup --scrub # aggressive cache cleaning
grew cleanup --prune=7 # remove cache older than a week
grew verify jq # check installed files against manifest
grew vuln-scan -q # scan for CVEs, only show critical/high severity findings
grew lock # pin your environment
grew audit --strict # lint your formulas
grew cache # show download cache
grew cache jq # show cache path for jq
grew cache --os=darwin jq # show cache path for a different OS
grew leaves -r | xargs grew uninstall # uninstall all top-level packages installed on request
grew dr # check for common problems (alias for doctor)
grew stores most of its data in its prefix directory, but some items (like Cask applications and background services) are linked to system directories. To completely remove grew and all of its traces:
# Stop and remove all background services
for s in $(grew services ls | awk 'NR>1 {print $1}'); do grew services stop $s; done
# Uninstall all macOS casks
for c in $(grew list --cask | awk '{print $1}'); do grew uninstall --cask $c; done
# macOS (Apple Silicon):
sudo rm -rf /opt/homegrew
# macOS (Intel):
sudo rm -rf /usr/local/homegrew
# Devmode (User-local install via --unsafe):
rm -rf ~/.homegrew
Open your shell configuration file (e.g., ~/.zshrc, ~/.bashrc, or ~/.config/fish/config.fish) and remove the line that initializes grew:
# Remove this line:
eval "$(/opt/homegrew/bin/grew shellenv)"
Restart your terminal, and grew is completely gone.
| Command | What it does |
|---|---|
install, i | Install formulas or casks (-f to force, -s to build from source, --force-bottle to force a bottle) |
uninstall, rm | Send formulas or casks to the void (-f to ignore missing or errors, delete all versions) |
autoremove | Transitively uninstall formulae that were only installed as a dependency and are no longer needed β removes the full orphan chain in one run (--dry-run supported) |
reinstall | Uninstall + install from scratch (--cask, -f without checking for previously installed keg-only or non-migrated versions) |
list, ls | See what you've collected |
leaves | List installed formulas that are not dependencies of another installed formula (-r for explicitly requested, -p for orphaned dependencies) |
info | Stalk packages |
search | Find the thing |
link | Weave formulas into your PATH |
unlink | Cut the thread |
update, up | Refresh tap definitions |
upgrade, ug | Get the new hotness |
outdated | The hall of shame |
cleanup | Remove old versions and prune download cache (-s to scrub all, --prune=DAYS) |
deps | Dependency spelunking (--tree for the visual view) |
verify | Check installed packages against their snapshot manifests |
vuln-scan | Scan installed packages for security vulnerabilities (OSV.dev) |
audit | Lint formula/cask definitions for quality and security |
create <url> | Scaffold a new formula from a URL (infers name, version, and SHA256) |
homepage <formula> | Open a formula or cask's homepage in the default browser |
uses <formula> | Show installed formulae that depend on the specified formula |
tap <user/repo> | Add a formula repository |
untap <user/repo> | Remove a tapped formula repository |
linkage <formula> | Inspect dynamic library dependencies for an installed formula |
lock | Generate, check, or show a reproducible lockfile |
sign | Sign formula checksums with an Ed25519 key |
services | Manage background services (start, stop, restart, list) |
setup | One-time prefix setup (requires sudo unless --unsafe) |
alias | Name things your way |
doctor, dr | It's not a bug, it's a misconfiguration |
pin / unpin | Freeze formulas to prevent upgrades |
completion | Generate shell completion (bash, zsh, fish) |
cache | Display download cache root or specific package cache paths |
config | What grew thinks it knows |
shellenv | Wire up your shell |
version | Print version and exit |
help | You got this |
grew keeps its stuff tidy under one roof. Tweak it with env vars:
| Variable | Default | What it is |
|---|---|---|
HOMEGREW_PREFIX | (inferred from binary location) | Root of the grew tree |
HOMEGREW_APPDIR | /Applications | Where casks live |
HOMEGREW_TAP_VERIFY | off | Tap commit signature policy (off, warn, strict) |
HOMEGREW_ALLOWED_HOSTS | (built-in allowlist) | Additional hosts for SSRF-protected downloads |
HOMEGREW_CLEANUP_MAX_AGE_DAYS | 120 | Max age in days for cached downloads |
Everything flows from the prefix:
/opt/homegrew/ (or /usr/local/homegrew on Intel)
βββ Cellar/ β installed packages (each keg has a .MANIFEST.json)
βββ Taps/ β formula definitions (git-cloned or API-fetched)
βββ bin/ β symlinked binaries
βββ lib/ β symlinked libraries
βββ include/ β symlinked headers
βββ opt/ β per-formula keg symlinks
βββ etc/ β trusted-keys (Ed25519 public keys, one per line)
βββ tmp/ β ephemeral stuff
βββ var/log/ β audit log
βββ grew.lock β lockfile (opt-in, created by `grew lock`)
grew is designed to be more secure than Homebrew out of the box:
| Feature | grew | Homebrew |
|---|---|---|
| Bottle signing | Ed25519 signatures verified against local trust store | None β relies on HTTPS + SHA256 only |
| Tap verification | Optional GPG/SSH commit signature enforcement (HOMEGREW_TAP_VERIFY) | None |
| Post-install sandbox | Read-only keg, no network, minimal env | Unsandboxed |
| Source build sandbox | macOS Seatbelt, no network | macOS Seatbelt only |
| Dual-hash verification | Self-updates and release assets use both SHA256 and SHA512 | None |
| Self-update health check | Patched binaries are execution-tested in a sandbox before replacement | None |
| Install manifests | Per-file SHA256 snapshot (.MANIFEST.json) at install time | None |
| Installation receipts | Provenance and dependency metadata (INSTALL_RECEIPT.json) stored alongside the manifest | Metadata stored in INSTALL_RECEIPT.json |
| Lockfile | Full dependency tree with hashes | None |
| Integrity check | grew verify + grew doctor snapshot check | None |
| Vulnerability scanning | Integrated vuln-scan powered by OSV.dev | Requires external gems/tools |
| macOS Quarantine | Automatically applies com.apple.quarantine to all downloads via LaunchServices | None |
| HTTPS enforcement | At parse time β HTTP URLs rejected before download | At download time |
| Path traversal protection | Validated at cellar, linker, loader, and archive extraction layers | Partial |
| Shell injection prevention | Namespace setup uses positional parameters to eliminate injection risks; systemd ExecStart and launchd plist values properly escaped | N/A |
| Zip Slip protection | Symlink indirection attacks blocked during tar/zip extraction | Partial |
| Command hardening | -- end-of-options on all external commands (git, systemctl, launchctl, hdiutil, tar, etc.) | Not consistently applied |
Gradual rollout: signature verification doesn't block installs until you add keys to etc/trusted-keys. Tap verification is opt-in via HOMEGREW_TAP_VERIFY. This lets you adopt security features incrementally.
Got ideas? Bugs? Grievances? β Open an issue
DepKind enum (Runtime, Build, Test, Optional, Recommended), topological sort with Kahn's algorithm, cycle detection with path reportingmake test-unit # run unit tests
make test-smoke # run quick health checks
make test-integration # run command-level integration tests
make test-e2e # run full lifecycle E2E tests
make check-all # run all of the above
make build # go generate + go build
make dev # devmode build (user-local)
Developer builds (make dev) can install to ~/.homegrew without root using grew setup --unsafe. See the README for details.
grew ships with a standalone patcher tool to generate and verify multi-hop binary updates:
patcher -v -D dist/ v1.0 v1.1 # Generate patches
patcher -U v1.0 v1.2 # Verify upgrade path
git checkout -b feature/your-cool-thing)git commit -m "Add the cool thing")git push origin feature/your-cool-thing)PRs welcome. Drama not so much.
License: MIT License β see the LICENSE file for details.
Acknowledgments: Best-README-Template, and everyone who ever squinted at a wall of package manager output and thought "there has to be a better way".