Releases: Yggdrasil-AI-labs/adsb-to-wdgwars
Release list
v2.0.11 — wigle v1.2.0 piped-stdin + smoke portability back-port
What's Changed
- v2.0.11: back-port wigle v1.2.0 piped-stdin + smoke portability fixes by @HiroAlleyCat in #19
Full Changelog: v2.0.10...v2.0.11
v2.0.10 — --preview flag (parser dry-run, Heimdall-aligned)
Adds `--preview` to Muninn. Parses the input, prints up to 6 normalised records as JSON-lines on stdout, then exits. No file write, no upload.
Mirrors Heimdall's `--preview` exactly so muscle memory transfers across the feeder family.
Use
```bash
./run.sh /path/to/your-capture.ndjson.gz --preview
```
Output is one JSON record per line — same shape as Heimdall's preview output. Easy to pipe into `jq`, `head`, etc.
When to use each dry-run mode
| Question | Flag |
|---|---|
| Did the parser understand my decoder's output? | `--preview` |
| What does the full converted payload look like? | `--stdout` |
| Does the upload pipeline work end-to-end without POSTing? | `--upload --dry-run` |
Added
- `--preview` flag.
- 6 new tests in `tests/test_preview.py` covering: zero exit, no file written, exactly-6 cap, JSON validity, fewer-than-6 padding, CLI help visibility.
- README section on confirming parser output before wiring `--upload` or `--schedule`.
Verified
57/57 tests pass. README linter clean. CI green across Python 3.10/3.11/3.12.
Full changelog: https://github.com/HiroAlleyCat/adsb-to-wdgwars/blob/main/CHANGELOG.md
v2.0.9 — Scheduling in --setup (watch + periodic, dry-run)
muninn.py --setup now offers a second optional step after the API key is saved: install a scheduled task that uploads new captures automatically.
Modes
| Mode | What it does | When to pick |
|---|---|---|
| Watch | Long-running daemon, uploads new files as they appear | Decoder writes a new file per capture (tar1090 chunks, NDJSON sessions) |
| Periodic | Runs every N minutes (1–60) against the current state | Decoder rewrites one rolling file in place (dump1090-fa, readsb, VRS) |
Per-platform mechanism (all user-scope, no sudo)
| Platform | Mechanism |
|---|---|
| Linux + systemd | ~/.config/systemd/user/muninn-upload.{service,timer} |
| macOS / Linux without systemd | user crontab with managed-by-muninn marker |
| Windows | schtasks /Create at user scope |
Safety net: --schedule-dry-run
Interactive flow defaults dry-run to yes — the installed task runs --dry-run, decoding and writing JSON locally without posting to wdgwars.pl. Re-run --schedule and answer No to flip to live uploads. Headless equivalent: --schedule-dry-run flag.
This catches the case where a user's host already has a configured API key and a bare --schedule install would start uploading from tick one.
Mode-switch hygiene
Switching from periodic → watch removes the orphan .timer so the stale timer doesn't keep firing into the long-lived watch service. Symmetric on watch → periodic.
Friendlier error for the post-v2.0.8 venv footgun
python3 muninn.py ... (system Python) after setup.sh installed deps into .venv/ now prints:
[muninn] missing dependency: gungnir.
[muninn] deps live in the project venv, not system Python.
[muninn] re-run with the venv interpreter, or use the run.sh wrapper:
[muninn] /path/.venv/bin/python muninn.py ...
[muninn] ./run.sh ...
instead of the bare ModuleNotFoundError: No module named 'gungnir' traceback.
Added CLI flags
--schedule(interactive, or headless with the flags below)--unschedule(idempotent — removes only entries markedmanaged-by-muninn)--schedule-mode {watch,periodic}--schedule-input DIR--schedule-glob PATTERN--schedule-interval MINUTES--schedule-dry-run
Verified live on Ubuntu 24.04 (systemd)
- ✓ Periodic install:
.service+.timerwritten, daemon-reloaded, enabled, active. - ✓ Watch install: long-running
Type=simpleservice withRestart=on-failure, active. - ✓ Periodic → watch switch: orphan
.timerremoved cleanly. - ✓ Dry-run install with configured key:
/api/upload-historynewest timestamp unchanged before/after. - ✓ Uninstall: both modes removed cleanly, no orphan unit files.
- ✓ 51/51 tests pass (19 existing + 32 new in
tests/test_scheduler.py).
Full changelog
https://github.com/HiroAlleyCat/adsb-to-wdgwars/blob/main/CHANGELOG.md
v2.0.8 — setup.sh: PEP 668 / Bookworm fix
setup.sh, run.sh, and update.sh now install Muninn into a project-local .venv/ instead of the system Python.
On Raspberry Pi OS Bookworm, Debian 12+, Ubuntu 23.04+, and Homebrew Python, the previous python3 -m pip install -r requirements.txt line errored out with error: externally-managed-environment (PEP 668). The script crashed before saving the API key. Reported by a Pi24 user in the WDGoWars Discord.
Fixed
setup.shno longer fails withexternally-managed-environmenton PEP 668 distros. Creates.venv/on first run and uses.venv/bin/pythonfor the install +--setup.run.shandupdate.shdetect.venv/bin/pythonand use it; fall back topython3if no venv is present (older installs keep working).- If
python3 -m venvitself fails (thepython3-venvapt package is missing on some Pi images), the script prints the exactsudo apt install -y python3-venv python3-fullline and exits cleanly instead of leaving a half-installed state.
Changed
- README "Option B — clone with git" now shows the venv-aware manual sequence to match what
setup.shdoes automatically.
Unchanged
setup.bat— Windows Python has no PEP 668 enforcement, sopython -m pip installstill works there.- Everything else: parsing, upload, gungnir transport, web build.
Verified
- Reproduced the original
externally-managed-environmenterror on Ubuntu 24.04 withmain. - Ran the new
setup.shon the same box:.venv/created,gungnir 0.1.2+pyModeS 3.3.0installed inside it,import muninnsucceeds against the venv interpreter.
Workaround for existing installs (no upgrade needed)
cd ~/adsb-to-wdgwars
python3 -m venv .venv
.venv/bin/pip install -r requirements.txt
.venv/bin/python muninn.py --setupFull changelog: https://github.com/HiroAlleyCat/adsb-to-wdgwars/blob/main/CHANGELOG.md
v2.0.7 — Web build: parse_sqb no longer hangs on Pyodide
v2.0.6 made import sqlite3 work in the Pyodide build. Parsing still hung silently. Root cause: parse_sqb used sqlite3.connect("file:...?mode=ro", uri=True). On Pyodide's WASM sqlite3, the URI form against the virtual filesystem silently hangs — connect() never returns and never raises.
Fixed
parse_sqbdetectssys.platform == "emscripten"and uses plainsqlite3.connect(str(path))on the web build- Falls back to URI-mode connect on every other platform (CLI users keep the live-DB safety)
- Applied to both CLI
muninn.pyand Pyodideweb/muninn.py
Verified
- CLI regression:
sbs1_real/stratux/mayhemstill 10 / 12 / 6 - CLI SQB still 1146 records from the same fixture (win32 takes URI-mode branch, identical output to v2.0.5)
Full changelog: https://github.com/HiroAlleyCat/adsb-to-wdgwars/blob/main/CHANGELOG.md
v2.0.6 — Web build: .sqb parsing fixed in-browser
The web (Pyodide) build advertises .sqb in the drop zone but dropped files produced [ERR] Couldn't parse BaseStation.sqb. Pyodide unvendors sqlite3 from stdlib into a separately-loadable package; without an explicit loadPackage("sqlite3") muninn's parse_sqb raises ModuleNotFoundError at parse time.
CLI was never affected — real CPython has sqlite3 baked into stdlib. SQB support has worked on CLI since v2.0.0; this brings the web build in line.
Fixed
web/app.js—pyodide.loadPackage(["micropip", "ssl", "sqlite3"])
Verified
- CLI regression:
sbs1_real/stratux/mayhemstill 10 / 12 / 6 - Real BaseStation SQB (637 aircraft, 788 flights) parses to 1146 records on CLI — same output now flows through the web build
Full changelog: https://github.com/HiroAlleyCat/adsb-to-wdgwars/blob/main/CHANGELOG.md
v2.0.5 — gungnir bump (upstream the /endpoint/* default)
Companion release to v2.0.4. v2.0.4 hotfix-flipped DEFAULT_API_URL locally to bypass Cloudflare's per-IP L7 DDoS rate-limit on /api/*. gungnir v0.1.2 now ships the same flip at the library level; this release re-points Muninn at gungnir's default so the source of truth lives upstream again.
Changed
requirements.txtgungnir pin: v0.1.1 → v0.1.2- Vendored
web/gungnir/{__init__,__version__}.pysynced to v0.1.2 muninn.py+web/muninn.pyrevert localDEFAULT_API_URLstring back togungnir.DEFAULT_API_URL
Unchanged
web/serve.pyupstream andweb/app.jslocalStorage default (already on/endpoint/upload/from v2.0.4)--api-urlflag,/api/me(--whoami)- All parser behaviour (regression baselines hit on first try)
Full changelog: https://github.com/HiroAlleyCat/adsb-to-wdgwars/blob/main/CHANGELOG.md
v2.0.4 — Default upload URL bypasses Cloudflare L7 rate-limit
WDGoWars portal sits behind Cloudflare. Free-tier L7 DDoS protection per-IP-rate-limits bursts of /api/* with HTTP 429 + error code 1027 BEFORE reaching the origin's PHP, and at Muninn's batch scale (cron-driven RTL-SDR rig bulk-uploading) this surfaces as intermittent 429s that gungnir's cooldown logic deepens into a back-off cycle.
Portal-side fix: /endpoint/* is a one-line PHP alias of /api/*. Same router, same HMAC envelope, same response. URL doesn't match CF's pattern; request reaches origin.
Changed
DEFAULT_API_URL(CLI + Pyodide flavours) flipped fromhttps://wdgwars.pl/api/upload/tohttps://wdgwars.pl/endpoint/upload/web/app.jslocalStorage default for the Endpoint setting updatedweb/serve.pyproxy upstream defaults to/endpoint/upload/- Docstrings, README,
--uploadhelp text updated
Notes
gungnir.DEFAULT_API_URLunchanged — other consumers keep the/api/*default until they bump independently--api-urlflag unchanged; force/api/upload/with--api-url https://wdgwars.pl/api/upload/if needed/api/me(used by--whoami) unchanged. Single-call, not affected by burst rate-limiting
Smoke-tested against fixtures: sbs1_real / stratux / mayhem parse to 10 / 12 / 6 aircraft as documented.
Full changelog: https://github.com/HiroAlleyCat/adsb-to-wdgwars/blob/main/CHANGELOG.md
v2.0.1 — update-checker semver fix
Patch release.
Fixed
_check_for_updatewas comparing the GitHub releases tag against__version__with!=instead of>. On v2.0.0 (ahead of the published release at the time), it falsely reportedv1.11.1 is availableand suggested downgrading. Now uses a small inline semver parser and reports only when the latest tag is strictly newer.
Added
_version_tuple()helper — tolerates a leadingvand pre-release / build suffixes. Unparseable versions skip the check gracefully.
Verification
Live muninn --whoami against wdgwars.pl after the fix: no more bogus update-suggestion line.
v2.0.0 — extract transport to gungnir
Highlights
Muninn's HMAC envelope, signed POST, key handling, cooldown, silent-drop detection, and SSL context now live in gungnir — a shared transport library that Heimdall and wigle-to-wdgwars will also sit on in subsequent releases. Muninn becomes a focused ADS-B layer over the shared transport.
Wire-protocol byte-identical to v1.11.1. Verified by a parity test and by a live upload of 12 real aircraft from a Lorain, OH dump1090 feed to your wdgwars.pl account. Existing v1.x cron jobs, API keys, and config paths keep working unchanged.
Free behavior wins
| Behavior | v1.11.1 | v2.0.0 |
|---|---|---|
| Transient 5xx + network errors | fail immediately | retry 3× with exponential backoff (2s / 4s) |
| 429 mid-batch | keep POSTing more chunks | stop batch, persist deadline, next cron tick respects it |
| Silent drop (HTTP 200 ok:true, zero counters) | log warning, rc=0 | log warning, rc=1 — surfaces to cron |
| Inter-chunk delay | back-to-back | 1s default |
| User-Agent | muninn/1.11.1 |
muninn/2.0.0 (+https://github.com/HiroAlleyCat/adsb-to-wdgwars) |
| API-key redaction | required len(key) > 8 (leaked short keys) |
always redacts on non-empty match |
Migration
pip install -r requirements.txt # pulls gungnir from the pinned commit
- No config-file changes.
~/.config/muninn/api.keyand%APPDATA%\muninn\api.keyread in place. - No cron-stanza changes. Same invocation, same exit codes.
- Function signatures preserved as thin shims (
upload,load_key,save_key,check_whoami, etc.) so any external script importing frommuninnkeeps working.
Compatibility note
gungnir is currently installed from a pinned commit URL in requirements.txt. When gungnir publishes to PyPI, that line will become a normal gungnir>=X.Y pin.