Skip to content

Docs site

This page is the meta-doc — how the docs site itself is built, hosted, and kept in sync between repos.

What you're looking at

The site at https://docs.tomoda.life is a federated MkDocs build pulling from two repos:

  • tomoda-labs/tomoda (this repo) — app + architecture + frontend + backend + style guide docs
  • tomoda-labs/devops — infrastructure, operations, observability, security docs (rendered under /devops/)

The federation uses mkdocs-monorepo-plugin configured in mkdocs.yml:

plugins:
  - monorepo

nav:
  # ... tomoda nav entries ...
  - DevOps: '!include devops/mkdocs.yml'

The plugin reads devops/mkdocs.yml at build time, merges its nav into the parent site, and resolves all the linked pages.

How devops/ lives in this repo — submodule + sparse checkout

devops/ is a git submodule tracking tomoda-labs/devops main:

# .gitmodules
[submodule "devops"]
    path = devops
    url = git@github.com:tomoda-labs/devops.git
    branch = main

But we only need the docs from devops, not the whole repo (which also contains infrastructure/, k8s/, scripts/, etc.). The submodule uses sparse checkout to materialise only docs/ + mkdocs.yml on disk. The rest lives in .git/objects but never appears in the working tree.

Sparse-checkout config (set automatically by task docs:update):

docs/*
/mkdocs.yml

If you ever want to see the full devops repo in your local checkout (e.g., to debug a build issue):

git -C devops sparse-checkout disable
# devops/infrastructure/, k8s/, scripts/, etc. appear

git -C devops sparse-checkout reapply
# back to docs-only

Local workflow

After cloning fresh:

# First time only — clones the submodule + installs Python toolchain
task docs:install

# Daily: serve docs locally with hot reload at http://localhost:8000
task docs

# Or: build the static site into ./site/
task docs:build

Both task docs and task docs:build run task docs:update as a dependency, which:

  1. Runs git submodule update --init --remote --recursive devops — pulls the latest commit from tomoda-labs/devops main into the submodule.
  2. Re-applies sparse checkout (in case mkdocs.yml references a new top-level file you haven't synced yet).
  3. Prints the short SHA of the synced devops commit so you know what you're previewing.

If you've been editing devops docs locally in a sibling ~/workspace/devops/ checkout, those edits won't appear in the federated build until you git push them to main. The submodule pulls from GitHub, not from a sibling directory. To preview unpushed devops docs:

  • Option A: push to a feature branch in devops, temporarily point the submodule at that branch (git -C devops checkout feature/foo), then run task docs. Don't commit the submodule pointer change unless you actually want the docs site to track that branch.
  • Option B: run task docs inside the devops repo itself — that uses devops's local task docs:build, which previews just the devops docs without federation.

Hosting

The federated site is hosted on Cloudflare Pages with Cloudflare Access gating via GitHub OAuth:

git push to either repo's main
    │
    ├─► tomoda push: Pages auto-rebuilds (native git webhook)
    └─► devops push: GitHub Action POSTs to Pages deploy hook → rebuild
            │
            ▼
Cloudflare Pages
    ├─► git clone tomoda (with submodule update --init --remote)
    ├─► sparse-checkout devops to docs/* + /mkdocs.yml
    ├─► pip install -r docs/requirements.txt
    ├─► mkdocs build
    └─► publish site/ → docs.tomoda.life
            │
            ▼
Cloudflare Access (GitHub OAuth IdP, restricted to tomoda-labs org)
    │
    ▼
User in a browser

End-to-end deploy time after a push is ~1-2 minutes.

Auto-deploy on devops push

Cloudflare Pages rebuilds on any push to the tomoda repo's main natively. To also rebuild when devops main changes, the devops repo has a workflow at .github/workflows/notify-docs-pages.yml that POSTs to a Cloudflare Pages deploy hook URL. The hook URL is stored as a repo secret (PAGES_DEPLOY_HOOK) in devops.

Workflow runs on the self-hosted ARC runners (runs-on: tomoda-arc). One curl POST, ~5s of compute. If ARC runners are offline, the workflow queues but the actual Pages rebuild doesn't fire — devops pushes won't refresh the live site until ARC is reachable. Tomoda pushes are unaffected.

Cloudflare Pages build configuration

In the Pages project settings:

Setting Value
Production branch main
Build command git submodule update --init --remote --recursive && git -C devops sparse-checkout set --no-cone 'docs/*' '/mkdocs.yml' && pip install -r docs/requirements.txt && mkdocs build --strict
Build output directory site
Root directory /
Environment variables PYTHON_VERSION=3.12

The submodule clone uses the GitHub access token Cloudflare Pages provisions when you connect the project (Pages is granted read access to private repos in the same org).

Auth — Cloudflare Access

Access policy on docs.tomoda.life:

  • Identity provider: GitHub (Cloudflare Access has GitHub as a built-in IdP; one-time OAuth setup)
  • Include rule: GitHub organizations → tomoda-labs
  • Action: Allow
  • Session: 24h

External contributors who need access just need to be added to the tomoda-labs GitHub org (or to specific teams within it, if you ever want tighter scoping).

Troubleshooting

"devops dir is empty after clone" — run task docs:update (or git submodule update --init --remote --recursive). Submodules don't auto-clone on a plain git clone.

"my devops doc change isn't appearing in task docs" — the submodule pulls from devops/main on GitHub, not a local sibling. Push your devops change to a branch + point the submodule at that branch temporarily, or run task docs inside the devops repo for an isolated preview.

"mkdocs complains about missing files in devops/" — sparse checkout may be hiding something. Run git -C devops sparse-checkout list to see what's materialised. If devops's mkdocs.yml was updated to reference a new top-level file (other than docs/* or /mkdocs.yml), add it to the sparse-checkout pattern in Taskfile.yml::docs:update.

"Pages build fails on the submodule clone" — usually means Pages doesn't have permission to read the devops repo. Verify the Pages project is connected to the tomoda-labs GitHub org with read access to both repos.

  • mkdocs.yml — federation config (!include devops/mkdocs.yml)
  • Taskfile.ymldocs, docs:build, docs:update, docs:install, docs:clean tasks
  • .gitmodules — submodule definition for devops/ tracking main
  • DevOps repo's docs/operations/bootstrap.md — Cloudflare account setup that the Pages project lives under