|
| 1 | +// SPDX-License-Identifier: CC-BY-SA-4.0 |
| 2 | +// SPDX-FileCopyrightText: 2026 Jonathan D.A. Jewell (hyperpolymath) <j.d.a.jewell@open.ac.uk> |
| 3 | += Deploying boj-server.net on Cloudflare Pages |
| 4 | +:toc: preamble |
| 5 | +:icons: font |
| 6 | + |
| 7 | +The public presence at https://boj-server.net[boj-server.net] is a *no-build static |
| 8 | +bundle* in the `site/` directory, served by Cloudflare Pages. It is both a permanent |
| 9 | +presence and an access point: an install configurator (base + NeSy / Agentic / |
| 10 | +Coordination bundles), and a browsable catalogue of all {counter:cart}139 cartridges |
| 11 | +generated from the canonical registry. |
| 12 | + |
| 13 | +== What the site is |
| 14 | + |
| 15 | +[cols="1,3"] |
| 16 | +|=== |
| 17 | +| `site/index.html` | The hub: hero, install configurator, catalogue browser, capabilities. |
| 18 | +| `site/assets/` | `style.css`, `app.js` (vanilla, zero-dependency), `favicon.svg`. |
| 19 | +| `site/catalog.json` | Snapshot of the registry (139 cartridges). Regenerate — see below. |
| 20 | +| `site/_headers` | Strict security headers (HSTS, CSP, etc.) in Pages format. |
| 21 | +| `site/_redirects` | `www` -> apex canonicalisation + convenience deep-links. |
| 22 | +| `site/CNAME` | Custom-domain marker (`boj-server.net`). |
| 23 | +| `site/.well-known/` | `security.txt` (RFC 9116), mirrored from the repo root. |
| 24 | +|=== |
| 25 | + |
| 26 | +No framework, no Node/npm, no build command. Cloudflare serves `site/` as-is. |
| 27 | + |
| 28 | +== Go-live path A — Dashboard Git-connect (recommended, chosen) |
| 29 | + |
| 30 | +`boj-server.net` is already a zone in the Cloudflare account, so this is a one-time, |
| 31 | +~5-minute dashboard task. No secrets are stored in the repo or CI. |
| 32 | + |
| 33 | +. *Create the project.* Dashboard -> *Workers & Pages* -> *Create application* -> |
| 34 | + *Pages* -> *Connect to Git* -> authorise and select `hyperpolymath/boj-server`. |
| 35 | +. *Build settings.* |
| 36 | + * Production branch: `main` |
| 37 | + * Framework preset: *None* |
| 38 | + * Build command: *(leave empty)* |
| 39 | + * Build output directory: `site` |
| 40 | + * Root directory: `/` |
| 41 | ++ |
| 42 | +Click *Save and Deploy*. The first deploy lands at `https://boj-server.pages.dev`. |
| 43 | +. *Attach the custom domain.* Project -> *Custom domains* -> *Set up a custom domain* |
| 44 | + -> `boj-server.net` -> *Continue*. Because the zone is already on Cloudflare, the |
| 45 | + apex record is created automatically (CNAME-flattened to the Pages target). Repeat |
| 46 | + for `www.boj-server.net` (the `_redirects` rule sends it to the apex). |
| 47 | +. *TLS posture.* Zone -> *SSL/TLS* -> *Full (strict)*; *Edge Certificates* -> enable |
| 48 | + *Always Use HTTPS*, *Automatic HTTPS Rewrites*, minimum TLS 1.2. Optionally enable |
| 49 | + *DNSSEC* and add the DS record at the registrar. |
| 50 | + |
| 51 | +That is the entire go-live. Every push to `main` then auto-deploys; pull requests get |
| 52 | +preview URLs for free. |
| 53 | + |
| 54 | +== Go-live path B — API / CLI (only if you want it automated or agent-driven) |
| 55 | + |
| 56 | +Two equivalent options; both need a *Cloudflare API token* and the *account ID*. |
| 57 | + |
| 58 | +`wrangler` (canonical uploader):: |
| 59 | ++ |
| 60 | +[source,bash] |
| 61 | +---- |
| 62 | +export CLOUDFLARE_API_TOKEN=... # scopes below |
| 63 | +export CLOUDFLARE_ACCOUNT_ID=... |
| 64 | +wrangler pages deploy site --project-name=boj-server |
| 65 | +---- |
| 66 | + |
| 67 | +Estate AffineScript scripts (parity with `standards/avow-protocol/scripts/`):: |
| 68 | +`scripts/cloudflare/CreatePagesProject.affine` creates the project + attaches |
| 69 | +`boj-server.net` and `www.boj-server.net`; `scripts/cloudflare/DeployDirect.affine` |
| 70 | +posts a deployment from `site/`. Both read `CLOUDFLARE_API_TOKEN` + |
| 71 | +`CLOUDFLARE_ACCOUNT_ID` from the environment. |
| 72 | + |
| 73 | +[#agent-access] |
| 74 | +== What an agent needs to "see and act" on DNS / Pages |
| 75 | + |
| 76 | +The connected *Cloudflare "Developer Platform" MCP* exposes only Workers (read), |
| 77 | +KV, D1, R2 and Hyperdrive — *no DNS/zones tools and no Pages-project tools*. That is a |
| 78 | +property of that MCP server, not of the token, so a broader token does not add DNS |
| 79 | +tools to it. To let an agent inspect and change DNS / Pages directly, provide a scoped |
| 80 | +*Cloudflare API token* (used against the REST API / `wrangler`, the same path as the |
| 81 | +`.affine` scripts): |
| 82 | + |
| 83 | +[cols="1,2"] |
| 84 | +|=== |
| 85 | +| Account › Cloudflare Pages › *Edit* | create/deploy the Pages project, attach domains |
| 86 | +| Zone › DNS › *Edit* (zone `boj-server.net`)| read/write DNS records |
| 87 | +| Zone › Zone › *Read* | resolve the zone id |
| 88 | +|=== |
| 89 | + |
| 90 | +Plus the *Account ID* and the `boj-server.net` *Zone ID* (Dashboard -> domain -> |
| 91 | +*Overview*, right sidebar). Export as `CLOUDFLARE_API_TOKEN` / `CLOUDFLARE_ACCOUNT_ID`. |
| 92 | +*Note:* for go-live path A none of this is required — attaching the custom domain in |
| 93 | +the dashboard auto-creates the DNS records. |
| 94 | + |
| 95 | +== Verify |
| 96 | + |
| 97 | +[source,bash] |
| 98 | +---- |
| 99 | +curl -sSI https://boj-server.net | grep -iE 'strict-transport|content-security|x-content-type' |
| 100 | +curl -sS https://boj-server.net/catalog.json | jq '.cartridges | length' # -> 139 |
| 101 | +curl -sSI https://boj-server.net/.well-known/security.txt |
| 102 | +---- |
| 103 | + |
| 104 | +== Regenerate the catalogue snapshot |
| 105 | + |
| 106 | +`site/catalog.json` is a committed snapshot. When the registry changes: |
| 107 | + |
| 108 | +[source,bash] |
| 109 | +---- |
| 110 | +# from the boj-server repo root, with boj-server-cartridges checked out alongside |
| 111 | +tools/site-catalog/build-catalog.sh ../boj-server-cartridges site/catalog.json |
| 112 | +---- |
| 113 | + |
| 114 | +It groups by the registry's directory taxonomy (reliable) rather than the free-text |
| 115 | +`domain` field, excludes `templates/`, and honours an explicit `available: false`. |
| 116 | + |
| 117 | +== Notes |
| 118 | + |
| 119 | +* Development happens on branch `claude/awesome-davinci-8afqgy`; open a PR into `main`. |
| 120 | +* `_headers` CSP is strict (`default-src 'self'`) — all script/style/data is |
| 121 | + first-party, so it needs no `'unsafe-inline'`. Keep new assets first-party or widen |
| 122 | + the policy deliberately. |
0 commit comments