fix(gnss): date replayed snapshots by the proxy-reported age, not cross-clock fetchedAt

The layer compared the server's fetchedAt with the browser's clock, so a
skewed client clock could shift or void the 30-minute window. The proxy now
returns ageMs (its own now minus fetchedAt, at serve time, on fresh, cached
and stale answers); the source refuses a snapshot without a finite,
non-negative ageMs, and the layer dates rows as its now() - ageMs. fetchedAt
stays in the payload for display.

Tests: proxy cached/stale answers report ageMs on the server clock; the
source rejects missing/invalid/negative ageMs; the layer replay test drives
ageMs. qa-gnss stubs send ageMs.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
Leo PechnickiandClaude Opus 5.5 authored and Mazeyar Moeini Feizabadi committed 2026-10-13 17:20:43 +02:00
1 parent 7e570a79ad
commit 783c5b16f4
9 files changed
+55 -25

No files matched your search

+4 -1
View File
@@ -145,7 +145,10 @@ export function gnssIntegrityProxy({
if (!allow(clientKey(req))) return json(429, { error: 'rate_limited' });
try {
const { value, stale } = await acquire(anchor);
json(200, stale ? { ...value, stale: true } : value, stale);
// ageMs is measured on this server's clock, so the browser can date
// the rows without comparing its clock to ours.
const ageMs = Math.max(0, now() - value.fetchedAt);
json(200, { ...value, ageMs, ...(stale ? { stale: true } : {}) }, stale);
} catch (error) {
if (error.status === 429) {
// Tell the browser how long the shared upstream cooldown has left.