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 dual SHA-256/512 verification (no funny business)
Binary delta updates β self-update uses efficient bspatch patches with pre-update vulnerability scanning
Ed25519 bottle signing β cryptographic signatures verified against a local trust store
Sandboxed builds & post-install β network denied, keg read-only for post-install, minimal env
Install snapshots β per-file SHA-256/512 manifests for tamper detection via grew verify
Lockfile β pin exact versions, hashes, and dependency trees for reproducible environments
Deterministic linking with opt symlinks and dry-run support (look before you link)
Dependency resolver with an optional tree view (for the visually inclined)
Doctor + audit β checks perms, HTTPS, broken links, snapshot integrity, cask notarization, and formula quality
Vulnerability scanning β queries OSV.dev for known CVEs, checks signatures, permissions, and file integrity
Hardened command execution β POSIX shell quoting via shellescape, -- end-of-options on all external commands, XML-safe plists, systemd specifier escaping
Zip Slip protection β archive extraction validates symlink indirection to prevent writes outside the destination directory
Structured logging via log/slog with CLI-friendly output β DEBUG, INFO, WARN, ERROR levels controlled by -v/-d flags
Services, aliases, shellenv β manage daemons, name things your way, wire up your shell
The fastest way β downloads the latest release binary with dual SHA-256/512 verification:
go install github.com/homegrew/grew/tools/getgrew@latest
getgrew
sudo 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 ./internal/... && go build -o grew
grew needs a home β a directory tree for the Cellar, symlinks, taps, and config:
sudo ./grew setup # macOS ARM β /opt/homegrew, Intel/Linux β /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.
# bash (~/.bashrc) or zsh (~/.zshrc)
eval "$(grew shellenv)"
# fish (~/.config/fish/config.fish)
grew shellenv fish | source
grew install jq # the classic
grew install -s ldns # build from source, like a purist
grew install --cask firefox # going big
That's it. No dark rituals. No 47-step setup guide.
grew link jq # stitch it in
grew deps --tree jq # what hath jq wrought
grew upgrade # stay fresh
grew cleanup -n # peek before you sweep
grew verify jq # check installed files against manifest
grew vuln-scan # scan for CVEs and integrity issues
grew lock # pin your environment
grew audit --strict # lint your formulas
grew doctor # check for common problems
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) & Linux:
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 | Install a formula or cask (-s to build from source) |
uninstall | Send it to the void |
reinstall | Uninstall + install from scratch |
list | See what you've collected |
info | Stalk a package |
search | Find the thing |
link | Weave a formula into your PATH |
unlink | Cut the thread |
update | Refresh tap definitions |
upgrade | Get the new hotness |
outdated | The hall of shame |
cleanup | Marie Kondo your Cellar |
deps | Dependency spelunking |
verify | Check installed packages against their snapshot manifests |
vuln-scan | Scan installed packages for security vulnerabilities |
audit | Lint formula/cask definitions for quality and security |
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) |
alias | Name things your way |
doctor | It's not a bug, it's a misconfiguration |
pin / unpin | Freeze a formula to prevent upgrades |
completion | Generate shell completion (bash, zsh, fish) |
config | What grew thinks it knows |
shellenv | Wire up your shell |
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 |
Everything flows from the prefix:
/opt/homegrew/ (or /usr/local/homegrew on Intel/Linux)
βββ 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 β HTTPS + SHA256 only |
| Binary delta updates | SHA-256/512 verified bspatch updates with pre-apply scanning | None (Full download only) |
| Tap verification | Optional GPG/SSH commit signature enforcement | None |
| Post-install sandbox | Read-only keg, no network, minimal env | Unsandboxed |
| Source build sandbox | macOS Seatbelt / Linux bwrap+unshare, no network | macOS Seatbelt only, no Linux |
| Install manifests | Per-file SHA-256/512 snapshot at install time | None |
| Lockfile | Full dependency tree with dual-hashes | None |
| Integrity check | grew verify + grew doctor snapshot check | None |
| Vulnerability scanning | grew vuln-scan queries OSV.dev for CVEs, checks signatures and permissions | 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 | POSIX shell quoting via shellescape for sandbox scripts; systemd and launchd values properly escaped | N/A |
| Zip Slip protection | Symlink indirection attacks blocked during tar/zip extraction | Partial |
| Command argument 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. Adopt security features at your own pace.
make check # go test -v -race ./...
make build # go generate + go build
make dev # devmode build (user-local)
make lint # golangci-lint
Developer builds (make dev) can install to ~/.homegrew without root using grew setup --unsafe. See the README for details.
git checkout -b feature/cool-thing)PRs welcome. Drama not so much.
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".
License: (Add a LICENSE to your repo!)