Skip to content
Sunset Beach, NC
Get Support

How fresh the data is

Nothing in Sunset Beach is fetched live when you open the app. A cron job on the Worker runs every 5 minutes, refreshes every upstream whose cache has expired, and publishes one assembled /api/conditions payload that every phone and browser then reads. So a reading is at most its source’s cache lifetime plus up to two cron ticks old — for the buoys that means roughly 5–15 minutes, for tides up to 6 hours (they are predictions, so it doesn’t matter), and for a fishing report up to an hour. When an upstream is down, the Worker serves the last copy that worked and labels it — “Cached” on the Sources screen, “Serving last known good” in the row’s detail — rather than showing nothing or passing old data off as live.

fetched by cron every 5 mingood fetch also writtenupstream down: servedflagged staleCache-Control 60 s + ETag/api/summary28 upstream feedsEdge cache per source TTLKV last-known-good copyAssembled /api/conditionspublished to KViOS app and web pageHome-screen widget

In prose: the cron fetches each upstream through the same code path a reader would, honouring that source’s cache lifetime; a successful fetch is stored in the edge cache and also written to KV as the last-known-good copy; the cron then assembles the whole conditions payload once and publishes it to KV, and every request for /api/conditions serves that copy verbatim. If an upstream fails or returns something unparseable, the KV copy is served in its place with stale: true and a reason. The widget reads the smaller /api/summary, built from the same cached sources.

The lifetime is set by what the upstream actually does. The full per-source table is on Where every number comes from; this is the shape of it.

SourceRefreshWhy
NWS active alerts2 minsafety feed
NDBC buoys 41024, 41013, 411105 minstations post every ~10 min
NWS gridpoint forecast10 minamended forecasts show up quickly
Springmaid Pier surge, BirdCast bucket listing10 min6-minute and 10-minute upstreams
Open-Meteo sky, marine; surf zone forecast index; coastal waters index15 minhourly models, products issued a few times a day
Surf zone forecast product, air quality30 min
eBird, pier fishing report, tropical outlook, sewer spills, coastal waters product1 h
Town calendar ICS, shellfish closures, right whale slow zones3 h
Launch Library 24 h, or 5 min while a visible launch is within T−75 to T+20 minrate-limited upstream; the short window is what lets a slip reach a Live Activity
Tides, turtle counts, water quality, iNaturalist recent, proclamations, nest-database canary6 hdeterministic or slow-moving
Pier concert page, seasonal whale zones12 h
Sound Waves page, iNaturalist species counts24 h
OBIS, iNaturalist megafauna, BirdCast objects, shellfish classification7 dhistorical, immutable, or a standing category
Right whale critical habitat30 da 2016 regulatory boundary

/api/conditions is not rebuilt per request. The cron assembles it once per tick and publishes it to KV; readers serve that copy until it is 10 minutes old (twice the cron cadence, so an ordinary KV lag never forces a rebuild), after which a request recomputes it. Every JSON response carries Cache-Control: public, max-age=60 and an ETag over the exact bytes, so a refresh more than a minute after the last one revalidates against the copy the phone already holds and, most of the time, gets a 304 with no body. The practical effect: the interpolated tide height, countdowns and the sun’s position in the payload can lag by up to two ticks, and generatedAt tells you exactly when the copy was built.

What “Cached” and “last known good” mean

Section titled “What “Cached” and “last known good” mean”

Every fetched panel carries a meta block with ok, fetchedAt, stale and staleReason. The app turns that into one of three states:

StatusSources row badgeRow detailMeaning
Live(no badge, shows how long ago it was fetched)“Live”The upstream answered inside its refresh window.
Cached”Cached""Serving last known good”, with a “Fell back because” line quoting the errorThe upstream failed, timed out, rate-limited, or returned something unparseable, so the previous good copy is shown. fetchedAt is when that copy was fetched.
Down”Unavailable""Unavailable”, with an “Error” lineNo usable copy exists — the panel shows nothing.

The Sources screen opens with a strip counting Live / Cached / Down out of the total, and its footer says: “Cached” means the upstream failed and the Worker served its last good copy rather than nothing. The Beach info tab carries the same tally in one row (“N live”, with ”· N cached” and ”· N down” added only when non-zero) that opens the Sources screen.

How long a fallback may be shown depends on the source: 1 day unless the adapter says otherwise, and longer where a stale answer still beats none — 30 days for water quality, the pier report and the proclamations (the pier shuts for the winter, so a months-old report is the truthful answer), 7 days for launches and eBird, 3 hours for the surge anomaly (a day-old anomaly describes a different weather system). Past that window the panel goes to Down rather than showing something misleading.

https://sunsetbeach.neuralconfig.com/api/health fans out to all 28 upstreams and returns:

  • oktrue only when every row is ok.
  • degradedtrue when any row is ok but stale; serving a last-known-good copy counts as degraded, not healthy.
  • sources — the 28 rows, each with key, label, source, url, fetchedAt, ok, stale, staleReason, a human note, and where the feed carries its own timestamp an observedAt (and for the three buoys an observationAgeMin).
  • generatedAt and elapsedMs.

The health report is memoised at the edge for 60 seconds and deliberately has no last-known-good tier of its own — a stale copy of the thing that reports staleness would be self-defeating.

Separately from caching, the cron keeps a small diary: once an hour it writes a ~1 KB projection of the published payload (the verdict, the sunset rating and the cloud profile it came from, the rip level, water temperatures, the surge anomaly, the next tide, turtle counts, AQI, the pier’s catch list) to KV for 90 days, and once a day rolls the hours up into a record kept for two years. /api/calibration reports the last 30 and 90 days of what the app said against what the sky then did. Nothing on the phone reads this; it exists so the sunset and score weights can one day be tuned against outcomes.