A native macOS GUI for Homebrew.
A small, fast desktop app for browsing, searching, installing, and snapshotting Homebrew packages. Full source, MIT-licensed, no telemetry, no accounts.
Homebrew is the standard package manager on macOS. brew-browser gives it a real native GUI: browse what you have installed, search the full catalog, install / uninstall / upgrade with live output, snapshot your setup to a Brewfile and restore it on a new Mac. Trending packages come from Homebrew's published analytics. The whole thing is a thin, respectful frontend over the brew CLI itself.
- Dashboard — your Homebrew setup at a glance: installed count, updates available, brew version, formula/cask split, top-categories donut chart, storage usage (Cellar / Caskroom / var/log / cache) with one-click "Reveal in Finder", and an opt-in Exposure card surfacing known vulnerabilities across your install
- Library — every installed formula and cask in one dense, filterable list, with outdated badges, sortable columns, category chip filters, an opt-in Vulnerable filter pill, inline severity dots, and a slide-over detail panel
- Discover — search the full Homebrew catalog (15,974 packages, bundled at build time + user-refreshable) by name or browse via the 19-category tile grid; multi-select chip filter
- Trending — top packages from Homebrew's published
formulae.brew.shanalytics, with 30 / 90 / 365-day windows, sortable columns, a velocity index (recent-month vs prior-11-month adoption signal), and opt-in per-package install-trend sparklines - Snapshots — save and restore Brewfiles using Homebrew's own
brew bundlemechanism; "set up a new Mac" in one click - Services — list, start, stop, and restart background services managed by launchd through
brew services - Security — opt-in vulnerability scanning. Surfaces known CVEs against your installed formulae via the official
brew vulnssubcommand (OSV.dev), with optional GHSA enrichment when you're signed into GitHub. Inline severity dots, per-package Security card with "Upgrade to fix" wired to the existing upgrade pipeline. Off by default; one-click installer forbrew vulnsitself when you opt in - Activity — every
brewinvocation streams live into a bottom drawer with full stdout/stderr; session history persists across launches (last 200 jobs, capped lines)
A global Cmd+K command palette covers the verbs. Cmd+0 returns to the Dashboard; Cmd+1…6 jumps between sections. Cmd+, opens Settings. Click the 🍺 brand to return home. Window dragging works from any panel header (native macOS overlay title bar + NSVisualEffectView vibrancy).
- Not a Homebrew replacement — every action shells out to the real
brewCLI - Not telemetry-funded — no analytics, no accounts, no phone-home
- Not freemium — there is no paid tier, because there is no tier
Direct download (recommended): grab the latest signed + notarized .dmg from the releases page, open it, and drag brew-browser to your Applications folder. No Gatekeeper warning. This is the build that's signed and tested directly each release, and it keeps you on the app's own verified updater (Settings → Network → Updates).
Homebrew:
brew tap msitarzewski/brew-browser
brew install --cask brew-browserInstalls the same notarized .dmg. Update later with brew upgrade --cask brew-browser.
Apple Silicon only for now. macOS 13 (Ventura) or newer.
Prereqs:
- Rust (stable, edition 2021+)
- Node.js 22+ and npm
- Homebrew itself
- Xcode Command Line Tools:
xcode-select --install
Then:
git clone https://github.com/msitarzewski/brew-browser
cd brew-browser
npm install
npm run tauri dev # development with HMR
npm run tauri build # produces a .dmg in src-tauri/target/release/bundle/A Tauri 2 shell hosts a SvelteKit + Svelte 5 frontend in the system WebView. A Rust backend exposes ~55 typed Tauri commands that shell out to brew via tokio::process and stream stdout/stderr back over typed IPC channels. The full Homebrew catalog is bundled at build time (~6 MiB gzipped) and refreshable on demand. Trending data comes straight from formulae.brew.sh's public analytics JSON, cached in memory for an hour. Optional GitHub integration uses OAuth Device Flow with the token stored only in the macOS Keychain. No shell plugin, no arbitrary command execution — every brew invocation is built in Rust from a small set of enumerated inputs. See docs/PLAN.md for the full design and memory-bank/backendApi.md for the complete IPC surface.
MIT licensed. No CLA. No EULA. No telemetry. No account. No dark patterns.
brew-browser makes outbound network calls in exactly twelve documented circumstances. Every one is initiated by something you did and gated by Settings → Network:
https://formulae.brew.sh/api/analytics/install+/install-on-request— fetched when you open the Trending tab. The primaryinstallendpoint includes dependency-pulled installs (which is whyca-certificatesalways tops the leaderboard);install-on-requestexcludes deps and surfaces what users actually typedbrew install ...for. Both windows are fetched in parallel and the backend joins all three time horizons (30d/90d/365d) to compute a velocity index per package. Cached in process memory (TTL configurable in Settings → Network; default 60 minutes). Uses Homebrew's own published install-analytics JSON; no API key, no account.https://formulae.brew.sh/api/{formula,cask}.json— the full Homebrew catalog. Bundled at build time so the app works offline. A user-initiated Refresh button on the Dashboard (or the Discover stale-catalog banner) writes a fresh copy to~/Library/Application Support/brew-browser/catalog/. Auto-refresh is off by default; Settings → Network offers weekly / daily opt-in.- Cask homepage probes — when the Discover or Trending tab renders an uninstalled cask that has a
homepagefield, the Rust backend probes that homepage for an icon (in order:/apple-touch-icon.png,<meta og:image>parsed from the homepage HTML,/favicon.ico). One probe per cask per week max — the result, including misses, is cached for 7 days. These probes are sandboxed: link-local, loopback, RFC1918, and cloud-metadata IPs are rejected before the request, and the same check runs again on every redirect hop to prevent SSRF. Settings → Network can scope this to installed only or disable it entirely. https://api.github.com/repos/{owner}/{repo}(read) — optional, off by default. When Settings → GitHub → "Show GitHub stats on package pages" is on, the PackageDetail panel fetches public repo metadata (stars, forks, last release date, archived state) for packages whose homepage (orurls.stable.url/urls.head.url/ caskurl) parses as a GitHub URL. The URL parser strictly allowlistsgithub.com(rejectsgist.,raw.githubusercontent., suffix-attack domains, path traversal). Results cached to~/Library/Application Support/brew-browser/github-cache/for 24 hours. Anonymous rate limit is 60 reqs/hr per IP; sign-in lifts it to 5,000/hr.https://github.com/login/{device,oauth}/*— optional, only when you click Sign in with GitHub in Settings (or hit the inline Re-authorize button on a scope-required toast). Uses OAuth Device Flow (RFC 8628): you see a user code, opengithub.com/login/devicein your browser, paste it, done. No embedded webview, no client secret, no callback URL. Scopes requested:read:user+public_repo+notifications(the minimum for username + star + file-issue + watch). Access token stored exclusively in macOS Keychain undercom.zerologic.brew-browser/github_access_token. The token is never returned to the frontend, never written to disk, and never logged — verified by unit tests.https://api.github.com/{user/starred,repos/.../subscription,repos/.../issues}(write) — optional, only when you click Star, Watch, or File-issue on a package detail page after signing in. Each action is gated server-side by a per-action OAuth scope check (public_repofor star + file-issue;notificationsfor watch/unwatch) so a token missing the right scope fails fast with a typedscope_requirederror before any GitHub round-trip.brewitself — every install, uninstall, upgrade, search, and snapshot shells out to the realbrewCLI. Whatever network callsbrewmakes (GitHub, OCI registries, bottle mirrors) happen exactly as they would if you ran the command yourself in a terminal. The full stdout/stderr stream is visible in the Activity drawer.- Your default browser — when you click the homepage button on a package, the URL is opened in your default browser via macOS
open(1). The app rejects any non-http(s)scheme before opening. https://brew-browser.zerologic.com/updater.json+https://github.com/msitarzewski/brew-browser/releases/download/v*/brew-browser_*_aarch64.app.tar.gz— the in-app updater (Phase 15, v0.3.0+). The manifest fetch happens when you click Check for updates now in Settings → Network → Updates, or on the auto-check timer if you opt in (off by default; 24h cadence). The artifact download follows only after the manifest'sversionis strictly greater than the running build. Every downloaded.app.tar.gzis verified against an embedded minisign public key + the manifest'ssha256before any on-disk side effect — mismatch aborts with no install. Skipping a version is recorded locally; a future release re-triggers the indicator.https://brew-browser.zerologic.com/trending-history/*— Enhanced Trending History (v0.4.0+). Distinct trust boundary from the Homebrew first-party endpoints above: this endpoint is operated by the brew-browser project, not by upstream Homebrew. Off by default. When you opt in via Settings → Network → Enhanced Trending History, the app fetches per-package historical install trends to power inline sparklines on the Trending tab and a chart on each package's detail panel. Only the package name you're viewing is sent (one HTTP GET per package); no IP is logged at the server, no cookies, no fingerprinting (seememory-bank/security.md§16 for the Caddy config that makes the privacy claim auditable). Master Offline Mode hard-locks this off regardless of the per-feature toggle.https://brew-browser.zerologic.com/enrichment/*— Live category & description updates. Same first-party host as Enhanced Trending, distinct/enrichment/*path — still a separate trust boundary from the always-on Homebrew endpoints. Off by default. brew-browser ships with bundled AI categories + descriptions; when you opt in via Settings → Network → Live category & description updates, it refreshes them: a tinyversion.jsonprobe on catalog refresh, the fullcategories.jsonwhen its version is newer, and a per-packageentry/<token>.jsonwhen you open that package's detail. Only the package name you're viewing is sent (one HTTP GET per token); no IP logged, no cookies. Requires AI Features on; master Offline Mode hard-locks it off regardless.https://api.osv.dev+https://github.com/https://gitlab.com/https://codeberg.org(via subprocess) +https://api.github.com/advisories/{GHSA_ID}(from our code) — opt-in Vulnerability Scanning (v0.5.0+). Distinct trust boundary because the OSV traffic and the source-forge tag resolution are opened by thebrew vulnssubprocess we shell out to, not by brew-browser's own binary. Off by default. When you opt in via Settings → Network → Vulnerability Scanning (and installbrew vulnsvia the one-click affordance if you don't already have it), the app runsbrew vulns --jsonto query OSV.dev for known CVEs against your installed formulae. When both the vulnerability-scanning toggle AND GitHub sign-in are on, eachGHSA-…-prefixed finding is enriched viaapi.github.com/advisories/{GHSA_ID}from our Rust code (triple-defense: master Offline Mode, vuln-feature toggle, GitHub-feature toggle — all three must align for one GHSA request).brew vulnsis published by Homebrew (Homebrew/homebrew-brew-vulns, by Andrew Nesbitt). Casks are not supported (brew vulnsis formula-only); cask UI rows render an honest "Cask coverage isn't supported" message rather than fake clean state. Master Offline Mode hard-locks the whole feature off regardless. Seememory-bank/security.md§17 for the endpoint audit and gate-composition table.
Every outbound call respects the Network settings — flip on Offline Mode in Settings to block all outbound traffic in one click. Settings persist to ~/Library/Application Support/brew-browser/settings.json; a corrupt or missing file fails closed (Offline Mode effectively on) until you hit Reset to defaults.
No analytics. No crash reporting. No third-party fonts or pixels. No fetch() from the frontend — every backend call goes through typed Tauri IPC.
The full network posture is verified line-by-line in memory-bank/security.md §5. Re-audits are welcome; the source is right there.
A full security audit lives at memory-bank/security.md. Current verdict: READY-FOR-SCRUTINY (0 critical / 0 high / 0 medium / 0 low / 0 nit open). All 16 findings from the initial audit are verified-fixed with passing tests. Independent tool battery passes: cargo audit 0 vulns, cargo deny check advisories+bans+licenses+sources ok, npm audit --omit=dev 0 vulns, semgrep with security-audit + OWASP-top-10 + Rust + TypeScript rulesets 0 findings, cargo clippy -D warnings clean. Zero unsafe Rust, zero @html/innerHTML/eval in the frontend, no tauri-plugin-shell (every brew invocation is built from typed Rust enums). SSRF defense includes a redirect-policy re-check on every hop.
Dependency posture:
- Rust:
cargo auditreports 0 vulnerabilities across 540 crates. The 17 unmaintained warnings and 1 unsoundness all sit in GTK/glib transitive deps that compile out on macOS. - npm (production):
npm audit --omit=devreports 0 vulnerabilities across 25 production packages. - Zero
unsafeRust in the entire backend.
Defense-in-depth choices:
- No
tauri-plugin-shell— the frontend cannot construct arbitrary shell commands. Everybrewinvocation is built in Rust from typed enums. - Scheme allowlist on the homepage opener — only
http(s)URLs reachtauri-plugin-opener. - SSRF filter on the cask icon cascade — private, link-local, loopback, and cloud-metadata IPs are rejected pre-flight and on every redirect.
- Path sandboxing on Brewfile import/export — IPC paths are validated against a forbidden-prefix list and a 1 MiB size cap.
rustls-tls+webpki-rootsfor all outbound HTTPS — no system trust store dependency.- Capability allowlist is minimal:
core:default,opener:default,core:event:default,dialog:allow-open,dialog:allow-save. Nofs:*, nohttp:*, noshell:*.
Issues and PRs on security topics are welcome. See SECURITY.md for the responsible disclosure process.
Contributions welcome. See CONTRIBUTING.md for the dev loop, project map, and the short list of things worth opening an issue about first. No CLA. Your contributions stay yours, licensed under MIT to match the project.
v0.5.0 shipped (signed + notarized). All seven core panes live: Dashboard, Library, Discover (with bundled catalog + 15,725 AI-curated friendly names and summaries), Trending, Snapshots, Services, and Activity. Optional GitHub integration via OAuth Device Flow is intent-discovered — sign-in only prompts when you actually try to star / watch / file an issue, never as static UI clutter. Two opt-in surfaces beyond the always-on core: Enhanced Trending History (v0.4.0+) for per-package install-trend sparklines, and Vulnerability Scanning (v0.5.0+) shelling out to the official brew vulns subcommand for CVE surfacing against your installed formulae. Settings ships with Offline Mode and a corrupt-recovery default. Native macOS title bar with traffic-light alignment, collapsible sidebar with persistent type-ahead search, (i) info popovers in place of AI badges for every enriched field.
v0.5.0 — opt-in vulnerability scanning. Highlights:
brew vulnsintegration. New Settings → Network → Vulnerability Scanning subsection (opt-in, off by default) shells out to the officialHomebrew/homebrew-brew-vulnssubcommand by Andrew Nesbitt to query OSV.dev for known CVEs against installed formulae. One-click installer for thebrew vulnssubcommand itself when you opt in — no terminal required.- Dashboard Exposure card. Severity-tiered counts (critical/high/medium/low), ✓ clean-state framing when no vulns, Scan-now button. Hidden when feature off.
- Sidebar count badge. Number of vulnerable packages with max-severity tone. Hidden when 0 or feature off.
- PackageRow severity dots + PackageDetail Security card. Inline severity indicator on every vulnerable row. PackageDetail Security card with per-CVE rows, severity pills, "Patched in X" badges, "Upgrade to fix" wired to the existing brew upgrade pipeline. Empty-summary entries fall back to canonical detail pages (NVD / OSV / GHSA).
- Library Vulnerable filter pill. Danger-toned count badge, pre-selected when you click "View vulnerable packages →" from the Dashboard.
- Optional GHSA enrichment. When both vuln-scanning AND GitHub sign-in are on, GHSA-prefixed advisories pull richer descriptions, patched-version ranges, and reference links from
api.github.com/advisories. Triple-gated; best-effort; 7-day cache. - Install-set SHA-256 fingerprint. Daily app opens with no install changes serve the cached report instantly instead of re-shelling
brew vulns(60+ seconds on 200 packages). - Post-install exposure heads-up. Once-per-session, after a successful install/upgrade, a passive toast surfaces if you have vulnerable packages elsewhere — the "install a thing → notice you have N existing CVEs" moment.
- Cask coverage gap stated honestly.
brew vulnsis formula-only; cask Security cards say so rather than faking clean state.
v0.4.0 — trending velocity + opt-in install-history endpoint. Highlights:
- Velocity index on Trending. Default sort. Compares each package's recent-month installs to its prior-11-month average so genuinely emerging packages surface ahead of stably-popular ones. New 🔥 / ❄️ / dash badges per row.
- Parallel install + install-on-request fetch. Server-side join across 30d/90d/365d windows lets the velocity math see all three time horizons in one pass instead of three sequential round-trips.
- Opt-in Enhanced Trending History. New Settings → Network → Enhanced Trending History subsection (off by default; distinct trust boundary from
formulae.brew.sh). When on, inline per-row sparklines on Trending + a per-package history chart on PackageDetail, fed by a project-operated endpoint (brew-browser.zerologic.com/trending-history/*) with IP-redacted server logs (the privacy claim is auditable — Caddy block pinned inmemory-bank/security.md§16). - Dashboard polish. Donut center text removed — the legend already carries the affordance, and removing the inner text frees up the chart for a cleaner look on narrow windows.
v0.3.1 — same-day cumulative point release on top of v0.3.0. Highlights:
- Magic search. Search now matches name + AI friendly-name + AI summary + upstream desc + category labels (and Tier B tags when they land). "video player" finds VLC, "Video & Audio" returns the whole category. Sub-20ms in-process scan via a new
local_searchIPC; replaces the oldbrew searchshell-out. - Curated Upgrade modal. "Choose…" button on the Dashboard Updates card lets you pick which outdated packages to upgrade. Single batched
brew upgrade <a> <b> ...streams into the Activity drawer. Pinned packages are checkbox-disabled. - Refresh actually refreshes everything. One click now runs
brew update(so brew learns about new upstream versions), refreshes the bundled catalog, AND reloads your installed list — in that order, with the brew-update output streaming to Activity. - Unified Description + Version columns across Library, Discover, and Trending. AI summary preferred when available; falls back to upstream
desc. Friendly name still appears below the token as a short scan-aid. - Donut hover-with-counts. Hover any slice (or its matching legend row) → the slice fattens, others dim, center text becomes "{count} / {label}".
- Report-to-brew-browser on every error. Failed action toasts + the Activity drawer's failed-job footer carry a button that opens a pre-filled GitHub new-issue URL with full context.
- Bundle identifier cleaned up:
dev.openbrew.browser→com.zerologic.brew-browser. v0.3.0 users re-sign-in to GitHub once via the existing Re-authorize button. - Activity history more durable. Cap raised from 50 to 200.
startJobpersists immediately. Persist + hydrate failures now log to console instead of swallowing silently.
v0.3.0 (in-app updater + GitHub coverage + issue #1 fixes):
- In-app updater. Title-bar pill notifies when a newer brew-browser version is available; Settings → Network → Updates owns the manual "Check now" button, the off-by-default daily auto-check, and the install action. Every artifact is verified against an embedded minisign public key before any on-disk side effect (sha256 first, then signature; mismatch aborts). Skipping a version is per-version, not per-install — a future release re-triggers the indicator.
- Offline Mode. "Paranoid Mode" renamed to "Offline Mode" everywhere user-visible. Same behavior — toggle in Settings blocks every outbound feature (catalog refresh, trending, GitHub, updater). Internal field stays
paranoid_modeto avoid a settings migration. - GitHub coverage expansion. Backend now resolves a canonical GitHub homepage by walking
homepage→urls.stable.url→urls.head.url(formula) orhomepage→url(cask). Packages likebat,fd,ripgrep— marketing-page homepages but GitHub-hosted source — now light up the Star / Watch / File-issue / Stats card. Personal-stats counter on the Dashboard sees the bigger denominator. - GitHub Octocat status chip in the title bar — green when signed in with required scopes, amber when scope-incomplete (click → Settings → GitHub to re-authorize). Hidden when signed out (no clutter).
- Actionable Re-authorize toast. When an authed action fails because the token doesn't carry the required scope (typical for v0.2.1-era tokens that pre-date the
notificationsscope), the failure toast offers a "Re-authorize" button that re-runs Device Flow requesting the full scope set. GitHub's consent screen shows only the missing scope; no sign-out needed. - Issue #1 fixes. Toast cascade on disconnect/reconnect (root cause: a cache loop in PackageDetail hammering Svelte's scheduler + structural misuse of
$effectfor one-shot side effects) resolved. Star, Watch, File-issue, and the sign-in flow all work cleanly. Reported by @heyjawrsh.
MIT. Do whatever you want with this.
- Homebrew — does all the actual work. This app is a respectful UI on top.
- Tauri — native shell without the Electron tax.
- Svelte — the runes-based reactivity that made the frontend small.
Built with Agency Agents, by the creator of Agency Agents — the multi-agent toolkit (Backend Architect, Frontend Developer, Security Engineer, Code Reviewer, Technical Writer, and friends) that orchestrated brew-browser's design and implementation. Powered by Claude Code in the terminal, running Opus 4.7 [1m].
If brew-browser saves you time, consider sponsoring on GitHub ♥. No paid tier — sponsorship is purely a thank-you, and it helps fund the Anthropic API spend that keeps the AI-curated catalog metadata fresh.
