Plate № 09 · Portfolio scaffolding
A pattern from the gf.cx specimen book
gfcx_subdomain_new · scaffold
"Don't hand-roll a new gf.cx subdomain. The wrappers encode prior friction — every new subdomain starts from the latest learnings, not a blank slate."
The problem
Each new <name>.gf.cx subdomain shares the same first hour of setup work: Pages project create, custom-domain binding, fonts, primitives (cards.css, media.css), declarative landing, 404 page, wrangler.toml, _redirects, deploy command, the dashboard URL for the custom-domain pane. Hand-rolling each one rediscovers the same friction every time — and forgets the small things (Workers-with-Assets run_worker_first, Rocket Loader data-cfasync, BASE_URL constant) the previous one taught.
The shape
One script — ~/bin/gfcx_subdomain_new.py — encodes the current state of "what a fresh gf.cx subdomain looks like." Run it with a name; it generates the repo, the Pages project, the deploy command line, and prints the dashboard URL where the custom-domain CNAME needs to bind. Every new subdomain starts from the latest learnings, not a blank slate.
The discipline
Each new subdomain that surfaces a friction (a missing field, a forgotten CF setting, a default that was wrong) feeds back into the scaffold itself before the next subdomain spins up. This is what makes the scaffold compound: the third subdomain inherits the second's edge cases, the tenth inherits all nine.
What the scaffold bakes in today (v0.3.0)
A fresh subdomain is no longer just a landing page — the generator stamps the full belts-and-braces baseline, so a new surface ships finished, not minimal. A surface missing any of these is under-finished:
- Declarative
site.config.json— a belts-and-braces manifest: the single source of truth for which baseline layers are wired. You audit a surface against it before calling it done, rather than eyeballing the HTML. (v0.3.0, 2026-06-19) - CF Access secure-by-default —
site.config.jsonnow declaresaccess: cf-access onfor every new surface (email-OTP for the owner, home-IP on the bypass whitelist). Public surfaces are the explicit opt-out (enabled:false) — because Access breaksog:imagecapture + social crawlers, anything shareable opts out deliberately. - Three-layer UX baseline — inline-link promotion, rollover thumbnails, and an auto-captured social card (
post_deploy.sh→media.gf.cx/<surface>/og/card.png), wired in rather than bolted on. - Diagnostic probe endpoints —
functions/ip.ts · cf.ts · trace.tsgive every surface/ip · /cf · /traceout of the box. - Smart, context-aware 404, a git-deploy workflow (
.github/workflows/deploy.yml), and surfaces.json snapshot registration at scaffold time. - Workers-with-Assets
run_worker_first = trueand Rocket-Loaderdata-cfasync="false"— the earlier gotchas, still carried forward.
The principle
Don't hand-roll a new gf.cx subdomain. The wrappers encode prior friction.
The same principle applies to the wider ~/bin/ portfolio: wrangler-deploy > raw npx wrangler, verify-cf-token > raw curl, gfcx_status_hub_render > hand-templated dashboard. Reach for the wrapper first — it has already paid the cost of every gotcha you haven't hit yet.
What the scaffold emits
~/bin/gfcx_subdomain_new.py <name>
# Outputs:
# ~/Code/<name>.gf.cx/ — local repo with all template files
# wrangler.toml — workers-with-assets, run_worker_first=true
# _redirects — trailing-slash + legacy redirect rules
# functions/ip.ts|cf.ts|trace.ts — diagnostic probes out of the box
# .github/workflows/deploy.yml — git-push → Pages auto-deploy
# site.config.json — baseline audit manifest
# post_deploy.sh — og:image capture on deploy
#
# Prints: Pages dashboard URL, custom-domain CNAME instruction
Reusable elements
gfcx_subdomain_new.py— the generator itself; lives at~/bin/gfcx_subdomain_new.py(canonical runtime, part of thexlab-co/toolkitcheckout).- Five-layer surface baseline — inline-link promotion, rollover thumbnails, social card auto-capture, favicon control-plane wiring, and made-with provenance footer; injected at scaffold time so every surface ships with the floor already raised.
site.config.jsonmanifest schema — the declarative audit contract; reusable by any audit tool (gfcx_favicon.py audit, future drift-guard) to verify a surface against its declared baseline without eyeballing HTML.- Diagnostic probe functions —
functions/ip.ts,cf.ts,trace.ts; copy the three files into any Pages project to get/ip · /cf · /traceimmediately. - Post-deploy og:image capture hook —
post_deploy.shcallsog_capture.pyafter each Pages deploy; swap the R2 key to adopt on any surface.
Reference
- Source
- Dan, 2026-05-27 ·
xlab-co/toolkit·~/bin/gfcx_subdomain_new.py. Compounded through v0.1.0 → v0.3.0 as new surfaces surfaced new friction. - In use / example
- srvc.gf.cx — scaffolded by this generator; ships the full baseline (favicon, made-with, social card, probe endpoints, CF Access). Copy its
wrangler.tomlas a starting template; swap the project name and custom domain. - Reusable elements
gfcx_subdomain_new.py· five-layer surface baseline ·site.config.jsonmanifest · diagnostic probe functions · post-deploy og:image hook (listed above).- Origin
- Compounding-kb throughline: "Bolt-on per-surface = friction, drift, and surfaces that never get the treatment. Scaffold-injected = the floor rises every time a new surface ships." — Dan, 2026-06-04.