Vai al contenuto
Spedizione in 24/48h in tutta Italia
Vai al contenuto
Navigazione documentazione

Questa pagina non è ancora disponibile nella lingua scelta. È mostrata la versione EN.

Frontend Delivery And Verification

Architecture, threat boundaries, and the obfuscation/CSP/SRI decisions are in ADR 0004.

Security Invariants

  • Browser JavaScript, CSS, source locations, API requests, and rendered values are observable even when minified.
  • Never put credentials, private keys, provider tokens, tenant secrets, or privileged-only data in a bundle, manifest, metadata file, HTML bootstrap, or public source map.
  • Authorization, tenant isolation, validation, prices, permissions, and every consequential business rule are enforced server-side. Client checks are UX only.
  • Minification is a delivery optimization. Obfuscation is disabled and is not a security control.

Profiles

Profile Command Contract
Development npm run dev Explicit development mode, readable source modules, HMR, browser debugging; not deployable output.
Production npm run build Tree-shaken/minified JS and CSS, content hashes, Vite manifest, no public maps, reproducibility metadata, raw/gzip budgets.

scripts/frontend-build-policy.mjs owns the shared production shape. config/quality/frontend-asset-budgets.json owns per-theme entry/chunk/CSS limits and is the canonical inventory of supported themes. Every theme build runs scripts/check-theme-asset-budget.mjs, which also rejects unhashed JS/CSS, public .map files, stale/mismatched lockfile metadata, and Default features accidentally pulled into the eager app graph.

That per-theme check runs inside one theme directory and therefore cannot see the published tree as a whole. npm run verify:themes (scripts/check-theme-build-inventory.mjs) owns the inventory question for public/build: every supported theme is present with .vite/manifest.json and build-metadata.json, no directory belongs to a retired theme, no unhashed js/app.js or css/app.css survived a non-empty output directory, no .map file is published, and each metadata file names its own theme. It is the last step of npm run deploy:themes and is the check to run before switching release traffic.

The root aggregates (purge:themes, update:themes, build:themes, deploy:themes) enumerate every supported theme; test:themes enumerates exactly the themes that declare a Vitest suite — lts1 deliberately has none, so it carries no test:lts1 script and its deploy chain stops at the build. tests/Unit/Theme/FrontendBuildPolicyContractTest.php derives all of that from the canonical inventory, so adding a theme to the budgets file fails until its scripts exist.

build-metadata.json is deterministic: it intentionally excludes timestamps, hostnames, usernames, absolute paths, environment variables, and Git working tree data. It is provenance, not a secret container.

Cache And Deployment Contract

  • Serve filenames containing a content hash with a long-lived immutable cache policy.
  • Revalidate HTML and .vite/manifest.json; do not cache them as immutable.
  • Upload the complete new public/build/{theme} tree before switching release traffic. Retain the previous tree while existing HTML can still reference it.
  • entryCss() and entryJs() resolve the manifest and emit hash URLs without mutable ?v= values. Their legacy fallback keeps old builds loadable only during the operator-controlled transition.
  • Never publish source maps. If private symbolication is later approved, upload maps directly to the approved monitoring service during the build and remove them before the public artifact is assembled.

Recorded Policy Evidence

Measured on 2026-09-06 against the owner-compiled public/build tree. Every supported theme passes scripts/check-theme-asset-budget.mjs; the figures are the entry chunk and the entry's CSS, raw and gzip, against their budgets.

Theme Entry raw / budget Entry gzip / budget CSS raw / budget CSS gzip / budget
default 91,270 / 360,000 29,986 / 120,000 171,665 / 230,000 41,554 / 60,000
sober 76,762 / 90,000 25,922 / 32,000 78,221 / 90,000 14,064 / 20,000
cc1 289,705 / 310,000 96,290 / 105,000 123,762 / 135,000 21,549 / 30,000
dm1 417,293 / 450,000 136,693 / 150,000 531,353 / 550,000 110,235 / 125,000
lts1 273,622 / 310,000 90,703 / 105,000 81,092 / 95,000 15,903 / 22,000
pc1 274,633 / 310,000 90,585 / 105,000 118,355 / 135,000 20,018 / 30,000
pc2 277,114 / 310,000 91,577 / 105,000 74,514 / 75,000 13,542 / 18,000
pc3 314,007 / 340,000 105,803 / 115,000 105,735 / 115,000 29,891 / 33,000

PC3's budgets were re-baselined on the same date. They had been provisioned while the theme was still a scaffold, and the completed public shell (nested module navigation, native locale disclosures, progressive mobile menu) exceeded the entry gzip limit by 803 bytes and the CSS gzip limit by 3,891 bytes. The new ceilings keep roughly 8% headroom over the delivered artifact, matching the convention used by the other themes. Two payload reductions are known and deliberately not taken yet, because both need a rebuild cycle the owner schedules: animejs is imported eagerly although initReveal() already exits under prefers-reduced-motion, and the @fontsource imports pull 33 @font-face blocks for subsets the theme does not use.

PC2's CSS raw figure sits at 99% of its budget. It is inside policy, but the next PC2 style change is expected to need a re-baseline or a trim.

Determinism, verified 2026-09-08

The owner rebuilt all eight themes on 2026-09-08. Every figure above is unchanged, and the content hashes are identical to the 2026-09-06 output — pc3 still emits js/app-B7HphJv8.js and css/app-CarRSKoV.css — so two independent builds of unchanged sources produced byte-identical artifacts. That is checklist item 4 below satisfied with evidence rather than assertion, and it is what makes the immutable cache policy safe: a rebuild does not invalidate a client's cached asset unless the source actually changed.

Each build-metadata.json still matches its theme's package-lock.json digest, declares source_maps: false and obfuscation: none, and the inventory audit at that date reported only two retired-theme directories. They were local ignored output, not supported themes.

Local inventory and browser recheck — 2026-09-14

npm run verify:themes now passes for all nine supported output trees, including dm2. The retired public/build/fhg1 and public/build/solar directories are already absent; no deletion or theme compilation was needed. The audit confirms manifests, metadata, hashed paths and no published source maps for this local tree. An isolated SQLite-backed Default home page returned HTTP 200; its JS, CSS and the loaded search chunk also returned HTTP 200 during a headless Chromium desktop capture. This local check does not establish CDN cache headers, deliberate-error symbolication, visual acceptance across all themes, or production server behavior. On 2026-09-14 the project owner confirmed completion of the remaining manual browser/cache/symbolication checks and the Default/Sober and public-theme visual review. This is owner acceptance; no raw browser trace or measurement was supplied with that confirmation.

The kiosk surface added no CSS: template/default/tpl/admin/kiosk/** styles itself with inline --k-* tokens and classes the bundles already carried, so default and sober came back byte-identical despite the new templates. A surface that needs no new utilities is also a surface that cannot look broken between a source change and the next operator-owned rebuild.

Operator Acceptance Checklist

Agents do not compile theme assets. From a clean checkout with the locked Node dependencies installed, the project owner may compile all nine supported themes from the repository root with npm run build:themes. The equivalent per-theme build and test sequence is:

npm --prefix template/default run build && npm --prefix template/default run test
npm --prefix template/sober run build && npm --prefix template/sober run test
npm --prefix template/cc1 run build && npm --prefix template/cc1 run test
npm --prefix template/dm1 run build && npm --prefix template/dm1 run test
npm --prefix template/dm2 run build && npm --prefix template/dm2 run test
npm --prefix template/lts1 run build
npm --prefix template/pc1 run build && npm --prefix template/pc1 run test
npm --prefix template/pc2 run build && npm --prefix template/pc2 run test
npm --prefix template/pc3 run build && npm --prefix template/pc3 run test

Then verify:

  1. Every build policy check passes and each output has .vite/manifest.json plus build-metadata.json.
  2. Manifest JS/CSS filenames include hashes; public/build/** contains no .map files, no stale unhashed app.js/app.css after the clean build, and no directory belonging to a retired theme. Steps 1 and 2 are scripted: run npm run verify:themes from the repository root and treat a non-zero exit as a release blocker.
  3. A clean browser load returns 200 for every manifest-referenced asset and no dynamic import fails.
  4. A second load reuses immutable hashed assets; changing an entry source changes its filename while an unchanged clean build keeps its digest. The second half was verified on 2026-09-08 — see "Determinism" above.
  5. Default and Sober admin sidebar, mobile navigation, active/ancestor state, tooltips, settings pages, and lazy editor/map/consent features remain usable. The scripted part of that check is tools/benchmarks/admin-sidebar-latency.sh; its metric contract and the accepted before/after evidence are in Admin sidebar startup performance.
  6. Each public theme is reviewed at desktop/mobile widths, light/dark mode, keyboard navigation, reduced motion, login, home, and one representative module page.
  7. A deliberate browser error remains diagnosable by release metadata and minified file/line. No workflow assumes a public source map.

Record the command results and any budget adjustment with the change review. Budgets may move only with a measured bundle-composition explanation.