Commit Graph
162 Commits
Author SHA1 Message Date
MrAlders0n caaa6a5974 chore(api): raise app floor to 2.0.0 (#219)
The app now versions alongside web and server.
2026-10-10 09:49:44 -04:00
MrAlders0n d788e82ab4 feat(stats): rank top advertisers by flood or direct (#218)
Top advertisers ranked by flood + direct, so chatty companions with few flood adverts topped the list. sort=flood|direct is now required and applied before the limit; client floors raised to app 1.0.0 / web 2.0.4.
2026-10-10 09:38:36 -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 ff8758b932 fix(db): read observer activity raw rows back to the rollup watermark
Hourly activity read raw rows only 3h back, so a longer rollup backlog left
hours drawn as zero. The raw tail now starts at the newest complete rollup
hour, capped at the 24h raw holdback, and the response reports rolledUntil
and rawFrom so clients can show anything still uncovered.
2026-10-02 19:03:46 -04:00
MrAlders0n 4ad869d711 fix(db): page known routes by (last_seen, id)
The cursor was the last row's last_seen in ms, so routes sharing that
millisecond (common within one upsert batch) never came back. GET /routes
takes an optional cursorId, backed by keyset indexes; cursor alone keeps
working as before.
2026-10-02 19:03:46 -04:00
MrAlders0n a1aff810e9 fix(api): answer 404 only for missing rows
Lookups turned every error into a 404, and region filters into a 400, so a
transient database failure looked final to clients that don't retry 4xx.
Missing rows stay 404/400; anything else is a 500. Empty /regions and
/iatas lists are [] rather than null.
2026-10-02 19:03:46 -04:00
MrAlders0n 2f4f92e31e fix(db): tie-safe trace paging, atomic migrations and honest stats windows
GET /traces takes an optional cursorTag so tags sharing a millisecond survive
paging. Migrations run under an advisory lock and record each file in the
same implicit transaction, so a crash can't strand a fresh database.
Overview windowHours counts the hours it covers, hourly observer activity
fills unrolled hours from raw rows, and integration fixtures are UTC.
2026-10-02 16:53:32 -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 2042c1617f feat(stats): roll hourly scope observers and advertising nodes 2026-10-02 15:24:33 -04:00
MrAlders0n 53d9f86dd4 feat(stats): split /stats/scopes packet counts by hour 2026-10-02 13:57:54 -04:00
MrAlders0n 3e0b6c074c feat(stats): report the longest path per hour in /stats/paths 2026-10-02 13:57:54 -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 119be44c0d feat(channels): filter channel list by keyKnown
Also seed the cursor integration test with MeshMapper membership; it still
used channel_iatas after channel lists moved to region scoping.
2026-10-01 09:06:50 -04:00
MrAlders0n f8ee1ac884 feat(meshmapper): import regional channels and scope channel lists by region 2026-09-30 23:09:40 -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 14354b03dc fix(telemetry): stop partial and bogus stats inflating bucketed counters
Bucketed airtime and receive errors used MAX - MIN per bucket, so one zeroed,
replayed or garbage row counted the whole running total (e.g. 47,737 s RX in a
6 h bucket). Buckets are now summed from per-row increments with dips, stale
replays and physically impossible jumps rejected. Ingest skips uptime-only
stats and a migration deletes the rows already stored.
2026-09-30 16:32:11 -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
MrAlders0n 50d35d8626 fix(api): emit observer statusMetadata as JSON 2026-09-30 10:31:08 -04:00
MrAlders0n 3b548900a0 fix(api): tolerate client clock skew and drop the dead route evidence cursor 2026-09-30 10:31:08 -04:00
MrAlders0n e7061f5e10 fix(api): log the cause of account 500s 2026-09-30 10:31:08 -04:00
MrAlders0n 6852074180 fix(ingest): record hearing paths right after the insert
MarkSent ran after the side effects, so a broker copy on another worker could win it and be streamed as a repeat of the first hearing. It now runs before any further DB work, unconditionally so the set is warm before anyone opts in, and never for TRACE, whose path bytes are per-hop SNR.
2026-09-30 10:31:08 -04:00
MrAlders0n d733975165 fix(backup): abort exports on shutdown and sweep stale staging dirs
srv.Shutdown never cancelled the request context, so a SIGTERM mid-export left the plaintext dump and pg_service.conf in TMPDIR. The handler now derives its context from the app context and the router removes staging dirs older than 20 minutes at startup.
2026-09-30 10:31:08 -04:00
n30nex 2abed6c195 feat(channels): expose recorded packet scope in message delivery 2026-09-30 07:19:03 -04:00
n30nex 83cbaff1cf fix: address release review follow-ups for #174 2026-09-30 07:19:03 -04:00
n30nex cd20bc5c34 test(routes): cover server-anchored evidence windows 2026-09-30 07:19:03 -04:00
n30nex c448a04ea0 feat(routes): add bounded retained observation evidence 2026-09-30 07:19:03 -04:00
n30nex dea5395783 fix: address release review follow-ups for #169 2026-09-30 07:19:02 -04:00
n30nex d3c330e8fb fix: address maintainer review for #169 2026-09-30 07:19:02 -04:00
n30nex 5fbba24a4a fix(observers): separate packet metrics from presence updates 2026-09-30 07:19:02 -04:00
MrAlders0n dec643a2ad feat(ws): opt-in allowed origins and observer key (#170) 2026-09-27 11:41:11 -04:00
MrAlders0n 91b4b457b5 perf(db): resolve packet endpoints at read time (#163)
Storing a full candidate snapshot on every observation was the wrong call. The same node lists got copied thousands of times, about 22 GB on prod. Resolve endpoints live like path hops and drop the column.
2026-09-25 13:59:07 -04:00
n30nex 8a3fa7e6bf feat(stats): add bounded path and hash-width analytics (#159) 2026-09-23 20:18:41 -04:00
n30nex 6107578c25 feat(stats): add bounded reception signal analytics (#157) 2026-09-23 20:16:22 -04:00
n30nex a25e2675c8 feat(backup): add protected database and config download (#154) 2026-09-23 20:13:40 -04:00
n30nex 4d2642abec feat(admin): add protected account lifecycle endpoints (#149) 2026-09-23 20:10:35 -04:00
n30nex 951b79bf6e refactor(api): stabilize router settings and admin route wiring (#155) 2026-09-19 21:10:45 -04:00
n30nex 6aba17c75e feat(nodes): indicate repeaters outside configured local borders (#153) 2026-09-19 15:27:34 -04:00
n30nex 775aedfd2a feat(admin): update runtime CORS origins consistently (#150)
* feat(admin): update runtime CORS origins consistently

* test(admin): preserve rate-limit headers after CORS updates

* test(admin): preserve router connection budget after rebase
2026-09-19 15:27:25 -04:00
n30nex c7ace0ce0e feat(ws): limit connection attempts and signal capacity shedding (#141) 2026-09-19 13:28:47 -04:00
n30nex cf5ff7a974 feat(stats): compare flood packets reported by two observers (#152)
* feat(stats): compare distinct flood packets across observers

* test(stats): use the migrated observation deduplication key
2026-09-19 10:36:39 -04:00
n30nex 6540b9253d feat(admin): expose selected startup configuration (#147) 2026-09-19 10:36:30 -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 85b1c3961d feat(packets): populate advert summaries in history and live events (#143) 2026-09-18 06:18:19 -04:00
n30nex bcdea76c26 feat(stats): filter scope counts by IATA and region (#142)
* feat(stats): filter scope counts by IATA and region

* perf(stats): aggregate region observations before counting scopes
2026-09-18 06:18:06 -04:00
n30nex b21279c747 feat(db): add operator account lifecycle storage (#148) 2026-09-18 06:17:59 -04:00
MrAlders0n e15f873597 fix(api): return JSON 500 when bucketed telemetry query fails (#137) 2026-09-10 07:55:52 -04:00