* 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.
Port the qualified server preview at 84ac3c8781 onto
org dev c7209b7043. Preserve upstream changes and
branch policies; keep the independent private Collector service separate.
Preview-Version: 2.2.0-n30nex.1
Preview-Revision: 84ac3c8781
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.
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.
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.
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.
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.
* feat(admin): update runtime CORS origins consistently
* test(admin): preserve rate-limit headers after CORS updates
* test(admin): preserve router connection budget after rebase
Refresh last_heard on duplicate observations too, capped at hourly, so
steady traffic can't age a channel out of the filter while its packets
stay retained (dedup key has no heard_at). Guard the seed join against
the docker /dev/shm cap like the trace one, and drop the unused
last_heard index column so the upserts stay HOT.
The IATA filter ran a correlated EXISTS over packets/observations with
ILIKE, which skipped the iata index and took ~7s live. Keep a small
channel_iatas table at ingest (like node_iatas) and filter against it.
Also honor the iatas= param so multi-site regions stop getting the
global list. Filter now ages out with packet retention rather than
matching any retained packet.
Quiet sites probed hundreds of thousands of packets to fill a page.
Walk idx_observations_iata_heard per requested site and dedup instead.
Filtered lists now order by site-local recency; the cursor follows.