Skip to content

fix(web): revalidate stable UI assets - #3934

Merged
dr-bonez merged 14 commits into
masterfrom
fix/startos-ui-cache-revalidation
Sep 17, 2026
Merged

dr-bonez merged 14 commits into
masterfrom
fix/startos-ui-cache-revalidation

Conversation

@helix-nine

@helix-nine helix-nine commented Sep 12, 2026 •

Copy link
Copy Markdown
Member

Summary

  • Split the UI cache policy: files the production Angular build declares in immutable-assets.txt are served public, max-age=31536000, immutable; everything else (index.html, assets/**, the service worker) is no-cache and revalidates against an ETag. The manifest is generated from the build's esbuild metafile (--stats-json), which lists only content-hashed outputs; a development build declares none.
  • The SPA fallback serves index.html for extensionless routes only, so a stale hashed chunk gets a 404 instead of a cached HTML body.
  • ETags differ per content encoding and every static response carries Vary: Accept-Encoding. A ranged request always serves identity bytes; an unparsable or unsatisfiable Range is ignored.
  • Signed S9PK file hashes are exposed as an encoding-independent File-Digest (the BLAKE3 of the decoded file) on full, ranged, gzip, and HEAD responses, and HEAD no longer reads the archive. The s9pk routes answer GET and HEAD only.
  • The Refresh Needed dialog has one Refresh button in both browser and PWA modes: it checks for and activates a waiting service-worker update, then reloads.
  • StartWRT reuses the manifest lookup and route predicate from start-core.
  • StartOS changelog and docs; StartTunnel 1.3.1 changelog, docs, and release notes.

Verification

  • cargo test -p start-core net::static_server --features=test — 9 passed
  • cargo check -p startwrt-core
  • cargo +nightly-2026-05-28 fmt --all --check, prettier on every touched file
  • npm run check:i18n, npm run check:i18n:tunnel, npm run check:ui
  • npm run build:tunnel: the production manifest holds 34 entries, every one a chunk-*/main-*/polyfills-*/styles-* hashed bundle; stats.json is removed; index.html, assets/** and 3rdpartylicenses.txt are absent
  • production Angular service-worker A/B (helix-nine): a plain reload retained the old main bundle, while checkForUpdate() + activateUpdate() followed by one reload loaded the new bundle

Closes #3765

@helix-nine helix-nine changed the title fix(start-os): revalidate stable UI assets fix(web): revalidate stable UI assets Sep 12, 2026
@helix-nine
helix-nine marked this pull request as ready for review September 12, 2026 23:25
@helix-nine
helix-nine force-pushed the fix/startos-ui-cache-revalidation branch from e2b1319 to 44a4a82 Compare September 14, 2026 20:51
@helix-nine

Copy link
Copy Markdown
Member Author

Updated the branch per the digest direction:

  • replaced Repr-Digest with encoding-independent File-Digest
  • restored gzip negotiation for S9PK archive entries, including gzip-only clients
  • kept the same complete decoded-file BLAKE3 value on full, ranged, gzip, and HEAD responses
  • updated both StartOS and Brochure Marketplace verification clients
  • fixed duplicate If-Range handling, removed the redundant route wrapper, made temp-file tests unwind-safe, and reused the cache/route predicates in StartWRT

I rebased onto current master. Focused and full StartOS/StartWRT tests, TypeScript checks, production UI/brochure builds, format checks, and the docs build pass; CI has restarted on the new head.

Comment thread shared-libs/crates/start-core/src/net/static_server.rs Outdated
Helix-Harness: pi
Helix-Model: openai-codex/gpt-5.6-sol
Helix-Harness: pi
Helix-Model: openai-codex/gpt-5.6-sol
Helix-Harness: pi
Helix-Model: openai-codex/gpt-5.6-sol
Helix-Harness: pi
Helix-Model: openai-codex/gpt-5.6-sol
Helix-Harness: pi
Helix-Model: openai-codex/gpt-5.6-sol
Helix-Harness: pi
Helix-Model: openai-codex/gpt-5.6-sol
Helix-Harness: pi
Helix-Model: openai-codex/gpt-5.6-sol
Helix-Harness: pi
Helix-Model: openai-codex/gpt-5.6-sol
Helix-Harness: pi
Helix-Model: openai-codex/gpt-5.6-sol
Helix-Harness: pi
Helix-Model: openai-codex/gpt-5.6-sol
Helix-Harness: pi
Helix-Model: openai-codex/gpt-5.6-sol
@helix-nine
helix-nine force-pushed the fix/startos-ui-cache-revalidation branch from 2968770 to 8ff967e Compare September 15, 2026 03:31
@dr-bonez dr-bonez added brochure Brochure marketplace repo Repository maintenance StartWRT labels Sep 16, 2026
The UI cache split, the immutable-assets manifest, the route-only SPA
fallback, Vary, and File-Digest stay. Dropped: q-value negotiation and
406, the saturating range parser, If-Range and weak validators, the
per-process nonce in every ETag (which invalidated every no-cache file
in every browser on each restart), the refresher and webmanifest
range/HEAD paths, and the path hardening and tests around a generator
whose input is our own build's metafile.

Embedded and installed-s9pk ETags go back to path + mtime, with the
content encoding appended so the identity and compressed
representations validate separately. A ranged request always serves
identity bytes: a gzip of an identity slice is not a valid 206, since a
range applies after content coding. An unparsable or unsatisfiable
Range is ignored instead of answered with 500.

The Refresh Needed dialog keeps one button, Refresh, in both browser
and PWA modes.
…e-revalidation

# Conflicts:
#	projects/start-os/CHANGELOG.md
#	projects/start-tunnel/CHANGELOG.md
#	projects/start-tunnel/release-notes/1.3.1.md
@helix-nine

Copy link
Copy Markdown
Member Author

VM-tested head 8e3996449 using the x86_64 image from run 35263416331. I upgraded a 0.4.0-beta.10 VM to this build, rebooted into 0.4.0.2 / kernel 7.1.8, and confirmed the installed FileBrowser package survived.

Passed live checks:

  • / and assets/img/icon.png: Cache-Control: no-cache, ETag, and Vary: Accept-Encoding.
  • main-366FZ54A.js: public, max-age=31536000, immutable; it is listed exactly in the generated 74-entry immutable-assets.txt, while index.html and assets/** are absent.
  • Extensionless /services/filebrowser/about falls back to index.html; missing .js, nested .svg, and .hidden paths return 404.
  • If-None-Match: "stale", <current-etag> returns 304 with no body. Root and main-bundle ETags remained byte-identical across a startd restart.
  • Range: bytes=0-15 plus Accept-Encoding: gzip returns a 16-byte identity 206 with the correct Content-Range; an unsatisfiable range is ignored and returns the full 200. HEAD returns no body with the full length; POST returns 405.
  • Through the real authenticated UI, opening FileBrowser's license fetched /s9pk/installed/filebrowser.s9pk/LICENSE.md as gzip with File-Digest. The advertised decoded-file BLAKE3 and an independent b3sum of Playwright's decoded 11,355-byte response were identical (79a6b58a…8c066a), and the frontend accepted it without a digest-mismatch error.
  • Forced a live serverInfo.version mismatch to exercise the new non-PWA refresh dialog: exactly one Refresh button, no Ok button, and clicking it caused a top-level navigation back to /services.

The workflow-dispatch image used ENVIRONMENT=dev; Chromium therefore had no service-worker registration, so this VM pass covers the non-PWA refresh branch. The production service-worker A/B remains the separate verification recorded in the PR body.

Test setup caveat unrelated to this diff: scripts/update-from-gha.sh staged and installed the image but did not reboot despite saying it would; I issued the reboot manually.

Helix-Harness: pi
Helix-Model: openai-codex/gpt-5.6-sol
@helix-nine

Copy link
Copy Markdown
Member Author

Merged current master (018535ac8) into this branch and resolved the sole conflict in package.json. The immutable-asset generation remains on the normal StartWRT build, while master's Pages demo build now calls that same build before creating 404.html; master's brochure 404.html generation is also retained.

Verified:

  • npm run build:deps
  • npm run build:wrt:demo (manifest generated, stats removed, 404.html matches index.html)
  • UI, StartWRT, StartTunnel, and brochure typechecks
  • all three web i18n checks
  • full Prettier check
  • cargo test -p start-core net::static_server --features=test — 9 passed

@dr-bonez
dr-bonez merged commit 650befa into master Sep 17, 2026
31 of 32 checks passed
@dr-bonez
dr-bonez deleted the fix/startos-ui-cache-revalidation branch September 17, 2026 22:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

brochure Brochure marketplace repo Repository maintenance StartOS StartTunnel StartWRT

Projects

None yet

2 participants