Design: Documentation site on Cloudflare Workers (static assets, PR previews, fork-safe CI)
Date: 2026-04-09 Status: Draft (brainstorm consolidated)
Note: Filename retains
cloudflare-pagesfor link stability; the implementation uses Workers + static assets (migration guide), notwrangler pages deploy.
Context
The repository publishes a static documentation site. Today the primary surface is the interactive document graph in web/public/index.html; the site will likely grow (more pages or a Vite-built tree under web/). CI packs _bundle/public/ (static) plus _bundle/worker/ (from the build checkout) into artifact site. Deploy checks out the default branch only (trusted cloudflare_site/wrangler.toml), downloads the artifact to _bundle/, copies only public/ and worker/ into cloudflare_site/ (rejecting any other top-level paths so wrangler.toml cannot be injected from the zip), then runs Wrangler.
Implemented: .github/workflows/site-build.yml and .github/workflows/site-deploy.yml use the build → artifact → workflow_run deploy split. Production uses wrangler deploy (Worker + static assets). Pull requests use wrangler versions upload --preview-alias … so previews get a stable *.workers.dev URL without promoting a new production version. The previous GitHub Pages workflow has been removed.
Operator setup: Cloudflare Worker, API token with Workers permissions, and GitHub Actions secrets/variables are required; see docs/site-deployment.md.
Goals
- Deploy this documentation site to Cloudflare Workers (static assets binding), not GitHub Pages and not the legacy Pages-only upload path.
- Per-PR previews, including fork PRs, using the two-workflow pattern: unprivileged build + artifact, privileged deploy.
- Integrate with GitHub Deployments using
site-previewandsite-production, withenvironment_urlpointing at the Worker URL (*.workers.devor custom domain). - Surface preview links via Deployments and a single upserted PR comment.
- Use stable workflow and artifact names centered on site as content grows beyond the mindmap.
- Roll out in two phases (fork validation, then upstream). Custom domains (e.g.
konflux.sh) attach to the Worker when DNS is ready.
Non-goals
- Rewriting site application code beyond packaging.
- Workers Builds (Cloudflare-hosted CI) as the source of truth—GitHub Actions remains the deploy driver unless the project later opts in.
- OIDC to Cloudflare in the initial design; API token in secrets is sufficient.
Approach comparison (condensed)
| Approach | Idea | Verdict |
|---|---|---|
A — workflow_run + artifact | Secretless build uploads artifact; privileged workflow deploys. | Chosen. Fork-safe. |
B — pull_request_target | Deploy with base-repo secrets on PR. | Rejected for untrusted build steps. |
| C — External bot | Webhook-driven deploy. | Rejected for this static site. |
Deploy tooling: Wrangler 4.x via cloudflare/wrangler-action, wrangler deploy for push to main, wrangler versions upload --preview-alias for pull_request previews (preview URLs).
Architecture
Workflow split
- Build (
site-build.yml):pull_request+pushtomain(nopathsfilter in current fork—runs on every PR/push; may be narrowed later). Producessiteartifact with_bundle/public/and_bundle/worker/(Worker sources from the build checkout). - Deploy (
site-deploy.yml): On successful Build Site, checkout default branch (trustedwrangler.tomlonly), download artifact to_bundle/, copypublic/+worker/intocloudflare_site/(only those subtrees; no wholesale extract into the Wrangler project root), then:push:wrangler deploy --name=<var>→ production.pull_request:wrangler versions upload(asset config fromwrangler.tomlonly) with--preview-alias pr-<pr-number>(falls back toworkflow_run.idonly when multiple open PRs share the same head) → preview only.
Permissions: Build: contents: read only. Deploy: actions: read, deployments: write, pull-requests: write. No pages: write for this site.
cloudflare_site/wrangler.toml
[assets].directory:./public(filled in CI).not_found_handling = "single-page-application"for the single-page mindmap.workers_dev = true,preview_urls = true.main = "worker/src/index.ts"— minimal pass-through toASSETStoday; future OAuth/API logic extends this file without changing deploy wiring.
GitHub environment names
site-preview— PR uploads;transient_environment: true.site-production— production deploy;production_environment: true.
Cloudflare
- One Worker; GitHub variable
CLOUDFLARE_PROJECT_NAMEholds the Worker name (name kept for backward compatibility). - API token: Workers (and Account Read if needed), not Pages-only.
Domains
- Default
*.workers.dev; custom domains on the Worker when ready (konflux.shupstream).
Security (fork PRs)
Same as before: minimal auditable build; deploy trusts artifacts from the known build workflow.
Removal of GitHub Pages
Done. Disable Settings → Pages if unused.
Rollout phases
Phase 1 — Fork
- Configure Worker + token + secrets; validate production and fork PR preview + comment.
Phase 2 — Upstream
- Land workflows + runbook; org secrets; Worker + optional
konflux.shon Worker routes.
Operator documentation
Testing and acceptance
mainpush: production Worker updates;site-productiondeployment with correct URL.- PR: preview URL on
workers.dev,site-preview, PR comment; fork build has no secrets. - Wrangler: pinned 4.30.0 in workflow; preview alias requires ≥ 4.21.0.
Spec self-review
- Consistency:
site-preview/site-production; Worker + static assets; artifactsite. - Scope: CI and packaging only.
- Workers vs Pages: Implementation is Workers; filename is historical.
