🛩️ planesnitch
Watches your configured locations and snitches the second something worth caring about flies within radius — military jets, government spooks, cops, emergency squawks, sketchy low-flyers, or whatever hex/type list you feed it. Pulls live ADS-B data from free public APIs (or your own ultrafeeder if you run one) — no SDR, no antenna, no hardware, just a config file. Alerts go to Telegram and/or a webhook.
For the full config schema, env vars, Docker run/compose, and Telegram bot setup, see references/setup.md.
Security & safety
planesnitch sends data outbound on every poll cycle — know what you're pointing it at:
- Location coordinates + radius leave your machine on every poll, sent to whichever ADS-B source(s) you configure (public APIs by default —
adsb.lol,adsb.fi,airplanes.live,adsb.one). If that's a concern, run your ownultrafeederand pointsourcesat it instead of the public endpoints. - External transmission on every alert, not just polling. Every alert fired through a
telegramnotification target sends the matched aircraft's coordinates (lat/lon), squawk, registration, and (ifattach_imageis left at its defaulttrue) a doc8643 image toapi.telegram.org. Every alert fired through awebhooknotification targetPOSTs the same data — plusimage_base64whenattach_imageis on — to whateverurlyou set. In both cases the location's identity and the aircraft's tracked position leave your host to that third party on every match. Pointnotify:only at Telegram bots/chats and webhookurls you run or explicitly trust; treat the notification target list as part of your threat model, not just the ADS-Bsources. config.yamlholds a live Telegram bot token and/or webhook URL + auth headers in plaintext. Treat it like a secret — don't commit it, don't paste it into chat, mount it read-only (:ro).- Alert content (aircraft position, squawk, registration, cached image) goes to every notification target you configure. Only point
notify:at Telegram chats and webhook endpoints you control. A webhook is an arbitrary outbound POST — vet the URL before adding it. - No inbound exposure by default beyond the health endpoint (
:8080) — planesnitch doesn't listen for anything else. It's a poll-and-push loop, not a server other things talk to.
When To Use
- Get pinged the moment a military, government, or police aircraft flies within range of a location you care about.
- Watch for emergency squawks (7500 hijack / 7600 radio failure / 7700 general emergency) near an airport, home, or anywhere else.
- Track specific aircraft by ICAO hex (a plane you're curious about) or by type (any A400M, any C-17, any Rafale).
- Get alerted on anything buzzing low overhead (altitude-filtered proximity watch).
- Monitor multiple locations at once — home, office, grandma's house, wherever — each with its own radius and its own alert rules.
- Feed aircraft alerts into your own systems via webhook (Home Assistant, a Discord relay, whatever).
When NOT To Use
- You want raw ADS-B reception off your own antenna — planesnitch is a consumer of ADS-B data, not a receiver. Run
ultrafeeder/dump1090separately and point aultrafeedersource at it if you want that. - You need historical flight-track playback or analytics — this is a live watch-and-alert tool, not a flight database.
- You need sub-second latency — it polls on an interval (
poll_interval, default15s), not a live stream.
Config Walkthrough
Everything lives in one config.yaml. Five sections: locations, sources, watchlists, alerts, notifications.
display_units — how altitude/distance/speed render in alerts and webhook payloads. aviation (ft/nm/kts, default), metric (m/km/km·h), imperial (ft/mi/mph).
locations — named points with lat, lon, radius, optional name (falls back to the key). Distance/altitude values accept unit suffixes (km, mi, nm, ft, m) — radius: 150km, radius: 50nm both work. Plain numbers default to km (distance) / ft (altitude).
sources — where the ADS-B data comes from. Public APIs (adsb_lol, adsb_fi, airplanes_live, adsb_one — no config needed) or your own ultrafeeder (needs a url). Multiple sources fetch in parallel and dedupe by ICAO hex, keeping whichever entry has the most fields. adsb_fi/airplanes_live/adsb_one return enriched data (name, operator, year); adsb_lol/ultrafeeder are raw-fields-only.
watchlists — what to look for. Six types: all (everything in radius), squawk (transponder codes), icao (specific hex addresses), icao_type (ICAO type designators like C17/B738), icao_csv (hex list from a CSV file in csv/ — see plane-alert-db), proximity (altitude-filtered — the "low flyer" watch). All types still respect the location's radius regardless of type.
alerts — wires watchlists to notification targets. Optional locations filter (omit = all locations checked). cooldown (durations: 5m, 1h30m, 90s, or plain seconds) stops the same aircraft from re-triggering the alert for a while.
notifications — telegram (bot_token + chat_id) or webhook (url + optional headers). Both support attach_image: false to skip the doc8643 aircraft-type photo (default true) — Telegram gets it as a photo attachment, webhooks get it as image_base64 in the JSON payload. Every alert sends location coordinates and aircraft tracking data off-host — telegram targets to api.telegram.org, webhook targets to whatever url you set (see Security & safety above). Only add notification targets pointing at Telegram bots/chats and webhook endpoints you control or trust.
Example config
poll_interval: 1m
display_units: aviation
locations:
home:
name: "Home"
lat: 38.8719
lon: -77.0563
radius: 150km
sources:
- type: adsb_lol
- type: adsb_fi
- type: airplanes_live
- type: adsb_one
watchlists:
emergencies:
type: squawk
values: ["7500", "7600", "7700", "7400", "7777"]
military:
type: icao_csv
source: plane-alert-mil.csv
government:
type: icao_csv
source: plane-alert-gov.csv
low_flyers:
type: proximity
min_altitude: 0ft
max_altitude: 3000ft
alerts:
- name: "Emergency Alert"
watchlists: [emergencies]
cooldown: 1m
notify: [tg_main, my_webhook]
- name: "Military Spotter"
watchlists: [military, government]
cooldown: 5m
notify: [tg_main]
notifications:
tg_main:
type: telegram
bot_token: "123456:ABC-DEF"
chat_id: "-100123456789"
my_webhook:
type: webhook
url: "https://example.com/hook"
headers:
Authorization: "Bearer xxx"
CSV watchlists (icao_csv type) need the matching file under csv/ — military/government/police/civilian/PIA/everything lists are pulled from plane-alert-db, 15,000+ aircraft catalogued by the plane-spotting community. See references/setup.md for the download commands and the full CSV list.
How To Run
# grab the example config and edit it — locations, sources, watchlists, alerts, notifications
curl -sL \
https://raw.githubusercontent.com/psyb0t/docker-planesnitch/main/config.yaml.example \
-o config.yaml
# minimal run — no CSV watchlists, no persistent image cache
docker run \
-v ./config.yaml:/app/config.yaml:ro \
psyb0t/planesnitch
# with CSV watchlists (military/gov/police lists) + persistent image cache
docker run \
-v ./config.yaml:/app/config.yaml:ro \
-v ./csv:/csv:ro \
-v ./images:/images \
psyb0t/planesnitch
A health endpoint runs on :8080; the image ships a built-in Docker healthcheck against it.
Full config field reference, env var table, Docker compose, and Telegram bot-token setup: references/setup.md.
