Technical Reference
Full technical documentation for the BTC website — stack, infrastructure, and maintenance.
This document covers everything needed to understand, maintain, or hand over the website. It assumes basic comfort with a terminal and Git.
Stack overview
| Layer | Technology |
|---|---|
| Site generator | Hugo v0.158.0 extended |
| Theme | Custom (themes/btc/) |
| CMS | Sveltia CMS (browser-based, GitHub backend) |
| Hosting | Cloudflare Pages |
| CMS OAuth proxy | Cloudflare Worker |
| Member gating | MembershipWorks member lock (mfl.js) |
| Source control | GitHub (Brighton-Tri-Club/brightontri-website) |
The site is fully static — Hugo builds Markdown content into HTML, which Cloudflare Pages serves. Member-only pages are hidden client-side by MembershipWorks’ member-lock script (see below).
Repository
- GitHub org:
Brighton-Tri-Club - Repo:
brightontri-website(private) - Default branch:
main— Cloudflare Pages deploys automatically on every push tomain - Clone URL:
git@github.com:Brighton-Tri-Club/brightontri-website.git(SSH) orhttps://github.com/Brighton-Tri-Club/brightontri-website.git(HTTPS)
The Sveltia CMS commits directly to main, so always run git pull before starting local work.
Local development
Prerequisites: Hugo extended v0.158.0 (installed via Homebrew).
cd /path/to/your/clone
hugo server --buildDrafts --disableFastRender
Then open http://localhost:1313 .
--disableFastRender is required — one of the event pages is large enough to hit Hugo’s fast render buffer limit without it.
The local CMS is not configured (Sveltia runs against the live GitHub repo). For CMS testing, add local_backend: true to static/admin/config.yml and run npx decap-server in a second terminal, but this is rarely needed.
Key files
| File | Purpose |
|---|---|
hugo.toml | Hugo configuration (base URL, title, menu, params) |
static/admin/config.yml | Sveltia CMS configuration — collections, fields, media |
themes/btc/layouts/partials/member-gate.html | MembershipWorks member-lock snippet injected on gated pages |
themes/btc/static/css/main.css | All site styles |
themes/btc/layouts/ | Hugo layout templates |
content/ | All page content as Markdown |
content/training-sessions/ | Individual training session pages (CMS folder collection) |
content/resources-for-committee/ | This section — protected, committee only |
content/resources-for-coaches.md | Coaches resources page — protected, coaches and committee |
Cloudflare Pages
Project name: brightontri-website
Production URL: brightontri-website.pages.dev (also brightontri.org once the domain is cut over)
Build command: hugo --minify
Build output directory: public
Deployments trigger automatically on every push to main. Build logs are visible in the Cloudflare dashboard under Pages → brightontri-website → Deployments.
Environment variables
The site build itself needs no secrets. Any WA_* or JWT_SECRET variables still present in the Cloudflare Pages dashboard are leftovers from the archived WildApricot login system and can be deleted.
CMS OAuth proxy
The Sveltia CMS authenticates editors via GitHub OAuth. Because a static site can’t handle the OAuth server-side callback itself, a small Cloudflare Worker acts as the proxy.
Worker name: brightontri-cms-auth
Worker URL: brightontri-cms-auth.rob-be7.workers.dev
This is configured in static/admin/config.yml under backend.base_url. The Worker holds the GitHub OAuth app credentials — these are in the Cloudflare Workers dashboard under the worker’s environment variables.
If editors can’t log in to /admin/, check the Worker is deployed and its GitHub OAuth app credentials are valid.
Member-only pages
Member-only pages are gated with MembershipWorks’ member lock (mfl.js) — the same mechanism used on the old SquareSpace site. The page hides itself on load, then the MembershipWorks script reveals it if the visitor is logged in to MembershipWorks as a member of one of the allowed levels; otherwise a login prompt is shown.
This is a courtesy gate, not security. The content is still present in the page source, so anyone technical can read it. Do not put genuinely sensitive material (passwords, personal data, finances) on the website — use Google Drive for that. Gated pages carry a noindex tag so search engines don’t surface them.
To gate a page, add member_gate: true to its front matter (in the CMS or the Markdown file). Section index pages (_index.md) use cascade to apply the gate to every page in the section. The allowed membership level IDs are configured in hugo.toml (mwMemberLevels).
Gated areas
| Path | Gate |
|---|---|
/discounts/ | Members (all levels) |
/resources-for-coaches/ | Members (all levels) |
/resources-for-committee/ (and sub-pages) | Members (all levels) |
The member lock can only distinguish MembershipWorks membership levels, not roles — so coach and committee pages are currently visible to any logged-in member. If MembershipWorks levels (or lockable labels) exist for coaches/committee, their IDs can be substituted in the gate for those sections.
Coaches list refresh
The coaches page (/the-coaches/) is generated from a private Google Sheet (the coach planner). An Apps Script endpoint exposes the current active coaches as JSON.
To refresh the page after coaching staff changes, run from the project directory:
/refresh-coaches
This is a Claude Code slash command (defined in .claude/commands/refresh-coaches.md). It fetches the endpoint, groups coaches by qualification level, updates content/the-coaches.md, commits, and pushes.
Domain
The live domain is brightontri.org, currently still on Squarespace. When ready to cut over:
- In Cloudflare, add
brightontri.orgas a custom domain on the Pages project - Update the nameservers at the domain registrar to point to Cloudflare
- Update
baseURLinhugo.tomlif not already set tohttps://brightontri.org - Repoint the Casual Kit link on
/kit/— it targets the SquareSpace shop atbrightontri.org/shop, which stops existing at cutover
Backup and recovery
The entire site source is in GitHub — this is the canonical backup. The public/ directory (build output) is not committed and is always regeneratable from source.
If the Cloudflare Pages project were lost, recovery is:
- Create a new Pages project connected to the GitHub repo
- Set build command to
hugo --minify, output directory topublic - Re-add all environment variables (from the list above — retrieve secrets from whoever holds them)
- Re-add the custom domain
There is no database. All content is Markdown files in the repository.