62 Commits
Author SHA1 Message Date
MrAlders0n a51d950e1f Pace the read-model backfills so they cannot starve a small server's disk (#257)
* 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
2026-10-20 12:16:31 -04:00
MrAlders0n 2b1ad1d6c6 feat(api): region, scope and advert pages answer 503 until their read model is ready, instead of falling back (#249) 2026-10-20 03:33:34 -04:00
MrAlders0n 0a41d935a0 End-state cleanup: slow pool, lookup-index and column drops, sequence headroom (#248)
* 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
2026-10-20 03:19:49 -04:00
MrAlders0n 16fb706d17 Packet sites: a read model that serves region packet pages while trusted (#247)
* 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
2026-10-20 00:18:33 -04:00
MrAlders0n 5ded8d925f Channel messages by site: a read model that serves region and scope pages while trusted (#246)
* 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
2026-10-19 21:20:21 -04:00
MrAlders0n 94b4b40f9b planguard: plan and result regression checks, fixture tier in CI, read-only mode (#240)
* 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
2026-10-19 13:45:39 -04:00
MrAlders0n 03f66d1ae4 Database pools by workload, plan router, observe-only guards, metrics (rest of #237) (#238)
* 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
2026-10-19 07:52:51 -04:00
MrAlders0n 3ae3c250d8 feat(config): plan layout, guard and admin listener settings; plan policy classes (#237) 2026-10-19 07:43:49 -04:00
MrAlders0n 8e0fd4b535 Routes: one statement per filter shape, exact bounded retention, stamp-free reconfirm (#235)
* 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
2026-10-19 05:39:38 -04:00
MrAlders0n 127fc76092 Raise the default per-client API limit to 600 a minute (#232)
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.
2026-10-18 23:12:57 -04:00
MrAlders0n 7297215555 feat(meshmapper): import repeater site details for node detail (#221) 2026-10-11 14:18:26 -04:00
MrAlders0n 9bbf8efee5 release: v2.0.4 (#217)
* 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
2026-10-09 22:17:23 -04:00
MrAlders0n 768889243c release: v2.0.3 (#215)
* 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>
2026-10-06 21:53:30 -04:00
MrAlders0n 041d9c1f45 release: v2.0.2
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.
2026-10-05 18:35:41 -04:00
MrAlders0n 0dca03cd4d release: v2.0.1 (#209)
- fix(meshmapper): refresh the longest-waiting scope catalogue first
- feat(config): default scope catalogue refresh to 24h
- chore: bump version
2026-10-03 16:51:11 -04:00
MrAlders0n 5ccb1a2ce3 fix(api): warn on untrusted proxies and tighten config validation
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.
2026-10-02 16:53:32 -04:00
MrAlders0n 98006a9364 feat(stats): serve historical stats from hourly rollups
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.
2026-10-02 07:43:56 -04:00
MrAlders0n f8ee1ac884 feat(meshmapper): import regional channels and scope channel lists by region 2026-09-30 23:09:40 -04:00
MrAlders0n 38b1a206b1 fix(meshmapper): respect MeshMapper's per-endpoint rate limits 2026-09-30 22:56:10 -04:00
MrAlders0n a970484b00 feat(meshmapper): import zone groups as regions 2026-09-30 22:56:10 -04:00
MrAlders0n bfda0d5281 feat(meshmapper): discover scope sources from the zone list 2026-09-30 22:46:00 -04:00
MrAlders0n 7267b8bca3 feat(meshmapper): accept scope sources outside configured regions 2026-09-30 22:27:36 -04:00
MrAlders0n 9a4e17b270 feat(meshmapper): import zones for every known IATA 2026-09-30 22:23:37 -04:00
MrAlders0n 2f49ffb3a3 feat(api): filter scope lists by region and catalogue membership
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.
2026-09-30 22:05:04 -04:00
MrAlders0n 3ac4035942 refactor(db): flatten migrations into a 2.0.0 baseline
2.0.0 is a fresh deploy, so the 1.x chain becomes one schema file. Startup now refuses a pre-2.0.0 database instead of applying the baseline on top.
2026-09-30 21:37:28 -04:00
MrAlders0n 0e242574fb feat(meshmapper): import region boundaries from the Zones API
Imported boundaries override borderFile on the border map and for foreign marking, falling back to the manual border when MeshMapper has none.
2026-09-30 12:38:36 -04:00
n30nex 574bba4e13 fix: address maintainer review for #174 2026-09-30 07:19:03 -04:00
n30nex c18e059e0d feat(scopes): import cached regional MeshMapper catalogues 2026-09-30 07:19:03 -04:00
MrAlders0n 90f9b50614 feat(ws): allow subdomain wildcards in allowed origins (#177) 2026-09-27 20:03:49 -04:00
MrAlders0n dec643a2ad feat(ws): opt-in allowed origins and observer key (#170) 2026-09-27 11:41:11 -04:00
MrAlders0n 3dee2098ba perf(db): batch retention deletes and tune autovacuum (#162)
Default packets.retention drops to 7 days.
2026-09-25 11:38:04 -04:00
n30nex a25e2675c8 feat(backup): add protected database and config download (#154) 2026-09-23 20:13:40 -04:00
n30nex 6aba17c75e feat(nodes): indicate repeaters outside configured local borders (#153) 2026-09-19 15:27:34 -04:00
n30nex c7ace0ce0e feat(ws): limit connection attempts and signal capacity shedding (#141) 2026-09-19 13:28:47 -04:00
n30nex dab228e9bb feat(logging): configure levels and structured runtime output (#144)
* feat(logging): configure levels and structured runtime output

* fix(logging): bound request-derived log fields

* fix(logging): retain malformed IATA source attributes safely

* test(logging): capture structured REST rate-limit records
2026-09-19 10:36:26 -04:00
n30nex 595ef4442b feat(api): rate limit REST requests by trusted client IP (#140) 2026-09-19 08:43:24 -04:00
n30nex 93f85cab51 feat(auth): protect the admin subtree with a bearer key (#146) 2026-09-18 06:18:27 -04:00
n30nex 22410cf4a6 fix(api): trust client IP headers only from configured proxies (#133) 2026-09-09 20:42:35 -04:00
n30nex 88bc0dc88a feat(retention): age out unreferenced observers when enabled (#126) 2026-09-09 18:45:08 -04:00
MrAlders0n 7d759d0a0f perf: known_routes retention, digest identity, and a working batched reconfirm (#98)
* fix: cascade channel_messages on packet delete

* rebuild known_routes keyed on (iata, path_key)

* add route retention delete, batch reconfirm, path_key upsert

* compute route path_key in store, add DeleteOldRoutes

* add routes retention config

* wire route retention into cleanup, batch reconfirm

* prune routes in reconfirm task, not cleanup

* expand routes retention docs in example config
2026-08-12 09:30:03 -04:00
Enot (ded) Skelly 99d7eb0bab feat: add node stale/delete config
closes: #93
2026-07-24 13:09:51 -07:00
Enot (ded) Skelly 3f2506fc8b feat: add geojson for iatas
closes #66
2026-07-24 12:59:48 -07:00
Enot (ded) Skelly 8e2798612b feat: add clock drift tracking
closes #71
2026-07-23 13:34:41 -07:00
MrAlders0n 41a3aa4711 feat(config): presence flush interval and packet ttl settings 2026-07-20 08:55:18 -07:00
Enot (ded) Skelly ecde7957f7 feat(config): pull out resolution into package and add tests
also adds a string method for pretty config printing
2026-06-15 14:35:14 -07:00
Enot (ded) Skelly 16b4394b30 chore(tests): add license header to recent test files 2026-06-15 14:13:34 -07:00
Enot (ded) Skelly fb1a474218 tests(config): add tests for config and seeding 2026-06-15 14:07:58 -07:00
Enot (ded) Skelly 4ebe4c869b feat: add route and neighbor reconfirmation task
prunes stale and ambiguous routes and neighbors

keep that data clean
2026-06-12 10:54:06 -07:00
Enot (ded) Skelly 044aecbd3c feat: background task scheduler
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
2026-06-12 10:28:32 -07:00
Enot (ded) Skelly a5bd8ccaf0 feat: add cors support
optionally control cross origin requests by origin and
method
2026-06-09 14:54:00 -07:00