Short answer: for a property-management usage dashboard, schedule a single fetch into a durable snapshot store, then render the stored values alongside the time of the last successful fetch. During a leaked-key drill, attach a credential generation and a request correlation ID to each fetch attempt. A chart that looks current but came from a revoked credential is an audit failure; a visibly old chart can still be useful.
| Approach | Pick this when | What the drill can prove | Main trade-off |
|---|---|---|---|
| Fetch from the browser | The viewer needs live, per-user data and the upstream supports delegated access | Which viewer requested data | Harder to keep shared snapshots and privileged credentials out of clients |
| Fetch on every dashboard request | Upstream latency and request volume are acceptable | Which server request read upstream data | Dashboard availability depends on upstream availability |
| Schedule fetches into a durable store | Several operators inspect the same property portfolio | Which credential generation populated the displayed snapshot | The chart must expose its age and failed refreshes |
Should a scheduled API fetch cache the usage chart during a leaked-key drill?
Pick browser fetching only if access is actually delegated to the signed-in operator. A shared account secret belongs on the server, not in shipped client code. If a building manager opens a dashboard on a personal laptop, browser storage is not an appropriate home for the portfolio-wide fetch credential. OWASP's secrets-management guidance covers secret access, rotation, and audit trails; those concerns do not disappear because the result is only a chart.
Pick request-time server fetching when the data must reflect an operator's current authorization at that instant. Keep upstream errors separate from an empty usage series: zero means a measured zero, while an error means you did not measure. This option makes each page load dependent on upstream response time and availability.
Pick scheduled snapshots when the question is operational: did access continue after the leaked credential was disabled, and what did the dashboard actually display during the drill? Picture three boxes: scheduler, snapshot store, dashboard. The scheduler reads upstream usage with a server-side credential; the store commits data and provenance together; the dashboard reads only committed snapshots. A separate attempt log records failures. No chart request needs the secret.
Keep that boundary visible.
Build a snapshot that can testify
Treat a snapshot as an immutable observation, not a mutable cache value with an unexplained expiration. For example, a nightly view of maintenance-message usage across 24 buildings might group counts by building and UTC day. That is an illustrative dataset, not a claimed workload or benchmark. Persist the aggregation window, the upstream observation time if provided, the local successful-fetch time, the credential generation identifier, and the correlation ID. Never store the secret itself in the row.
The transaction boundary matters. Insert the validated series and its provenance in one transaction, then advance a pointer to the new snapshot. A partial series must not replace yesterday's complete one. Keep failed attempts in an append-only attempt log with start time, outcome, and correlation ID. Record status categories and bounded error messages; do not log credentials or raw authorization headers. Alert on repeated failed refreshes and on snapshot age crossing the team's stated tolerance. These are different signals: one describes the pipeline, the other describes what viewers see. Consider a worker that receives the first 23 of 24 building totals before its connection closes: replacing the dashboard's prior complete snapshot with those partial totals would make the absent building look like it had no usage. The refresh must fail as a whole, leave the old snapshot and its timestamp untouched, and record the rejected attempt. An operator can then distinguish an incomplete fetch from an actual zero without guessing from the chart.
Here is the central operation in TypeScript. The store and upstream are interfaces so the example does not presume a provider's route, response shape, or database driver. The caller supplies a credential from its secret manager and a stable generation label; only the label is persisted.
type Point = { buildingId: string; dayUtc: string; count: number };
type Usage = { points: Point[]; observedAt: string | null };
type Attempt = { id: string; startedAt: string; generation: string };
interface Upstream { readUsage(secret: string, signal: AbortSignal): Promise<Usage> }
interface Store {
commitSnapshot(input: {
points: Point[]; observedAt: string | null; fetchedAt: string;
generation: string; attemptId: string;
}): Promise<void>;
recordFailure(input: { attemptId: string; failedAt: string; reason: string }): Promise<void>;
}
async function refresh(
upstream: Upstream, store: Store, secret: string, attempt: Attempt
): Promise<void> {
try {
const usage = await upstream.readUsage(secret, AbortSignal.timeout(10_000));
if (!Array.isArray(usage.points) ||
!usage.points.every(p => Number.isFinite(p.count) && p.count >= 0)) {
throw new Error("invalid usage series");
}
await store.commitSnapshot({
points: usage.points, observedAt: usage.observedAt,
fetchedAt: new Date().toISOString(),
generation: attempt.generation, attemptId: attempt.id
});
} catch (error) {
await store.recordFailure({
attemptId: attempt.id, failedAt: new Date().toISOString(),
reason: error instanceof Error ? error.name : "unknown error"
});
throw error;
}
}
That timeout bounds the upstream read, not the whole transaction. Give the scheduler one active run per dataset, and retry transient failures with a bounded backoff outside this function. Make snapshot commits idempotent by attempt ID so a worker restart does not create two apparent observations. Validate upstream timestamps before storage and parse them as instants, not local wall-clock dates. A successful fetch time is not proof that the provider's underlying measurement window is complete; show both timestamps when both exist.
Fresh is not complete.
What should the dashboard say when refresh stops?
Use the last committed snapshot until policy says it is too old to display, but label it with an absolute UTC fetch timestamp and a relative age. Compute age on the server or client from the same instant; never reset it on page reload. For example, after a failed 09:00 UTC refresh, a page opened at 09:07 UTC can show "Last successful fetch: 08:00 UTC" and "Age: 67 minutes." Those times illustrate the calculation, not a promised schedule. Show a distinct "refresh failed" state if the attempt log says the latest run failed. If no successful snapshot exists, show unavailable, not a zero-valued chart.
A drill should walk the same path as an incident. Disable the exposed credential, activate its replacement, trigger a refresh, and verify that subsequent successful snapshots carry the new generation label. Then check the attempt log for the old generation: requests made after revocation must be visible for investigation, including rejected attempts. Compare the dashboard's displayed snapshot ID with the committed row and its correlation ID. Finally, deliberately fail one refresh and confirm the timestamp stays put while the failure alert fires. This is a test of evidence, not just of rotation. The choice to retain the old snapshot during an upstream outage favors honest continuity over an apparently empty dashboard; the choice to show its age prevents that continuity from being mistaken for live data.
Access to that evidence needs its own boundary. Building-level operators should see only buildings they are authorized to inspect; incident responders may need the generation label and attempt history. Keep those permissions separate. Metrics can report snapshot age and consecutive failures without embedding building identifiers or secrets in high-cardinality labels. For retention, agree on the investigation window with security and legal teams before pruning attempt records. Storage cost grows with snapshot frequency and cardinality, so aggregate to the resolution the chart actually needs while preserving the audit fields required by the drill.
Limits of the snapshot
A fresh fetch does not prove upstream completeness, and a revoked key does not prove every previously captured copy was erased. The design does provide a narrower, testable claim: which credential generation produced each displayed usage snapshot, when the system successfully stored it, and whether later attempts failed. Put those facts next to the chart. Operators can then tell stale evidence from missing evidence without guessing from the shape of a line.
Top comments (0)