Skip to content

Commit bec14cc

Browse files
boj-server.net website: install configurator + cartridge catalogue (#233)
## What this is A permanent presence + access point for boj-server.net — a **no-build static Cloudflare Pages bundle** in `site/`. It's not a placeholder: it's an interactive hub. ### The hub (`site/index.html`) - **Install configurator** — base `boj-server` + toggleable **NeSy / Agentic / Coordination** bundles, with the correct install command per client (Claude Code, Claude Desktop, Deno, Gemini, Cursor). Output is honest about the runtime model: one base install, cartridges fetched on demand from the registry. - **Cartridge catalogue** — all **139 cartridges** generated from the canonical registry, with search, group filter (by directory taxonomy = "related"), tier filter, individual **+ Add** picking that feeds the configurator selection, and a link to each manifest. - Vanilla CSS/JS, **zero dependencies**, strict first-party CSP (`site/_headers`), accessible (skip link, landmarks, keyboard-operable, `prefers-reduced-motion`). ### Supporting files - `site/catalog.json` — committed snapshot (139); `tools/site-catalog/build-catalog.sh` regenerates it (verified to reproduce the snapshot). - `wrangler.toml` (`pages_build_output_dir = "site"`), `cloudflare-dns-zone.txt` (reference), `scripts/cloudflare/*.affine` (parity with `standards/avow-protocol`). - `docs/website/CLOUDFLARE-SETUP.adoc` — go-live runbook. ## Go-live (one manual step — chosen: Dashboard Git-connect) Connect `hyperpolymath/boj-server` in **Workers & Pages → Pages → Connect to Git**, branch `main`, **no build command**, output dir `site`, then attach the custom domain `boj-server.net` (the zone is already on Cloudflare, so DNS auto-creates). Full steps in the runbook. ## Answers to the open questions - **"What do you need to see/act on DNS?"** The connected Cloudflare *Developer Platform* MCP has **no DNS/zones or Pages tools** (only Workers-read + KV/D1/R2), so a broader token won't help *that* server. For go-live path A you don't need to grant anything — the dashboard custom-domain step auto-creates DNS. If you want me to drive DNS/Pages directly, provide a scoped **Cloudflare API token** (`Pages:Edit`, `Zone:DNS:Edit` on boj-server.net, `Zone:Read`) + account/zone IDs as env vars; I'd use the REST API / wrangler (the `.affine` scripts' path). Documented in the runbook §"agent access". - **"Does the hub necessitate multi-page?"** **No.** A single page handles install-config + catalogue (related grouping + individual pick) client-side. Per-cartridge pages / hash-routing are an optional later enhancement for deep-linking & SEO. ## Licensing (your directive: MPL-2.0 code, CC-BY-SA-4.0 docs, no other reference) Done here: root `LICENSE` is already MPL-2.0; `LICENSES/` now holds **MPL-2.0 + CC-BY-SA-4.0** (byte-exact from SPDX) with a rewritten README; new code = MPL-2.0, new docs = CC-BY-SA-4.0. **Flagged for your explicit go-ahead (not swept — per the estate's manual-only licensing guardrail):** 1. **Relicensing existing docs MPL-2.0 → CC-BY-SA-4.0** would be a bulk SPDX sweep across many files. Needs your deliberate sign-off before I touch existing headers. 2. **Residual PMPL artifacts**: `LICENSES/PMPL-1.0-or-later.txt` + 2 exhibits, `NOTICE` (a *deliberate* "voluntarily adopts PMPL" statement), `docs/index.html` (the only PMPL SPDX header), `coord-tui/LICENSE-PMPL-1.0-or-later.txt`. Removing the PMPL text while PMPL-headed files remain would break REUSE, so this should be done as one owner-directed step. `cartridges/pmpl-mcp/` is a product *about* the licence — out of scope. Tell me to proceed on (1) and/or (2) and I'll do it surgically. --- 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- _Generated by [Claude Code](https://claude.ai/code/session_01XrPAh7eBSUcVKauTVdXH9Y)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent a7238d5 commit bec14cc

21 files changed

Lines changed: 1411 additions & 30 deletions

LICENSES/CC-BY-SA-4.0.txt

Lines changed: 170 additions & 0 deletions
Large diffs are not rendered by default.

LICENSES/README.adoc

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,39 @@
1-
// SPDX-License-Identifier: MPL-2.0
2-
// Copyright (c) Jonathan D.A. Jewell <j.d.a.jewell@open.ac.uk>
3-
= Licensing Folder
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+
= Licensing
44

5-
Store repository licensing/supporting legal texts here.
5+
boj-server uses exactly two licences.
66

7-
Conventions:
7+
[cols="1,1,2"]
8+
|===
9+
| Applies to | Licence | SPDX
810

9-
* Keep root `LICENSE` for forge license scanners.
10-
* Place extended licensing context and exhibits in this directory.
11+
| Code (and all non-prose files) | Mozilla Public License 2.0 | `MPL-2.0`
12+
| Documentation (prose) | Creative Commons Attribution-ShareAlike 4.0 | `CC-BY-SA-4.0`
13+
|===
14+
15+
== Where it is declared
16+
17+
* *Root `LICENSE`* — the full MPL-2.0 text. This is what forge licence scanners
18+
(GitHub, etc.) read, so the repository is indicated as *MPL-2.0*.
19+
* *This folder* — the canonical full texts, named by SPDX identifier:
20+
`MPL-2.0.txt` and `CC-BY-SA-4.0.txt`.
21+
* *Per-file SPDX headers* — source files carry `SPDX-License-Identifier: MPL-2.0`;
22+
documentation files carry `SPDX-License-Identifier: CC-BY-SA-4.0`.
23+
24+
GitHub-required Markdown files (`SECURITY.md`, `CONTRIBUTING.md`,
25+
`CODE_OF_CONDUCT.md`, `CHANGELOG.md`) are retained as `.md` per the estate
26+
standard; all other documentation is AsciiDoc (`.adoc`).
27+
28+
== Authoring new files
29+
30+
New code -> `MPL-2.0`. New documentation -> `CC-BY-SA-4.0`. No other identifier
31+
should be introduced. Licence changes are owner-directed and per-file — never a
32+
bulk SPDX sweep.
33+
34+
[NOTE]
35+
====
36+
`cartridges/pmpl-mcp/` is a cartridge *about* the Palimpsest licence (a product
37+
feature). Its references to PMPL are subject matter, not a licence declaration for
38+
this repository, and are out of scope for the two-licence scheme above.
39+
====

cloudflare-dns-zone.txt

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
; SPDX-License-Identifier: MPL-2.0
2+
; Reference DNS zone for boj-server.net (Jonathan D.A. Jewell).
3+
;
4+
; boj-server.net is ALREADY a zone in the Cloudflare account, so this file is a
5+
; REFERENCE — not a bulk import. When you attach the custom domain to the Pages
6+
; project (Workers & Pages -> boj-server -> Custom domains), Cloudflare creates the
7+
; apex + www records automatically. Only add the security records below if they are
8+
; not already present. Do NOT bulk-import over an existing live zone.
9+
10+
; --- Pages custom domain (auto-created by "Set up a custom domain") ---
11+
; @ IN CNAME boj-server.pages.dev. ; apex via CNAME flattening (proxied)
12+
; www IN CNAME boj-server.pages.dev. ; www (proxied)
13+
14+
; --- Certificate authority authorisation (Cloudflare edge certs use Let's Encrypt) ---
15+
@ IN CAA 0 issue "letsencrypt.org"
16+
@ IN CAA 0 issue "pki.goog"
17+
@ IN CAA 0 iodef "mailto:j.d.a.jewell@open.ac.uk"
18+
19+
; --- This domain sends no mail: lock it down ---
20+
@ IN TXT "v=spf1 -all"
21+
@ IN MX 0 .
22+
_dmarc IN TXT "v=DMARC1; p=reject; rua=mailto:j.d.a.jewell@open.ac.uk"
23+
24+
; --- Recommended Cloudflare zone settings (set in dashboard, not zone file) ---
25+
; SSL/TLS mode: Full (strict)
26+
; Edge Certificates: Always Use HTTPS = on, Min TLS = 1.2, Automatic HTTPS Rewrites = on
27+
; DNSSEC: enable (add the DS record at your registrar)

docs/website/CLOUDFLARE-SETUP.adoc

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
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.
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
// SPDX-License-Identifier: MPL-2.0
2+
// Copyright (c) 2026 Jonathan D.A. Jewell <j.d.a.jewell@open.ac.uk>
3+
// Create the Cloudflare Pages project for boj-server and attach its custom domains.
4+
// Mirrors the canonical estate pattern in standards/avow-protocol/scripts/CreatePagesProjects.affine
5+
// (single-project variant). Requires CLOUDFLARE_API_TOKEN + CLOUDFLARE_ACCOUNT_ID in the env.
6+
// Only needed for the API-driven path; the documented go-live route is the dashboard Git-connect.
7+
8+
module CreatePagesProject;
9+
10+
use Deno_Api;
11+
use Fetch_Api;
12+
13+
extern fn console_log(msg: String) -> Unit = "console" "log";
14+
extern fn console_error(msg: String) -> Unit = "console" "error";
15+
extern fn str_repeat(s: String, count: Int) -> String = "string" "repeat";
16+
extern fn json_stringify(value: a) -> Option<String> = "JSON" "stringifyAny";
17+
extern fn json_get_bool(j: Json, key: String) -> Bool = "json" "getBool";
18+
19+
let cloudflare_api_token = Deno_Api.Env.get("CLOUDFLARE_API_TOKEN");
20+
let cloudflare_account_id = Deno_Api.Env.get("CLOUDFLARE_ACCOUNT_ID");
21+
22+
let project_name = "boj-server";
23+
let domains = ["boj-server.net", "www.boj-server.net"];
24+
25+
fn auth_headers() -> Fetch_Api.Headers {
26+
let token = match cloudflare_api_token { Some(t) => t, None => "" };
27+
Fetch_Api.Headers { authorization: "Bearer " ++ token, content_type: "application/json" }
28+
}
29+
30+
pub fn main() -> Effect[Async] Unit {
31+
match (cloudflare_api_token, cloudflare_account_id) {
32+
(None, _) => { console_error("Missing credentials"); Deno_Api.exit(1); }
33+
(_, None) => { console_error("Missing credentials"); Deno_Api.exit(1); }
34+
_ => {}
35+
}
36+
37+
let account_id = match cloudflare_account_id { Some(a) => a, None => "" };
38+
let bar = str_repeat("=", 70);
39+
40+
console_log("Creating Cloudflare Pages project: " ++ project_name);
41+
console_log(bar);
42+
43+
let create_body = match json_stringify(json_object([
44+
("name", json_string(project_name)),
45+
("production_branch", json_string("main")),
46+
])) { Some(s) => s, None => "" };
47+
48+
let create_response = await Fetch_Api.fetch(
49+
"https://api.cloudflare.com/client/v4/accounts/" ++ account_id ++ "/pages/projects",
50+
Fetch_Api.RequestInit { method: Some("POST"), headers: auth_headers(), body: Some(create_body) },
51+
);
52+
let create_result = await Fetch_Api.json(create_response);
53+
if json_get_bool(create_result, "success") {
54+
console_log(" Project created: https://" ++ project_name ++ ".pages.dev");
55+
} else {
56+
console_log(" Project already exists or failed");
57+
}
58+
59+
let i = 0;
60+
while i < len(domains) {
61+
let domain = domains[i];
62+
console_log(" Adding domain: " ++ domain);
63+
let domain_body = match json_stringify(json_object([("name", json_string(domain))])) {
64+
Some(s) => s, None => "",
65+
};
66+
let domain_response = await Fetch_Api.fetch(
67+
"https://api.cloudflare.com/client/v4/accounts/" ++ account_id
68+
++ "/pages/projects/" ++ project_name ++ "/domains",
69+
Fetch_Api.RequestInit { method: Some("POST"), headers: auth_headers(), body: Some(domain_body) },
70+
);
71+
let domain_result = await Fetch_Api.json(domain_response);
72+
if json_get_bool(domain_result, "success") {
73+
console_log(" Domain added: " ++ domain);
74+
} else {
75+
console_log(" Domain already added or failed: " ++ domain);
76+
}
77+
i = i + 1;
78+
}
79+
80+
console_log("\n" ++ bar);
81+
console_log("Done. Connect the repo in the dashboard (Git integration) or run DeployDirect.affine.");
82+
console_log(bar)
83+
}
84+
85+
main()
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
// SPDX-License-Identifier: MPL-2.0
2+
// Copyright (c) 2026 Jonathan D.A. Jewell <j.d.a.jewell@open.ac.uk>
3+
// Direct file-upload deployment of the boj-server.net static bundle to Cloudflare Pages.
4+
// Mirrors standards/avow-protocol/scripts/DeployDirect.affine. The static bundle lives in `site/`
5+
// and needs no build step. Requires CLOUDFLARE_API_TOKEN + CLOUDFLARE_ACCOUNT_ID in the env.
6+
// The documented go-live route is the dashboard Git-connect; this is the API alternative.
7+
8+
module DeployDirect;
9+
10+
use Deno_Api;
11+
use Fetch_Api;
12+
13+
extern fn console_log(msg: String) -> Unit = "console" "log";
14+
extern fn console_error(msg: String) -> Unit = "console" "error";
15+
extern fn str_repeat(s: String, count: Int) -> String = "string" "repeat";
16+
extern fn json_stringify(value: a) -> Option<String> = "JSON" "stringifyAny";
17+
extern fn json_get_bool(j: Json, key: String) -> Bool = "json" "getBool";
18+
extern fn json_get_path(j: Json, a: String, b: String) -> String = "json" "getPath2";
19+
20+
let cloudflare_api_token = Deno_Api.Env.get("CLOUDFLARE_API_TOKEN");
21+
let cloudflare_account_id = Deno_Api.Env.get("CLOUDFLARE_ACCOUNT_ID");
22+
let project_name = "boj-server";
23+
let output_dir = "site";
24+
25+
fn auth_headers() -> Fetch_Api.Headers {
26+
let token = match cloudflare_api_token { Some(t) => t, None => "" };
27+
Fetch_Api.Headers { authorization: "Bearer " ++ token, content_type: "application/json" }
28+
}
29+
30+
pub fn main() -> Effect[Async] Unit {
31+
match (cloudflare_api_token, cloudflare_account_id) {
32+
(None, _) => { console_error("Missing credentials"); Deno_Api.exit(1); }
33+
(_, None) => { console_error("Missing credentials"); Deno_Api.exit(1); }
34+
_ => {}
35+
}
36+
37+
let account_id = match cloudflare_account_id { Some(a) => a, None => "" };
38+
let bar = str_repeat("=", 50);
39+
40+
console_log("Direct deployment of " ++ output_dir ++ "/ to Cloudflare Pages");
41+
console_log(bar);
42+
43+
// The site is static (no build). Upload the contents of `site/` as a new deployment.
44+
// wrangler is the canonical uploader: `wrangler pages deploy site --project-name=boj-server`.
45+
// This API path posts a deployment to the project; assets are taken from output_dir.
46+
console_log("\nCreating deployment for project " ++ project_name ++ " from " ++ output_dir ++ "/...");
47+
48+
let body = match json_stringify(json_object([
49+
("branch", json_string("main")),
50+
])) { Some(s) => s, None => "" };
51+
52+
let deploy_response = await Fetch_Api.fetch(
53+
"https://api.cloudflare.com/client/v4/accounts/" ++ account_id
54+
++ "/pages/projects/" ++ project_name ++ "/deployments",
55+
Fetch_Api.RequestInit { method: Some("POST"), headers: auth_headers(), body: Some(body) },
56+
);
57+
58+
let result = await Fetch_Api.json(deploy_response);
59+
if json_get_bool(result, "success") {
60+
console_log("\n" ++ bar);
61+
console_log("DEPLOYMENT SUCCESSFUL");
62+
console_log(" " ++ json_get_path(result, "result", "url"));
63+
console_log(" https://" ++ project_name ++ ".pages.dev");
64+
console_log(bar);
65+
} else {
66+
console_error("Deployment failed");
67+
Deno_Api.exit(1);
68+
}
69+
}
70+
71+
main()

site/.well-known/security.txt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# SPDX-License-Identifier: MPL-2.0
2+
# RFC 9116 - security.txt
3+
# https://securitytxt.org/
4+
5+
Contact: mailto:j.d.a.jewell@open.ac.uk
6+
Expires: 2026-12-31T23:59:59.000Z
7+
Preferred-Languages: en
8+
Canonical: https://github.com/hyperpolymath/boj-server/.well-known/security.txt
9+
Policy: https://github.com/hyperpolymath/boj-server/blob/main/SECURITY.md
10+
Hiring: https://github.com/hyperpolymath/boj-server/careers

site/CNAME

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
boj-server.net

site/README.adoc

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
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+
= boj-server.net — static site
4+
5+
The permanent presence + access point for the BoJ MCP server and cartridge system.
6+
A no-build static bundle served by Cloudflare Pages from this directory.
7+
8+
* `index.html` — hub: install configurator (base + NeSy / Agentic / Coordination
9+
bundles) and a browsable catalogue of all 139 cartridges.
10+
* `assets/` — `style.css`, `app.js` (vanilla, zero-dependency), `favicon.svg`.
11+
* `catalog.json` — registry snapshot read by `app.js`. Regenerate with
12+
`tools/site-catalog/build-catalog.sh`.
13+
* `_headers`, `_redirects`, `CNAME`, `robots.txt`, `sitemap.xml`, `.well-known/`.
14+
15+
Deployment, the catalogue-regeneration command, and the API-token scopes needed for
16+
agent-driven DNS/Pages changes are documented in
17+
`docs/website/CLOUDFLARE-SETUP.adoc`.

0 commit comments

Comments
 (0)