The Backstage plugin
The CRAYON-C4 plugin for Backstage adds an Architecture tab to your catalog entities. It shows the C4 model that lives in the entity's own repository, with the same viewer you use in the app: drill from the system context down through containers and components, select an element to read what it is, and open the decisions recorded against it.
It is read-only. Nobody edits anything from Backstage; the model changes where it always does — in the repository, through the CLI, the VS Code extension, or the app.
What you need
- A Backstage app on the new frontend system, with the standard backend system.
- Source-control integration configured in Backstage for the repositories that hold your
models (the integration the catalog already uses to read
catalog-info.yaml). The plugin reads through it, so private repositories work with no extra credentials. - A CRAYON-C4 model committed to each repository you want to show.
You do not need a CRAYON-C4 account to read a model in Backstage.
Set it up, step by step
Step 1 — commit a model to the repository.
From the repository root:
npx crayon-c4 .
That writes crayon-c4-project.json (the model) and crayon-c4-provenance.json (a small
sidecar that lets later runs tell generated elements from the ones you wrote by hand).
Commit both. Open the model in the app first if you want to curate names, descriptions
and layout — your edits are kept when the model is refreshed later (see
Keeping it current).
In a monorepo, scope it to one service with --root, and put the model in that service's
folder:
npx crayon-c4 . --root services/billing --out services/billing/crayon-c4-project.json
Step 2 — annotate the catalog entity.
In the entity's catalog-info.yaml:
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
name: billing
annotations:
backstage.io/source-location: url:https://github.com/acme/shop/tree/main/services/billing/
crayon-c4/model: crayon-c4-project.json
spec:
type: service
lifecycle: production
owner: team-billing
| Annotation | Meaning |
|---|---|
crayon-c4/model | The model file, as a path relative to the entity's source location. This is what switches the tab on — entities without it get no Architecture tab. |
backstage.io/source-location | A standard Backstage annotation: the folder the entity lives in, with a trailing /. Backstage sets it automatically for entities registered from a repository; add it yourself only if it is missing. Without it, the plugin falls back to the location the entity was registered from — its catalog-info.yaml — and resolves the path beside that file, which for a model at the repository root is the same place. |
The path is resolved the way Backstage resolves every relative annotation
(backstage.io/adr-location, backstage.io/techdocs-ref): through the source-control
integration, so GitHub, GitLab, Bitbucket and Azure DevOps each get the URL shape they expect.
It must stay inside that folder. A full URL, a path starting with /, or one containing ..
is refused on purpose — the plugin reads only what belongs to the entity.
Step 3 — install the two plugins.
The backend plugin reads the file; the frontend plugin draws the tab.
yarn --cwd packages/backend add @squora/backstage-plugin-crayon-c4-backend
yarn --cwd packages/app add @squora/backstage-plugin-crayon-c4
Register the backend plugin:
// packages/backend/src/index.ts
backend.add(import('@squora/backstage-plugin-crayon-c4-backend'));
The frontend plugin is picked up automatically when your app uses feature discovery (the
default in a new Backstage app). If you list features by hand, add it to createApp:
import crayonC4 from '@squora/backstage-plugin-crayon-c4';
createApp({ features: [crayonC4 /* , …your other features */] });
Step 4 — check it.
Restart Backstage, open an annotated entity, and look for the Architecture tab. The first time you open it the viewer loads (a little over a megabyte, and only when you open the tab — pages that never use it do not pay for it).
What readers see
- The diagrams. The same layers you navigate in the app, with breadcrumbs, drill-in and the side panel describing the selected element.
- Decisions. If the model contains decision records, a Decisions tab lists them — searchable, filterable by status, newest first. A row opens the record: context, decision, consequences, linked elements and the supersede chain. Selecting an element on a diagram also lists the decisions linked to it.
- Backstage's own look. The viewer uses the font of the page it sits in, so it reads as part of your portal rather than as an embedded foreign page.
The Decisions tab appears only when the committed file actually contains decision records — for example because you curated the model in the app and exported it, or extracted them with the VS Code extension. A file generated by the CLI alone has none. See Decisions.
Keeping it current
The Architecture tab shows whatever is committed on the entity's default branch — so the model is only as fresh as the commit. Two habits keep it honest, both described in full under Run it in CI in the CLI page:
- Gate pull requests.
npx crayon-c4 . --starting crayon-c4-project.json --checkexits with code 6 when the architecture moved but the model didn't, and writes nothing. - Refresh automatically. Run the same command with
--applyon a schedule (or on merge) and open a pull request with the result. Your names, descriptions and layout survive the refresh; removals and rename conflicts are left for a person unless you say how to decide.
Whoever curates the model does it in the app or the editor, commits the file, and the tab updates on the next page load — there is nothing to redeploy in Backstage.
When you update the plugin, update the backend and frontend packages together. A model written by a newer CRAYON-C4 than the plugin understands is refused with a message telling you to update.
Who can see what
- The plugin looks the entity up as the person viewing it, so Backstage's catalog permissions apply: nobody can read a model for an entity they cannot open.
- The repository is read by the Backstage backend through its source-control integration. The browser never receives a repository token.
- The viewer runs in a sandboxed frame with no access to Backstage's page, cookies or storage. Links to outside websites in a description open in a new tab.
Troubleshooting
The tab shows the reason when it cannot draw the model. What each message means:
| Message | What to do |
|---|---|
| The entity has no crayon-c4/model annotation. | Add the annotation (step 2). Without it the tab does not appear at all, so seeing this means the page was opened some other way. |
| The entity has no source location to resolve the model against. | Add backstage.io/source-location to the entity. |
| Only url source locations are supported. | The source location must be a url: one, as it is for entities registered from a repository. |
| crayon-c4/model must be a path relative to… / …must stay inside… | Use a plain relative path such as crayon-c4-project.json or docs/model.json — no URL, no leading /, no ... |
| The model file was not found in the repository. | The path is wrong, or the file is not on the branch the source location points at. |
| Not allowed to read this model. | The Backstage backend's source-control integration cannot read that repository. Check the integration's access, the same way you would for catalog-info.yaml. |
| The model file is not valid JSON. / The file is not a CRAYON-C4 project. | Regenerate the file with npx crayon-c4 . and commit it. |
| The model uses schema version N… | The model is newer than the plugin. Update both plugin packages. |
| The model is larger than 10 MB. | Narrow the model — see the init command and .crayon/config.json in Document from code. |
| The model could not be read from the repository. | A temporary failure reaching the repository. Reload; if it persists, the Backstage backend log has the cause. |
If the tab loads but a diagram is empty or looks wrong, open the same file in the app (New project → From file): the app reports exactly what is wrong with it.
What it does not do
- It does not edit. There is no editing, regenerating or AI action in Backstage.
- It does not create catalog entities. The tab shows a model; it does not turn the model's systems and containers into Backstage entities.
- It shows the C4 diagrams and the decisions, nothing else. The deployment and F4 flow diagrams that a model file may also carry are not drawn in Backstage.
Where next
- Document from code — generating the model, the
.crayon/config.jsonreference, and running it in CI. - The VS Code extension — generate, curate and extract decisions without leaving your editor.
- Decisions — what a decision record is and how it links to elements.
- C4 structure — the element types and levels you will be drilling through.