TrackerControl for iOS is a web service and analyser pipeline for finding trackers in iOS apps. It is inspired by TrackerControl for Android and related research on privacy analysis for mobile apps.
The project has two parts:
- A Node.js/Express website where people can search App Store apps and view tracker reports.
- An analyser worker that downloads free App Store apps, analyses them on an actual iPhone, and uploads the results back to the website.
The website also includes jurisdiction analysis, showing which companies and countries control detected tracking infrastructure.
- Search free iOS apps from the App Store.
- Queue apps for analysis by popularity and staleness.
- Detect embedded tracker signatures and declared tracking domains.
- Store current and historical analysis results.
- Show tracker, permission, and jurisdiction summaries.
- Run the analyser from macOS or a Raspberry Pi host.
Only free App Store apps are queued for analysis. The queue prioritises apps with more stored App Store reviews, then rechecks stale analyses over time.
analyser/ App download, install, scan, and upload scripts
lib/ Shared website helpers, including jurisdiction analysis
migrations/ SQL migrations
models/ PostgreSQL access layer
routes/ Express routes and analyser API endpoints
scripts/ Maintenance scripts
views/ Pug templates
public/ Browser assets
static/ Static image assets
Website:
- Node.js
- npm
- PostgreSQL
DATABASE_URLpointing at a database with theappstable
Analyser:
- An iPhone reachable over SSH
trackerscaninstalled on the iPhone- Matching
UPLOAD_PASSWORDon the website and analyser
Install dependencies:
npm installCreate a root .env file:
DATABASE_URL=postgres://user:password@host:5432/database
UPLOAD_PASSWORD=change-me
CURRENT_ANALYSIS_VERSION=4
BODY_LIMIT=25mb
PORT=3000Run migrations:
npm run migrateStart the website:
npm run watchFor production:
npm run startOpen http://localhost:3000 if PORT=3000 is set.
Copy the example config:
cp analyser/.env.example analyser/.envSet at least:
SERVER=https://your-server.example
UPLOAD_PASSWORD=change-me
ANALYSIS_VERSION=4
ANALYSIS_MODE=trackerscan
TRACKERSCAN_CMD="ssh iphone trackerscan"
TRACKERSCAN_SIGNATURES=/var/mobile/ios_signatures_v2.json
TRACKERSCAN_SIGNATURE_SET=ios-v2Log in to ipatool once on the analyser host:
ipatool auth loginRun the queue processor:
bash analyser/processQueue.shTo analyse one app immediately:
ONLY_APP_ID=com.spotify.client bash analyser/processQueue.shThe default analysis path uses trackerscan and uploads analysis version 4. The legacy Frida flow is still available with:
ANALYSIS_MODE=frida bash analyser/processQueue.shThe analyser has conservative download and retry limits by default. Tune these in analyser/.env only if the host, network, and storage can handle the extra load.
The analyser can run on Linux/arm64. A Raspberry Pi needs node, python3, unzip, zip, curl, openssh-client, libimobiledevice tools, ideviceinstaller, and a Linux arm64 ipatool.
Use the installer:
sudo bash scripts/setup-raspi-analyser.shSee raspberry-pi-analyser.md for the full setup, including systemd, SSH aliases, ipatool, and RAM-backed IPA storage.
The website exposes analyser endpoints:
GET /queuereturns the next app to process.GET /pingmarks the analyser online.POST /uploadAnalysisstores successful analysis results.POST /reportAnalysisFailurestores failed analysis results.
Analyzer requests authenticate with Authorization: Bearer $UPLOAD_PASSWORD.
Health checks:
GET /healthzreturns200when the website can reach PostgreSQL.GET /healthz/analyserreturns200when the analyser has pinged in the last hour.
Useful maintenance commands:
npm test
npm run queue-status
npm run priority-report -- --limit=20
npm run backup-db
npm run migrateReset one app for a fresh analysis:
npm run reset-app -- --appid=com.google.ios.youtube --applyWithout --apply, the reset command runs as a dry run.
Set CURRENT_ANALYSIS_VERSION=4 on the website when version 4 results should be treated as current.
The queue will reprocess apps when:
- They have never been analysed.
- Their analysis version is stale.
- Their analysis is older than
STALE_ANALYSIS_DAYS. - A previous processing marker has expired after
PROCESSING_TIMEOUT_MINUTES.
The default stale window is 180 days. The default processing timeout is 120 minutes.
This project is licensed under AGPLv3.