watchpost

module
v0.10.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 27, 2026 License: MIT

README

Watchpost

A terminal-native live weather station: a watchlist dashboard of NWS observations, forecasts and alerts, marine conditions and tides where you are near the coast, and NOAA Weather Radio — the real relays where they exist, or your location's own forecast read aloud where they don't.

What it looks like

Real data, 133×44, the default theme. Rendered from the dashboard itself (UAT 121).

The Synthwave '84 theme

Install

macOS and Linux (amd64 / arm64):

curl -fsSL https://raw.githubusercontent.com/branden-thompson/watchpost/main/scripts/install.sh | sh

The installer picks the release for your OS and architecture, verifies its SHA-256 against the release's checksums.txt, installs to ~/.local/bin (or /usr/local/bin when that is writable and ~/.local/bin is not on your PATH), and tells you the next step. Knobs: WATCHPOST_VERSION=v0.9.0, WATCHPOST_INSTALL_DIR=…. Windows: download watchpost-windows-amd64.exe from the release page.

Manual install always works too: download the binary and checksums.txt from Releases, verify, chmod +x, put it on your PATH.

Requirements: a UTF-8 locale and a font with the block/arrow glyphs (any modern terminal — not the Linux VT console). Linux binaries link against glibc (not Alpine/musl); the radio voice on Linux (Piper, installed the first time you tune in) needs glibc ≥ 2.29 and libstdc++6, and audio goes to PulseAudio/PipeWire or ALSA (libasound2).

First run

watchpost           # the dashboard — on a first run the Setup window opens over it

Setup is a window like every other: both questions are on screen — your default location (type-ahead over the embedded index; enter keeps the current one on a re-run) and the optional NASA FIRMS key (paste it, masked, ctrl+r reveals; leave it empty for the default data set — an empty key also keeps a stored one). tab moves between the questions; enter on the key line saves. s opens it again any time; watchpost setup starts the dashboard with it open. On Linux/Windows the radio voice (Piper) installs itself the first time you tune in; V lists six correspondents — Lessac, Amy, Ryan, Joe (US), Alan, Alba (GB) — and picking one you have not used yet downloads it (~63 MB, verified) with progress in the player.

Config lives in $XDG_CONFIG_HOME/watchpost/config.toml (~/.config/…), written 0600. The HTTP cache and the voice live under $XDG_CACHE_HOME/watchpost/ (~/.cache/…, ~/Library/Caches/… on macOS) and are safe to delete.

Using it

Key Does
move between locations (favourites, then RECENT / SEARCHED)
enter location details (hourly, extended, marine + tides, fire — hotspots and incidents nearby, station and distance)
A / alert details / previous, next alert
l look up a city or zip — lands at the top of RECENT and stays there across runs
ctrl+a / shift+delete add the focused location to the watchlist (max 10) / remove it
space + - radio: tune the focused location / volume
r Repeat: Off · One · Watchlist (plays each favourite in turn)
m Mode: Synth (your location's forecast, read aloud) · Nearest Relay (live NOAA Weather Radio)
v V T visualizer · voice chooser (with preview) · player size
f c ºF / ºC
t colour theme (persisted; drop your own token JSON in <config>/themes/ — every colour, the tables' table.header / table.muted / table.name included)
s Setup: default location + optional NASA FIRMS key (opens by itself on a first run)
a S ? q About + data credits · API status · help · quit

One-shot: watchpost report "El Cajon, CA" (plain text) or --json (schema 1.0.0-rc, additive changes only — watchpost schema prints it; the checked-in copy is pkg/schema/). Exit codes: 0 ok · 1 no usable data or bad arguments · 2 a provider is degraded (an unkeyed off FIRMS does not count); warnings never change the code. providers[].status is ok | degraded | off. Shell completion: watchpost completion bash|zsh|fish|powershell.

--ascii draws the row marks and the Help legend with ASCII characters (> playing, R on repeat, n* fires nearby, n! alerts) for terminals or screen readers that mishandle the glyphs.

Diagnostics: WATCHPOST_DEBUG_TIMING=1 prints launch→full-view time on exit; WATCHPOST_DEBUG_PPROF=1 serves pprof on 127.0.0.1:6060 (or WATCHPOST_DEBUG_PPROF_ADDR), plus /debug/counters (live request, publish and memory counters as JSON) and /debug/dump (write a profile set). The S modal shows the request counters per host since launch. A running dashboard writes a diagnostic dump — heap, allocs, goroutine and threadcreate profiles with counters.json — under the cache directory's profiles/ on kill -USR1 <pid> (macOS/Linux; on Windows use /debug/dump); dumps are at least a minute apart and the newest twelve are kept. watchpost report <loc> --verbose appends one request-counter line per host. The soak and benchmark harness lives in scripts/quality/ and make quality-bench.

Fire

Every location watches for wildfire the way it watches for alerts: a row wears an orange with a count (named incidents nearby, or 1 for unnamed hotspots; bold when a hotspot is burning hard) and the detail modal (enter) has a FIRE section — satellite hotspots inside a 25 km ring (bearing, distance, strength in MW, satellite, age), named incidents within 50 km (acres, % contained) and the Red Flag / Fire Weather alert when one is active. Two sources need no key: NOAA's Hazard Mapping System (analyst-curated satellite detections, refreshed every 10 minutes) and NIFC's WFIGS incident list. The plain report and --json carry the same data.

NASA FIRMS (optional, free key). FIRMS adds VIIRS detections minutes old, straight from the satellites, before an analyst has looked at them. Get a MAP_KEY at https://firms.modaps.eosdis.nasa.gov/api/map_key/ (an email address; the key arrives at once), then press s in the dashboard (or run watchpost setup), tab to the key line and paste it — masked, ctrl+r reveals; the window shows a stored key's last four characters and whether FIRMS accepts it — or add it to config.toml:

[providers.firms]
key = "your-32-character-map-key"

The key takes effect at once — no relaunch. It rides only in request paths to firms.modaps.eosdis.nasa.gov and never appears in output or logs (a key that is not 32 hex characters is refused before anything is written). Without a key FIRMS reads off in the API status (S) and contributes nothing — HMS and WFIGS carry the default.

Thresholds are configurable ([fire] in config.toml; these are the defaults):

[fire]
radius_km          = 25   # hotspot ring around each location
incident_radius_km = 50   # named-incident ring
min_frp_mw         = 5    # hotspots below this fire radiative power are noise
bold_frp_mw        = 50   # at or above this the mark and the strength read bold
min_confidence     = "nominal"   # low | nominal | high — FIRMS points; HMS points are analyst-curated and always pass

Data and credits

Watchpost reads public sources and shows their credits in the About window (a): National Weather Service / NOAA (forecasts, observations, alerts, products, coastal waters, transmitter list — public domain); NDBC buoys and CO-OPS tides/currents (NOAA); NOAA-NESDIS Hazard Mapping System fire detections and NIFC WFIGS incidents (public domain); active fire data from NASA FIRMS (https://earthdata.nasa.gov/firms, NASA open data — attribute LANCE/FIRMS); GeoNames and Open-Meteo geocoding (CC BY 4.0, https://creativecommons.org/licenses/by/4.0/); NWR audio relayed by wxradio.org and weatherUSA (community relays — relayed audio lags and is not for life-safety use). Watchpost is not affiliated with NOAA, NIFC or NASA.

Not a substitute for official warnings. Everything here is fetched on a schedule and can lag; for life safety use NOAA Weather Radio and Wireless Emergency Alerts. Coverage is US-only in 0.9.0 (NWS); non-US locations resolve but carry no weather data yet.

Building from source

Go 1.25. make build (binary in ./dist, version stamped from git describe), make verify (fmt, vet, race, import-direction and watermark gates with positive controls), make release-matrix (all targets, CGO off), make install-test (installer end to end against a local server). The terminal UI kit (go-studs, MIT, same author) is carried in-tree under third_party/go-studs (its LICENSE and NOTICE.md ride with it; import paths rewritten), so the tree builds anywhere with no private access.

Licence

MIT — see LICENSE. Use it freely; keep the copyright and permission notice (attribution).

Directories

Path Synopsis
Package app is the composition root (Option C): the ONLY place that wires domains, platform, and modes together.
Package app is the composition root (Option C): the ONLY place that wires domains, platform, and modes together.
cmd
watchpost command
Command watchpost is the terminal-native live weather station.
Command watchpost is the terminal-native live weather station.
domains
fire
Package fire holds what the wildfire providers share (B5): the proximity rules (from `[fire]` in config; AI-3 defaults), the confidence scale, the distance test, and hotspot clustering.
Package fire holds what the wildfire providers share (B5): the proximity rules (from `[fire]` in config; AI-3 defaults), the confidence scale, the distance test, and hotspot clustering.
fire/firms
Package firms reads NASA FIRMS active-fire detections (B5; AI-3): the keyed upgrade over HMS — VIIRS NOAA-20/21 near-real-time points minutes old, via the area CSV API.
Package firms reads NASA FIRMS active-fire detections (B5; AI-3): the keyed upgrade over HMS — VIIRS NOAA-20/21 near-real-time points minutes old, via the area CSV API.
fire/hms
Package hms reads NOAA-NESDIS Hazard Mapping System fire detections (B5, live-probed 2026-08-25; keyless): one KMZ for the whole continent, refreshed every 10 minutes, holding a merged `hms_fire<date>.kml` with analyst-curated points from GOES-East/West, NOAA-20/21, Suomi NPP and MODIS — ~25k placemarks, each with Lon/Lat/YearDay/Time/Satellite/Method/ Ecosystem/FRP in its description.
Package hms reads NOAA-NESDIS Hazard Mapping System fire detections (B5, live-probed 2026-08-25; keyless): one KMZ for the whole continent, refreshed every 10 minutes, holding a merged `hms_fire<date>.kml` with analyst-curated points from GOES-East/West, NOAA-20/21, Suomi NPP and MODIS — ~25k placemarks, each with Lon/Lat/YearDay/Time/Satellite/Method/ Ecosystem/FRP in its description.
fire/wfigs
Package wfigs reads NIFC's Wildland Fire Interagency Geospatial Services current-incident layer (B5, live-probed 2026-08-25; keyless, public domain): one GeoJSON query for every active wildfire in the country (~600, under the layer's 2,000-record cap), answered for each location by distance.
Package wfigs reads NIFC's Wildland Fire Interagency Geospatial Services current-incident layer (B5, live-probed 2026-08-25; keyless, public domain): one GeoJSON query for every active wildfire in the country (~600, under the layer's 2,000-record cap), answered for each location by distance.
locations
Package locations resolves user queries (city names, US zips) to LocationRefs — embedded-first (geodata index: offline, ~7µs) with the Open-Meteo geocoder as online fallback for misses (AI-8 hybrid; a fallback resolve carries the geocode_fallback warning so honesty survives — §10.2).
Package locations resolves user queries (city names, US zips) to LocationRefs — embedded-first (geodata index: offline, ~7µs) with the Open-Meteo geocoder as online fallback for misses (AI-8 hybrid; a fallback resolve carries the geocode_fallback warning so honesty survives — §10.2).
locations/coverage
Package coverage answers one question for the location resolvers: does the National Weather Service serve this place? Both the offline index (domains/locations) and the online geocoder (domains/locations/openmeteo) need the same answer, and neither may import the other — so it lives here (red-team 0.9.0 round 2 N-2: the offline path refused Puerto Rico while the online path accepted Paris).
Package coverage answers one question for the location resolvers: does the National Weather Service serve this place? Both the offline index (domains/locations) and the online geocoder (domains/locations/openmeteo) need the same answer, and neither may import the other — so it lives here (red-team 0.9.0 round 2 N-2: the offline path refused Puerto Rico while the online path accepted Paris).
locations/geodata
Package geodata is the embedded offline geocoding index — the S2 spike's measured "compact" representation: two go:embed gzipped TSVs decompressed once at Load into single backing byte slices with sorted offset indexes; rows parse lazily on access.
Package geodata is the embedded offline geocoding index — the S2 spike's measured "compact" representation: two go:embed gzipped TSVs decompressed once at Load into single backing byte slices with sorted offset indexes; rows parse lazily on access.
locations/openmeteo
Package openmeteo resolves location queries via the Open-Meteo Geocoding API (keyless; AI-8).
Package openmeteo resolves location queries via the Open-Meteo Geocoding API (keyless; AI-8).
marine/coops
Package coops reads NOAA CO-OPS Tides & Currents (B3 UAT 61, live-probed; free, no key): tide predictions (high/low, 3,499 stations), observed water level (301 gauges) and tidal-current predictions (4,430 stations).
Package coops reads NOAA CO-OPS Tides & Currents (B3 UAT 61, live-probed; free, no key): tide predictions (high/low, 3,499 stations), observed water level (301 gauges) and tidal-current predictions (4,430 stations).
marine/ndbc
Package ndbc reads NOAA National Data Buoy Center observations (B3 UAT 29, live-probed; free, no key): the active-station list gives every buoy's position, and each station's realtime2 text product carries the latest observed wave height (WVHT), dominant period (DPD), mean direction (MWD), and water temperature (WTMP) — the 5-day product.
Package ndbc reads NOAA National Data Buoy Center observations (B3 UAT 29, live-probed; free, no key): the active-station list gives every buoy's position, and each station's realtime2 text product carries the latest observed wave height (WVHT), dominant period (DPD), mean direction (MWD), and water temperature (WTMP) — the 5-day product.
radio/player
Package player plays NOAA Weather Radio relays (B4, architecture §5/§10.4, AI-5): Icecast HTTP → ICY metadata strip → go-mp3 → resample → oto.
Package player plays NOAA Weather Radio relays (B4, architecture §5/§10.4, AI-5): Icecast HTTP → ICY metadata strip → go-mp3 → resample → oto.
radio/spectrum
Package spectrum turns the player's latest PCM into visualizer band levels (UAT 92).
Package spectrum turns the player's latest PCM into visualizer band levels (UAT 92).
radio/stream
Package stream resolves a location to NOAA Weather Radio audio (B4, architecture §5/§10.6, AI-4): the vendored NWS transmitter table says which transmitter covers a county and where every transmitter stands; two community Icecast directories (wxradio.org, weatherUSA) say which transmitters are actually relayed.
Package stream resolves a location to NOAA Weather Radio audio (B4, architecture §5/§10.6, AI-4): the vendored NWS transmitter table says which transmitter covers a county and where every transmitter stands; two community Icecast directories (wxradio.org, weatherUSA) say which transmitters are actually relayed.
radio/synth
Package synth is the synthesized NOAA-Weather-Radio-style broadcast (B4 step 2, architecture §5 "Synth", AI-13): when no relay carries a location's transmitter — 89 % of them — the NWS text products the real broadcast reads from are fetched, normalized into spoken English, and voiced locally.
Package synth is the synthesized NOAA-Weather-Radio-style broadcast (B4 step 2, architecture §5 "Synth", AI-13): when no relay carries a location's transmitter — 89 % of them — the NWS text products the real broadcast reads from are fetched, normalized into spoken English, and voiced locally.
weather/nws
Package nws is the National Weather Service provider — the mandatory US source and the harmonization tie-break authority (T-E′, OQ-9).
Package nws is the National Weather Service provider — the mandatory US source and the harmonization tie-break authority (T-E′, OQ-9).
modes
report
Package report renders Snapshots for stdout: --json (machine, schema v1.0-rc) and --report-only (line-oriented plain text — also the documented screen-reader surface, R-12d/G-9a).
Package report renders Snapshots for stdout: --json (machine, schema v1.0-rc) and --report-only (line-oriented plain text — also the documented screen-reader surface, R-12d/G-9a).
tty
Package tty hosts the live TUI program (modes/ — reads ONLY platform/snapshot per the import lint; M5 is structural).
Package tty hosts the live TUI program (modes/ — reads ONLY platform/snapshot per the import lint; M5 is structural).
pkg
schema
Package schema generates the published JSON Schema (draft 2020-12) for the watchpost report envelope, by reflection over platform/snapshot types — the struct IS the source of truth, so schema drift is impossible by construction (M5; architecture §10.3).
Package schema generates the published JSON Schema (draft 2020-12) for the watchpost report envelope, by reflection over platform/snapshot types — the struct IS the source of truth, so schema drift is impossible by construction (M5; architecture §10.3).
platform
astro
Package astro computes sunrise/sunset from position and date (NOAA solar equations) — no provider carries them, so the assembler fills every Daily row from geometry (B3 UAT 32).
Package astro computes sunrise/sunset from position and date (NOAA solar equations) — no provider carries them, so the assembler fills every Daily row from geometry (B3 UAT 32).
config
Package config owns the watchpost user configuration file.
Package config owns the watchpost user configuration file.
declset
Package declset lists a Go package directory's top-level declarations so a test can pin them against a golden: a pure file move (quality pass Q2) must not add, drop or rename one, and `git diff --stat` cannot certify a 1→13 split (rename detection needs whole-file similarity — red-team R2-22).
Package declset lists a Go package directory's top-level declarations so a test can pin them against a golden: a pure file move (quality pass Q2) must not add, drop or rename one, and `git diff --stat` cannot certify a 1→13 split (rename detection needs whole-file similarity — red-team R2-22).
geo
Package geo holds the pure great-circle helpers shared by providers that pick stations by proximity (NDBC buoys, NWS observation stations — UAT 60).
Package geo holds the pure great-circle helpers shared by providers that pick stations by proximity (NDBC buoys, NWS observation stations — UAT 60).
httpx
Package httpx is watchpost's single outbound HTTP client.
Package httpx is watchpost's single outbound HTTP client.
invariant
Package invariant provides side-effect-free invariant checks with explicit error-return recovery — the ratified Go idiom for P10 Rule 5 (P10-05-INVARIANT- DENSITY, ruling D-2).
Package invariant provides side-effect-free invariant checks with explicit error-return recovery — the ratified Go idiom for P10 Rule 5 (P10-05-INVARIANT- DENSITY, ruling D-2).
render
Package render is the D-9 pivot seam: the ONLY package allowed to import go-studs.
Package render is the D-9 pivot seam: the ONLY package allowed to import go-studs.
sched
Package sched is the tiered fetch scheduler (architecture §3, §10.8) — THE single freshness authority.
Package sched is the tiered fetch scheduler (architecture §3, §10.8) — THE single freshness authority.
snapshot
Package snapshot is THE data contract (architecture.md §2, §10.1, §10.11).
Package snapshot is THE data contract (architecture.md §2, §10.1, §10.11).
term
Package term owns terminal capabilities and the keybinding data model.
Package term owns terminal capabilities and the keybinding data model.
tz
Package tz memoizes time-zone loading (B3 UAT 74).
Package tz memoizes time-zone loading (B3 UAT 74).
third_party
go-studs/components
Package components provides professional terminal UI building blocks following the GO-STUDS design pattern.
Package components provides professional terminal UI building blocks following the GO-STUDS design pattern.
go-studs/rendering
Package rendering provides terminal rendering utilities for GO-STUDS components.
Package rendering provides terminal rendering utilities for GO-STUDS components.
go-studs/theme
Package theme provides terminal theme detection and management for STUDS components.
Package theme provides terminal theme detection and management for STUDS components.
tools
alertrec command
Command alertrec records live NWS alert-feed states as replay fixtures (architecture §10.8): it polls /alerts/active for the given zones on a cadence and appends one JSONL feed-state line per poll — replayable by the domains/alerts harness (M2/M3).
Command alertrec records live NWS alert-feed states as replay fixtures (architecture §10.8): it polls /alerts/active for the given zones on a cadence and appends one JSONL feed-state line per poll — replayable by the domains/alerts harness (M2/M3).
nwrtable command
Command nwrtable turns the NWS county-coverage list (CCL.js — the public-domain JS arrays behind weather.gov/nwr/county_coverage) into the vendored transmitter table domains/radio/stream/transmitters.csv (architecture §10.6, B4).
Command nwrtable turns the NWS county-coverage list (CCL.js — the public-domain JS arrays behind weather.gov/nwr/county_coverage) into the vendored transmitter table domains/radio/stream/transmitters.csv (architecture §10.6, B4).
slope command
Command slope is the quality pass's growth statistic (plan §1, §2.1; red-team BQ-1, RT-3, R2-1): given a soak's post-GC heap series it reports the per-day slope with an autocorrelation-robust confidence interval, the 30-day projection of that interval's upper edge, and the DETECTION FLOOR — the smallest 30-day growth the run could have certified — so a "no growth" verdict is a measurement, not an assertion.
Command slope is the quality pass's growth statistic (plan §1, §2.1; red-team BQ-1, RT-3, R2-1): given a soak's post-GC heap series it reports the per-day slope with an autocorrelation-robust confidence interval, the 30-day projection of that interval's upper edge, and the DETECTION FLOOR — the smallest 30-day growth the run could have certified — so a "no growth" verdict is a measurement, not an assertion.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL