Endringslogg
Gjeldende publisert versjon: v1.24.1
Her logges alle publiseringer til stoyvakt.sjekk.org i henhold til semver — PATCH for feilrettelser, MINOR for nye funksjoner, og MAJOR for bryteendringer.
Fiks: manglende linje for måler 2 i «LAeq siste 24 timer»
- Grafene på «Mine målinger» brukte `hsl(var(--token))` mens designsystemet bruker oklch-verdier. Fargene ble dermed ugyldige, og linjen for «Skien stasjon — måler 2 (soundpi2)» ble tegnet usynlig.
- Alle farger i grafene bruker nå `var(--token)` direkte, slik at begge stasjonene vises med hver sin farge i graf og forklaring.
Togpasseringer på stasjonssidene henter faktisk data fra Entur
- Panelet «Togpasseringer siste 24t (±500m)» sto alltid tomt fordi cachetabellen `train_passings` aldri ble fylt av noen jobb. Stasjonssiden synkroniserer nå fra Entur ved lasting.
- Ny server-funksjon `syncStationTrainPassings` finner jernbanestopp innenfor 1500 m av målestasjonen og lagrer avganger for siste 24 t + neste 6 t. Visningen filtrerer fortsatt på ±500 m.
- Nye databasefunksjoner `get_station_point` og `upsert_train_passings` er kun kjørbare for systemet (service_role), ikke for anonyme eller innloggede brukere.
- Synk kjøres maks hvert 5. minutt per stasjon og faller stille tilbake til eksisterende cache hvis Entur er utilgjengelig.
Tydeligere Lden/Lnight-kolonner i målepunkt-tabellen
- Målepunkter fra inneværende døgn (og natten som ikke er ferdig) viste tomt strek i kolonnene 'Lden (døgn)' og 'Lnight (natt)'. De viser nå «pågår», siden indikatoren først kan beregnes når perioden er over.
- «—» er nå forbeholdt døgn med for lav datadekning. Forklaringsteksten under tabellen er utvidet med begge tilfellene.
Lden/Lnight også i målepunkt-tabellen
- Tabellen 'Målepunkter' på målesiden viser nå kolonnene 'Lden (døgn)' og 'Lnight (natt)' med den beregnede døgnindikatoren for døgnet/natten målepunktet hører til. Lnight følger datoen natten startet.
- Verdier fra døgn med for lav datadekning vises ikke, og foreløpige/indikative verdier merkes med *. Kolonnene er tydelig forklart som døgnindikatorer, ikke verdier for det enkelte målepunktet.
Navigasjon: offentlig Målinger-lenke i hovedmenyen
- Ny toppnivå-lenke 'Målinger' i desktop-hovedmenyen, med direkte lenke til den aktive offentlige målesiden `/maalinger/skien-stasjon-2`. Gjør måledata tilgjengelig uten å måtte gå via 'Min konto'.
- 'Mine målinger' under 'Min konto' beholdes uendret for innloggede brukere.
Lden/Lnight-paneler på målesiden
- Ny RPC `get_public_station_noise_indicators(_slug, _days)` beregner Lden og Lnight per lokal dato (Europe/Oslo) fra allerede lagrede LAeq-målinger. Energinormert (logaritmisk) snitt vektet med hver målings varighet — ikke aritmetisk dB-snitt.
- Periodemodell: dag 07–19 (0 dB), kveld 19–23 (+5 dB), natt 23–07 (+10 dB, kun i Lden). Lnight er nattens LAeq uten tillegg. Kalendertid-modell: vektene 12/24, 4/24 og 8/24 er faste også på DST-netter. Natten knyttes til datoen den starter på.
- Datadekning per periode (dag/kveld/natt/totalt) returneres i prosent med status `complete` (≥ 90 %), `partial` (≥ 70 %) eller `not_enough_data` (< 70 %). Tersklene er StøyVakt-interne kvalitetskrav, ikke regulatoriske — dokumentert i kode og UI.
- Godkjent av prosjekteier 2026-08-11 etter verifisering av alle fem kontrollpunkter: terskler, nattperiode = startdato, kalendertid-DST, UI-merking som beregnet indikator, og ingen gamle Lden/Lnight-verdier (beregnes on-the-fly i RPC-en).
- Nytt bibliotek `src/lib/noise-indicators.ts` med `energyAverage`, `calculateLden`, `calculateLnight`, `roundNoiseIndicator`, `coverageStatus` og `osloPeriod`, samt regresjonstester i `tests/noise-indicators.test.ts` (referanseverdier, +6.395 dB-sanity, DST-overganger i mars og oktober).
- `/maalinger/$stationSlug` viser Lden og Lnight i to egne paneler (`NoiseIndicatorPanels`) med trend, siste verdi, datadekning, statusmerking og forbehold om at verdiene er beregnede indikatorer — ikke klasse 1/2-dokumentasjon. Lenker til `/hensetting#nattindikatorer` og `/om-stoy` er flyttet hit.
- Ingen endringer i Raspberry Pi-script, HMAC/API-klient eller måleinnsending. Eksisterende LAeq/LAFmax/FFT-paneler er uendret.
Sikkerhet: strammere tilgang til mottaker- og spekterdata
- `entities`: fjernet den brede `USING (true)`-lesepolicyen for `authenticated`. Ingen ikke-admin har lenger rad-tilgang til basetabellen, og `phone`/`health_authority_email` er utilgjengelig for alle utenom admin og service_role.
- `entities_public` er ikke lenger et SECURITY DEFINER-view. Viewet er nå `security_invoker = true` og henter data via den kontrollerte SECURITY DEFINER-funksjonen `list_public_entities()` som kun returnerer trygge kolonner (id, name, kind, email, website, postal_address, einnsyn_id, county, jurisdiction).
- `measurements_spectrum_external`: `SpectrumExternal: public read` med `USING (true)` er erstattet av en policy som kun gir tilgang til målinger fra godkjente og aktive stasjoner (`is_public_station()`), pluss en egen eier/admin-policy (`is_station_owner()` / `has_role`). `microphone` og `notes` er fortsatt kolonne-revokert for anon/authenticated.
- Verifisert via REST: `entities_public` og trygge spekterkolonner gir 200, mens `entities?select=phone` og `measurements_spectrum_external?select=microphone` gir 401.
Prosess: TanStack-avhengigheter og røyktest-rutine
- Dokumentert prosjektregel: `@tanstack/react-router`, `@tanstack/react-start`, `@tanstack/router-plugin` og `@tanstack/react-query` skal aldri oppgraderes enkeltvis uten å sjekke kompatibilitet (f.eks. via `npm view @tanstack/react-start@<x> dependencies`).
- Røyktest-rutine etter dependency-endringer: alltid verifiser at `/hensetting`, `/om-stoy` og `/maalinger/skien-stasjon` laster uten runtime-feil før merge/publisering.
- Registrert i `docs/known-technical-debt.md` som vedlikeholdt prosessregel.
Fiks: blank skjerm på grunn av inkompatible router-versjoner
- Rettet `TypeError: null is not an object (evaluating 'resolveDispatcher().use')` som ga blank side i preview.
- Årsak: v1.21.1-oppgraderingen etterlot `@tanstack/react-router`, `@tanstack/react-start` og `@tanstack/router-plugin` på innbyrdes inkompatible versjoner, slik at to ulike router-implementasjoner ble lastet samtidig.
- Avhengighetene er justert til et samsvarende sett: `@tanstack/react-router@1.170.25` og `@tanstack/react-start@1.168.42`.
/hensetting: måleforklaring, FFT/tonalitet og krysslenker til målesiden
- Fire nye seksjoner på `/hensetting`: «Hvordan måles hensettingsstøy?» (med definisjonsboks for LAeq, LAFmax, Lden, Lnight og FFT), «Slik leser du målesiden», «Tonalitet, rentoner og lavfrekvent during» og «FFT og frekvensanalyse ved hensettingsstøy».
- Ny informasjonsboks «Hva StøyVakt kan og ikke kan vise» med tydelig avgrensning mot juridisk overskridelse, kalibrert dB SPL og klasse 1/2-dokumentasjon.
- Lden/Lnight-forklaringen utvidet: nattindikatoren er ofte mer direkte relevant ved hensetting, og verdier med mangelfullt datagrunnlag skal merkes foreløpig/indikativ/ikke tilgjengelig.
- Tiltaksseksjonen utvidet med «Måling og etterkontroll» (nattperiode/Lnight, LAFmax, tonalitet, impulslyd, varighet og gjentakelse).
- Klikkbar innholdsfortegnelse med ankere på alle 11 seksjoner; krysslenker til `/maalinger/skien-stasjon`, `/om-stoy` og `/klage` («Slik kan du dokumentere støy selv» peker til eksisterende `/sensor-guide`).
- `/maalinger/$stationSlug` lenker tilbake til `/hensetting#nattindikatorer` ved døgnprofilen og `/hensetting#fft-frekvensanalyse` ved FFT-panelet.
Sikkerhetsfiks: kontaktinfo i virksomhetsregisteret
- `entities`: all direkte tabelltilgang fjernet for `anon`. Innloggede brukere har nå kun selektiv kolonne-SELECT på trygge felt (navn, type, org.nr., postadresse, nettsted, postnummer, fylke, kommunenummer, einnsyn-id, område).
- `entities.email`, `entities.phone` og `entities.health_authority_email` er ikke lenger lesbare via direkte tabelloppslag for `anon`/`authenticated`.
- `entities_public`-viewet er satt til `security_invoker = false` slik at offentlige sider fortsatt viser mottakernes offisielle postmottak-adresser, uten at rå tabelltilgang åpnes.
- RLS-policyen «Entities: public safe columns read» (USING true for anon+authenticated) erstattet av en policy som kun gjelder innloggede brukere.
- Revisjonsfunksjonen `audit_sensitive_column_privileges` utvidet med `entities.email` og anon-tilgang, slik at CI fanger regresjoner.
Ny offentlig måleside for soundpi2 + fiks av kolonnetilgang
- Ny måler «Skien stasjon — måler 2 (soundpi2)» har fått offentlig side `/maalinger/skien-stasjon-2` og er knyttet til hensettingsanlegget Skien, slik at begge målerne listes på anleggssiden.
- v1.21.1 fjernet all tabelltilgang til `measurements_external` og `measurements_spectrum_external` for `anon`/`authenticated` uten å legge tilbake de trygge kolonnene. Resultat: `/stasjoner/$id` viste «Ingen FFT-/spekterdata mottatt ennå» selv når data kom inn.
- Selektiv kolonne-GRANT gjenopprettet (tid, nivåer, varighet, posisjon, samplerate, dominerende frekvens og frekvensbånd). `notes`, `microphone` og `api_key_label` forblir utilgjengelige.
- Verifisert: offentlige RPC-er filtrerer ikke på `measurement_type` eller mikrofonnavn — `usb_fft` fra Logitech C920 vises på lik linje med tidligere `lifecam_fft`.
Sikkerhetsfikser: kolonnetilgang på måletabeller + avhengigheter
- `measurements_external`: `notes` og `api_key_label` er ikke lenger lesbare for `anon`/`authenticated`. Full table-REVOKE + selektiv kolonne-GRANT (jf. lærdommen fra v1.20.2 om at kolonne-REVOKE er additiv).
- `measurements_spectrum_external`: `notes` og `microphone` er ikke lenger lesbare for `anon`/`authenticated`. `/stasjoner/$id` viser nå mikrofonmodell fra stasjonsraden i stedet.
- Oppgradert `@tanstack/react-router`, `@tanstack/react-start` og `@tanstack/router-plugin` for å fjerne kritisk `seroval`-sårbarhet (GHSA-mv8w-475r-vwqw).
- Verifisert at `entities.phone` og `entities.health_authority_email` allerede er utilgjengelige for `anon`/`authenticated` (kolonne-GRANT fra v1.20.x).
Driftsvarsling når målestasjoner slutter å sende data
- Ny tabell `station_data_alerts` og RPC `evaluate_station_data_alerts` som åpner varsel når en aktiv målestasjon ikke har levert data innen tidsvinduet (spekter/FFT: 6 timer, dB-målinger: 3 timer), og lukker varselet automatisk når data kommer inn igjen.
- Nytt endepunkt `POST /api/public/monitor/station-data` beskyttet med delt hemmelighet (`x-monitor-secret`, konstant-tid sammenligning). Kalles av scheduler hvert 15.–30. minutt.
- Ny e-postmal `varsel-datastopp` til driftsadresse (`platform_config.email_post`) og stasjonens `contact_email_for_alerts`. E-post sendes kun ved overgang til/fra avbrudd — ikke ved hver kjøring.
- Ny UI-melding `DataStalenessNotice` på `/maalinger/$stationSlug` og `/stasjoner/$stationId` som forklarer hvilken datastrøm som mangler, siden når, og at dB-målinger er upåvirket når kun frekvensanalysen står stille.
- Ny dokumentasjon `docs/station-monitoring.md`.
Datakontroll: dB-inntak verifisert, FFT-data står stille siden 26.6
- Verifisert at `/api/v1/measurements` mottar dB-data fra Skien-stasjonen (Wensn WS1361, `ccfa9e06-…`): 84 588 målinger totalt, 279 siste døgn, siste måling 02.08.2026 07:03. Alle offentlige RPC-er (`get_public_station_levels/daily/hour_profile/events`) returnerer korrekte, stigende tidsserier.
- Avdekket at spekterdata (`measurements_spectrum_external`) ikke er mottatt siden 26.06.2026 06:51 — 4 580 rader totalt. FFT-panelene på `/maalinger/skien-stasjon` viser derfor «Ingen frekvensdata i valgt periode» for 24t/7d/30d. Dette er en avsenderside-tilstand (Pi/LifeCam), ikke en API- eller visningsfeil.
- UI: «Dominerende frekvens (siste relative FFT)» viser nå måletidspunktet, slik at det ikke fremstår som en fersk verdi når spekterdata er gammel.
- UI: y-aksen i lydnivå-grafen bruker nå avrundet toppverdi (nærmeste 10 dB over maks) i stedet for å vise ubearbeidet desimaltick som «126.545».
Sikkerhetsherding: effektiv UPDATE-lockdown på fixed_measurement_stations
- Verifisering av v1.20.1 avdekket at kolonne-nivå `REVOKE UPDATE (hmac_secret, api_client_id, approval_status, …)` ikke fikk effekt fordi tabell-nivå `UPDATE`-grant til `authenticated` overstyrte den (Postgres kolonne-grants er additive, ikke restriktive).
- Fix: `REVOKE UPDATE ON public.fixed_measurement_stations FROM anon, authenticated, PUBLIC`, deretter `GRANT UPDATE` kun på trygge eier-redigerbare kolonner: `station_name`, `geom`, `hardware_type`, `hardware_kind`, `microphone_model`, `calibration_date`, `calibration_reference_db`, `measurement_interval_seconds`, `active`, `notes`, `description`, `contact_email_for_alerts`, `facility_id`.
- Bekreftet via `has_column_privilege`: `hmac_secret`/`api_client_id`/`approval_status`/`approved_by`/`approved_at`/`owner_user_id`/`public_slug` UPDATE = false for `authenticated`; ufarlige felter forblir editerbare.
CI-test: has_column_privilege-regresjonsvern for sensitive kolonner
- Ny RPC `audit_sensitive_column_privileges` (SECURITY DEFINER, EXECUTE til anon/authenticated/service_role) returnerer faktisk `has_column_privilege`-status for sensitive kolonner i `entities` og `fixed_measurement_stations` mot forventet matrise. Eksponerer kun privilegie-metadata, ikke radinnhold.
- Ny vitest-test `tests/sensitive-column-privileges.test.ts` kaller RPC-en via anon-klient og feiler dersom én eneste kolonne avviker fra forventet. Fanger automatisk regresjoner som v1.20.1 (kolonne-nivå REVOKE overstyrt av tabell-nivå GRANT — Postgres kolonne-grants er additive, ikke restriktive).
- Matrise dekker negativ-kontroller (`hmac_secret`, `api_client_id`, `approval_status`, `approved_by`, `approved_at`, `owner_user_id`, `public_slug`, `entities.phone`, `entities.health_authority_email`) og positiv-kontroller (`station_name`, `geom`, `active`, `description`).
Sikkerhetsherding: entities, correspondence, fixed_measurement_stations
- `entities`: REVOKE SELECT på kolonnene `phone` og `health_authority_email` fra `anon` og `authenticated`. Frontend bruker `entities_public`-viewet og påvirkes ikke; admin og service_role beholder full tilgang.
- `correspondence`: fjernet RLS-policy «Correspondence: anon insert on anon cases». Anonyme brukere kan ikke lenger plante falske korrespondanserader på saker hvis ID-er er offentlige via `cases_public`. Innkommende e-post fra anonyme avsendere går utelukkende via HMAC-signert webhook med service_role.
- `fixed_measurement_stations`: REVOKE SELECT på `hmac_secret` og `api_client_id` fra `anon` og `authenticated` (stasjons-eiere kan ikke lenger lese hemmelighetene via Data-API-et). REVOKE UPDATE på de samme kolonnene + `approval_status/approved_by/approved_at` — kun service_role/admin kan endre.
- Forenklet eier-UPDATE-policy «FixedStations: owner updates limited» til ren eierskapssjekk; de tidligere «frys»-subspørringene er overflødige etter kolonne-nivå REVOKE.
/hensetting: ny verifisert presedens — Hamar Briskebyen (sak 2023/6262)
- Ny verifisert CmsCard på /hensetting i seksjonen «Presedens og dokumentspor», plassert etter MHiG-saken (26/5032) og før Marienborg-blokken: «Hamar Briskebyen — klagesak om støy fra hensatte tog (sak 2023/6262)». Andre dokumenterte Bane NOR-presedens etter Skien.
- Direkte sitater fra Bane NORs brev (Magne Trønnes, prosjektleder, 04.07.2023) som erkjenner at Lden er årsgjennomsnitt og at vinduer forutsettes lukket i beregningene.
- Direkte sitater fra naboklage (Bjørn Inge Sletta, 13.08.2023) som bruker samme argumenter (gjennomsnitt vs. akutt belastning, kumulativ støy etter T-1442 kap. 2.5) som er aktuelle i sak 26/5032.
- Eksplisitt presisjon om at Statsforvalter Innlandet kun var kopimottaker, ikke har behandlet saken.
- To nye entries i `verified-case-numbers`-allowlist: `2023/6262` (Statsforvalter Innlandet) og `202300472-23` (Bane NOR). Saksnumrene vises nå uten `[ikke verifisert]`-tag.
- Anker `#hamar-2023-6262` for senere dyplenking.
Sikkerhetsherding: korrespondanse, HMAC-secret, måleinnsending, dispatch-logg
- `correspondence`: fjernet RLS-policy «Correspondence: case owner reads». Saks-eiere leser fortsatt korrespondanse via `correspondence_public`-viewet (uten e-postadresser/attachments). Admin leser alt som før. UI på `/saker/$caseRef` bruker allerede viewet, så ingen funksjonell endring.
- `correspondence`: INSERT-policy strammet inn — innloggede brukere kan kun opprette korrespondanse-rader på saker de selv eier, eller når de har admin-rollen. Hindrer at en innlogget bruker planter falske meldinger i andres saker via enumererbare case-ID-er. Server-funksjoner (e-post-webhook, sendCaseEmail, innsyn-loggføring) bruker service_role og er upåvirket.
- `fixed_measurement_stations.hmac_secret`: REVOKE SELECT på kolonnenivå fra `authenticated` og `anon`. Speiler tidligere lockdown av `api_client_id`/`api_key_hash`. Pi-installasjon henter secret kun via service_role-RPC.
- `measurements_external`: INSERT-policy for `authenticated` krever nå `station_id IS NOT NULL` og at brukeren eier stasjonen. Hindrer at en innlogget bruker dytter målinger inn i den offentlige datasettet med fritekst-`source_name` uten stasjonskobling. HMAC-signert `/api/v1/measurements` (service_role) er uendret.
- `dispatch_log`: REVOKE INSERT/UPDATE/DELETE fra `authenticated` og `anon`. Eksplisitt bekreftelse på at sende-loggen kun skrives av service_role (server-side). Kun admin SELECT-policy fra før.
Offentlig målestasjon: målepunkt-tabell viser nå Dom. Hz og Lav 63–500
- `get_public_station_events`: utvidet toleransevinduet for sammenstilling mellom `measurements_external` og `measurements_spectrum_external` fra ±30 sekunder til ±5 minutter. Pi-en sender spektraldata på et eget tidsraster (~1–3 min forskjøvet fra dB-målingene), så det gamle vinduet matchet aldri og kolonnene «Dom. Hz» og «Lav 63–500» sto tomme i tabellen på `/maalinger/skien-stasjon`. Ingen endring i datagrunnlag, Pi/API/HMAC, RLS eller andre RPC-er.
/innspill: utvidet argumentasjon + offentlig måleside som supplerende dokumentasjon
- Nytt felt `maalesideUrl` i InnspillData (default `https://stoyvakt.sjekk.org/maalinger/skien-stasjon`) som lar brukeren legge inn URL til offentlig måleside. Lagt inn i FIELDS etter `referanseRapport`.
- Seksjon 1: nytt avsnitt om offentlig måleside som supplement til klagen.
- Seksjon 2: nytt avsnitt om at monoton/vedvarende støy på dagtid også påvirker livskvalitet, konsentrasjon og bokvalitet — ikke bare søvn og natt.
- Seksjon 7: nytt punkt 6 om vurdering av lavfrekvent og tonal karakter.
- Seksjon 8 utvidet og omdøpt til «Supplerende egendokumentasjon og offentlig måleside» med tydelig avgrensning mellom egne målinger og fagkyndig kartlegging, og eksplisitt merking av frekvensanalysen som relativ/indikativ — ikke kalibrert dB SPL.
- Seksjon 9: nytt tredje kulepunkt om at måledata og offentlig måleside skal legges til grunn som indikasjon.
Offentlig målestasjon: tydeligere graftekster og LAeq/LAFmax-tittel
- Lydnivå-graf: tittel endret til «Lydnivå over tid · LAeq / LAFmax (dB)». LAmax-linjen fjernet fordi LAmax og LAFmax er sammenfallende i datagrunnlaget fra Wensn WS1361 — vises som overlappende linje uten ekstra informasjon. Forklaring lagt inn rett under tittelen.
- Hver graf på `/maalinger/$stationSlug` har nå en kort, praktisk forklaring rett under tittelen: lydnivå over tid, daglig oppsummering, døgnprofil, relativ frekvensanalyse, siste frekvensprofil og målepunkt-tabell. Mål: hjelpe publikum å forstå hva mønstrene betyr for opplevd støy.
- Ingen endring i RPC-er, datagrunnlag, Pi/API/HMAC-oppsett eller `/stasjoner/$stationId`.
Skien: slugene flyttet til den ekte Wensn WS1361-stasjonen
- Database hadde to Skien-stasjoner: en tom placeholder (`a19f120a-…`) hadde `facility_slug='skien'`/`public_slug='skien-stasjon'`, mens den ekte Pi-stasjonen (`ccfa9e06-…`, Wensn WS1361) med ~70 000 målinger og 201 spektrumrader manglet slugs. `/maalinger/skien-stasjon` viste derfor ingen data.
- Migrasjon: slugene flyttet til `ccfa9e06-…`. Placeholderen (`a19f120a-…`) satt til `active=false`, `approval_status='suspended'` for å unngå duplikatforvirring i admin. Ingen måledata eller Pi/API/HMAC-oppsett endret.
- Resultat: `/maalinger/skien-stasjon` viser nå reelle målinger fra Pi-en for 24t/7d/30d/90d, og `PublicFacilityStations` på Skien hensetting-side peker til riktig stasjon.
Skien-stasjonen reaktivert for offentlig presentasjon
- Eksisterende Skien-stasjon (a19f120a-…) satt til `active=true` og `approval_status='approved'` slik at public-RPC-ene (`get_public_station_*`, `list_public_stations`) returnerer data og `/maalinger/skien-stasjon` kan verifiseres. Ingen endring i Pi/API/HMAC-oppsett, ingen ny stasjon. `facility_slug='skien'` og `public_slug='skien-stasjon'` beholdt.
Offentlig presentasjon av målestasjoner (/maalinger/$stationSlug)
- Ny offentlig presentasjonsside `/maalinger/$stationSlug` med statuskort, periodevelger (24t/7d/30d/90d), lydnivå over tid (LAeq/LAmax/LAFmax), daglig oppsummering, døgnprofil, relativ FFT-serie, siste frekvensprofil og hendelsestabell. Tidsserier sorteres stigende på tid (eldste til venstre).
- Nye public-RPC-er (SECURITY DEFINER, kun godkjente + aktive stasjoner, ingen HMAC/mikrofonmodell/notes): `get_public_station_summary`, `get_public_station_levels`, `get_public_station_daily`, `get_public_station_hour_profile`, `get_public_station_spectrum`, `get_public_station_spectrum_latest`, `get_public_station_events`, `get_public_stations_for_facility`, `list_public_stations`. Aggregering velges via `_bucket` (raw/15min/hour/day).
- `fixed_measurement_stations`: nye kolonner `facility_slug` (kobling til hensettingsanlegg) og `public_slug` (lesbar URL, UNIQUE). Skien stasjon (a19f120a-…) satt til `facility_slug='skien'`, `public_slug='skien-stasjon'`.
- Skien hensetting-side viser nå offentlig liste over faste målestasjoner via ny komponent `PublicFacilityStations`, med direkte lenke til `/maalinger/skien-stasjon`.
- FFT/frekvensanalyse merket tydelig som «relativ», «indikativ», «ikke kalibrert dB SPL» — mikrofonmodell og LifeCam fjernet fra all offentlig tekst. Egen forklaringsseksjon «Hva viser målingene?» med syv kort (lydnivå, varighet, natt, tonalitet, lavfrekvent, gjentakelse, monoton dag).
- Diskret «Åpne teknisk stasjonsside»-knapp kun for innloggede eiere/admin. Anonyme brukere ser ren offentlig presentasjon.
- Sitemap: lagt til dynamiske `/maalinger/$slug`-entries fra `list_public_stations` (cache 1t). Eksisterende `/stasjoner/$stationId` (eier/admin), API, Pi-oppsett, InfluxDB/Grafana og admin er uendret.
Sikkerhet: hindre spoofing av målinger mot fremmede stasjoner
- RLS INSERT-policy på `measurements_external` strammet inn: innloggede brukere kan kun sette inn rader uten `station_id`, eller med en `station_id` som peker på en `fixed_measurement_stations`-rad de eier (`owner_user_id = auth.uid()`). Tidligere kunne enhver innlogget bruker tilskrive målinger til hvilken som helst stasjon. HMAC-signert `/api/v1/measurements` (service_role) er uendret.
SEO: per-side metadata, canonical, og:url, JSON-LD og sitemap-utvidelse
- Stasjonssiden (`/stasjoner/$stationId`): lagt til `head()` med unik title, description, og:title/og:description/og:url, og:type=article og self-referencing canonical.
- Forside (`/`): differensiert title fra root, lagt til og:url og canonical.
- `/changelog`: lagt til og:url, canonical og CollectionPage JSON-LD.
- `/hensetting`: lagt til og:url, canonical og Article JSON-LD.
- Sitemap: lagt til `/innspill` og `/presse` (mine-* er auth-gated og holdes utenfor indeks).
Fix: tidsakse på stasjonsgrafer sorteres stigende
- Stasjonssiden (`/stasjoner/$stationId`): tidsseriegrafer (Siste 24t Laeq/Lamax, daglig 30d) sorteres nå eksplisitt `measured_at ASC` før de sendes til Recharts. Tidligere `.slice().reverse()` antok DESC fra RPC og kunne gi feil retning på x-aksen. FFT-panelet brukte allerede `order ascending: true` og er uendret. Ingen API/Pi-endringer.
Fix: rotate_station_api_key feilet med 'gen_random_bytes does not exist'
- Aktivert `pgcrypto` i `extensions`-schema og oppdatert `public.rotate_station_api_key` til å kalle `extensions.gen_random_bytes(32)` med `search_path = public, extensions`. Rotasjon av HMAC-secret fungerer igjen. Ingen eksisterende secrets endret.
Skien reaktivert + kolonne-revoke på hmac_secret
- Skien-stasjonen (a19f120a-…) satt til `active = true`, `approval_status = approved`. HMAC-secret ikke rotert.
- Sikkerhet: `REVOKE SELECT (hmac_secret) ON public.fixed_measurement_stations FROM authenticated, anon`. Stasjonseiere kan ikke lenger lese HMAC-hemmeligheten via PostgREST; kun service-role (server-side HMAC-verifisering) har tilgang. Verifisert med `has_column_privilege('authenticated', …, 'hmac_secret', 'SELECT') = false`.
- Frontend publisert til produksjon (Publish → Update) slik at FFT-panelet på /stasjoner/$stationId blir synlig.
API-utvidelse: spectrum[] + ny FFT-tabell + UI-paneler for relativ frekvensanalyse
- POST /api/v1/measurements aksepterer nå både `measurements[]` (kalibrerte dB) og `spectrum[]` (relativ FFT/oktavbånd) i samme batch. Begge arrayer er valgfrie, men minst én må være ikke-tom. Eksisterende HMAC-modell uendret (timestamp + "." + raw_body, lowercase hex, sha256= prefix, konstant-tid, ±300s replay-vindu).
- Ny tabell `measurements_spectrum_external` med 8 oktavbånd (20–8000 Hz) + aggregert `low_band_63_500`, `peak_frequency_hz`, `peak_level_db_relative`, `microphone`, `sample_rate_hz`. RLS public read, kun service-role insert via RPC `insert_station_spectrum` (samme approved/active-sjekk som dB-RPC-en).
- Rename: `fixed_measurement_stations.api_key_hash` → `hmac_secret` (feltet inneholder selve HMAC-hemmeligheten, ikke en hash). Skien-stasjonens secret er bevart uendret. RPC-er `register_station` og `rotate_station_api_key` oppdatert. Admin-UI rotasjonsflyt uendret.
- /stasjoner/$stationId: nytt panel «Relativ frekvensanalyse (FFT)» som viser oktavbånd-energi 20–8000 Hz og dominerende frekvens (Hz) over tid. Tydelig merket «relativ FFT-energi — IKKE kalibrert dB SPL».
- Oppdatert `docs/pi-integration.md` med ny kontrakt, curl- og Python-eksempel (placeholders), responseksempler (200/401 bad_signature/401 replay), feilkode-tabell og sikkerhetsnotat.
- Sikkerhet: ingen ekte `hmac_secret` i docs, migrations, seed, frontend eller logs. Logging i API-handler kun `station_id` + DB feilkode — aldri signatur, secret, raw body eller auth-header.
/innspill: kontinuerlig eksponering — helger, helligdager og ferier (re-bekreftet)
- /innspill: bekreftet avsnitt i seksjon 2 «Hva slags støy dette gjelder» om at hensettingsstøy er kontinuerlig også lørdager, søndager, helligdager og ferier — naboer får ingen restitusjonsperiode mellom arbeidsuker. Argumentet underbygger varigheten av den helsemessige eksponeringen.