ADR-0052: Authorize exact static style identities
- Status: Accepted
- Context: first integration feedback #629
Decision
Static publications may request at most 64 strictly sorted, unique SHA-256
identities of exact style-element text through styleHashes in their signed web
manifest. Each identity has the canonical sha256: plus 64 lowercase hexadecimal
shape. This field is restricted to componentless static-site-v1 publications.
The native validator, capture adapter and JSON Schema enforce the finite shape;
native validation additionally checks order and retained allocation bounds.
The host must separately enable httpIngress.allowStaticStyleHashes. Omission
and false preserve the strict profile; selecting an HTML document that requests
hashes then returns 403. The host constructs CSP from parsed digests and extends
only style-src. Publishers cannot supply arbitrary directives, script hashes,
nonces, origins, wildcards or unsafe keywords. Style attributes are not enabled.
The host opt-in applies to its admitted static publications; operators should
enable it only where their publisher review policy covers these style identities.
There are no nonces and no response rewriting. Hashes name deterministic style bytes generated by the reviewed application/framework code. Source-owned inline bootstrap scripts and static styles should become external files at build time, before inventory, packaging and signing. A fixed public nonce would authorize unreviewed injection and is rejected as a design. A fresh nonce would require an additional representation and cache contract and is outside this decision.
The complete web manifest is already package content and admission identity. Changing only the style list therefore changes the signed package/publication identity. Immutable HTML bytes and their byte ETag remain unchanged. Every HTML 200, HEAD, 304 and directory redirect derives policy from its selected, currently eligible publication. Errors and non-HTML assets keep the strict CSP. The shared asset cache contains bytes, not CSP authority; publications sharing one blob cannot inherit one another's policy. Existing admission rechecks remain in force.
Resource ownership
Manifest accounting includes the bounded vector and string capacities. A response policy adds at most 3,456 bytes (64 encoded sources), charged within the existing finite HTTP exchange reservation. No worker, wait queue or permanent owner is added. Policy construction occurs under the existing asset read permit; the response retains that permit until delivery or bounded cleanup completes. The existing response-header bound expands by exactly this finite allowance.
Framework strategy
The maintained Angular 20.3.32 / PrimeNG 20.4.0 fixture uses AOT code, an injected
APP_BASE_HREF, lazy routes and reviewed Aura/Lara runtime styles. Its recorded
style set must equal the committed reviewed list; observing new styles never
automatically authorizes them. Other components, themes or framework versions
require a separate review and a repeat of browser qualification.
Docusaurus 3.10.2 generates English and German sites with deliberate baseUrl
and portable chunk names. An explicit build transformation externalizes owned
classic bootstrap code and the generator's hidden-symbol style. It rejects
unreviewed style attributes, event handlers and base elements. This is trusted
build tooling, not a sanitizer for uploaded HTML. All transformations precede
package identities and the original/transformed page identities are recorded.
Verification
Shared schema/native fixtures cover valid, empty, excessive, malformed and inapplicable hash lists. Native HTTP tests cover the operator opt-in, equal-byte publications with different policy, shared cache reuse, HEAD, 304, redirects and strict errors. The maintained CI workflow signs all four actual framework builds, publishes them to a native node and drives Chromium through lazy routes, theme changes, both locales and mounted redirects. It checks exact HTML bytes and denial of unlisted inline script/style, foreign resources and base injection. Disabling the host setting and restarting must deny the Angular HTML while continuing to serve the Docusaurus publications. Build success alone is never reported as native browser qualification.
The node reserves eight shared asset read/output owners, enough for the browser's six concurrent HTTP/1 chunk requests. The previous four-owner limit could reject ordinary cold Docusaurus loads. The 32 MiB shared payload cache and immediate exhaustion rejection remain fixed; a retained-output test fills all eight owners and verifies the next request cannot queue or fall through to a renderer.