Docs site runbook (docmd → Cloudflare Pages)

Docs site runbook (docmd → Cloudflare Pages)

The internal docs site publishes repo-root docs/ + .agents/docs/ as a
static docmd site at https://fitxers.usebrioso.com, gated behind Cloudflare
Zero Trust Access. This file covers how it works and the manual setup that
the repo cannot do for you.

How it works

  • Workspace: apps/docs (@brioso/docs).
  • pnpm --dir apps/docs build (wired as mise run docs:build):
    1. src/stage-content.ts cleans apps/docs/site-docs/ and copies
      repo-root docs/ (markdown/assets only) to the tree root and
      .agents/docs/** to site-docs/internal/ (everything except
      .DS_Store — the seo CSVs and papers PDFs ship too).
    2. docmd build (docmd.config.json) renders the static site to
      apps/docs/dist, including llms.txt, llms-full.txt, llms.json.
    3. src/copy-functions.ts copies apps/docs/pages-functions/ into
      dist/functions/ so the deploy dir carries the Pages Function.
  • Deploy: .github/workflows/deploy-docs.yml — push to beta touching
    .agents/docs/**, docs/**, apps/docs/**, or the workflow itself.
    Cheap ubuntu-latest job: checkout → deploy-runtime → turbo run build --filter=@brioso/docs → wrangler pages deploy apps/docs/dist --project-name=docs-brioso. Secrets: CLOUDFLARE_API_TOKEN +
    CLOUDFLARE_ACCOUNT_ID from the beta GitHub Environment (asserted by
    the secrets-check job in deploy-pr-checks.yml, names only).

pages.dev bypass

docmd output is pure static, so the block ships as a Cloudflare Pages
Function (apps/docs/pages-functions/_middleware.ts): any request whose
hostname ends in .pages.dev gets a bare 404 Not found; everything else
passes through. Because Access sits in front of the custom domain only,
this middleware is what keeps <project>.pages.dev from exposing the docs
unauthenticated. Don’t remove it without replacing the guard.

URLs

Agents fetch the corpus through Access with a service token. The token
lives in Infisical (prod environment, /infra folder, keys
CF_ACCESS_CLIENT_ID / CF_ACCESS_CLIENT_SECRET) — same source of truth
as the rest of the infra secrets (infisical-env-guide.md). Any machine
with an infisical login session pulls it the standard way:

# fetch the corpus with the token exported from Infisical
infisical run --env=prod --path=/infra -- \
  curl -sS -o /tmp/llms-full.txt \
    -H "CF-Access-Client-Id: $CF_ACCESS_CLIENT_ID" \
    -H "CF-Access-Client-Secret: $CF_ACCESS_CLIENT_SECRET" \
    https://fitxers.usebrioso.com/llms-full.txt

GitHub Actions uses the copies in the beta environment secrets (same
key names); those are write-only and not an agent retrieval path.

Setup state (as of 2026-10-17)

All steps below are done; this section records the concrete resources so
future maintenance doesn’t have to rediscover them:

  • Pages project: docs-brioso (account usebrioso,
    id ea4dcf9f4d3179bfead02c492d8f59e3, production branch beta).
  • Custom domain: fitxers.usebrioso.com attached (zone usebrioso.com),
    DNS CNAME fitxers → docs-brioso.pages.dev (proxied — Access only runs on
    proxied traffic).
  • Access app: Brioso Docs (fitxers.usebrioso.com), id
    e67ab30c-cb84-4cdd-88c5-bdd6a53bdd22, session 12h. Two policies:
    • team emails (allow): carloshpiedra@gmail.com, josocjoq+dev@pm.me,
      quino@usebrioso.com, carlos@usebrioso.com.
    • agent service token (bypass): include
      {"service_token": {"token_id": "<token uuid>"}}. The API rejects
      {"service_token": {}} and {"service_token": {"id": ...}} — it must
      be token_id. Bypass (not allow) is what lets the token through
      without an interactive login.
      Free tier caps at 50 users.
  • Agent service token: docs-agents (client id
    67eb0874715f4e2cba520cb1b3640369.access). Values in Infisical
    prod /infra and GitHub beta env secrets. Rotated twice on
    2026-10-17 (learn: don’t clean up the only copy of a secret before it’s
    in the store).
  • GitHub beta environment secrets: CLOUDFLARE_API_TOKEN,
    CLOUDFLARE_ACCOUNT_ID, CF_ACCESS_CLIENT_ID, CF_ACCESS_CLIENT_SECRET.

Recreate-from-scratch checklist (if ever rebuilding)

  1. Cloudflare Pages project: wrangler pages project create docs-brioso --production-branch=beta. The API token needs Cloudflare Pages: Edit.
  2. Custom domain: attach fitxers.usebrioso.com to the project; create
    the DNS CNAME (proxied) — the API token needs DNS: Edit.
  3. Access app: Zero Trust → Access → Applications → self-hosted app for
    fitxers.usebrioso.com, allow policy = the team emails.
  4. Access service token for agents: Access → Service credentials →
    Service Tokens → create. Authorize via an app policy with decision
    bypass and include {"service_token": {"token_id": "<uuid>"}}.
    Store in Infisical prod /infra (CF_ACCESS_CLIENT_ID /
    CF_ACCESS_CLIENT_SECRET) and as GitHub beta env secrets.
  5. GitHub Environment secrets (beta): CLOUDFLARE_API_TOKEN +
    CLOUDFLARE_ACCOUNT_ID. Until both exist, the deploy-docs workflow fails
    and the secrets-check job on PRs is red.

Adding / rotating

  • New docs page: drop the .md anywhere in docs/ (end-user) or
    .agents/docs/ (internal, lands under /internal/). Push to beta;
    the paths-filtered workflow deploys it. No navigation config needed —
    docmd titles from the H1 and builds the tree automatically.
  • New human user: add them to the Access app policy (50-user cap).
  • Rotate the agent service token: create a new token, repoint the
    bypass policy’s token_id, update Infisical + GitHub beta secrets,
    then delete the old token (deletion fails while it’s still referenced by
    the policy — update the app first).
  • Rotate the deploy API token: rotate in Cloudflare (My Profile → API
    Tokens), update the CLOUDFLARE_API_TOKEN secret in the beta
    GitHub Environment + Infisical.