Northlight Content — Hosting Go-Live Runbook¶
SUPERSEDED (2026-07-10): the deployed chain is Gitea
brightworks-sites→ generated GitHub mirror (lusoris/thedesknook) → GitHub Pages, with Cloudflare as DNS/proxy only — seeapps/ai/paperclip-dispatcher/files/northlight-org.json(production) and thenorthlight-publishskill, which are authoritative. The Cloudflare Pages plan below was not adopted; its AdSense/analytics/GSC sections still apply. Terraform Pages/web-analytics resources from this plan are orphans to repoint or remove.
Operator runbook to take Northlight Content (SFW tech/lifestyle/hobby network) from empty repo to a live, monetized, indexed site on Cloudflare Pages.
- Host: Cloudflare Pages (free global CDN, best Core-Web-Vitals, already
Cloudflare-managed). Git-integration builds from GitHub (
github.com/lusoris/northlight). - Framework: Astro static build (Task A/B template).
- IaC: the Pages project is Terraform (
terraform/in the k8s repo, providercloudflare/cloudflare ~> 5.0— 5.x = attributes, not blocks;account_id = var.cloudflare_account_id, zone viadata.cloudflare_zone.main). No Cloudflare click-ops except the one-time GitHub OAuth authorization (§2), which cannot be done via API. - Secrets/tokens: Pages build env vars.
PUBLIC_*values are non-secret (they ship to the browser). Nothing here is a SealedSecret — these live in the Pages project config, set once per go-live.
Legend for each step: [you] = manual operator action · [auto] = handled
by terraform apply / Pages build · → = where the value/secret goes.
0. Prerequisites¶
- Astro template built and committed locally (Task A/B output in
/home/kilian/dev/northlight-site). - Terraform Pages resource added under
terraform/(Task B): acloudflare_pages_project.northlightwithbuild_config(build commandnpm run build, output dirdist),sourcepointing at the GitHub repolusoris/northlighton branchmain. ThePUBLIC_*/CF_ANALYTICS_TOKENapp-config values are set later in the Pages dashboard (they don't exist at provision time). Avar.northlight_domainvariable (default"") gates the custom-domain resource.
1. Create the GitHub repo + push the template¶
- [DONE]
github.com/lusoris/northlightis already created (private) and the Astro template is pushed tomain, verified building on node 22. To re-push or update from the local copy at/home/kilian/dev/northlight-site:
cd /home/kilian/dev/northlight-site
git add -A && git commit -m "content: <change>"
git push origin main
- Note: gitea (
git.dev.cauda.dev/lusoris) is dev-home, but Cloudflare Pages builds from GitHub — the production remote for this repo is GitHub. Content agents push here (see thenorthlight-publishskill). - → Code lives in
lusoris/northlighton GitHub, branchmain.
2. Terraform the Pages project + authorize GitHub¶
- [you]
terraform planthenterraform applyinterraform/. This creates thecloudflare_pages_project.northlightundervar.cloudflare_account_id. - [auto] The project, build config, and production env-var scaffold are created by Terraform.
- [you] One-time, dashboard-only (cannot be API'd): in the Cloudflare
dashboard → Workers & Pages → the northlight project → connect to Git →
authorize the GitHub app on the
lusoris/northlightrepo. This grants CF the OAuth permission to pull + build on push. After authorizing, trigger (or push) once to confirm the first production deployment goes green. - → Pages project bound to the GitHub repo; builds fire on every push to
main. PR/branch pushes get preview URLs at<branch>.northlight.pages.dev.
3. Point a domain¶
- Default (no domain yet): the site is live at
https://northlight.pages.dev. Fine for the dark/pre-launch build. - [you] When ready for a real domain, set
var.northlight_domain(in the tfvars) to the hostname andterraform apply. - If the domain is a cauda.dev subdomain, Terraform can add the
cloudflare_pages_domain+ the DNS record viadata.cloudflare_zone.main. - If it's a new apex domain, register it (separate registrar per the
anonymity/jurisdiction split), add the zone to Cloudflare, then set
var.northlight_domainand apply. - [auto] Terraform attaches the custom domain to the Pages project and provisions the CF-managed TLS cert. HTTPS is enforced by Pages.
- →
var.northlight_domain→ thecloudflare_pages_domainresource. Prod URL becomeshttps://<northlight_domain>/.
4. Enable Cloudflare Web Analytics¶
- [you] Dashboard → Analytics & Logs → Web Analytics → add a site for the Pages hostname. Copy the beacon token it issues. (Privacy-first, cookieless, no CMP needed — chosen over GA4.)
- [you] Set the token as a
CF_ANALYTICS_TOKENproduction env var on the Pages project — Settings → Environment variables in the CF dashboard (these app-config values are set post-launch and don't exist at IaC time, so they live in Pages env, not the Terraform that provisions the project). The template'sAnalytics.astroinjects the CF beacon when this var is present. - →
CF_ANALYTICS_TOKEN→ Pages prod env → browser beacon. Non-secret.
5. Apply for Google AdSense (after real content + traffic)¶
- Gate: AdSense approval needs a site with genuine, original content and some organic traffic. Do this after the launch-readiness gate is GO and pages are indexed — not at day zero.
- [you] Apply at adsense.google.com with the production domain. Google
reviews; on approval you receive a publisher ID
ca-pub-XXXXXXXXXXXXXXXX. - [you] Set
PUBLIC_ADSENSE_CLIENT=ca-pub-XXXX…as a Pages prod env var (dashboard → Settings → Environment variables). TheAdSlotcomponent and the AdSense loader read it; with it unset, no ad markup renders (safe pre-approval). - [you] Fill
public/ads.txtwith the AdSense line Google provides:google.com, pub-XXXXXXXXXXXXXXXX, DIRECT, f08c47fec0942fa0. Commit + push. A correctads.txtis required for fill/RPM — a missing one silently drops eligible demand. - →
PUBLIC_ADSENSE_CLIENT→ Pages prod env; publisher line →public/ads.txtin the repo. Both non-secret.
6. Join Amazon Associates¶
- [you] Enroll at the Amazon Associates program (associates-amazon) for the
target locale(s); provide the live site. You receive a store/associate tag
like
northlight-20. - [you] Set
PUBLIC_AMAZON_TAG=northlight-20as a Pages prod env var (dashboard → Settings → Environment variables). TheAffiliateLinkcomponent appends this tag to anyamazon.product URL automatically (it detects Amazon by hostname — nomerchantprop) — agents never hand-write it. - Note: Associates requires qualifying sales within the first 180 days to keep the account; drive real affiliate content before enrolling.
- →
PUBLIC_AMAZON_TAG→ Pages prod env → affiliate URLs. Non-secret.
7. Submit sitemap to Google Search Console¶
- [you] Add the property in Google Search Console for the production domain.
Verify ownership — easiest is a DNS TXT record, which you add via Terraform
on
data.cloudflare_zone.main(keeps DNS in IaC, no click-ops). - [you] Once verified, submit the sitemap URL:
https://<domain>/sitemap-index.xml(Astro's@astrojs/sitemapemits this on build; draft pages are excluded). - [you] This is the flip-to-index moment: only after the launch gate is GO
and
noindexis removed site-wide should the sitemap be submitted. Capture the pre-index baseline first. - → GSC verification TXT →
data.cloudflare_zone.mainDNS record (Terraform). Sitemap URL → submitted in the GSC UI.
Value / secret placement summary¶
| Value | Type | Where it goes |
|---|---|---|
| Astro template code | code | github.com/lusoris/northlight (branch main) |
var.northlight_domain |
tfvar | Terraform → cloudflare_pages_domain |
CF_ANALYTICS_TOKEN |
public env | Pages prod env (dashboard) |
PUBLIC_ADSENSE_CLIENT (ca-pub-…) |
public env | Pages prod env (dashboard) |
| AdSense publisher line | file | public/ads.txt in the repo |
PUBLIC_AMAZON_TAG (…-20) |
public env | Pages prod env (dashboard) |
| GSC verification TXT | DNS | data.cloudflare_zone.main record (Terraform) |
All PUBLIC_* values are non-secret by design (they render into the client). None
are SealedSecrets. The only non-IaC step is the one-time GitHub OAuth authorization
in §2, which Cloudflare requires through the dashboard.
Ordering rule¶
§1–4 can be done during the dark build. §5–7 are post-flip: AdSense and
Associates want real content + traffic, and the sitemap is submitted only after
the launch-readiness gate is GO and indexing is opened. See
docs/ai/launch-readiness-gate.md.