Snitches on every interesting aircraft that dares fly near your locations β military jets, government spooks, emergency squawks, sketchy low-flyers, or whatever the fuck you tell it to watch for. Monitor multiple locations at once β your house, your office, grandma's house, Area 51, whatever. Rats them out straight to your Telegram or webhook like a paranoid neighbor with radar.
No SDR required. No antenna. No hardware. Just an internet connection and a config file. Alerts via Telegram and/or webhooks. Works anywhere on the fuckin planet β and for as many places as you want.
# grab the example config and edit it
# with your location + notification settings
curl -sL \
https://raw.githubusercontent.com/psyb0t/docker-planesnitch/main/config.yaml.example \
-o config.yaml
# optional: download CSV watchlists for
# military/gov/police tracking
# (see Plane-Alert-DB section below)
# mkdir -p csv
# BASE=https://raw.githubusercontent.com/sdr-enthusiasts/plane-alert-db/main
# curl -sLo csv/plane-alert-mil.csv $BASE/plane-alert-mil.csv
# curl -sLo csv/plane-alert-gov.csv $BASE/plane-alert-gov.csv
# curl -sLo csv/plane-alert-pol.csv $BASE/plane-alert-pol.csv
# curl -sLo csv/plane-alert-civ.csv $BASE/plane-alert-civ.csv
# curl -sLo csv/plane-alert-pia.csv $BASE/plane-alert-pia.csv
# curl -sLo csv/plane-alert-db.csv $BASE/plane-alert-db.csv
# let it rip β without CSV watchlists
docker run \
-v ./config.yaml:/app/config.yaml:ro \
psyb0t/planesnitch
# or with CSV watchlists β mount your csv/ dir
docker run \
-v ./config.yaml:/app/config.yaml:ro \
-v ./csv:/csv:ro \
psyb0t/planesnitch
# full setup with persistent aircraft-type image cache
docker run \
-v ./config.yaml:/app/config.yaml:ro \
-v ./csv:/csv:ro \
-v ./images:/images \
psyb0t/planesnitch
2026-03-07 22:25:39 [planesnitch] INFO planesnitch starting
2026-03-07 22:25:39 [planesnitch] INFO health endpoint listening on :8080
2026-03-07 22:25:40 [planesnitch] INFO watchlist military: 8709 aircraft loaded
2026-03-07 22:25:40 [planesnitch] INFO watchlist government: 1743 aircraft loaded
2026-03-07 22:25:40 [planesnitch] INFO fetched 14 aircraft
2026-03-07 22:25:40 [planesnitch] INFO ALERT [Military Spotter] [home] ae07e1 TEDDY64
Single YAML file. Define watchlists (what to snitch on), alert rules (when to lose your shit), and notification targets (where to scream about it).
Control how altitude, distance, and speed show up in alerts and webhook payloads:
display_units: aviation # default
| Preset | Altitude | Distance | Speed |
|---|---|---|---|
aviation | ft | nm | kts |
metric | m | km | km/h |
imperial | ft | mi | mph |
Define one or more named locations. Each location has coordinates and a search radius.
Units: Any distance or altitude value in the config accepts a unit suffix:
km,mi,nm,ft,m. They all convert internally β use whatever makes sense.radius: 100miandmax_altitude: 1kmboth work. Plain numbers without a suffix default to km for distances and ft for altitudes.
locations:
home:
name: "Home"
lat: 38.8719
lon: -77.0563
radius: 150km
area51:
name: "Area 51"
lat: 37.2350
lon: -115.8111
radius: 50nm
Each location can have an optional name for pretty display in alerts. Falls back to the key if not set.
Where to get the goods. Use multiple sources β they fetch in parallel for each location and deduplicate by ICAO hex, keeping the entry with the most data.
Note:
adsb_fi,airplanes_live, andadsb_onereturn enriched data (full aircraft name, owner/operator, year).adsb_lolandultrafeederonly return raw ADS-B fields. When using multiple sources, planesnitch automatically keeps the richest entry for each aircraft.
sources:
# Public APIs β no hardware, no bullshit
- type: adsb_lol
- type: adsb_fi
- type: airplanes_live
- type: adsb_one
# Local ultrafeeder β if you're running your own receiver
- type: ultrafeeder
url: http://ultrafeeder:80/tar1090/data/aircraft.json
Tell the snitch what to look for. All types respect the location's radius β aircraft outside the radius are ignored regardless of type.
| Type | Matches On | Source |
|---|---|---|
all | Every aircraft | Everything within the location's radius |
squawk | Transponder squawk code | Inline list |
icao | ICAO hex address | Inline list |
icao_type | ICAO type designator (doc 8643β ) e.g. C17, B738 | Inline list |
icao_csv | ICAO hex from CSV | Local file in csv/ dir (plane-alert-dbβ format) |
proximity | Altitude filter | Location radius + altitude limits |
watchlists:
# The panic buttons
emergencies:
type: squawk
values: ["7500", "7600", "7700"]
# 8,709 military aircraft β the big boys
military:
type: icao_csv
source: plane-alert-mil.csv
# Government aircraft
government:
type: icao_csv
source: plane-alert-gov.csv
# Police / law enforcement
police:
type: icao_csv
source: plane-alert-pol.csv
# Stalk specific aircraft by hex
my_planes:
type: icao
values: ["4ca123", "a12345"]
# Stalk by aircraft type β any A400M, Rafale, or Alpha Jet
cool_jets:
type: icao_type
values: ["A400", "RFAL", "AJET"]
# Every single aircraft in range
everything:
type: all
# WTF just buzzed my house
low_flyers:
type: proximity
min_altitude: 0ft
max_altitude: 3000ft
Connect watchlists to notifications. Optionally filter by locations β if omitted, all locations are checked. Cooldown so it doesn't spam the shit out of you about the same C-17 doing laps for 3 hours. Durations support s, m, h β e.g. 5m, 1h30m, 90s, or plain seconds:
alerts:
- name: "Emergency Alert"
watchlists: [emergencies]
cooldown: 1m
notify: [tg_emergencies, my_webhook]
- name: "Military Spotter"
watchlists: [military, government]
cooldown: 5m
notify: [tg_spotting]
# Only alert for this watchlist at specific locations
- name: "Everything at Home"
locations: [home]
watchlists: [everything]
cooldown: 1m
notify: [tg_main]
Telegram β different alerts to different channels:
notifications:
tg_emergencies:
type: telegram
bot_token: "123456:ABC-DEF"
chat_id: "-100123456789"
tg_spotting:
type: telegram
bot_token: "123456:ABC-DEF"
chat_id: "-100987654321"
When an aircraft has an ICAO type designator (field t), planesnitch fetches the matching aircraft image from doc8643.comβ and:
image_base64Images are cached in images/ (mount -v ./images:/images to persist across restarts). Misses are recorded as .notfound markers so types without images aren't re-fetched. The cache survives indefinitely β delete the dir to force a refresh.
To opt out per notification target (e.g. avoid blowing up a chat with photos, or cut webhook payload size on Home Assistant), set attach_image: false on the target. Default is true. If every target attached to a rule opts out, planesnitch skips the doc8643 fetch entirely for that alert β no wasted bandwidth or disk.
notifications:
tg_main:
type: telegram
bot_token: "..."
chat_id: "..."
attach_image: false # text-only alerts to this chat
Webhook β POSTs a JSON array of alert objects per poll cycle:
notifications:
my_webhook:
type: webhook
url: "https://example.com/hook"
headers:
Authorization: "Bearer xxx"
Webhook payload schema β always a JSON array, even for a single alert:
[
{
"alert": "Military Spotter",
"location": "Home",
"match": {
"reason": "icao_csv_match",
"watchlist": "military",
"info": {
"Registration": "94-0067",
"Operator": "USAF",
"Type": "BOEING C-17A Globemaster III",
"ICAO Type": "C17",
"CMPG": "Mil",
"Tag 1": "Cargo",
"Tag 2": "Strategic Airlift",
"Tag 3": "Freedom Delivery",
"Category": "US Military",
"Link": "https://w.wiki/..."
}
},
"units": {
"altitude": "ft",
"distance": "nm",
"speed": "kts"
},
"image_base64": "/9j/4AAQSkZJRgABAQ...", // null if no image cached
"aircraft": {
"hex": "ae07e1",
"flight": "TEDDY64",
"registration": "94-0067",
"type": "C17",
"description": "BOEING C-17A Globemaster III",
"owner_operator": "USAF",
"year": "1994",
"squawk": "1613",
"emergency": "none",
"altitude": 12350,
"lat": 37.9306,
"lon": -78.7019,
"speed": 413,
"track": 245.3,
"distance": 98.9
}
}
]
The match object varies by watchlist type:
| Watchlist Type | Match Fields |
|---|---|
squawk | {"reason": "squawk", "watchlist": "...", "squawk": "7700", "distance_km": 12.3} |
icao | {"reason": "icao_match", "watchlist": "...", "distance_km": 12.3} |
icao_type | {"reason": "icao_type_match", "watchlist": "...", "type": "C17", "distance_km": 12.3} |
icao_csv | {"reason": "icao_csv_match", "watchlist": "...", "info": {"Operator": "..."}, "distance_km": 12.3} |
all | {"reason": "all", "watchlist": "...", "distance_km": 12.3} |
proximity | {"reason": "proximity", "watchlist": "...", "distance_km": 12.3} |
π Emergency Alert
π¨ squawk 7700 (EMERGENCY)
βοΈ RYR1234
π©οΈ BOEING 737-800 | EI-ABC | 2015
πΌ RYANAIR
π 45.5000, 28.1000 | 3,200 ft
π 6 nm from home
π¨ 280 kts
πΊοΈ https://globe.adsb.fi/?icao=4ca123
π Military Spotter
βοΈ TEDDY64
π©οΈ BOEING C-17A Globemaster III | 94-0067 | 1994
πΌ USAF
π·οΈ USAF β USAF
π 37.9306, -78.7019 | 12,350 ft
π 99 nm from home
π¨ 413 kts
π‘ squawk 1613
πΊοΈ https://globe.adsb.fi/?icao=ae07e1
Click the link, watch the bastard in real time on globe.adsb.fiβ .
/newbot, get a tokenhttps://api.telegram.org/bot<TOKEN>/getUpdates for your chat IDgetUpdates for the channel ID (starts with -100)Uses the community-curated lists from sdr-enthusiasts/plane-alert-dbβ β 15,000+ aircraft catalogued by the fine degenerates of the plane spotting community:
| List | Count | File |
|---|---|---|
| ποΈ Military | 8,709 | plane-alert-mil.csvβ |
| ποΈ Government | 1,743 | plane-alert-gov.csvβ |
| π Police | 932 | plane-alert-pol.csvβ |
| βοΈ Civilian | 4,530 | plane-alert-civ.csvβ |
| π Privacy (PIA) | 94 | plane-alert-pia.csvβ |
| π Everything | 15,914 | plane-alert-db.csvβ |
Download what you need into your csv/ directory:
mkdir -p csv
BASE=https://raw.githubusercontent.com/sdr-enthusiasts/plane-alert-db/main
curl -sLo csv/plane-alert-mil.csv $BASE/plane-alert-mil.csv
curl -sLo csv/plane-alert-gov.csv $BASE/plane-alert-gov.csv
curl -sLo csv/plane-alert-pol.csv $BASE/plane-alert-pol.csv
curl -sLo csv/plane-alert-civ.csv $BASE/plane-alert-civ.csv
curl -sLo csv/plane-alert-pia.csv $BASE/plane-alert-pia.csv
curl -sLo csv/plane-alert-db.csv $BASE/plane-alert-db.csv
plane-alert-db.csv contains everything (mil + gov + pol + civ + pia) in one file. If you just want to watch all 15,000+ aircraft, use that one and skip the rest.
Re-download whenever you want fresh data. Or write your own CSV β just needs an ICAO hex column first.
The skillβ works in any agent that reads .agents/skills/, and installs natively in the clients below.
claude plugin marketplace add psyb0t/agents
claude plugin install planesnitch@psyb0t
codex plugin marketplace add psyb0t/agents
codex plugin add planesnitch@psyb0t
Installed via the marketplace, invoke it as $planesnitch:planesnitch. Codex also picks the skill up automatically with no install in any repo containing .agents/skills/, where it invokes as plain $planesnitch.
The skill is published to ClawHub on every release:
openclaw skills install @psyb0t/planesnitch
βββ planesnitch/
β βββ __init__.py
β βββ __main__.py # entry point + main loop
β βββ config.py # config loading, unit conversion, constants
β βββ sources.py # ADS-B API fetching + dedup
β βββ geo.py # distance calculations
β βββ watchlists.py # watchlist loading + matching
β βββ alerts.py # alert checking + cooldowns
β βββ images.py # doc8643 aircraft type image caching
β βββ notify.py # telegram + webhook formatting + sending
βββ config.yaml.example # example config β copy to config.yaml and fill in your shit
βββ csv/ # CSV watchlists go here, mounted to /csv
βββ images/ # cached doc8643 type images, mounted to /images
βββ run.sh # build + run in docker
βββ requirements.txt
βββ Dockerfile
βββ README.md
Not 47 microservices. It watches planes and sends messages. That's it.
Environment variables:
| Variable | Default | Description |
|---|---|---|
LOG_LEVEL | INFO | Set to DEBUG for verbose output |
PLANESNITCH_CONFIG | config.yaml | Path to config file |
PLANESNITCH_CSV_DIR | /csv | Path to CSV watchlist files |
PLANESNITCH_IMAGES_DIR | /images | Path to cached doc8643 aircraft-type images |
A health endpoint runs on port 8080. The Docker image includes a built-in healthcheck against it.
WTFPLβ β Do What The Fuck You Want To.
Content type
Image
Digest
sha256:e696491a4β¦
Size
48.6 MB
Last updated
2 months ago
docker pull psyb0t/planesnitch