This article describes how this wiki itself works: a Grav CMS site whose content lives in a git repository, with two companion services — a fetcher that syncs the repo on git webhooks, and a telnet BBS front-end. Wiki changes surface on a JSON API that the Updater bot polls to announce updates on Discord.
Everything lives in a single repository — internal/wiki-pages on Gitea — which holds the wiki content (pages/), the Grav configuration, the theme, and the companion services. Docker Compose runs three services from it:
| Service | What it does |
|---|---|
grav |
Grav CMS (PHP 8.1 + Apache). The repo's pages/, config/, themes/, etc. are bind-mounted into the container, so the live site always serves whatever is in the working tree. |
wiki-fetcher |
Tiny Go HTTP hook (fetcher/). GET /pull?secret=… commits any local edits, rebase-pulls the repo, and pushes — keeping the checkout and the remote in sync. |
wiki-bbs |
Telnet front-end (bbs/, built on rubbs) that browses the wiki through the JSON API. See BBS Server. |
Grav uses folder-based routing: each page is a pages/<section>/<slug>/default.md file with YAML frontmatter, and the sidebar is curated by hand in config/sitenav.yaml. Pages can be edited two ways:
default.md, commit, push. The webhook flow below deploys it./admin) — Grav writes straight into the bind-mounted working tree; the fetcher commits these edits back to git on its next /pull.Pages under projects/ and team/ are login-protected; images live in assets/ and are served statically at /wiki-assets/….
wiki-pages repo on Gitea (or edits a page through the admin panel)./pull endpoint. The fetcher first commits any local admin-panel edits, then git pull --rebase + push, so both directions stay in sync./api/updates.json lists every page ordered by modification time.The API endpoints are themselves Grav pages (pages/api/) rendered through twig templates in themes/ttgwiki/templates/:
| Endpoint | Purpose |
|---|---|
/api/sitemap.json |
Every routable page: route, title, modified, tags, private flag (no bodies). |
/api/page.json?route=<route> |
One page with raw markdown + rendered HTML. |
/api/updates.json?limit=N |
Pages ordered by modification time — this is what the Updater bot watches. |
/api/pages.json?tag=<tag> |
Pages filtered by taxonomy tag (the public site's Blog and HowTos feeds use this). |
Private pages never expose their body unless the request carries token=<SUPERVISOR_PASSWORD>; the BBS uses the same token for its supervisor login.
https://git.teletype.hu/internal/wiki-pages