Skip to content

[316] Set tiered Cache-Control on published dashboard objects - #349

Draft
CarsonDavis wants to merge 1 commit into
316-republish-stack-convergencefrom
316-dashboard-cache-control
Draft

[316] Set tiered Cache-Control on published dashboard objects#349
CarsonDavis wants to merge 1 commit into
316-republish-stack-convergencefrom
316-dashboard-cache-control

Conversation

@CarsonDavis

@CarsonDavis CarsonDavis commented Aug 27, 2026

Copy link
Copy Markdown
Collaborator

Closes #316 — PR 3 of 3, stacked on #348 (which stacks on #330). Retarget to development as the stack merges; the close fires when this final piece lands.

Uploads dashboard objects with tiered Cache-Control headers so fronting caches (ours and a customer's CloudFront) show republishes immediately, with no purge on the customer's side.

  • Three tiers — the entry page and mission config are no-cache; the content-hashed bundle and the never-reused uploaded assets are long-lived immutable; everything else is short-lived. A new test pins the webpack config to content-hashed output names so nothing un-hashed can drift into the immutable tier. One cacheControlForKey helper, table-driven tests.
  • Copy paths fixed — the S3 copy operations now set MetadataDirective: REPLACE with correct ContentType and CacheControl, which previously carried no Cache-Control at all. Tradeoff: REPLACE re-derives ContentType from the key's extension instead of preserving the source object's header — equivalent for every type that flows through these paths today (upload images and the demo CSV are all in the extension map).
  • Customer doc — restores the "honor our Cache-Control headers" section of serving-a-dashboard-from-your-domain.md, which this PR makes true.

Verification: 1615 unit tests green.

A republish has to show up immediately through a fronting cache we
cannot invalidate — ours or a customer's CloudFront — so every object
now carries a Cache-Control chosen by its key: the entry page and the
baked config revalidate before every use, the keys that are content-
addressed by construction are immutable, and everything else gets a
short 5-minute TTL.

Two key classes earn the immutable tier. The webpack output under
build/static/(js|css|media) is content-hashed — a premise now pinned by
tests/unit/webpackHashedOutput.spec.js, which fails if a production
naming option loses its hash. The plugin uploads under
assets/<mission>/<subdir>/uploads/ are named crypto.randomUUID() by the
upload router and never overwritten, so their keys are content-addressed
too.

The S3 copy paths (copyPrefix, copyObjectIfExists) also switch to
MetadataDirective REPLACE, since CopyObject's default COPY preserves
the source's metadata verbatim and cannot set a header the source
object never had. REPLACE means supplying ContentType too, taken from
the same extension map the uploads use.
@CarsonDavis
CarsonDavis force-pushed the 316-dashboard-cache-control branch from 7d8b397 to f0af5d7 Compare August 27, 2026 18:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant