Backstage plugin · early development
Markdown documentation for the software catalog.
Colophon publishes a repository's docs/ tree as Markdown — not as
HTML — and serves it two ways from one source of truth: rendered in Backstage
for people, and exposed through MCP for coding agents.
A colophon is the note in a book recording how it was made: the press, the date, the typeface. This plugin's manifest is exactly that, for your docs.
Status: early development. Published to npm under the
next dist-tag — npm install @brnby/plugin-colophon@next.
The bundle contract may still change without a schema version bump, so pin an
exact version if you depend on it.
01 — The problem
TechDocs commits to HTML at build time
By the time Backstage sees the documentation, the Markdown is gone. Everything downstream has to work against generated HTML instead of the thing an author actually wrote, and that cost lands in two places at once.
Theming becomes CSS patching of the Material-for-MkDocs DOM
inside a shadow root — selector surgery on a foreign document that was never
meant to be restyled. Anything agent-facing has to
reconstruct meaning from that same markup: a table an agent needs to reason
about arrives as a nest of anonymous <div> elements, and a
code sample arrives
with syntax highlighting spans threaded through it.
This is not a novel observation. Backstage's own RFC #3998 documents the cost — fragile selector patching, roughly 77% of generated bytes unused, shadow DOM breaking responsive layout — and proposes exactly this direction. It was never implemented. Meanwhile Material for MkDocs entered maintenance mode in November 2025, and TechDocs has an open, undecided migration RFC #33990 for a replacement engine.
Colophon keeps Markdown as the canonical stored artifact and renders at the edges. Theming flexibility and agent exposure stop being two hard problems and become two consumers of one structured source.
02 — The shape
One publish, three consumers
CI runs one command. Object storage holds the immutable content; Postgres holds the small, constantly queried index derived from it. Every surface Backstage exposes reads from that index.
The manifest a publish produces is a complete index of a revision — titles, descriptions, the navigation tree, and every heading anchor. The backend can render navigation, build a table of contents, and validate cross-page links without fetching a single Markdown blob. Only page bodies require a blob read.
03 — It runs
Publishing this repository's own docs
Colophon's documentation is itself a Colophon bundle. Publishing it with our own CLI is the fastest way to notice when the developer experience is bad — and it is where two real bugs were found. Below is the actual output of a publish against the five-page corpus in this repository.
$ colophon validate ./docs
5 pages, 0 assets
$ colophon publish ./docs --bundle-id github.com/yorch/colophon --local-dir ./colophon-storage
5 pages, 0 assets
revision c25e73f7eee0
uploaded 5 blobs (8.7 kB), reused 0 (0 B)
$ colophon publish ./docs ... # run again, unchanged
5 pages, 0 assets
revision c25e73f7eee0
uploaded 0 blobs (0 B), reused 5 (8.7 kB)
The revision id is identical. A revision id is the sha-256 of
the canonicalised manifest, and that hash deliberately excludes
createdAt and publisher: both describe the
run, not the documentation. An early version included them, and a
retried pipeline grew history forever — exactly the duplicate history that
content-addressing exists to prevent.
Nothing was uploaded. Every blob was already present and got
reused. That is not an optimisation for the repeat case; it is what makes
retained history affordable at all. A release branch that differs from
main by three pages stores three blobs, not a second copy of the
corpus.
04 — The intended interface
What it is meant to look like
There is no running Backstage instance behind this site, so there are no screenshots. The two figures below are hand-drawn wireframes of the intended interface — illustrations, not captures of working software.
Illustration of the entity docs tab. Nav comes from the manifest; the body is rendered from Markdown with Backstage UI components.
Illustration of the cross-repository docs home. The chips are channels — mutable pointers at immutable revisions.
05 — Read on
Where the decisions are
Architecture
Storage, revisions, channels
Why the pipeline splits across two storage systems, and what treating routing as a mutable pointer buys you.
Implementation
The bundle contract
A real manifest, the chunking rules, and the five packages that negotiate through them.
For agents
The MCP surface
Four read-only actions that return Markdown, riding on Backstage's MCP Actions Backend.
Getting started
Publish from CI
The catalog annotation, the CI step, and every app-config key the backend reads.