Docs Hosting & Deployment¶
The Hordago docs portal is private-first and deploys automatically to
Cloudflare Pages on every merge to dev. Two portals are published from one
build:
| Portal | Content | Cloudflare Pages project | Access |
|---|---|---|---|
| Public | Leak-tested subset (build_docs_portal.py --visibility public) |
hordago-docs |
Open (or Access-gated if you prefer) |
| Private | Full content | hordago-docs-private |
Cloudflare Access (authed) |
How deployment works¶
.github/workflows/docs-portal.yml runs on every push/PR touching the docs:
buildjob (self-hosted fleet) — builds both portals, runs the public leak-test, verifies the private site returns 200, uploads both as artifacts.deployjob (GitHub-hostedubuntu-latest) — runs only on merge todev, downloads the artifacts, andwrangler pages deploys each portal to its Cloudflare Pages project.
The deploy job runs on a GitHub-hosted runner because the self-hosted fleet's
default-deny egress cannot reach api.cloudflare.com (the same egress class
that forces CodeQL to weekly — see velocity-workflow#2250).
The deploy job is a no-op until the Cloudflare secrets are set — it checks
for CLOUDFLARE_API_TOKEN and cleanly skips (never failing the build) if the
operator hasn't provisioned it yet. So this workflow can merge safely today; the
first real deploy happens on the next dev merge after setup.
Operator setup (one-time, ~10 min)¶
Requires a Cloudflare account and wrangler (npm i -g wrangler or npx wrangler).
1. Create the two Pages projects
wrangler pages project create hordago-docs --production-branch main
wrangler pages project create hordago-docs-private --production-branch main
2. Mint a scoped Cloudflare API token + set the GitHub secrets
Create an API token with the Account → Cloudflare Pages → Edit permission (Cloudflare dashboard → My Profile → API Tokens), then:
gh secret set CLOUDFLARE_API_TOKEN -R Hordago-Labs/hordago # paste the scoped token
gh secret set CLOUDFLARE_ACCOUNT_ID -R Hordago-Labs/hordago # your CF account id (wrangler whoami)
3. Put Cloudflare Access in front of the private portal (private-first gating)
In the Cloudflare dashboard → Zero Trust → Access → Applications, add a
self-hosted application for the hordago-docs-private.pages.dev hostname (or its
custom domain) with a policy allowing only your org's identity
(email/IdP/GitHub). This is what keeps the full docs private.
4. (Optional) Custom domains + private-URL verification
- Add custom domains to each Pages project in the dashboard (e.g.
docs.hordago-labs.<yourdomain>public,docs-internal.<yourdomain>private), then updatesite_urlinmkdocs.ymlto the public custom domain. - Set the
HORDAGO_PRIVATE_DOCS_URLrepo variable to the live private URL so the build job'scheck-urlstep verifies the deployed private site returns 200:
gh variable set HORDAGO_PRIVATE_DOCS_URL -R Hordago-Labs/hordago \
--body "https://docs-internal.<yourdomain>/"
After setup¶
Every merge to dev that changes docs/site/** or mkdocs.yml
auto-publishes: the public portal to hordago-docs (leak-safe) and the full
private portal to hordago-docs-private (behind Access). The weekday cron also
refreshes the maintenance-heartbeat page. No manual step per update.
To deploy on demand, use the workflow's workflow_dispatch trigger.