diff --git a/src/.hugo_build.lock b/src/.hugo_build.lock
new file mode 100644
index 0000000..e69de29
diff --git a/src/README.md b/src/README.md
new file mode 100644
index 0000000..5731372
--- /dev/null
+++ b/src/README.md
@@ -0,0 +1,21 @@
+# cspresso.cafe (Hugo)
+
+This directory is a Hugo conversion of the original static HTML for **cspresso.cafe**.
+
+## Local dev
+
+```bash
+hugo server
+```
+
+## Build
+
+```bash
+hugo
+```
+
+The generated site will be in `public/`.
+
+Notes:
+- `uglyURLs = true` so the output paths match the original `*.html` URLs (e.g. `docs.html`).
+- `recipes.html` includes an alias for `examples.html`.
diff --git a/src/content/_index.html b/src/content/_index.html
new file mode 100644
index 0000000..532db27
--- /dev/null
+++ b/src/content/_index.html
@@ -0,0 +1,308 @@
+---
+title: "cspresso - Brew a Content Security Policy"
+description: "cspresso crawls a site with headless Chromium (Playwright) and emits a draft Content-Security-Policy based on what loads."
+---
+
+
+
+
+
+
Brew a Content Security Policy
+
Turn real page loads into a CSP you can ship.
+
+ cspresso crawls up to N same‑origin pages with headless Chromium (Playwright), watches the assets that load,
+ and emits a draftContent-Security-Policy header.
+
+ A header line you can paste into your vhost, or parseable info with --json
+
+
+
+ Tip: if an existing CSP might block loads during analysis, add --bypass-csp.
+
+
+
+
+
+
+
+
# Evaluate a candidate CSP (Report-Only) and fail CI on violations
+cspresso https://mig5.net \
+ --bypass-csp \
+ --evaluate "default-src 'self'; img-src 'none';" \
+ --json
+
+{
+ [...]
+ "violations": [
+ {
+ "console": true,
+ "disposition": "report",
+ "documentURI": "https://mig5.net/",
+ "text": "Loading the image 'https://mig5.net/logo.svg' violates the following Content Security Policy directive: \"img-src 'none'\". The policy is report-only, so the violation has been logged but no further action has been taken.",
+ "type": "info"
+ },
+ {
+ "console": true,
+ "disposition": "report",
+ "documentURI": "https://mig5.net/static/mig5.asc",
+ "text": "Applying inline style violates the following Content Security Policy directive 'default-src 'self''. Either the 'unsafe-inline' keyword, a hash ('sha256-4Su6mBWzEIFnH4pAGMOuaeBrstwJN4Z3pq/s1Kn4/KQ='), or a nonce ('nonce-...') is required to enable inline execution. Note that hashes do not apply to event handlers, style attributes and javascript: navigations unless the 'unsafe-hashes' keyword is present. Note also that 'style-src' was not explicitly set, so 'default-src' is used as a fallback. The policy is report-only, so the violation has been logged but no further action has been taken.",
+ "type": "info"
+ }
+ ]
+}
+
+# exit code: 1 if violations detected
+
+
+
+
+
+
+
+
+
+
+
+
+
+
How it works
+
+ cspresso lets the browser do the hard part: execute the page, watch what it loads, and distill origins into directives.
+
+
+
+
+
+
+
+
Crawl
+
Visit up to --max-pages same-origin pages and let the app’s JS run.
+
+
+
+
+
+
Observe
+
Track scripts, styles, images, fonts, frames, and “connect-like” requests.
+
+
+
+
+
+
Draft a CSP
+
Emit a baseline policy plus observed origins per directive.
+
+
+
+
+
+
Evaluate
+
Inject a candidate as Report‑Only and capture violations with an exit code for CI.
+
+
+
+
+
+ Inline script/style is tricky: nonces must be generated per response, and hashes must match bytes exactly.
+ cspresso reports what it sees, but you should review and tighten before enforcing.
+
+
+
+
+
+
+
+
+
+
+
Popular flags
+
A few options that tend to matter in real deployments.
+
+
+
+
+
+
--bypass-csp
+
Strip existing CSP response headers so they don’t block discovery or evaluation.
+
+
+
+
--evaluate
+
Inject a candidate policy as Report‑Only and exit 1 if any violations are detected.
+
+
+
+
--include-sourcemaps
+
Heuristically discover sourcemap origins and add them to connect-src.
+
+
+
+
--upgrade-insecure-requests
+
Emit upgrade-insecure-requests in the proposed policy.
+
+
+
+
--browsers-path
+
Control where Playwright installs Chromium (handy for AppImage/CI caches).
+
+
+
+
--json
+
Machine-readable output: CSP, visited URLs, notes, and evaluation violations.
+
+
+
+
+
+
+
+
+
+
Install
+
pipx, pip, Poetry, or a standalone AppImage from Releases.
# Recommended
+pipx install cspresso
+
+# Or plain pip (use a venv)
+pip install cspresso
+
+
+
+
+
Playwright browsers
+
+ cspresso can auto-install Chromium for Playwright if it isn’t present. By default it installs into ./.pw-browsers
+ for deterministic builds and easy CI caching.
+
+
+
+ Override with --browsers-path or PLAYWRIGHT_BROWSERS_PATH.
+
+
+
+
+
+
+
+
+
+
+
+
poetry add cspresso
+
+
+
+
+
Linux deps
+
+ If Chromium won’t start due to missing libraries, try --with-deps (may require elevated privileges).
+
+ cspresso crawls up to --max-pages same-origin pages in Chromium, observes what loads, and emits a draft CSP.
+
+
+
+
+
Install
+
+
+
+
+
# Recommended
+pipx install cspresso
+
+# Or plain pip (use a venv)
+pip install cspresso
+
+# An AppImage is also available on the
+# git repo Releases page.
+
+
+
+
+
+
Python + Playwright
+
+ You need Python 3.10+ and Playwright’s Chromium. cspresso can auto-install Chromium if missing.
+
+
+
+ Verify Releases artifacts with the mig5 key:
+ https://mig5.net/static/mig5.asc
+ (fingerprint 00AE817C24A10C2540461A9C1D7CDE0234DB458D).
+
+
+
+
+
+
+
+
Run
+
+
+
cspresso https://example.com --max-pages 10
+
+
+ The crawl stays on the same origin (it follows internal links) and will wait for networkidle plus a small “settle”
+ delay to catch late fetches.
+
+
+
+
+
Output
+
+ Default output prints visited URLs as comments and then the proposed header line.
+ Use --json for machine-readable output.
+
+
+
+
cspresso https://example.com --json
+
+
+
+
+
Inline scripts & styles
+
+
Why this is hard
+
+ If the page uses nonces, you must generate a new nonce per HTML response and inject it into both the CSP header and the HTML tags.
+ Hashes only work when inline content is stable byte-for-byte. For style="..." and on* attributes,
+ browsers require 'unsafe-hashes' for hashes to apply. Not to worry, cspresso will detect these and generate the hashes
+ in its response, offering them as style-src-attr options or with unsafe-hashes for older browsers.
+
+
+
+
+
+
Evaluate (Report-Only)
+
+ Provide a CSP string to --evaluate and cspresso will inject it as
+ Content-Security-Policy-Report-Only on HTML responses during the crawl. If any violations are detected, it exits with code 1.
+
+
This is a great way of testing a cspresso-brewed CSP before actually adding it to your site.
+
diff --git a/src/content/evaluate.html b/src/content/evaluate.html
new file mode 100644
index 0000000..5fd64fd
--- /dev/null
+++ b/src/content/evaluate.html
@@ -0,0 +1,100 @@
+---
+title: "cspresso Evaluate"
+description: "Evaluate a candidate Content-Security-Policy by injecting it as CSP Report-Only and failing if violations are detected."
+---
+
+
+
+
Evaluate
+
Test a CSP before you enforce it
+
+ Use --evaluate to inject a candidate policy as Content-Security-Policy-Report-Only,
+ collect violations, and fail the run if anything would break.
+
+ Tip: keep your CSP string quoted; it usually contains spaces and semicolons.
+
+
+
+
+
+
+
+
Why --bypass-csp matters
+
+ If the target site already sets an enforcing CSP, it can block loads and change runtime behaviour.
+ That can hide potential violations in your candidate policy. Using --bypass-csp strips existing CSP headers
+ on HTML responses during the crawl.
+
+
+
Safety note
+
+ Bypassing CSP means you’re letting the page execute without those protections. Run evaluation only on sites you trust,
+ or in a sandboxed environment.
+
+ If you cache Playwright browsers, set --browsers-path to a persistent directory.
+
+
+
+
+
Troubleshooting
+
+
+
+
Sourcemaps causing connect-src noise
+
+ DevTools often fetches sourcemaps even when headless browsing doesn’t. If you want to model those requests,
+ use --include-sourcemaps to add sourcemap origins to connect-src.
+
+
+
+
+
+
Non-HTML crawled resources
+
+ If your site has downloadable files on the same origin, consider --ignore-non-html to avoid edge cases
+ like browser “word-wrap” injected styles affecting hashes.
+
+
+
+
+
+
+
diff --git a/src/content/recipes.html b/src/content/recipes.html
new file mode 100644
index 0000000..4a5efab
--- /dev/null
+++ b/src/content/recipes.html
@@ -0,0 +1,87 @@
+---
+title: "cspresso Recipes"
+description: "cspresso recipes: common commands for crawling, debugging, sourcemaps, AppImage usage, and CI evaluation."
+aliases:
+ - "examples.html"
+---
+
+
+
+
Recipes
+
Practical workflows
+
A handful of commands that cover most real-world cspresso usage.
+
+
+
+
+
Draft a CSP
+
+
+
cspresso https://example.com --max-pages 10
+
+
+ Start here, then audit the output. Crawls won’t cover every flow (auth-only pages, conditional loads, A/B tests, etc.).
+
+ Exits 1 if the candidate policy would be violated - great for PR checks.
+
+
+
+
+
diff --git a/src/content/security.html b/src/content/security.html
new file mode 100644
index 0000000..e03a402
--- /dev/null
+++ b/src/content/security.html
@@ -0,0 +1,73 @@
+---
+title: "cspresso Security"
+description: "Security notes for cspresso: it runs a real browser; what bypassing existing CSP means; and how to use it safely."
+---
+
+
+
+
Security
+
Security notes
+
+ cspresso runs a real browser. That’s the point - and also the main safety consideration.
+
+
+
+
+
+
+
What cspresso does
+
+ cspresso launches Chromium via Playwright and loads your target pages. The site’s JavaScript and CSS execute like a normal browser session.
+ Network requests are observed to build a draft CSP, and (optionally) a candidate policy is injected as Report‑Only to capture violations.
+
+
+
+
+
About --bypass-csp
+
+
It can change risk
+
+ Bypassing CSP strips existing CSP headers on HTML responses. This option is provided in order to avoid the outcome of the rendering negatively influencing what cspresso thinks a good CSP should be.
+ If a site is compromised, CSP might have been limiting what injected scripts could do (that's the whole point of a CSP!); bypassing removes that layer.
+
+
+
+ Recommendation: only use --bypass-csp on sites you trust, or run cspresso inside a sandboxed environment (VM/container).
+
+
+
+
+
+
Data handling
+
+ cspresso’s primary output is a policy string and metadata (visited URLs, notes, and - in evaluation mode - detected violations).
+ Treat the output as sensitive if your site URLs or CSP reveal internal endpoints.
+
+
+
+
+
Hardening tips
+
+
+
Prefer CI / disposable environments
+
Running in CI makes it easy to isolate and to cache Chromium via --browsers-path.
+
+
+
Limit crawl scope
+
Keep --max-pages small and start from a stable landing page to reduce surprises.
+
+
+
Review before enforcing
+
cspresso emits a draft. Tighten directives (especially script-src/connect-src) and consider nonces.