discover · compare · collections · requirements · contribute · feed · watching · stats · help with your tokens
isaaclins/freesnitch · source
Snapshot 0 · 2026-11-04T12:47:28Z · Swift · MIT
Content fingerprint
b8b8e98ddfdc5312118e30b3a6206161693f947c7a3e57126cf157b3c502b32cGitHub description at capture
Native macOS application firewall with per-process filtering, connection alerts, traffic map, rules, DNS over HTTPS, and zero telemetry.
README at capture
<p align="center">
<picture>
<source media="(prefers-color-scheme: light)" srcset="screenshot-light.png">
<img src="screenshot.png" alt="FreeSnitch: open-source macOS application firewall" width="800">
</picture>
</p>
<p align="center">
<b>English</b> |
<a href="docs/README.ar.md">العربية</a> |
<a href="docs/README.es.md">Español</a> |
<a href="docs/README.ja.md">日本語</a> |
<a href="docs/README.zh-Hans.md">简体中文</a> |
<a href="docs/README.zh-Hant.md">繁體中文</a>
</p>
<h1 align="center">FreeSnitch</h1>
<p align="center">
<b>See what your Mac is talking to. Block what you don't trust.</b><br>
Free, open-source application firewall for macOS. No subscription, no telemetry, no upsell.
</p>
<p align="center">
<a href="https://github.com/isaaclins/freesnitch/releases/latest"><img src="https://img.shields.io/github/v/release/isaaclins/freesnitch?style=flat-square&label=release" alt="Latest release"></a>
<img src="https://img.shields.io/badge/macOS-13.0+-blue?style=flat-square" alt="macOS 13.0+">
<img src="https://img.shields.io/badge/Swift-5.10-orange?style=flat-square" alt="Swift 5.10">
<a href="https://github.com/isaaclins/freesnitch/releases/tag/v1.1.0"><img src="https://img.shields.io/badge/Firewall_build-Notarized-success?style=flat-square" alt="Notarized firewall build"></a>
<a href="LICENSE"><img src="https://img.shields.io/github/license/isaaclins/freesnitch?style=flat-square" alt="MIT License"></a>
<a href="https://github.com/isaaclins/freesnitch/stargazers"><img src="https://img.shields.io/github/stars/isaaclins/freesnitch?style=flat-square" alt="Stars"></a>
</p>
<p align="center">
<a href="https://github.com/isaaclins/freesnitch/releases/download/v1.1.0/FreeSnitch-1.1.0.dmg"><b>Download FreeSnitch 1.1.0</b></a> -
<a href="#install">Install</a> -
<a href="#why-this-exists">Why this exists</a> -
<a href="#what-it-does">What it does</a> -
<a href="#how-it-works">How it works</a> -
<a href="#permissions">Permissions</a> -
<a href="#screenshots">Screenshots</a> -
<a href="#contributing">Contributing</a>
</p>
---
## Install
**[Download FreeSnitch 1.1.0](https://github.com/isaaclins/freesnitch/releases/download/v1.1.0/FreeSnitch-1.1.0.dmg)** (`FreeSnitch-1.1.0.dmg`, 12.5 MB) or pick a file from the [release page](https://github.com/isaaclins/freesnitch/releases/tag/v1.1.0).
- macOS 13 or later, one universal build for Apple Silicon and Intel.
- Signed with a Developer ID, notarized and stapled by Apple. It is the firewall build, with the Network System Extension.
- Later versions arrive through the built-in Sparkle updater.
1. Open the disk image and drag FreeSnitch to **Applications**. It has to live there, see below.
2. Open FreeSnitch and allow its helper in **System Settings > General > Login Items & Extensions > Allow in the Background**.
3. When macOS asks, allow the FreeSnitch system extension in System Settings. That is the part that filters per process.
To check the download before you open it:
```console
$ shasum -a 256 ~/Downloads/FreeSnitch-1.1.0.dmg
f15857e6e328936b1623cfe309c1d7a357d1150f77fbe2e24a8ed7c1650382cd
```
Coming from PureSnitch? Run `Scripts/uninstall_puresnitch.sh` first. The old and new content filters must not run together.
**FreeSnitch has to live in `/Applications`.** macOS refuses to install background helpers for an app launched from a mounted disk image or from Downloads, so drag it across before opening it. FreeSnitch will tell you if you forget.
On first launch the app registers a privileged helper and opens the Network Monitor with a banner asking you to approve it in **System Settings > General > Login Items & Extensions > Allow in the Background**. Until that switch is on, macOS blocks the helper and the app cannot see traffic. The window updates on its own once you approve it, with no relaunch needed.
### Build from source
Two builds exist. The difference is about who can build them, not about which one is the real product. Every release built by `Scripts/release.sh` is the firewall build, so that is what the download above is. The monitor build exists so contributors can work on the app without holding this team's Developer ID provisioning profiles.
#### Firewall build, the one that ships
Generated from `project-netext.yml`. It embeds `io.isaaclins.freesnitch.netext.systemextension` and provides per-process filtering. It requires the Developer ID provisioning profiles for the app and extension in `Profiles/`, plus Developer ID signing.
```bash
xcodegen generate --spec project-netext.yml
xcodebuild -project FreeSnitch.xcodeproj -scheme FreeSnitch -configuration Release \
-derivedDataPath build build
```
Install the signed firewall build in `/Applications`. On first launch, approve the system extension in System Settings.
#### Monitor build, for contributors without the profiles
Generated from `project.yml`. It includes the app, helper, monitoring, rules UI, DNS proxy, and `pfctl` integration, and omits the Network System Extension so it builds without provisioning profiles. Use it to work on everything except per-process filtering. It is not what users receive.
```bash
brew install xcodegen
git clone https://github.com/isaaclins/freesnitch.git
cd freesnitch
xcodegen generate --spec project.yml
xcodebuild -project FreeSnitch.xcodeproj -scheme FreeSnitch -configuration Release \
-derivedDataPath build build
open build/Build/Products/Release/FreeSnitch.app
```
This build needs no system extension approval, because it has no extension to approve. It still has to run from `/Applications` and needs the helper approved, like the release.
## Uninstall
Start in the app: **FreeSnitch > Settings > Uninstall**. Under System Integrity Protection, which is on by default, the app is the only component macOS lets deactivate the FreeSnitch system extension. `systemextensionsctl uninstall` answers `At this time, this tool cannot be used if System Integrity Protection is enabled.` even for root, so a script cannot do this part.
The Uninstall tab asks you to confirm twice, then turns enforcement off, disables the content filter, and requests deactivation of the network extension. macOS usually finishes that only after a restart, and the app reports what the system actually answers rather than claiming success when the request was accepted.
What is left needs administrator rights and is yours to do:
1. Switch FreeSnitch off in **System Settings > General > Login Items & Extensions**. That is what removes the privileged helper. FreeSnitch never unregisters its own service for you, because doing that silently is how a working install gets broken (see #24).
2. Restart if the app says the deactivation finishes after a restart.
3. Run the script, which does the file and `pf` work with its guards in place:
```bash
sudo bash Scripts/uninstall_freesnitch.sh --yes
# also delete your rules, profiles and blocklists:
sudo bash Scripts/uninstall_freesnitch.sh --yes --remove-database
```
The script refuses to remove the app while macOS still holds a record for the extension, or while the helper is still running from that bundle. Without `--remove-database` it keeps `/Library/Application Support/FreeSnitch/freesnitch.sqlite`, which holds your rules and can exceed 300 MB, so a reinstall finds them again. It flushes only the shared `puresnitch` `pf` anchor and never disables `pf` globally, and it keeps the empty anchor file because `/etc/pf.conf` still references it. Legacy PureSnitch data is never touched.
Without a checkout, the Uninstall tab shows and copies the equivalent commands.
## Why this exists
Little Snitch is the gold standard for application firewalls on macOS. It costs $59 per machine. LuLu is free and offers per-process kernel filtering, but its rules manager is spartan and there is no world map, traffic graph, or built-in blocklist library. FreeSnitch offers per-process filtering in its firewall flavour. The macOS built-in firewall blocks inbound. It does nothing for outbound traffic.
So most Mac users sit between three choices: pay $59, accept a barebones UI, or have no visibility into what their machine talks to at all.
FreeSnitch is the fourth choice:
- **Little Snitch-style UI.** Menubar status item, world map, rules manager, and connection alert popups. If you've used LS, you already know how to use this.
- **Free under MIT.** Read the code, fork it, audit it. The matcher, DNS proxy, pf integration, and UI are all open.
- **No telemetry.** No analytics SDKs. No crash reporters phoning home. No "anonymous usage" pings. Network requests are limited to enabled blocklists, the DoH resolver you choose, offline geolocation data sources, and Sparkle update checks.
- **Built like a Mac app, not a port.** Native SwiftUI for the windows, real `NSStatusItem` for the menubar, `SMAppService` for the privileged helper, and XPC over a Mach service for the GUI to daemon bridge.
- **Signed and notarized.** [Version 1.1.0](https://github.com/isaaclins/freesnitch/releases/tag/v1.1.0) is the firewall build, signed with a Developer ID and notarized by Apple.
What FreeSnitch is **honest** about: released builds ship the per-process firewall, and `Sources/NetExt/FilterDataProvider.swift` is a real `NEFilterDataProvider` that evaluates every new socket flow and returns an allow or drop verdict, or pauses the flow for a decision from the GUI. The monitor build omits that extension so contributors can build without this team's provisioning profiles; it is a development convenience, not the shipped product.
## What it does
### Network Monitor
A live world map of connections reported by the helper, with a per-process bandwidth sidebar and a summary pane that ranks the top processes, domains, and countries by traffic volume. Search the process list and inspect the connections currently reported by the monitor.
### Rules Manager
The rules UI includes All Rules, Active, Deny, Temporary, Unapproved categories, Rule Groups, and Blocklists in the sidebar. Each rule shows process, allow or deny action, priority, and hit count. Search by process or remote host. Match glob hostnames, CIDR ranges, and port-specific rules, with time-bounded rules supported by expiry dates in the rule model and matcher.
### Connection Alerts
Alert mode can show a decision window for a new connection, with Allow or Deny, a remember choice, and controls for scope and duration. The current alert handler records a remembered rule using the connection host or IP; scope and duration selections are not yet applied. The app has three modes: Alert, Silent Allow, and Silent Deny. Switch modes from the menubar.
### DNS over HTTPS
Built-in DoH client with example upstreams for Cloudflare, Quad9, and Google, plus support for a custom DoH endpoint. When Enforcement is enabled, the helper can run a local DNS proxy on `127.0.0.1:53` and filter queries sent to that proxy before forwarding allowed queries over DoH.
### Blocklist Library
Ships with 1Hosts Lite, OISD Small, StevenBlack, HaGeZi Multi Light, URLhaus, Anti-PopAds, Peter Lowe, and AdGuard DNS sources. When Enforcement is enabled, the helper's local DNS proxy filters DNS names against enabled lists. Blocklists do not stop connections to hardcoded IP addresses or names resolved by an app's own encrypted DNS, such as Chrome and Firefox DoH.
### Packet-Level Blocking
When Enforcement is enabled, a `puresnitch` anchor in `pfctl` provides IP, CIDR, and port blocking at the kernel. These rules apply regardless of which process initiated the connection.
### Profiles
A profile is a place and how strict you are there: a default strictness, a selection of deny blocklists, the networks that select it, and a few rules that only apply there. The helper seeds `default`, `home`, `public-wifi`, and `lockdown`.
Rules layer exactly twice: the `Always` set plus the active profile's own rules. New rules default to `Always`, and the rule editor's `Applies to` control is where that changes. Deny blocklists stack without limit, because anything any list blocks is blocked and order cannot matter. Creating a profile copies nothing: it inherits every `Always` rule immediately.
A network is recognised by the MAC address of its default gateway. FreeSnitch never reads the Wi-Fi network name and never asks for Location Services. A network only ever selects a profile after you bind it with "use this here"; an unknown network changes nothing.
Switching profiles affects new connections only. Verdicts are made in `handleNewFlow`, so replacing the active rule set cannot retroactively drop an established session such as SSH. Every switch is announced with the active and paused rule counts and an Undo, and the active profile is always visible in the menu bar.
### Menubar Status Item
Live up and down throughput when enabled, a five-minute traffic graph, a top-process list, a denied-count badge, and a one-click mode picker.
## How it works
```
┌─────────────────────────────────────┐
│ FreeSnitch.app │
│ ┌────────────────────────────────┐ │
│ │ SwiftUI GUI │ │
│ │ - Menubar status item │ │
│ │ - Network Monitor window │ │
│ │ - Rules Manager window │ │
│ │ - Connection Alert popups │ │
│ └──────────┬─────────────────────┘ │
│ │ XPC (Mach service) │
│ ┌──────────▼─────────────────────┐ │
│ │ FreeSnitchHelper (root daemon)│ │
│ │ - pfctl anchor manager │ │
│ │ - DNS proxy (UDP/TCP :53) │ │
│ │ - DoH upstream (Cloudflare) │ │
│ │ - Blocklist fetch + parse │ │
│ │ - nettop + lsof stream parser │ │
│ │ - SQLite rule store │ │
│ └────────────────────────────────┘ │
└────────────────┬────────────────────┘
│
┌────────────────────┼────────────────────┐
│ macOS networking │
│ pfctl · DNS · Network Extension │
└─────────────────────────────────────────┘
```
Four parts make up the monitoring and enforcement path:
1. **DNS interception.** When Enforcement is enabled, a local DNS proxy on `127.0.0.1:53` answers queries sent to it. Blocklisted domains return NXDOMAIN. Everything else forwards over DoH to the resolver of your choice. This is the only blocklist enforcement path: the Network Extension and `pfctl` do not receive blocklist entries, so hardcoded IP connections and names resolved by an app's own encrypted DNS, such as Chrome and Firefox DoH, are not covered.
2. **pfctl anchor.** When Enforcement is enabled, a `puresnitch` anchor in `/etc/pf.conf` carries block rules for IPs, CIDR ranges, and ports that apply to every app. A deny rule that names an app is never written there, because `pf` cannot tell apps apart; the Network Extension enforces it per process. An app's allow rule is, so an address feed never blocks what you allowed.
3. **Process and connection observability.** `nettop -P -L 0 -x -J bytes_in,bytes_out` is parsed continuously for per-process bandwidth. `lsof -i -n -P -F pcnT` snapshots active connections every two seconds. Both feed the GUI's process list and world map. The menu bar speed and the traffic graph come from the network interfaces' byte counters, read once a second.
4. **Per-process filtering.** In the firewall flavour, the Network System Extension evaluates each new socket flow against the shared rule set and can allow, drop, or pause it for a GUI decision.
## Anatomy of a rule
```
Rule(
processBundleId: "com.example.app" // optional
processPath: "/Applications/Example.app/…"
remoteHost: "*.tracker.com" // glob
remoteIP: "1.2.3.0/24" // CIDR
remotePort: 443
direction: outgoing | incoming | any
action: allow | deny | ask
scope: process | domain | ip | port | any
priority: 100
profile: "always" // or one profile name
temporary: false
expiresAt: Date?
)
```
The matcher walks enabled rules in descending `priority` order and applies the first match. With no match, it falls back to the active mode (`alert`, `silentAllow`, `silentDeny`).
## Permissions
FreeSnitch needs to install a small **privileged helper** at first launch in order to:
- read per-process connection state via `nettop` and `lsof`
- and, only if you turn on **Enforcement** in Settings, write `pfctl` rules to `/etc/pf.anchors/puresnitch` and bind `127.0.0.1:53` for the local DNS proxy
Enforcement is **off by default**. Out of the box FreeSnitch watches; it does not touch your firewall or your resolver until you ask it to.
The helper is installed via `SMAppService.daemon`, the modern replacement for `SMJobBless`. macOS will surface it in **System Settings > General > Login Items & Extensions** as a service you can enable, disable, or remove with a single switch. FreeSnitch never asks for your password during normal operation. The helper handles privileged calls on its own through XPC.
What FreeSnitch does **not** do:
- It does not collect telemetry, crash reports, or usage analytics.
- It does not require an account, license check, or any kind of identity.
- It sends network requests only to enabled blocklist sources, the DoH resolver you pick, the offline geolocation data sources used for the map, and the configured Sparkle update feed.
- It does not modify your firewall or DNS settings until you turn on Enforcement in Settings.
## Screenshots
All screenshots are FreeSnitch 1.0 running on sample data (`Scripts/screenshots.sh`), in light and dark mode.
| Network Monitor | Rules |
|---|---|
|  |  |
| **Insights** | **Profiles** |
|  |  |
| **Connection alert** | **Menu bar** |
|  |  |
## Comparison
| | FreeSnitch | Little Snitch | LuLu | macOS Firewall |
|---|---|---|---|---|
| License | **MIT, open source** | Commercial, $59 | GPL, open source | Apple, closed |
| Price | **Free** | $59 / Mac | Free | Bundled |
| World map / traffic graph | ✅ | ✅ | ❌ | ❌ |
| Rules manager (LS-style) | ✅ | ✅ | basic | ❌ |
| DNS proxy + DoH | ✅ | ✅ | ❌ | ❌ |
| Domain blocklists out of the box | ✅ (8 sources) | ✅ | ❌ | ❌ |
| pf-based IP/CIDR blocking | ✅ | ✅ | n/a | basic |
| Per-process kernel filtering | ✅ in firewall flavour | ✅ | ✅ | ❌ |
| Outbound blocking | ✅ | ✅ | ✅ | ❌ |
| Inbound blocking | ✅ | ✅ | ✅ | ✅ |
| Telemetry | none | none | none | n/a |
| Auditable source | yes | no | yes | no |
FreeSnitch's firewall flavour provides per-process kernel filtering alongside its rules UI. LuLu remains a free, open-source alternative with its own Network Extension implementation. If you want the Little Snitch-style UI without paying $59, that is what FreeSnitch is for.
## Status
Per-process outbound filtering works today, through a signed Network System Extension that evaluates every new socket flow. Alongside it: DNS-level filtering with blocklists and DNS over HTTPS, `pfctl` enforcement, a rules manager, a live traffic monitor, a command line interface that mirrors the GUI, and Sparkle update support.
The current release is [FreeSnitch 1.1.0](https://github.com/isaaclins/freesnitch/releases/tag/v1.1.0), published on 25 October 2026. Every release is listed on the [releases page](https://github.com/isaaclins/freesnitch/releases).
There is no fixed roadmap here, and pretending otherwise ages badly. Direction is decided in the open on the [issue tracker](https://github.com/isaaclins/freesnitch/issues), where proposed work carries the reasoning behind it and the constraints it has to respect.
## FAQ
**Is this a Little Snitch clone?** It is an independent open-source alternative with a deliberately similar user interface. The blocking engine, DNS proxy, and matcher are written from scratch. No Little Snitch source, assets, or proprietary plist formats are used. "Little Snitch" is a registered trademark of Objective Development Software GmbH. This project is not affiliated with or endorsed by Objective Development.
**Does FreeSnitch send my traffic anywhere?** FreeSnitch itself has no telemetry, analytics, or phone-home service. It contacts enabled blocklist sources, the DoH upstream you choose, the offline geolocation data sources used for the map, and the configured Sparkle update feed. IP-to-country lookups use downloaded local data rather than a per-connection lookup service.
**How does per-process filtering work?** The shipping build embeds a Network System Extension built from `Sources/NetExt/FilterDataProvider.swift`. It evaluates each new socket flow against the shared rules and can allow, drop, or pause it for a GUI decision. It needs user approval of the system extension on first launch. The contributor build, generated from `project.yml`, leaves that extension out so it can be built without provisioning profiles, and falls back to observation plus DNS and pfctl enforcement.
**Will this run on Intel Macs?** Yes. The release is a universal binary with `arm64` and `x86_64` slices, and macOS 13 is the minimum.
**Does it show an app's declared Internet Access Policy?** It can, and in practice it almost never will. FreeSnitch parses Little Snitch's `.lsiap` format and displays it when an app ships one, but adoption never happened: scanning a working Mac found the file in 0 of 64 installed apps. The parser is kept because the data would be genuinely useful if it existed, showing what an app says a connection is for before you deny it. Treat it as latent support rather than a feature you will see.
**How is it different from LuLu?** [LuLu](https://github.com/objective-see/LuLu) is an excellent free, open-source alternative with its own Network Extension implementation. FreeSnitch differs in its Little Snitch-style UI, world map, traffic graph, mode picker, DoH support, blocklist library, and written-from-scratch rule engine. Try both and use whichever fits.
**Does it work alongside Pi-hole, AdGuard Home, or NextDNS?** Point FreeSnitch's DoH upstream at your own DoH endpoint. FreeSnitch can then provide a per-device enforcement layer on top of your network-wide blocker.
**What about Tailscale, WireGuard, or ProtonVPN?** FreeSnitch's `PFManager` writes IP, CIDR, and port rules to the `puresnitch` anchor. It does not configure VPN interfaces. The DNS proxy handles queries sent to `127.0.0.1:53`; the current app does not change the Mac's system DNS settings.
## Project structure
```
freesnitch/
├── Sources/
│ ├── GUI/ # SwiftUI app, updater, system extension manager
│ ├── Helper/ # Privileged daemon (pfctl, DNS proxy, nettop)
│ ├── NetExt/ # Network System Extension and per-process provider
│ └── Shared/ # Rule model, SQLite store, XPC protocol, matcher
├── Resources/
│ ├── Assets.xcassets/
│ └── Branding/
├── Scripts/
│ ├── audit_firewall_safety.sh
│ ├── make_icon.sh
│ ├── release.sh
│ ├── render_icon.swift
│ ├── screenshots.sh # README and website screenshots from demo data
│ ├── test_uninstall_safety.sh
│ ├── uninstall_freesnitch.sh
│ └── uninstall_puresnitch.sh
├── docs/ # Website, architecture, translations, screenshots
├── .github/ # Issue templates and CI workflows
├── .gitignore
├── project.yml # Contributor build, no system extension
├── project-netext.yml # Shipping build, with the system extension
├── CONTRIBUTING.md
├── README.md
├── LICENSE # MIT
├── screenshot.png # Repository hero screenshot (dark)
└── screenshot-light.png # Same, light mode
```
## Security
- The privileged helper is installed via `SMAppService.daemon`, the modern replacement for `SMJobBless`. Trust boundary is the system Login Items & Extensions list.
- The XPC interface is typed; the helper validates every request shape and refuses anything outside the declared protocol.
- pfctl rules are scoped to the single `puresnitch` anchor. Enabling Enforcement may add that anchor reference to `/etc/pf.conf`, but it does not replace unrelated rules.
- The DNS proxy listens on port 53 only when Enforcement is enabled.
- Rule removal, including removal of selected rules, is exposed in the Rules Manager. There is no blocklist reset operation in the current UI.
If you find a security issue, please open a private security advisory rather than a public issue.
## Contributing
Pull requests welcome. See [CONTRIBUTING.md](CONTRIBUTING.md).
Especially welcome:
- Blocklist and filtering coverage, especially cases that bypass DNS-level blocking
- Translations beyond English
- Additional blocklist providers
- XCTest coverage for the rule matcher and DNS proxy
- Documentation and testing for the signed firewall build and first-launch system extension approval
## Acknowledgments
- [Moamen Basel](https://github.com/momenbasel) is the original author of [PureSnitch](https://github.com/momenbasel/puresnitch).
- This repository is an independently maintained continuation of that original MIT-licensed work.
- Moamen Basel does not maintain or endorse this repository, and it is not affiliated with him.
- [@objective-see](https://github.com/objective-see) for [LuLu](https://github.com/objective-see/LuLu), the reference free firewall for macOS
- [Objective Development](https://www.obdev.at/) for shaping what an outbound firewall UI should feel like with Little Snitch
- [1Hosts](https://github.com/badmojr/1Hosts), [OISD](https://oisd.nl/), [StevenBlack](https://github.com/StevenBlack/hosts) and [HaGeZi](https://github.com/hagezi/dns-blocklists) for the blocklist work everyone in this space stands on top of
- Cloudflare, Quad9 and Google for free public DoH resolvers
## License
MIT. See [LICENSE](LICENSE). Use it, fork it, or ship it under your own name if you want. The only requirement is that the license notice stays.