Skip to content

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:

  1. build job (self-hosted fleet) — builds both portals, runs the public leak-test, verifies the private site returns 200, uploads both as artifacts.
  2. deploy job (GitHub-hosted ubuntu-latest) — runs only on merge to dev, downloads the artifacts, and wrangler 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 update site_url in mkdocs.yml to the public custom domain.
  • Set the HORDAGO_PRIVATE_DOCS_URL repo variable to the live private URL so the build job's check-url step 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.