Architecture
System overview
┌─────────────────────────────────────────────────────────────────┐
│ Member repos (GitHub) │
│ │
│ trickfire-can/ trickfire-gui/ ak-series-lib/ │
│ ├── docs/ ├── docs/ ├── docs/ │
│ ├── docs.config.json ├── docs.config.json └── docs.config.json│
│ └── .github/workflows/ └── .github/workflows/ │
│ docs.yml (push) docs.yml (push) │
└──────────────────────┬──────────────────────────────────────────┘
│ GitHub Actions (reusable workflow)
│ workflow_call → sync-docs.yml
▼
┌─────────────────────────────────────────────────────────────────┐
│ Debian server /home/trickfire/docs/ │
│ │
│ content/ ← synced by member CIs │
│ ├── trickfire-can/ │
│ │ ├── docs/ │
│ │ └── docs.config.json │
│ ├── trickfire-gui/ │
│ └── ak-series-lib/ │
│ │
│ build/ ← output of docusaurus build │
│ scripts/build.sh ← git pull + pnpm install + build │
│ docusaurus.config.ts ← reads content/ dirs dynamically │
└──────────────────────┬──────────────────────────────────────────┘
│ nginx (localhost:80)
│
┌────┴────┐
│cloudflared│ ← outbound tunnel, no open ports
└────┬────┘
│ Cloudflare network
▼
docs.trickfirerobotics.com
Components
trickfire-docs repo
This repository (TrickfireRobotics/trickfire-docs) serves two roles:
- npm package — the
trickfire-docsCLI consumed vianpx. It providestrickfire-docs init,trickfire-docs dev, andtrickfire-docs build. Member repos never install it as a dependency. - Docusaurus site — the actual docs website. The
docusaurus.config.tsat the root readscontent/at build time and registers one@docusaurus/plugin-content-docsinstance per repo.
Member repos
Any TrickFire project repo. After running npx trickfire-docs init, the repo contains:
docs/— markdown filesdocs.config.json— project name, description, and optional sidebar.github/workflows/docs.yml— fires on push tomain, calls the reusable sync workflow
Member repos need no npm dependencies for the docs tooling. npx trickfire-docs downloads and runs the CLI on demand. Member repos never need to update their CI workflow — all changes to the sync logic happen in sync-docs.yml inside this repo.
Debian server
Hosts everything at /home/trickfire/docs/. The self-hosted Actions runner (label: docs) runs directly on this machine, giving sync jobs direct filesystem access without SSH transfers.
Directory layout on server:
/home/trickfire/docs/
├── content/ ← gitignored, populated by sync jobs
│ ├── .gitkeep
│ └── <repo-name>/
│ ├── docs/
│ └── docs.config.json
├── build/ ← Docusaurus output, served by nginx
├── scripts/
│ └── build.sh
├── src/, docusaurus.config.ts, package.json, … ← from git
└── node_modules/
GitHub Actions
Two workflow types:
| Workflow | File | Trigger |
|---|---|---|
| Member sync | sync-docs.yml (reusable) | Called by each member repo's CI |
| Framework deploy | deploy.yml | Push to main in this repo |
Both workflows run on the [self-hosted, docs] runner and call scripts/build.sh at the end.
Cloudflare tunnel
A cloudflared daemon on the server opens an outbound tunnel to Cloudflare's network. No inbound ports need to be opened in the server firewall. Cloudflare terminates TLS and proxies traffic to nginx on localhost:80.
Data flow: doc update
- Developer pushes changes to
docs/in a member repo. - GitHub Actions triggers
docs.ymlin that repo. docs.ymlcallssync-docs.yml@mainin this repo.- The self-hosted runner on the server runs:
rsync docs/ /home/trickfire/docs/content/<repo>/cp docs.config.json /home/trickfire/docs/content/<repo>/bash /home/trickfire/docs/scripts/build.sh
build.shrunsgit pull,pnpm install,pnpm website:build.- Docusaurus reads all dirs in
content/, parses eachdocs.config.json, registers a docs plugin per repo, and outputs a static site tobuild/. - nginx serves
build/to incoming requests from the Cloudflare tunnel.
Data flow: framework update
- A change to the Docusaurus site itself (theme, config,
src/) is merged tomain. deploy.ymlruns on the self-hosted runner.build.shpulls the latest framework code and rebuilds.content/is untouched — it's gitignored and survives the pull.