Repository navigation
Releases: isaaclins/freesnitch
Release list
FreeSnitch 1.0.0
FreeSnitch is a free, open-source application firewall for macOS. It shows which app on your Mac talks to which server, puts it on a live map, and lets you allow or deny it per process. MIT licensed, no account, no telemetry.
Download
| File | What it is |
|---|---|
| FreeSnitch-1.0.0.dmg | Disk image. Start here. |
| FreeSnitch-1.0.0.zip | The same app as a zip. |
FreeSnitch33-*.delta |
Sparkle delta updates. The app's updater uses these; you never need them by hand. |
- Requires macOS 13 or later. One universal build for Apple Silicon and Intel.
- Signed with a Developer ID, notarized and stapled by Apple. This is the firewall build, with the Network System Extension.
- Updates arrive through the built-in Sparkle updater.
Install
- Open the disk image and drag FreeSnitch to Applications. It has to run from there, or macOS will not install its helper.
- Open it and allow the helper in System Settings > General > Login Items & Extensions > Allow in the Background.
- When macOS asks, allow the FreeSnitch system extension. That is the part that filters per process.
Coming from PureSnitch? Run Scripts/uninstall_puresnitch.sh first; the two content filters must not run together.
Checksums (SHA-256)
610ef02c2201dba159172f774fd22fa88f6cbc3b79449bef9dacaab5ce361044 FreeSnitch-1.0.0.dmg
d52a450147643451b8a54d7489aeeb0350a2f26105d9ab15ca2c10bff205c08a FreeSnitch-1.0.0.zip
Also attached as SHA256SUMS.txt. Check with shasum -a 256 -c SHA256SUMS.txt in the folder you downloaded to.
What's new in 1.0
FreeSnitch 1.0 is the release where the app stops looking like a firewall console and starts looking like a Mac app. The filtering engine, the helper and the network extension are the ones you have been running since 0.4.5. What changed is every screen in front of them, and the twenty issues that said those screens were hiding, mislabelling or quietly losing what they were supposed to tell you.
The whole app is native now (#68)
Settings used to be the only page built from system controls, so it was the only page that inherited Mac behaviour: correct control sizing, focus rings, keyboard traversal, accessibility, contrast. Every other page was hand-drawn on a fixed dark palette.
- Rules is a real
Tablewith sortable columns, selection, and a selection-aware context menu. - Insights and the Network Monitor are real
Lists, with system selection and arrow key traversal. - The window has a real
NSToolbar, a real sidebar material, and one translucent band instead of a staircase of them. - The connection alert is a macOS permission dialog rather than a drawing of one.
- The app's own colour palette is gone. Surfaces are system backgrounds and materials, text is semantic label colours, and emphasis follows the accent colour you picked in System Settings. Light mode, Increase Contrast and the text size setting work without a per-colour audit. What survives is two named colours, sent and received, because in versus out is the one distinction the app makes with colour and it must not move when your accent does.
Rules
- The table says which rules are in force (#134). There is an "Applies to" column, drawn in secondary when that profile is not active, and a Profiles section in the sidebar that filters to Always or to one profile. A rule waiting for a place you are not in no longer reads exactly like one enforcing right now.
- The rule editor offers every profile (#134). It used to offer Always or the active profile and nothing else, so a rule for a third profile could be neither created nor moved. A row's context menu can move an existing rule between profiles.
- Rule groups are real (#134). The four group rows were hardcoded names nothing ever wrote, so all four were permanently empty while the group rules actually carry, Insights, had no row. The section is built from the rules and is left out when there are none.
- The inspector says when (#134). Created, last used, and, for a temporary rule, when it expires.
- The Information pane is resizable and remembers its width, and so is the sidebar (#123). The window minimum came down to 900 by 560, which fits the smallest display Apple ships.
Blocklists
- One checkbox, one meaning, one source of truth (#135). The checkbox in the Rules sidebar meant "enabled globally" and the identical-looking one under Profiles meant "belongs to this profile", and they read from two snapshots that could disagree on screen. Both now mean the profile-scoped thing the helper actually enforces, from one snapshot.
- Refreshing says what it is doing (#135). Downloading hundreds of thousands of entries used to be silent. There is a spinner while it runs and a sentence afterwards saying how many lists and names arrived, or what went wrong.
- Adding a list waits for the answer (#135). The editor used to dismiss before the helper replied and put the failure on a page you were not looking at. It stays up until the helper answers and shows the refusal in place.
- A new list says "Not downloaded yet" instead of a bare 0, and the header and footer counts agree while you are searching (#135).
- You can search a blocklist, edit any of them, and see what is on one.
Network Monitor and the map
- The denied badge lands on the denied list (#138). "Recently denied" in the menu bar panel used to open a Monitor that never showed whether a connection was allowed, denied or pending. It opens a filtered view now.
- The map is legible and follows the appearance (#121). Labels were white text on fixed black at hardcoded sizes, down to 8 points. They use text styles and materials now, so they respond to the appearance, Increase Contrast and the text size setting.
- A place on the map answers questions (#138). Nodes carry the apps that reached them, and a Locations menu opens a card naming those apps with the connection count and traffic. Each place also says its own name exactly once, which it previously did twice.
- Location Services is a Settings row (#138), with the standard explanation, instead of a privacy decision buried in an unlabelled glyph floating on the map. It is off by default and the map estimates from your time zone.
Insights, profiles and the menu bar
- Insights reads like a report rather than a console, its actions actually run, and rules it proposes carry their group.
- Profiles can be told apart at a glance, the profile chip picks a profile, and Profiles is a list with a detail pane.
- The menu bar panel is 320 points wide, the width it always declared and never got.
Uninstall
The uninstall says what it does, keeps its place, and quits (#133). The plan now lists the steps in the order they happen, including the one place FreeSnitch unregisters its own privileged service, which only ever happens in an uninstall you confirmed. It runs in its own sheet, so switching pages halfway through cannot tear it down, and it can be resumed if you close it between the deactivation and the removal. When it finishes it offers to quit, and quitting takes the menu bar icon with it.
Also fixed
Destructive actions ask first, and they all ask the same way. Failures say what went wrong in the words you read, not in the words the code throws. Disabled controls mean disabled. The alert panel is as tall as its card. The Spaces switch decides where alerts appear. Values in the Information pane really do copy. The keyboard drives the lists.
Known limits
Blocklists filter DNS names only. They do not stop connections made to hardcoded IP addresses or to names resolved by an app's own encrypted DNS, such as Chrome and Firefox with DoH enabled.
Map locations are estimates from IP geolocation, not measurements.
FreeSnitch 0.4.5
Fixed
The firewall no longer switches itself off after a helper restart or update (#70).
Until now the network extension compared rule snapshots by generation number alone. The extension's counter lives in extension memory and survives a helper restart, while the helper's counter came from the database and could legitimately start over. A restarted or updated helper therefore looked older forever, every snapshot it sent was refused, and the Mac stayed unfiltered until the next reboot.
Snapshots are now ordered by (helper session, generation):
- a restarted helper opens a new session and is accepted immediately, at any generation
- a lower generation inside one session is still rejected, as before
- a snapshot from an older session is rejected even at a higher generation
- the published generation is made durable whenever it advances, so it survives a restart
- an extension still holding a pre-update generation converges on its own, with no reboot
The session counter is a persisted counter, not a clock value or a random id, so a backwards clock correction or a dual boot cannot make a newer helper look older. If the extension does refuse a snapshot, the app now recovers automatically by restarting the filter provider, bounded to 3 attempts spaced 30s apart, and never asks you to reboot.
The helper stands down when the app is dragged to the Trash (#71).
If the FreeSnitch bundle is gone, the root helper now stops enforcing instead of leaving a registered service behind whose binary no longer exists. It deliberately never unregisters, boots out or deletes anything, because an in-place update is exactly the moment a bundle looks briefly absent. It requires 5 separated absence observations spanning at least 600s before it stands down.
Repair Helper does the privileged restart itself (#69).
Confirmed fixed since 0.4.1: repairing a stale helper happens behind a macOS authorization prompt, not by pasting a sudo command into Terminal. The enabled service is never unregistered, so your approval stays intact.
FreeSnitch 0.4.4
FreeSnitch 0.4.4
Uninstalling now works the way macOS intends
FreeSnitch deleted its own app bundle during uninstall. That turns out to be the one thing you must not do to an app that contains a system extension: moving the app to the Trash through Finder is what triggers macOS to remove the extension. Deleting the bundle any other way takes the app away and leaves the extension installed and running.
So uninstall no longer deletes anything in your Applications folder. It unregisters the helper, flushes its firewall anchor and removes its data, then hands the app to Finder exactly as if you had dragged it to the Trash yourself, which is what makes macOS remove the network extension too.
That also means the privileged step is smaller than before: it can no longer remove an application at all.
Afterwards the app tells you how to confirm it worked, and only mentions the System Settings route as a recovery step for the case where macOS does not finish the job, rather than presenting it as a chore you always have to do.
FreeSnitch 0.4.3
FreeSnitch 0.4.3
A cleaner uninstall, with less restarting
Uninstalling used to end by telling you to restart, and part of that was FreeSnitch's fault rather than a rule of macOS.
FreeSnitch only disabled its content filter configuration and left it installed. macOS keeps a system extension alive while a filter configuration still points at it, so the extension could not finish deactivating and ended up waiting for a reboot. FreeSnitch also asked macOS to deactivate the extension without waiting for that first step to finish, so the two raced.
Uninstall now removes the configuration and waits for it before asking macOS to deactivate the extension.
macOS may still want a restart to finish tearing an extension down, and if it does the app says so. But it is no longer being asked to clean up after us first.
Turning enforcement off is unchanged and still keeps the configuration, so switching filtering back on does not make you approve the extension again.
FreeSnitch 0.4.2
FreeSnitch 0.4.2
Uninstalling actually uninstalls
The Uninstall screen used to print a list of sudo commands and ask you to run them in Terminal. One of them was sudo bash Scripts/uninstall_freesnitch.sh, a script that only exists if you cloned the source. If you installed FreeSnitch the normal way, from the disk image, you were given instructions you could not follow.
Remove FreeSnitch now removes FreeSnitch. It unregisters the privileged helper, flushes its firewall anchor, deletes its data and deletes the app, asking you to authorize that once, the way any Mac app does. Then it deletes your own FreeSnitch files, which needs no password, so it does not ask for one.
All that is left afterwards is a restart, because macOS only finishes removing a network extension on restart. The screen tells you that instead of handing you a list of chores.
You no longer need to visit System Settings to remove the helper either; that was the one step FreeSnitch refused to do for you, and during an uninstall it is exactly what you asked for.
If the removal fails, the commands are still shown, as a fallback rather than as the plan, and without the script you do not have.
FreeSnitch 0.4.1
FreeSnitch 0.4.1
Repair Helper now repairs
After an automatic update, the app would notice that the privileged helper was still running the previous build, say so clearly, and then tell you to open Terminal and run a sudo command. Meanwhile the network extension had no helper to talk to, so traffic was not being filtered until you did it by hand.
A button called Repair Helper has to actually repair. It now restarts the helper for you, and macOS asks you to authorize it the normal way. If you dismiss that prompt nothing happens and the banner stays, which is how declining should work.
The action is deliberately narrow: it restarts the existing service and never unregisters it. Unregistering an enabled helper can remove the service outright, which is exactly why this was left manual before. Your approval in System Settings is untouched.
If the restart genuinely cannot be carried out, the command is still shown, as a fallback rather than as the plan.
FreeSnitch 0.4.0
FreeSnitch 0.4.0
The release where FreeSnitch stops only answering "is this allowed?" and starts answering "what is my Mac actually talking to, and has that changed?"
Insights, a traffic memory
A new screen (Command-Option-I) that groups traffic by app, showing what each one contacted, how often, and when it was first seen.
- Names come only from DNS answers this Mac actually saw. There are no online lookups, and the build now fails if that ever changes.
- Nothing is enforced automatically. Insights can propose a rule, but a proposal becomes a rule only when you accept it.
- Addresses that were never resolved to a name get their own section, described as a signal rather than a verdict.
- Storage is root-owned, with 14 days of raw records and a year of rollups, an off switch, and a purge that really removes the database.
Alert mode you can leave switched on
Alert mode now interrupts on first contact only. A destination this app has talked to before is allowed without a prompt, so the constant re-asking is gone, while explicit rules still decide first.
If the contact history is unavailable, stale, or unreadable, FreeSnitch asks rather than assuming. The alert also tells you why it is asking, and shows a running tally of how many contacts were allowed silently.
Apps that change behaviour after an update
FreeSnitch records the version of each app alongside what it contacted, and flags destinations that only appeared after an update, showing the old and new build. It states co-occurrence, not causation, and never blocks anything on its own.
Profiles: a place plus how strict you are there
Profiles are real now. Exactly two layers ever apply: your Always rules plus the selected profile. Deny layers stack.
- A place is recognized by the network's gateway hardware address, not the Wi-Fi name, because reading the Wi-Fi name requires Location Services and a firewall should not demand that.
- Automatic switching only happens for a network you bound yourself. An unknown network never switches your profile.
- A switch is always visible in the menu bar, names what was paused, and is undoable.
- Switching does not tear down connections you already have open; the new strictness applies to new ones.
IP and CIDR blocklists
Address feeds are now their own kind of list, enforced in the packet filter where addresses are actually seen, so a connection made straight to an IP is covered.
Safety is decided before the feed is consulted: loopback, FreeSnitch's own traffic, DHCP, and your configured resolvers can never be blocked by a feed, and your own rules still win. A feed that fails to download, is too large, or is malformed contributes nothing and says why. If the packet filter rejects an anchor carrying a feed, the anchor is reloaded without it, so a bad feed can never cost you your own rules.
Measured: 120,000 entries load in about a second, and lookup cost stays flat as the list grows.
Answer alerts from the command line
freesnitch alerts list
freesnitch alerts answer <ID> --deny --scope ip --temporaryAlerts are listed with a stable ID, the process, the destination, and the seconds left. Answering is exactly-once: a second answer, an expired alert, or an unknown ID each report specifically what happened instead of failing vaguely.
The FreeSnitch app must be running, because a connection alert is a flow paused against the running app. The CLI says so plainly instead of looking broken, and a flow never stays paused longer than its existing timeout.
Uninstall from inside the app
Settings now has an Uninstall screen that removes FreeSnitch properly instead of leaving a privileged helper and a network extension behind. It explains what macOS will not let it remove without your involvement rather than pretending it succeeded.
One window, with pages
FreeSnitch used to open a separate window for every view. Monitor, Rules, Insights, Profiles and Settings are now pages in a single window with a sidebar, so opening Rules switches the window you already have instead of adding another one to your Dock and Mission Control. It remembers both the window and the page you were on.
The connection alert is deliberately still its own panel, because it has to appear over whatever app you are in.
The map shows who you are actually talking to
- Connection arcs from your Mac to every endpoint, drawn as curved great-circle paths and split correctly at the antimeridian. Weight and opacity follow how busy the endpoint is.
- No Location Services. The arcs used to be hidden entirely unless you granted location access, which is an absurd thing for a firewall to ask. Your Mac is anchored by a pin you can place yourself, with an offline time-zone estimate until you do, labelled as an estimate. CoreLocation is now strictly opt-in and off by default.
- City-level nodes that split as you zoom. Every IP in a country used to land on the same dot. The map now groups by country when zoomed out and separates into real cities as you zoom in.
- IPv6 endpoints appear at all. They were never geolocated before, so a meaningful share of traffic was silently missing from the map.
Geolocation stays offline. The database is downloaded once and compiled into a memory-mapped index; there are no per-connection lookups. IP geolocation by DB-IP, CC BY 4.0.
The monitor groups by app
The live monitor is a tree: each app expands into the destinations it actually contacted, with rolled-up totals and traffic bars, and allow or deny controls on every row so you can decide where you are looking. Rows are ordered by arrival and never re-sort by traffic, so a row cannot move out from under your cursor between aiming and clicking.
Keyboard
Pages follow the sidebar: ⌘1 Network Monitor, ⌘2 Rules, ⌘3 Insights, ⌘4 Profiles, ⌘5 Settings, and ⌘, still opens Settings. The old shortcuts encoded the page's name rather than its position, ⌘⌥N read as "new", and Profiles had no shortcut at all.
Fixes
- DNS answers were being pruned at their five-minute TTL, which would have erased nearly every hostname and made almost all traffic look unresolved. They now age out with the 14-day window.
- Rules already covered by an existing enabled rule are no longer re-proposed.
- A fresh install now starts in Silent Allow and says so, instead of quietly implying it is blocking.
- Remembering an alert for a destination with no host name produced a rule with no destination constraint at all, so an allow silently granted that app access to everything and a deny blocked it from everything. The destination is now carried into the rule, and a decision that cannot be expressed is refused rather than stored.
- The main window rendered blank panes: the sidebar and the app tree drew nothing and the remaining panes showed only their lower half.
- Top Processes listed bundle identifiers at 0 B while the rest of the same screen showed correct totals.
- The map legend was drawn on top of the Apple Maps wordmark.
⌘,cleared the page name from the window's title bar.
FreeSnitch 0.3.1
A same-day fix for a defect found while verifying 0.3.0 on a real installation.
What was wrong
If your rule store contained a rule that a later validation rule rejects, the helper answered rule requests with an empty payload. Every rule disappeared from the app and from freesnitch rules list, while sitting intact in the database, and the error blamed a version mismatch that did not exist.
This affected reverse-DNS rules (.in-addr.arpa, .ip6.arpa) that earlier builds created before those names were refused as destinations. On the machine this was found on, 19 of 113 distinct rule hostnames were of that kind, and that was enough to hide all 217 rules.
Because the same encoder produces the snapshot delivered to the network extension, this could also stop policy reaching the filter, which fails open.
The fix
Bounding a payload and judging its content are now separate jobs:
- Bounds (byte size and rule count) still apply to everything crossing a process boundary. That is the protection added in 0.3.0 and it is unchanged.
- Content is judged where rules ENTER the policy: adding, updating, or importing. Reverse-DNS destinations are still refused there, exactly as before.
- Reading your own stored rules back out no longer re-judges them, so a rule accepted by an older build stays visible and manageable.
- A failure on an XPC path is now logged with its reason instead of being flattened into an empty reply.
If you installed 0.3.0 and your rules appeared to vanish, they were never lost. Update to 0.3.1 and they are all there.
Everything else
Unchanged from 0.3.0. See the 0.3.0 notes for the full feature list, safety model, and known limitations.
FreeSnitch 0.3.0
FreeSnitch is an open-source macOS application firewall. It shows you which processes reach the network, where they connect, and lets you decide what happens next. Everything stays on your Mac: no account, no telemetry, no phone-home.
This is the first public release.
Install
- Download
FreeSnitch.dmgbelow, open it, and drag FreeSnitch to Applications. - Launch FreeSnitch and approve the system extension when macOS asks. Approval happens in System Settings, under Login Items and Extensions, Network Extensions.
- Approve the privileged helper once. The helper is what enforces policy, including before you log in.
The app is signed with a Developer ID and notarized by Apple, so Gatekeeper accepts it without any right-click workaround. Verify that yourself before trusting it:
spctl --assess --type execute --verbose /Applications/FreeSnitch.app
codesign --verify --strict --deep --verbose=2 /Applications/FreeSnitch.app
xcrun stapler validate /Applications/FreeSnitch.appRequires macOS 13 Ventura or later.
What you get
- A live map of outbound connections, grouped by process, with byte counts, destination, country, and port.
- Alert mode, which asks before a new connection is allowed, and Silent Allow, which records without interrupting you.
- Rules by process, hostname, IP, CIDR range, and port, with priorities, temporary rules, and groups.
- A DNS proxy with DNS-over-HTTPS upstreams and domain blocklists.
- Kernel-level enforcement through a
pfctlanchor for IP, CIDR, and port rules, alongside the per-flow network extension. - A full command line interface,
freesnitch, covering rules, status, diagnostics, and import/export. - Policy at boot, so rules apply between startup and your first login instead of leaving a gap.
Safety model
A firewall that fails badly is worse than no firewall, so the failure behavior is explicit and tested:
- The filter fails open. If it cannot reach policy, traffic is allowed rather than silently cutting your machine off the network.
- Loopback, the app's own traffic, DHCP, and your configured DNS resolvers are never blocked, so a bad rule cannot lock you out of your own network stack.
- XPC channels carrying policy validate the peer's code signature, so an unsigned local process cannot inject rules.
- Rule payloads are bounded before they are decoded in privileged processes, by byte count, rule count, and per-field length, and the transport, boot, and import limits are consistent with each other.
- The helper owns the authoritative policy snapshot, so the GUI cannot overwrite a CLI change with stale state.
- DNS policy is published as one atomic snapshot, so a query is never judged against a mixture of old and new rules, and is verified race-free under ThreadSanitizer.
- Rule backups use one versioned file contract shared by the GUI and CLI, validated as a whole before anything is written, so an import is all-or-nothing.
- The helper reports the build it is actually running, separately from the build installed on disk, so an update that leaves a stale privileged daemon behind is surfaced with its exact fix instead of being hidden.
All of this is enforced by a safety audit and four verification harnesses that must pass before a release can be built.
Known limitations
Read these before relying on it:
- Blocklists filter DNS names only, and only while Enforcement is on. Software that connects to hardcoded IP addresses, or resolves names through its own encrypted DNS such as Chrome or Firefox DoH, is not stopped by a blocklist. Per-process, IP, CIDR, and port rules still apply. See #22 and #51.
- No traffic history view yet. Grouping who talks to what over time, and proposing rules from it, is designed but not shipped: #23.
- The CLI cannot answer pending connection alerts yet: #25.
- Profiles are not implemented yet: #31.
- Rule policy is bounded at 4096 rules for live delivery and 800 rules for the pre-login boot snapshot. Exceeding the boot limit is reported rather than silently truncated.
Uninstall
FreeSnitch installs a privileged helper and a system extension, so dragging the app to the Trash is not enough. From a checkout of this tag:
sudo Scripts/uninstall_freesnitch.sh --yesThat deactivates the system extension, unloads and removes the helper, flushes the pfctl anchor, and removes the app.
Verify what you are running
FreeSnitch is auditable on purpose. The source for this build is the v0.3.0 tag, and you can build and sign it yourself with Scripts/release.sh. The safety properties above live in Scripts/audit_firewall_safety.sh, which you can run against the source to confirm the claims in this document are actually enforced.