Tollgate is a personal iOS ad blocker. It runs as an on-device packet tunnel (a Network Extension "VPN" whose traffic never leaves the phone), blocks ad and tracker domains at the DNS layer, and filters HTTPS requests through a local proxy that trusts a user-installed root certificate, so full URL rules (EasyList, uBlock Origin and AdGuard syntax) apply to Safari, web views and third-party apps.
It is not distributed through the App Store. It is built and signed by GitHub Actions with the owner's Apple Developer account and installed from a Linux workstation.
Status: M3. The tunnel runs the Rust core (DNS blocking with DNS over HTTPS, and HTTPS
filtering through a local proxy once the certificate is trusted). The app downloads,
caches and compiles the filter lists (daily, in the background), installs the certificate,
and has an Activity tab with recent blocks and Settings for lists, your own rules, allowed
sites, never-filtered hosts and learned certificate pins. The core is verified on Linux with
devproxy; the on-device checks are in docs/experiments/m2.md and
docs/experiments/m3.md. See the
design and the
feasibility research.
core/: Rust workspace. Filtering, DNS and the HTTPS proxy live here and are developed and tested on Linux withcargo test.tollgate-ffiexposes them to Swift through uniffi.core/tools/devproxy: runs the same DNS responder and proxy on the workstation for Firefox;devproxy --helpand the checklist.ios/: a thin SwiftUI app and aNEPacketTunnelProviderextension. The Xcode project is generated fromios/project.ymlwith XcodeGen; nobody edits a.xcodeproj..github/workflows/ios.yml: on an Apple silicon runner, cross-compiles the Rust core foraarch64-apple-ios, generates the Swift bindings, generates the project, archives, signs and uploadsTollgate.ipaas a workflow artifact. Without signing secrets (for example on pull requests from forks) it only checks that everything compiles.- Identifiers (bundle IDs, App Group, profile names) live in
tooling/config.envonly.
-
In App Store Connect, Users and Access, Integrations, create a Team API key with Admin access and save the
.p8file somewhere private, for example~/.config/tollgate/. -
In the Apple Developer portal, register the App Group named in
tooling/config.env. -
With the iPhone connected over USB:
cat > ~/.config/tollgate/asc.env <<EOF ASC_KEY_ID="XXXX" ASC_ISSUER_ID="your-issuer-id" ASC_KEY_PATH="~/.config/tollgate/AuthKey_XXXX.p8" EOF tooling/asc/provision.py setup
The tool reads
~/.config/tollgate/asc.envfor any of the three variables not set in the environment.This registers the phone, creates both App IDs with the Network Extensions and App Groups capabilities, creates an Apple Development certificate and the development profiles. The phone's UDID and name are read with libimobiledevice, or with the pinned pymobiledevice3 when it is not installed. If more than one device is connected, or neither tool can read them, add
--udid <UDID> --name <name>. -
In the portal, assign the App Group to both App IDs, then run
tooling/asc/provision.py profilesagain. -
tooling/asc/push-secrets.shstores the certificate and profiles as repository secrets.
Everything written by the provisioning tool goes to tooling/asc/out/, which is gitignored.
gh workflow run ios.yml --repo mmaher88/tollgate --ref <branch> # build the branch tip first
tooling/scripts/fetch-ipa.sh <branch> # the CI build of the branch tip (default main)
tooling/scripts/install.sh # install on the USB-connected iPhone
tooling/scripts/logs.sh tunnel # stream the extension's logsfetch-ipa.sh refuses a build older than the branch tip unless given --allow-stale, and
prints the run number (#N), run id and URL, and the commit (kept in build/ipa/BUILD_INFO).
The app shows "N (short commit)" under Diagnostics, Build, which matches the run number and
the short commit.
The first install of a development-signed app asks for Developer Mode on the phone (Settings, Privacy & Security, Developer Mode), followed by a reboot.
In the app: tap Turn on (allow the VPN configuration), let the filter lists download, then
follow the Setup steps to install and trust the certificate before switching on HTTPS
filtering. tooling/scripts/logs.sh streams the app, the tunnel and the Rust core together.
To try the core on Linux without a phone, run devproxy (see
docs/experiments/m1-devproxy.md).
MIT