Read this in other languages: Türkçe
A macOS menu bar app that gets around DPI-based blocking, turns itself on when you need it and — just as importantly — turns itself off cleanly when you don't.
It bundles SpoofDPI as its engine and adds the parts that make it usable every day: rules, a real interface, a diagnostics tab and a safety net that keeps a crash from taking your internet down with it.
- macOS 14 (Sonoma) or newer · Apple Silicon
- MIT licensed · engine is Apache-2.0
- Interface in English and Turkish
Running SpoofDPI by hand works, but the setup around it is where the pain is. This project started from a working hand-rolled setup — a shell script, a LaunchAgent and a hand-edited TOML file — and every feature below fixes something that actually went wrong with it:
| Problem with the manual setup | What ezDPI does |
|---|---|
A pgrep loop every 5 seconds, so the proxy arrived seconds late |
Reacts to NSWorkspace launch/quit notifications, no polling |
The network interface was hard-coded to Wi-Fi, so it silently did nothing on Ethernet or a dock |
Finds the connected network services every time it turns on |
| If the engine crashed, the system proxy stayed pointed at a dead port — every app loses network | Writes the previous proxy state to disk before switching, restores it on crash, quit or SIGTERM |
| Changing a rule meant editing a TOML file | Everything is in the interface; the TOML is generated |
Diagnosis was manual: read a log, curl -x one domain at a time |
Test tab checks every domain directly and through the proxy, and explains the result in plain language |
Rules that decide when it runs. Three kinds, and any of them can trigger it:
- While an app is open — pick an app, ezDPI runs exactly as long as it does.
- On a certain network — a specific Wi-Fi network, or a connection type (Wi-Fi, Ethernet).
- During certain hours — days of the week and a time range, midnight-crossing ranges included.
You can also force it on or off from the menu at any time.
Your own sites. Type example.com and ezDPI covers example.com, *.example.com
and **.example.com. No wildcard syntax to learn. You can add a site straight from the
menu bar without opening settings.
Three methods instead of knobs. Most people should never see split-mode or
chunk-size. The Sites tab offers Standard, Alternative 1 and Alternative 2 — if a site
does not open, you work down the list. Turn on advanced settings and the raw values are
all still there.
A test tab that answers the question. For every address it reports one of:
- Already works, ezDPI not needed.
- Blocked, but ezDPI opens it.
- Still blocked with ezDPI — try another method.
- Blocked (certificate not trusted) — which means something is intercepting TLS on your connection.
It cleans up after itself. The previous proxy configuration is saved to disk before anything changes. On a normal quit it is restored. If ezDPI is killed, the state file survives and the next launch restores it. There is also a Turn off now button in the menu that unwinds everything immediately.
Relaunch an app inside ezDPI's environment. Some apps — the Discord updater is the
well-known one — read https_proxy only once, at launch, and ignore the system proxy
entirely. If ezDPI starts after the app, that app never sees it. The menu can quit an
app and start it again with the environment in place, holding the engine up across the
restart so there is no gap.
URL scheme for automation: ezdpi://on, ezdpi://off, ezdpi://auto,
ezdpi://relaunch?bundle=com.hnc.Discord. Works from Shortcuts, Raycast or a shell.
Grab the latest ezDPI-x.y.z.zip from the releases page,
unzip it and drag ezDPI.app into /Applications.
The build is ad-hoc signed, not notarized, so macOS will not open it on the first try. Either:
- right-click the app → Open → Open in the dialog, or
- run
xattr -dr com.apple.quarantine /Applications/ezDPI.app
Verify the download if you want to:
shasum -a 256 ezDPI-0.1.0.zip
# compare against the .sha256 file published with the releasebrew install spoofdpi # engine binary, bundled at build time
git clone https://github.com/aliakpoyraz/ezdpi.git
cd ezdpi
export DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer
./build.sh # produces build/ezDPI.appTo sign with your own Developer ID:
CODESIGN_IDENTITY="Developer ID Application: Your Name (TEAMID)" ./build.shbuild.sh copies the engine from /opt/homebrew/bin/spoofdpi into the app bundle.
Point ENGINE_SRC somewhere else if you built the engine yourself.
- Launch ezDPI. It lives in the menu bar, not the Dock.
- Open Settings → Sites and add the addresses that do not open for you.
- Open Settings → When and add a rule — for example, while Discord is open.
- Leave the menu on Automatic.
- Open Settings → Test and press Test sites to confirm it is doing something.
Supervisor evaluates rules, drives engine and proxy as one unit
├── Engine protocol; SpoofDPIEngine runs the bundled binary as a process
├── SystemProxy networksetup: snapshot, apply, restore
├── AppWatcher NSWorkspace launch/quit notifications
├── NetworkWatcher NWPathMonitor + CoreWLAN
└── ScheduleWatcher 30-second evaluation tick
The engine sits behind a protocol. Everything above it — rules, sites, interface — stays the same if the engine is ever swapped for a fork or a Network Extension.
The generated TOML always ends with a catch-all rule that passes non-listed traffic through untouched. That matters: fragmenting all traffic breaks TLS on unrelated sites, payment pages in particular.
The order of operations when turning off is deliberate: restore the system proxy first, stop the engine second. The other way around leaves a window where every request goes to a port that is no longer listening.
An app says "update failed" while ezDPI is on.
That app reads https_proxy only at launch. Turn on Extra support for some apps in
Settings → General, then use Relaunch with ezDPI in the menu bar for that app.
A site still does not open. Sites tab → change the method: Standard → Alternative 1 → Alternative 2. Test after each.
My internet is broken and ezDPI is not running. Launch ezDPI once; it detects the leftover state and restores your proxy settings. To do it by hand:
networksetup -setwebproxystate Wi-Fi off
networksetup -setsecurewebproxystate Wi-Fi off
launchctl unsetenv https_proxy"Cannot read the Wi-Fi name." macOS 14 and later require Location permission to read an SSID. Grant it, or match on connection type instead of network name.
The port is busy. The default is 18080 and ezDPI moves to the next free port on its own. You can pin a port in advanced settings.
| Path | Contents |
|---|---|
~/Library/Application Support/ezDPI/config.json |
the single source of truth: settings, sites, rules |
~/Library/Application Support/ezDPI/engine.toml |
generated engine config, do not edit |
~/Library/Application Support/ezDPI/proxy-state.json |
present only while the proxy is on; used for crash recovery |
~/Library/Logs/ezDPI/ezdpi.log |
app log |
~/Library/Logs/ezDPI/engine.log |
engine output |
- The bundled engine is arm64 only. Intel Macs need a universal binary.
- Releases are ad-hoc signed, not notarized.
- Reading a Wi-Fi network name requires Location permission on macOS 14+.
- The engine reads its config once at startup, so changing a setting restarts it.
- There is no "turn on when I visit a listed site" trigger yet. The planned approach is a PAC file so that only listed domains are routed through the engine.
ezDPI is MIT licensed — see LICENSE.
The bundled engine, SpoofDPI, is Apache-2.0 and is redistributed unmodified. See NOTICE and third_party/SpoofDPI-Apache-2.0.txt.
This is a connectivity tool. What is permitted varies by country and by network. You are responsible for using it in line with the rules that apply to you.