docker-planesnitch: Verlinkt Elk Vliegtuig Dat Het Waagt Langs Je Te Vliegen

Ik woon onder een aanvliegroute. Militaire vrachtvliegtuigen, regeringsjets, politiehelikopters, af en toe een noodsquawk, het vliegt allemaal over mijn hoofd en ik had geen idee wat het was. Natuurlijk kon ik Flightradar24 openzetten en de hele dag naar een kaart staren, maar ik ben geen gepensioneerde met een verrekijker. Ik wilde iets dat de lucht voor mij in de gaten houdt en tegen me schreeuwt zodra er iets interessants opduikt.
Dus bouwde ik planesnitch. Een Docker-container die publieke ADS-B-API’s afvraagt, vliegtuigen langs je volglijsten legt, en alarmen afvuurt naar Telegram of webhooks. Militaire jets, overheidsspionnen, noodsquawks, laagvliegers met een luchtje, wat je hem ook opdraagt te bewaken. Meerdere locaties tegelijk. Geen SDR-hardware, geen antenne, geen gezeik. Alleen een configuratiebestand en een internetverbinding.

Hoe Het Werkt

Bij elke pollcyclus (instelbaar, standaard 1 minuut) haalt planesnitch vliegtuigdata op bij maximaal 4 publieke ADS-B-API’s, parallel:

Het ontdubbelt op ICAO-hexcode en houdt de invoer met de meeste datavelden. Sommige bronnen geven verrijkte data terug, vliegtuigtype, eigenaar of exploitant, registratie, bouwjaar, terwijl andere je alleen kale ADS-B-velden geven. Planesnitch houdt automatisch de rijkste invoer per vliegtuig.
Daarna haalt het elk vliegtuig door je volglijsten en je alarmregels. Match gevonden? Het maakt er een bericht van en knalt dat naar je Telegram-bot of je webhook-endpoint. Cooldowns per alarm en per vliegtuig voorkomen dat het je volspamt over dezelfde C-17 die drie uur lang rondjes draait.
Draai je je eigen ADS-B-ontvanger met ultrafeeder, dan kun je planesnitch daar ook op richten, dezelfde configuratie, je voegt alleen een lokale bron-URL toe.

Installatie

# Grab the config and edit it
curl -sL 
  https://raw.githubusercontent.com/psyb0t/docker-planesnitch/main/config.yaml.example 
  -o config.yaml
# Optional: download military/gov/police watchlists
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
# Run it
docker run 
  -v ./config.yaml:/app/config.yaml:ro 
  -v ./csv:/csv:ro 
  psyb0t/planesnitch

Dat is alles. Je zet je coördinaten en het token van je Telegram-bot in de configuratie, en je bent aan het verlinken.

De Configuratie

Eén YAML-bestand regelt alles. Je bepaalt waar je zit, waar het op moet letten, wanneer het moet alarmeren, en waar het naartoe stuurt.

Locaties

Bewaak zoveel plekken als je wilt. Je huis, je kantoor, het huis van je oma, Area 51, elk met een eigen zoekradius.

locations:
  home:
    name: "Home"
    lat: 38.8719
    lon: -77.0563
    radius: 150km
  area51:
    name: "Area 51"
    lat: 37.2350
    lon: -115.8111
    radius: 50nm

Eenheidssuffixen werken overal, km, mi, nm, ft, m. Kale getallen vallen standaard terug op km bij afstanden en op ft bij hoogtes.
Automatisch groeperen van nabije locaties. Als twee locaties dicht genoeg bij elkaar liggen dat één API-aanroep ze allebei dekt, clustert planesnitch ze automatisch en doet het één upstream-verzoek in plaats van twee. Zet je je huis en je kantoor allebei op 50km radius en liggen ze 30km uit elkaar? Eén API-treffer, de resultaten verdeeld over beide locaties. Definieer een dozijn overlappende punten, planesnitch blijft samenvoegen tot een groep tegen de maximale omhullende cirkel aan loopt, en begint dan een nieuwe. Dat houdt je tegen je rate-limitquotum te verbranden aan overbodige queries op hetzelfde stuk lucht.
Cooldowns per bron. Elke upstream-bron krijgt zijn eigen rate-limitcooldown, als adsb.lol 429’s begint te gooien, trekt die ene bron zich terug op zijn eigen schema terwijl adsb.fi en je lokale ultrafeeder gewoon door blijven pollen. Geen enkele API-afknijping legt de hele feed plat.

Volglijsten

Zes soorten volglijsten vertellen planesnitch wie het moet verlinken:

watchlists:
  # Emergency squawk codes
  emergencies:
    type: squawk
    values: ["7500", "7600", "7700", "7400", "7777"]
  # 8,709 military aircraft from community database
  military:
    type: icao_csv
    source: plane-alert-mil.csv
  # Government aircraft
  government:
    type: icao_csv
    source: plane-alert-gov.csv
  # Stalk specific aircraft by ICAO 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"]
  # Everything within radius
  everything:
    type: all
  # WTF just buzzed my house
  low_flyers:
    type: proximity
    min_altitude: 0ft
    max_altitude: 3000ft

De squawkcodes zijn de paniekknoppen van de luchtvaart, 7700 is algemene noodsituatie, 7600 is radiostoring, 7500 is kaping, 7400 is noodsituatie met een onbemand luchtvaartuig, 7777 is militaire onderschepping. Het soort shit waar je van wilt weten als het 6 mijl van je huis gebeurt.
Het type icao_csv koppelt aan plane-alert-db, een door de gemeenschap samengestelde database met meer dan 15.000 interessante luchtvaartuigen, bijgehouden door de fijne degeneraten uit de plane-spottingwereld:

  • Militair, 8.709 luchtvaartuigen
  • Overheid, 1.743 luchtvaartuigen
  • Politie, 932 luchtvaartuigen
  • Civiel, 4.530 opmerkelijke luchtvaartuigen
  • Privacy (PIA), 94 privacybewuste exploitanten
  • Alles, 15.914 bij elkaar

De volglijst icao_type (v1.6) matcht op de typeaanduiding volgens ICAO doc 8643, de code van 3-4 tekens waarmee de luchtvaart het vliegtuigmodel zelf aanduidt, niet de registratie. C17 is elke C-17 Globemaster op de planeet. B738 is elke 737-800. RFAL is elke Rafale. AJET is elke Alpha Jet. Gooi de aanduidingen die jou interesseren in values: en je krijgt alarmen voor elk toestel van dat type dat je radius in vliegt, ongeacht wie het bezit of welke registratie eronder hangt.
De nabijheidsvolglijst is er om laagvliegers te vangen. Je stelt een hoogtebereik in en planesnitch waarschuwt je zodra er iets binnen je radius onder dat plafond vliegt. Handig om “wat was dat in godsnaam” te beantwoorden als er om 2 uur ‘s nachts iets je ramen laat rammelen.

Alarmen

Je koppelt volglijsten aan notificatiedoelen. Desgewenst filter je op locatie, laat je dat weg, dan worden alle locaties gecontroleerd. Cooldowns voorkomen spam:

alerts:
  - name: "Emergency Alert"
    watchlists: [emergencies]
    cooldown: 1m
    notify: [tg_emergencies]
  - name: "Military Spotter"
    watchlists: [military, government]
    cooldown: 5m
    notify: [tg_spotting]
  - name: "Everything at Home"
    locations: [home]
    watchlists: [everything]
    cooldown: 1m
    notify: [tg_main]

Duurstrings accepteren s, m, h, dus 5m, 1h30m, 90s, of kale seconden werken allemaal. Verschillende alarmen kunnen naar verschillende Telegram-kanalen, noodgevallen naar het ene, militair spotten naar het volgende, laagvliegers naar een derde.

Notificaties

Telegram en webhooks. Stuur verschillende alarmen naar verschillende bestemmingen:

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"
  my_webhook:
    type: webhook
    url: "https://example.com/hook"
    headers:
      Authorization: "Bearer xxx"

Vliegtuigfoto’s (v1.6)

Wanneer een vliegtuig een ICAO-typeaanduiding heeft (bijvoorbeeld C17, B738, RFAL), haalt planesnitch de bijbehorende vliegtuigfoto op van doc8643.com en:

  • hangt hem als foto aan het Telegram-bericht (de alarmtekst wordt het bijschrift, en gaat het bijschrift over Telegrams limiet van 1024 tekens, dan wordt het opgesplitst in een tekstbericht gevolgd door de foto, zodat je de inhoud nooit kwijtraakt)
  • stopt de JPEG-bytes (base64) in webhook-payloads, in het nieuwe veld image_base64, null als er geen afbeelding in de cache zit

Afbeeldingen worden op schijf gecachet onder /images binnen de container, mount -v ./images:/images om ze herstarts te laten overleven. De cache is gesleuteld op typeaanduiding, dus alle ruim 8.000 C-17’s delen één fotobestand. Missers worden vastgelegd als .notfound-markeringen, zodat types zonder foto op doc8643 niet elke cyclus opnieuw worden opgehaald. Verwijder de cachemap om een verversing af te dwingen.

De cachemap gebruikt atomaire schrijfacties (.tmp plus os.replace) en een asyncio.Lock per type, zodat meerdere alarmen die tegelijk afgaan op hetzelfde vliegtuigtype elkaar bij het ophalen niet in de weg zitten. Typeaanduidingen uit de upstream ADS-B-feeds worden gevalideerd tegen ^[A-Z0-9]{1,8}$ voordat er ook maar iets aan het bestandssysteem wordt aangeraakt, geen pad-traversal die via een vergiftigd t-veld naar binnen glipt, en elk antwoord met een content type dat niet image/* is wordt geweigerd, zodat de HTML-challengepagina’s van Cloudflare de cache niet kunnen vergiftigen.

Afzien van Afbeeldingen, per Doel (v1.7)

Foto’s zijn geweldig voor een persoonlijk Telegram-kanaal. Ze zijn een regelrechte nachtmerrie voor een Home Assistant-webhookontvanger die elke cyclus 80 KB base64 moet wegkauwen, of voor een drukke spotterschat waar iedereen allang weet hoe een C-17 eruitziet. Dus elk notificatiedoel accepteert attach_image: false om terug te zakken naar alleen tekst:

notifications:
  tg_main:
    type: telegram
    bot_token: "..."
    chat_id: "..."
    attach_image: false   # plain sendMessage, no photo
  my_webhook:
    type: webhook
    url: "https://example.com/hook"
    attach_image: false   # image_base64 will be null

De standaard is true, dus voor alleen tekst kies je zelf. Het slimme stuk: als alle notificatiedoelen die aan een bepaalde alarmregel hangen afzien, slaat planesnitch het ophalen bij doc8643 voor dat alarm helemaal over. Geen verspilde netwerkaanroep, geen verspilde schijfactie, geen nodeloos genomen lock. Doelen met attach_image=true en attach_image=false op hetzelfde alarm door elkaar halen haalt nog steeds één keer op en bedient allebei, de fotocache is gedeeld.

Hoe de Alarmen Eruitzien

Telegram-alarmen zijn opgemaakt met emoji’s en met alle data die je in één oogopslag wilt hebben:

🔔 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

Klik op de link en je krijgt een realtimekaart van het vliegtuig op globe.adsb.fi. De squawkregel bevat ook de betekenis, planesnitch heeft een ingebouwde database met de betekenissen en reikwijdtes van squawkcodes, dus het vertelt je waarom die code ertoe doet.
Webhook-payloads zijn JSON-arrays met volledige metadata, vliegtuigdetails, reden van de match, informatie over de volglijst, CSV-metadata als het uit plane-alert-db komt, afstand tot de locatie, en weergave-eenheden. Alles wat je nodig hebt om er je eigen integraties bovenop te bouwen.

Weergave-eenheden

Drie voorinstellingen bepalen hoe hoogte, afstand en snelheid in de alarmen verschijnen:

display_units: aviation   # ft / nm / kts (default)
display_units: metric     # m / km / km/h
display_units: imperial   # ft / mi / mph

De omrekening gebeurt bij het versturen. Het interne rekenwerk is altijd metrisch. Gebruik wat past bij jouw situatie, ben je piloot, luchtvaarteenheden. Ben je een normaal mens, metrisch. Ben je Amerikaan, imperiaal.

Hot Reload en Health

Wijzigingen in de configuratie worden bij de volgende pollcyclus opgepikt, zonder de container te herstarten. CSV-volglijsten verversen zichzelf elke 24 uur. Een health-endpoint op poort 8080 geeft uptime, tijdstip van de laatste poll en het aantal vliegtuigen prijs, voor monitoring en orkestratie.

Kort Samengevat

Een Docker-container die de lucht in de gaten houdt en elk interessant vliegtuig bij je Telegram verlinkt. Militaire jets, regeringsvliegtuigen, noodsquawks, laagvliegers met een luchtje, specifieke registraties, hele vliegtuigtypes, wat je maar wilt. Meerdere locaties, meerdere bronnen, meerdere notificatiedoelen, doc8643-vliegtuigfoto’s rechtstreeks aan je alarmen gehangen, allemaal vanuit één YAML-bestand.
Geen SDR-hardware. Geen antenne. Geen eigen ontvanger. Alleen publieke ADS-B-API’s en een paranoïde configuratiebestand.

Nu Met een Agent-skill

De repo levert een agent-skill onder .agents/skills/planesnitch/, door CI gepubliceerd op ClawHub bij tag-pushes. Hij documenteert de hele bewakingsoppervlakte, de militaire, overheids- en politie-CSV’s van plane-alert-db, de noodsquawks 7500/7600/7700, eigen lijsten met ICAO-hex en -types, hoogtedrempels voor laagvliegers, of gewoon “alles” als je jezelf haat, over alle gratis ADS-B-API’s waar hij uit kan putten.
Dus in plaats van je eigen vliegtuigverklikker elke keer aan een assistent uit te leggen, installeert hij de skill en weet hij al welke knoppen er zijn.
Pak het hier: github.com/psyb0t/docker-planesnitch
Onder WTFPL-licentie, want vliegtuigen verlinken hoort geen licentieovereenkomst te vereisen.

Zo Zet Je Het In Je Agent

Zodat je assistent de vliegtuigverklikker kan opzetten zonder dat jij de configuratie voorleest. Alles onder .agents/ staat in één marketplace gecatalogiseerd, dus het zijn twee commando’s:

claude plugin marketplace add psyb0t/agents
claude plugin install planesnitch@psyb0t

Codex gebruikt dezelfde marketplace met een ander werkwoord, codex plugin add planesnitch@psyb0t, omdat codex plugin install niet bestaat. Hij vindt de skill ook uit zichzelf in een checkout van de repo, aangezien hij .agents/skills/ native scant zonder dat er iets geïnstalleerd is.