Skip to content

Commit 33e8cd5

Browse files
committed
docs: correct the CSP contrast on the docs site too
The deployment page carried the same framework-versus-app nonce contrast the source comment now denies, in nearly the same words as the blog sentence already fixed. style-src has no nonce source, so unsafe-inline admits every inline style including WebJs's own head rule, and the page now says that before listing what makes the allowance hard to give up. The global-error guidance said an inline style there must carry the nonce. Under the default policy that is inert for style and load-bearing only for script, so both places now say which is which. The scaffold-sync rule went too far the other way. Several cards do carry a prune note, and they are the ones where removal spans paths or another card depends on them, so the rule now describes that shape instead of forbidding it. AGENTS.md also documents selective pruning, which a blanket prohibition contradicted.
1 parent 2139f5f commit 33e8cd5

4 files changed

Lines changed: 11 additions & 7 deletions

File tree

.claude/skills/webjs-scaffold-sync/SKILL.md

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -210,10 +210,14 @@ Run the mandatory generate + boot + check verifications in PARALLEL when more th
210210
test, the counterfactual).
211211
6. Respect the prose-punctuation invariant (#11) in every comment and doc, and
212212
keep each demo densely commented (a header stating the webjs concept + the
213-
why, and inline comments on the non-obvious idiom). Do NOT add a per-demo
214-
prune line: `npm run gallery:clear` sheds the whole gallery in one step, so
215-
the full-stack demos deliberately carry no such comment. The scaffold teaches
216-
by its comments, and a thin demo is a bug.
213+
why, and inline comments on the non-obvious idiom). Add a prune note where
214+
removal is NOT obvious, which is what the demos that carry one do: a card
215+
spanning several paths, or one that another card depends on (the auth and
216+
server-actions cards name each other). A self-contained demo needs none,
217+
since `AGENTS.md` tells the app-building agent to delete the
218+
`app/features/<x>` route and its `modules/<x>` and `npm run gallery:clear`
219+
sheds the lot. The scaffold teaches by its comments, and a thin demo is a
220+
bug.
217221

218222
## Audit-mode procedure (sweep the scaffold for drift)
219223

AGENTS.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -290,7 +290,7 @@ Default export receives `{ children, params, searchParams, url }`, must embed `c
290290

291291
### Error / loading / metadata routes
292292

293-
`error.{js,ts}` default-exports `({ error, ...ctx }) => TemplateResult` (catches sibling-page / deeper render errors, innermost wins, prod sends only `error.message`). `loading.{js,ts}` wraps the sibling page in `Suspense` with an immediately-flushed fallback. `forbidden.{js,ts}` / `unauthorized.{js,ts}` render the nearest 403 / 401 boundary for a thrown `forbidden()` / `unauthorized()` (#848). Two **root-only** boundaries (`app/` root exactly): `global-error.{js,ts}` is the app-wide catch-all tried after the nested `error` boundaries are exhausted, and it renders its **own** `<!doctype><html><body>` document (returned verbatim, since a root-layout failure is when it fires). Because it is returned verbatim (no framework `<head>` splice), it ships **no importmap or boot script**, so keep it **static HTML with no components/hydration** (a last-resort page must not depend on the module system that may have just failed); under an opt-in CSP, an inline `<style>`/`<script>` in it must carry the nonce via `cspNonce()`. `global-not-found.{js,ts}` renders for an unmatched-anywhere URL when no `not-found` matches. `not-found` is nearest-wins from the throwing page's chain (#848 fixed the prior root-only behavior). Metadata routes (`sitemap`, `robots`, `manifest`, `icon`, `apple-icon`, `opengraph-image`, `twitter-image`) live at app root or static segments only and default-export a possibly-async function; `sitemap(entries)` / `sitemapIndex(sitemaps)` from `@webjsdev/server` serialize spec-valid XML.
293+
`error.{js,ts}` default-exports `({ error, ...ctx }) => TemplateResult` (catches sibling-page / deeper render errors, innermost wins, prod sends only `error.message`). `loading.{js,ts}` wraps the sibling page in `Suspense` with an immediately-flushed fallback. `forbidden.{js,ts}` / `unauthorized.{js,ts}` render the nearest 403 / 401 boundary for a thrown `forbidden()` / `unauthorized()` (#848). Two **root-only** boundaries (`app/` root exactly): `global-error.{js,ts}` is the app-wide catch-all tried after the nested `error` boundaries are exhausted, and it renders its **own** `<!doctype><html><body>` document (returned verbatim, since a root-layout failure is when it fires). Because it is returned verbatim (no framework `<head>` splice), it ships **no importmap or boot script**, so keep it **static HTML with no components/hydration** (a last-resort page must not depend on the module system that may have just failed); under an opt-in CSP, an inline `<script>` in it must carry the nonce via `cspNonce()` (an inline `<style>` needs one only if you tighten `style-src`, since the default allows inline style outright). `global-not-found.{js,ts}` renders for an unmatched-anywhere URL when no `not-found` matches. `not-found` is nearest-wins from the throwing page's chain (#848 fixed the prior root-only behavior). Metadata routes (`sitemap`, `robots`, `manifest`, `icon`, `apple-icon`, `opengraph-image`, `twitter-image`) live at app root or static segments only and default-export a possibly-async function; `sitemap(entries)` / `sitemapIndex(sitemaps)` from `@webjsdev/server` serialize spec-valid XML.
294294

295295
### Route handlers (`app/**/route.{js,ts}`)
296296

website/app/docs/deployment/page.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ npm run start -- --port 8080</code-block>
7979
<code-block>&#123;
8080
"webjs": &#123; "csp": true &#125;
8181
&#125;</code-block>
82-
<p><code>true</code> turns on a strict-by-default policy: <code>script-src 'nonce-&lt;minted&gt;' 'strict-dynamic' 'self' https:</code> plus <code>default-src 'self'</code>, <code>object-src 'none'</code>, <code>frame-ancestors 'self'</code>, and an inline-style allowance. That last one is there for the styles your app puts in the document, which do not carry a nonce: a light-DOM component's <code>&lt;style&gt;</code> block in <code>render()</code>, a shadow component's <code>static styles</code>, an inline <code>&lt;style&gt;</code> written by a page or layout, and <code>style="…"</code> attributes, which no nonce can reach at all since nonces apply to elements rather than attributes. The framework's own head style rule carries the nonce. On every request the framework mints a CSPRNG nonce (16 random bytes, base64), stamps it on every inline <code>&lt;script&gt;</code>, the importmap, and the <code>modulepreload</code> hints it emits (the same value <code>cspNonce()</code> returns during SSR), and sets the header carrying that exact nonce. The nonce on the header and the nonce on the scripts are one minted value, so there is no drift, and it changes every request.</p>
82+
<p><code>true</code> turns on a strict-by-default policy: <code>script-src 'nonce-&lt;minted&gt;' 'strict-dynamic' 'self' https:</code> plus <code>default-src 'self'</code>, <code>object-src 'none'</code>, <code>frame-ancestors 'self'</code>, and an inline-style allowance. That last one is needed because <code>style-src</code> carries no nonce source at all, so every inline style is admitted by <code>'unsafe-inline'</code>, the framework's own head rule included. What keeps it hard to give up is the style your app puts in the document: a light-DOM component's <code>&lt;style&gt;</code> block in <code>render()</code>, a shadow component's <code>static styles</code>, an inline <code>&lt;style&gt;</code> written by a page or layout, and <code>style="…"</code> attributes, which no nonce could reach even if the directive honoured one, since nonces apply to elements rather than attributes. On every request the framework mints a CSPRNG nonce (16 random bytes, base64), stamps it on every inline <code>&lt;script&gt;</code>, the importmap, and the <code>modulepreload</code> hints it emits (the same value <code>cspNonce()</code> returns during SSR), and sets the header carrying that exact nonce. The nonce on the header and the nonce on the scripts are one minted value, so there is no drift, and it changes every request.</p>
8383
<p>For a custom policy, give an object. <code>directives</code> is merged over the strict defaults (override one directive without restating the rest; a <code>null</code> value drops a default directive), and <code>reportOnly: true</code> emits <code>Content-Security-Policy-Report-Only</code> for a staged rollout:</p>
8484
<code-block>&#123;
8585
"webjs": &#123;

website/app/docs/error-handling/page.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ export default function GlobalError({ error }: { error: Error }) {
101101
${'`'};
102102
}</code-block>
103103
104-
<p>Keep <code>global-error.ts</code> static (no components / hydration): it is returned verbatim with no importmap or boot script, so it must not depend on the module system that may have just failed. Under an opt-in CSP, give any inline <code>&lt;style&gt;</code> the <code>cspNonce()</code>.</p>
104+
<p>Keep <code>global-error.ts</code> static (no components / hydration): it is returned verbatim with no importmap or boot script, so it must not depend on the module system that may have just failed. Under an opt-in CSP, give any inline <code>&lt;script&gt;</code> the <code>cspNonce()</code>. An inline <code>&lt;style&gt;</code> needs one only if you tighten <code>style-src</code>, since the default policy allows inline style outright.</p>
105105
106106
<p><code>global-not-found.ts</code> renders for a URL that matches nothing anywhere, when no <code>not-found.ts</code> applies.</p>
107107

0 commit comments

Comments
 (0)