The mesh, drawn
╔══════════════════════════════════════╗
┌──────────────────────────╣ MQTT bus (mosquitto, TLS) ╠────────────┐
│ ╚══════════════════════════════════════╝ │
│ │
│ topics: │
│ sdr/<node>/ais/raw sdr/<node>/marine/wx │
│ buoy/<id>/event hydrophone/<id>/detection │
│ feed/orcasound/<site> feed/ais/marinetraffic │
│ feed/cam/<site>/keyframe alert/correlated │
│ │
▼ │
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ SigDigger│ │ LoRa │ │ iPhone │ │ Public │ │ Public │ │ Public │
│ + Pi5 │ │ buoy ×N │ │ hydro │ │ AIS │ │ OrcaSound│ │ RTSP cams│
│ AIS/WX │ │ events │ │ stream + │ │ feed │ │ feeds │ │ (curated │
│ raw │ │ (32B) │ │ CoreML │ │ (NOAA/ │ │ (open │ │ public- │
│ │ │ │ │ events │ │ MT API) │ │ stream) │ │ list) │
└──────────┘ └──────────┘ └──────────┘ └──────────┘ └──────────┘ └──────────┘
│ │ │ │ │ │
└─────────────┴─────────────┴──────┬──────┴─────────────┴─────────────┘
▼
┌────────────────────────┐
│ Correlation engine │
│ (Node-RED / Python) │
│ rules → alerts │
└─────────────┬──────────┘
▼
┌────────────────────────┐
│ Public dashboard │
│ Leaflet map + HLS + │
│ spectrogram strip │
└────────────────────────┘
Sensor inventory
| Source | What it gives | Owner | Topic |
|---|---|---|---|
| SigDigger / RTL-SDR at Pier 70 | AIS, marine VHF, NOAA WX, Coast Guard P25 | You | sdr/pier70/* |
| LoRa buoys (×N) | 32-byte tonal-call events with SNR & CNN class | You | buoy/<id>/event |
| iPhone hydrophone | Live HLS audio + CoreML detection metadata | You | hydrophone/pier70/* |
| OrcaSound public feeds | Live audio from 4 Salish Sea sites — CC-BY | Orcasound | feed/orcasound/* |
| NOAA AIS NDBC API | Vessel position / class / speed; sea-state | NOAA — public | feed/ais/noaa |
| MarineTraffic API (free tier) | Cross-validate against your own AIS | MarineTraffic | feed/ais/mt |
| OpenSky Network ADS-B | Aircraft over the bay (overflight noise) | OpenSky | feed/adsb/opensky |
| Public RTSP marine cams | Visual confirmation; vessel ID | Various — see ethics | feed/cam/<site> |
| NOAA NDBC buoy API | Wind, wave, sea temp at 46087, 46088, etc. | NOAA — public | feed/buoy/ndbc |
Shodan, but ethically — what role it actually plays
Read this twice
Shodan indexes any device that answers a probe on the open Internet. Some of those devices are intentionally public (a marina's "view of our slip" RTSP, a port authority's webcam, a research station's data feed). Some are misconfigured consumer cameras whose owners did not consent. Hitting the second class is unauthorized access. CFAA in the US, Computer Misuse Act in the UK — same answer either way.
This project uses Shodan in exactly one role: discovery of feeds whose operators have published or advertised them as public, with manual review and a written-down "yes, this is public" check before they go in the curated cam list. It is not a tool for finding unsecured cameras. If a feed lacks a clear public banner, social-media post, or About page, it doesn't go on the list.
The discovery query that is reasonable
# Find INTENTIONALLY-public marine/port webcams to add to the curated list. # Then go look at each result, find their About page, and only include those # with a clear "this is public" statement. shodan search 'product:"Sensoray Camera Server" port:80 has_banner:true' --limit 20 shodan search 'http.title:"Live Marina Cam" country:US' --limit 20 shodan search 'org:"Port of Seattle" product:webcam' --limit 5
Better data sources, no Shodan needed
- EarthCam & SkylineWebcams publish a marine category with embed URLs.
- Washington State Ferries exposes a public terminal-camera page (slow refresh, but legit).
- Port of Seattle / NOAA publishes weather stations & tide gauges with REST endpoints.
- WSDOT publishes a public RTSP catalog for the West Seattle bridge cams.
- OrcaSound publishes its hydrophone feeds with a permissive license.
For this project, that's the camera list. Shodan doesn't add anything you can't get through these channels.
Correlation rules — turning sensors into alerts
Correlation is the entire point. Each rule is a filter on the MQTT bus that emits to
alert/correlated when the conjunction holds.
Rule 1 — Vessel-strike risk
# Pseudo: Node-RED / Python correlator if orca_call_within(buoy.id, last_60s) and \ ais_vessel_within_km(buoy.lat, buoy.lon, 1.5) and \ ais_vessel.speed_kts > 7.0: emit_alert({ "type": "vessel-strike-risk", "buoy": buoy.id, "vessel": ais_vessel.mmsi, "distance_km": ais_vessel.dist, "severity": "high", })
Rule 2 — Pod direction-of-travel estimate
Two LoRa buoys + one iPhone hydrophone with synced clocks → time-difference-of-arrival on a tonal call gives you a hyperbolic intersection. Not a position, but a direction-of-travel sketch you can overlay on the map.
Rule 3 — Unknown emitter
SigDigger sees an AIS/Marine-VHF burst whose MMSI is not in the AIS catalog → flag for review. Useful for catching radio tests, foreign vessels, or fishery patrols.
Rule 4 — Quiet-bay window
OrcaSound + your buoys agree there's no orca activity AND AIS shows no vessel within 5 km AND the wind is < 5 kt → emit "good listening" — drives a "go to the dock now" push notification.
The bus — minimal mosquitto + TLS
# /etc/mosquitto/conf.d/salish.conf listener 8883 cafile /etc/letsencrypt/live/sigint.example/chain.pem certfile /etc/letsencrypt/live/sigint.example/cert.pem keyfile /etc/letsencrypt/live/sigint.example/privkey.pem allow_anonymous false password_file /etc/mosquitto/passwd acl_file /etc/mosquitto/aclfile # aclfile excerpt: # user buoy-001 # topic write buoy/001/event # user dashboard # topic read #
The dashboard — Leaflet, HLS, spectrogram
One static page on this site (or a Netlify build) that subscribes to the MQTT bus over WebSockets and renders:
- Map. Leaflet, OpenSeaMap tiles, AIS pins (your decoder + NOAA), buoy markers, iPhone-stream marker, RTSP-cam clickable thumbnails.
- Live spectrogram strip. Pull the HLS audio from the iPhone, run a 1024-point FFT in a Web Audio worklet, draw a 30-second waterfall under the map.
- Alert ticker. Subscribed to
alert/correlated, last 50 events, severity-colored. - Cam tiles. Curated public RTSP cameras, embedded as HLS via the same MediaMTX instance that handles the iPhone ingest. Each tile clearly shows the source attribution.
It runs in a tab. It does not need a backend beyond the broker.
The legal/ethical floor — non-negotiable
Ground rules
- SRKWs are endangered. If a public alert from this project ends up summoning a crowd of boats to a sighting, that's harm. Alerts are time-delayed and location-fuzzed to a 1-km grid for non-trusted subscribers. Only researchers and partners get exact coords.
- No active acoustic. Passive listening only. No pingers, no ATOC, no nothing.
- Public feeds only. If a camera's source isn't publicly advertised by its operator, it is not on the list. Period. Shodan is a discovery aid, not a license.
- Attribution. Every external feed gets credit on the dashboard. OrcaSound is CC-BY; treat it accordingly.
- Open data out. Your own sensor data is published under CC-BY-SA. No takesy-backsies.
- Coordinate with the pros. Talk to Center for Whale Research and Orca Network. They are not threatened by citizen instrumentation; they are tired of bad citizen instrumentation. Don't be that.
Build order
- Stand up the broker. $5 VPS, mosquitto + TLS + WebSockets. Test with mosquitto_pub.
- Wire the SDR. Pi 5 + RTL-SDR Blog v4 → SoapySDRServer → SigDigger remote.
Get AIS decoding into
sdr/pier70/ais/raw. - Pull the public feeds. Tiny Python service (
feeds.py) that polls NOAA, OrcaSound, OpenSky, MarineTraffic and republishes to MQTT. - iPhone hydrophone. Larix → MediaMTX → HLS. Add CoreML detection metadata to
hydrophone/pier70/detection. - First buoy. Heltec V3 + DIY piezo. Get heartbeat + threshold detector packets flowing.
- Correlation rules. Node-RED first (visual, fast iteration), Python later.
- Dashboard. Static page, Leaflet + mqtt.js + Web Audio. Ship.
- Then more buoys for direction-of-travel triangulation.
Estimated cost (one operator, 1 buoy + 1 SDR + 1 phone)
- Hardware
- ~$272 buoy + $40 SDR/Pi adders + ~$385 iPhone rig = $697
- Monthly
- $5 VPS + $15 cellular SIM = $20/mo
- Time to first packet
- 2 weekends if you're patient with antennas
What this is, and what it isn't
It is
- A real, working citizen sensing rig you can build
- Open, attributable, locally-hosted
- Useful for personal whale-watching, port-watching, vessel-noise journaling
- A teaching scaffold for SDR / IoT / fusion engineering
It is not
- Calibrated marine-mammal science — that needs IRB-equivalent process
- An OSINT toolkit for surveilling people
- A replacement for licensed Coast Guard / NOAA infrastructure
- An excuse to deploy gear without permission