* Pace the one-off backfills by duty cycle so they cannot starve a small server's disk
* Plan the read-model fills with custom plans and analyze new read-model tables
* Analyze only never-analyzed read-model tables, bound the fill pause, and reset fill progress
* feat(db): drop six lookup-table indexes that no plan reads
* feat(db): drop known_routes.last_reconfirmed_at, which nothing reads
* feat(metrics): report how much of each database sequence is used
* refactor(db): retire the legacy API pool; slow region and message statements get their own two connections
* feat(config): database.slow_pool_size sizes the slow pool
* refactor(db): route the two former read-model fallbacks to the slow pool
* fix(metrics): sequence headroom reads any schema name and marks cycling sequences
* feat(metrics): count the 503s pages answer, by reason and model; warn about a busy slow pool once a minute
* refactor(db): expose the adverts page mapping and the original channel message pages for oracles
* refactor(db): name the read-model fallbacks the slow pool runs; count only what this tree refuses
* feat(config): the slow pool defaults to 8 connections while it also serves untrusted read-model pages
* feat(db): packet_sites table and indexes
* feat(ingest): keep each packet's site row in the observation insert
* feat(db): packet_sites backfill, reconcile and audit
* feat(db): region packet pages from packet_sites when trusted, with an exact page cursor
* feat(db): packet_sites front sweeper with plan and liveness metrics
* feat: boot repair, reconcile, audit, sweep and first-time verification for packet_sites
* fix(db): read no site range when no packet was first heard in the window
* fix(db): read the window guard's maxima through indexes
* test(planguard): packet_sites statements active, data-derived cases, diff and storediff
* docs(api): pageCursor applies to region packet pages
* fix(db): drop packet_sites trust again after the unsited fallback write
* fix(db): the packet_sites audit refills a pair at its newest hearing
* fix(db): bounded region reads fill the page within the request, else today's statement answers
* fix(planguard): tie_cut needs both pages at the limit and members of other identities
* fix: packet_sites sweep without JIT, one verification VACUUM, read-only checks to the newest id
* docs(api): cursor and pageCursor on region pages served without nextPageCursor
* test: drop local baselines of entries the copy skips; plain race schedule names
* test(planguard): storediff walks of the bounded region shapes; local baselines after the overlay rebuild
* fix(db): drop message-site trust again after the unseeded insert
* fix(db): a region page cursor's fallback to today's statement continues from its exact position
* feat(db): channel_message_iatas table and indexes
* feat(ingest): record channel message sites at insert and on every later hearing
* feat(db): channel message site backfill and two-range reconcile
* feat(db): serve region and scope message pages from the site read model when trusted
* feat: boot repair, periodic reconcile and first-time verification for the message site read model
* test(planguard): channel message site statements, diff and storediff cases
* feat(background): log when a read-only server trusts the message site read model
* test(planguard): storediff vars in query values, and message cases read from the data
* fix(ingest): record a stored hearing's channel message site right after the observation insert
* fix: bounded message dispatch for filtered all-channel pages, fresher read-only trust, earlier invalidation
* fix: record a hearing's site after its path is marked and skip the backstop when the message was found; read-only trust ends at the writer's deadline
* feat(planguard): read sqlc statement text and load a strict plan corpus
* fix(planguard): fail on unextracted statements, keep fragments mode-neutral
* feat(planguard): plan assertions, scoped sort exemptions and narrow retired-index verdicts
* fix(planguard): an errored run always fails
* fix(planguard): keep subplan conds off their parent scan; fail on unanalyzed budgets
* feat(planguard): read-only plan runner, reports and the run command
* fix(planguard): validate identifiers in emitted scripts; prove read-only runs
* feat(planguard): end-state corpus, planted regressions and the generated cached-plan policy
* fix(planguard): derive observer cases from data; mark legacy generic plans
* fix(planguard): bound the dense-region route resolvers; test policy refusals
* fix(planguard): re-measure the dense-region route cases; cover every policy refusal
* feat(planguard): synthetic fixture built through the migrations, checked in CI
* ci(planguard): build the fixture once; guard fixture data for identifiers
* fix(planguard): hash every fixture generator input; harden the fixture guard
* feat(planguard): snapshot diff over every column, named result classes and cursor walks
* fix(planguard): bound the relaxed diff classes; derive the cursor tie from data
* fix(planguard): refuse multi-statement text in the corpus runner
* feat: read-only server mode and an HTTP page diff between two builds
* fix(planguard): refuse escape strings and psql meta-commands in corpus text
* fix: storediff follows each build's own cursor; prove read-only gates via the log
* fix(planguard): match the PostgreSQL lexer on high-bit bytes; check every emitted line
* feat(admin): daily read-only plan check on the maintenance pool, its metric and report
* feat(planguard): daily prod check behind planguard.daily_check
* fix(planguard): cancel fresh runs at shutdown; expose a never-ran check
* ci(planguard): pin the fixture server and collation; refresh baselines
* fix(planguard): calibrate route budgets on one dataset; skip entries whose index is absent
* feat(db): five pgx pools with fixed session settings and Prometheus tracing
* feat(guards): per-family observe/enforce guards with client slots, deadlines and a shadow budget
* fix(guards): attribute only guard deadlines and classify like the handlers
* feat(db): route API statements by sqlc name; class C always unnamed
* feat(admin): statement statistics by sqlc name; create pg_stat_statements where allowed
* fix(db): skip pg_stat_statements when no schema is creatable
* feat(background): index health against post-rebuild baselines, Postgres activity, job and ingest metrics
* feat: separate API, ingest and maintenance stores; guards on the public API; metrics listener
* fix(db): keep ingest writes waiting, size the ingest pool to the host, tidy
* test(db): give the statements probe a query id of its own
* feat(db): background_state table and the (hop_count, iata) route index
* perf(db): one /routes statement per filter shape, hop pair on custom plans
* feat(db): keyset route retention and grace slices at exact cutoffs
* feat(background): hourly route retention with a bounded grace walk and watermark
* fix(background): bound the grace walk at microsecond cutoffs
* feat(db): stamp-free reconfirm sweep over the route primary key
* feat(background): pace route reconfirm at routes.reconfirm_rows_per_hour, no stamps
* test(routes): drive route retention end to end and tidy comments
* fix(profiling): sample during the hourly route retention run
Opening Map and Nodes on a region of a few thousand nodes could spend the
whole 300-request minute and leave the map half loaded. 600 still sits
under the fail2ban flood jail's 900, so the server's 429 answers first.
* perf(routes): batch cross-IATA and hash route search (#216)
One search fired a /routes/cross request per IATA pair (138k on 2026-10-08). Route search now takes an optional iatas list and runs a fixed number of index-backed queries. /info advertises built-in minimum client versions.
* chore: bump version to 2.0.4
* chore: add .github/CODEOWNERS for main branch policy
Signed-off-by: MrAlders0n <MrAlders0n@smal.ca>
* chore: add .github/workflows/main-source-guard.yml for main branch policy
Signed-off-by: MrAlders0n <MrAlders0n@smal.ca>
* feat(meshmapper): authenticate API imports (#211)
* feat(observers): add traffic-sorted directory (#212)
* fix(observers): rank available traffic over rolling seven days
* chore: bump version to 2.1.0
* chore: set release version to 2.0.2
* feat(api): add /info with mobile min app version (#214)
* chore: bump version to 2.0.3
---------
Signed-off-by: MrAlders0n <MrAlders0n@smal.ca>
Release the changes on `dev` as v2.0.2: authenticated MeshMapper imports and the traffic-sorted observer directory, including rolling seven-day counts from available history. Bump the API version and regenerate Swagger.
MeshMapper integration requires an API key. Regional admins’ existing per-region or grouped-region coverage keys also work for these APIs. Deployments should set `MESHMAPPER_API_KEY` and restart Beacon.
Validation: `go build ./...`, `gofmt -l .` (empty), `go vet ./...`, `go test ./...`, `govulncheck ./...`, and race tests for MeshMapper and scopestore passed in a clean checkout. PostgreSQL integration tests require CI because Docker is unavailable locally.
Warn at startup when limits are on without trusted_proxies, and once when a
request carries forwarding headers Beacon ignores. Reject short or whitespace
admin keys and negative durations. Key default-window stats by hour, return
500 for scope lookup failures, and validate IATA path params.
Stats endpoints read hourly rollup tables filled once per closed hour by one
advisory-locked task, instead of materialized views that re-scanned every
observation on each refresh. IATA-set tables give exact distinct counts for any
IATA set or region. Cleanup holds raw rows for unrolled hours (up to 24h).
Adds /stats/series; ingest keeps packet observation counts, observer last IATA,
channel message counts and trace tag summaries. 001_baseline.sql is rewritten,
so pre-release 2.0 databases must be recreated. See docs/historical-stats.md.
Region-filtered /scopes and /stats/scopes now list manual scopes configured
for the region and imported scopes whose MeshMapper catalogue includes the
IATA, instead of inferring membership from observed traffic. Filtered
/scopes returns string[] like the unfiltered call.
Manual scopes now require a region in config.
move refresh views and delete tasks to scheduler
add new config for refresh and cleanup intervals
new deployments can set these in minutes to see data faster