The company site
document:docs/siteOne command builds one site from every enrolled repository.
ac site builds one Starlight site from the repositories you enrol. Documents,
events, services and fields share one index, with Starlight’s search, navigation
and dark mode.
Build it
Section titled “Build it”npx autocatalog site --topic catalog --owner acme \ --site https://acme.github.io --base /catalog/The command lists every repository acme owns with the catalog topic, clones
each one with its history, and writes the site to dist/. A team enrols its
repository by adding the topic on GitHub. Nobody edits a list in the site’s
repository, and the site’s owners approve nothing.
| Flag | Meaning |
|---|---|
--topic, --owner | Clone every repository the owner has with the topic |
--repo <url...> | Clone these repositories as well |
[root] | Build from checkouts already on disk, or from one repository |
--site, --base | Where the site is served, for links, the sitemap and llms.txt |
--title | The site’s title |
--description | One sentence for the home page and llms.txt |
--brand | A folder with your logo, favicon and theme |
--theme | An accent colour by name |
--out | Output directory, dist by default |
--dev | Serve with live reload |
--eventcatalog | Draw diagrams with EventCatalog, inside this site |
--docs-schema | The docs frontmatter contract to enforce |
Set the flags once in package.json and reuse them for the dev server:
{ "scripts": { "build": "autocatalog site --topic catalog --owner acme --site https://acme.github.io --base /catalog/", "dev": "npm run build -- --dev" }}The default favicon is the three provenance marks (declared, extracted,
inferred). --brand replaces it; see Make it yours.
The command prints how many repositories it cloned and whether it had a token.
Private repositories need GITHUB_TOKEN or GH_TOKEN. The command sends the
token as a request header, over HTTPS to github.com or the host in
GITHUB_SERVER_URL, so it never lands in a clone’s .git/config.
The command clones each repository once, even when you enrol it by topic and
by --repo. When a clone fails, it names the repository with git’s message and
builds the site from the rest. It writes --out once the build completes, so a
failed build leaves your last site in place.
Run npx autocatalog site . inside one repository to preview what it publishes
before you enrol it.
Every document must satisfy the company’s frontmatter contract, or the build
fails and names the document. The contract is --docs-schema when given, else
contracts/docs-frontmatter.json at the root, else the one an enrolled
repository publishes there, such as a handbook. Copies other repositories keep
with ac docs sync-schema are the same contract; two repositories publishing
different ones fail the build, naming both, until --docs-schema picks one.
A repository whose document breaks the contract is left out and the rest publish. When that leaves no documents at all, the build fails, so a deploy keeps the last good site.
What each page shows
Section titled “What each page shows”-
/lists every document by repository, with its owner and last change. -
/<repo>/<path>/is one document: its owner, the date git last saw it change, a link to the file on its repository, and the documents on either side of itsrelatededges. -
/events/<repository>/<domain>/<id>/is one event: where its contract was found, its schema as a tree, its examples, any narrative the team wrote beside it, and the services that produce and consume it. The repository and domain are in the address, so two repositories can publish the same event id. Earlier versions sit under it atv/<version>/, with what changed between each: a committed catalog’sversioned/folders, or, for a repository that publishes onlycontracts/, each earlier version its contracts declared in the last 50 commits tocontracts/, read from git without checking anything out. A contract that declares no version has no history. Without a committedevents/, a repository’s events are whatcontracts/holds (JSON Schema, OpenAPI, Avro, protobuf) plus any autotel-message-contract approved file,__contracts__/*.approved.txt, anywhere in it. Nothing else outsidecontracts/is read, and that walk skipsnode_modules,distand dot-folders and reads only regular files. -
Runtime evidence: when a repository has
.autotel/snapshot.json, where autotel’sArchitectureSnapshotSubscriberwrites it, the site joins it onto that repository’s current events, committed catalog or discovered contracts alike, so the event page shows what ran without committingevents/, and contract history from git still works. Only a regular file inside the repository is read, never a symlink. A snapshot that does not read is a warning in the build, and the repository’s events publish without evidence. -
/services/<name>/lists what one service produces and consumes. The pages come from each event’sproducersandconsumers, so declaringproducers: ['InventoryManager']on an event creates the InventoryManager page. -
/domains/<domain>/gathers the events in one domain, the services around them and their owners, with a map of the domain./domainslists them all. -
Maps on service and domain pages use EventCatalog’s MIT visualiser. A service map shows the services on the other side of each event, and the catalog map adds commands, queries and the tables and collections each service reads and writes.
-
When a repository has a snapshot, the maps draw it on each producer and consumer edge. An edge that ran is solid in the extracted colour, and thicker the more often it ran. One the catalog declares that the snapshot never saw is dashed and grey. One that ran though nobody declared it, a consumer or producer only the snapshot names, is in the inferred colour. Event names ignore case and
.,_,-, soorder.placedisOrderPlaced. A repository without a snapshot draws its edges as before. -
Those maps also have a Replay mode, which sends markers across the edges that ran, more often where the snapshot saw more, and a Live mode when the site is given a server-sent events feed. Live moves an edge each time a frame names its event: a JSON frame like
{ "name": "order.placed", "producer": "OrderService" }moves that producer’s edge, or every producer’s when it names none, and every consumer’s edge with a hollow marker, since a producer’s frame does not prove delivery. Passac site --live-url <url>(and--live-event <name>, the SSE event name,messageby default), or setlive: { url, event }on theautocatalogDocsplugin. The site only listens; it does not serve the feed. With reduced motion an edge brightens in place instead of carrying a marker. -
/apis/<repository>/<name>/renders each OpenAPI 3 spec a repository commits undercontracts/with Scalar, and publishes the spec beside it asopenapi.json. Without JavaScript the page lists the operations. A service links to the API whose title names it or whose webhooks it produces. -
/specs/<repository>/<name>/shows any other specification a repository commits undercontracts/, as written and highlighted, with the file beside it under its own name. A WSDL, XML Schema, XML, GraphQL or RAML file counts by its extension, and a JSON or YAML file when it declaresasyncapi:. Any other JSON or YAML file counts when a sidecar beside it declares it:carrier-sla.autocatalog.jsonholding{ "specification": { "name": "Carrier SLAs", "summary": "…", "service": "ShippingService" } }. The service it names, or one its title names, lists it under APIs and specifications. A sidecar naming a service no event mentions is a build warning. Only regular files up to 1 MB of text are published./specslists them all. -
/flows/<repository>/<id>/draws a business process a repository declares inflows/<id>.md: each step, the branches between them and the Markdown below the frontmatter. The map walks through it one step at a time, and groups it by domain or by owning team, as boxes or lanes. Each step names an event asdomain/id, a service or an agent, or someone outside the catalog:---title: Order to dispatchsteps:- id: customeractor: { name: Customer }next_step: { id: pay, label: Pays }- id: payservice: PaymentServicenext_steps:- { id: captured, label: Card accepted }- { id: declined, label: Card declined }- id: capturedevent: payments/payment.captured- id: declinedtitle: Ask for another card---A step that a later failure has to undo names the step that undoes it:
compensated_by: { id: refund, label: Order cannot ship }. The map draws it as its own edge, and the step list says “If a later step fails, Refund the payment undoes it.”When a saga carries out the flow, the page shows how its runs went, from the runtime snapshot of whichever repository runs it (workflow runs): “Ran 4 times: 3 completed, 1 compensated.”, each step’s runs beside it, and any step the workflow ran that the flow does not name. The workflow is the one named as the flow is, by id or title;
workflow: FulfilOrdernames another. When two repositories record a workflow of that name, the flow’s own repository wins, and with neither the build says so rather than guess.An
externalSystemstep takes aname,summaryandurl, and acustomstep atitle,summaryandurl. A flow can name another repository’s resources. One whose step names nothing in the catalog is left out with a warning.ac docs checkfails a flow that does not hold together (a step leading nowhere, a step naming two things, a link that is not a web address) and leaves a step naming something outside the checkout to the site, since one repository’s CI cannot see the others. The walkthrough starts at the first step nothing leads to, so a flow where every step has one leading to it is a build warning. A hop the catalog does not back, such as a service the flow says produces an event no producer declaration names, is a build warning, and the flow still publishes: the flow is what the team says happens, and the catalog is what the code declares. Event and service pages list the flows they are a step in, and/flowslists them all.A flow’s map opens grouped the way its frontmatter says:
lanes: teamgroups it by owning team as boxes, andlanes: { by: system, style: lanes }stacks each system in a lane.byisdomain,system,teamordomain-system. A step the catalog places in the wrong lane, or does not place at all, says where it sits withlane: { team: Storefront }(any ofdomain,system,team); the step’s own lane wins over the catalog’s. -
/designs/<repository>/<id>/draws architecture a repository proposes indesigns/<id>.md, before anyone builds it. The frontmatter holds resources and relations in the vocabulary of EventCatalog Studio’s model, so the same design opens there:---title: Refundsstatus: proposedresources:- { id: payments, type: domain, name: Payments }- { id: refunds, type: system, name: Refunds, parent: payments }- { ref: service/PaymentService, parent: payments }- { ref: event/payments/payment.captured }- { id: RefundService, type: service, parent: refunds }- { id: refund.issued, type: event, name: Refund issued }relations:- { from: RefundService, to: payment.captured, kind: receives }- { from: RefundService, to: refund.issued, kind: sends }notes:- { text: Who approves a refund over £500?, resource: RefundService }---A resource names a catalog resource with
ref:(written asabout:writes one), or gives anidandtype; one written by name is the catalog’s when the catalog has a resource of that kind and name. Relation kinds aresends,receives(both from the service to the message),routes-through,reads,writesandconnects. The page says what the design adds (“1 new service, 1 new event and 2 new relations”), draws what is new in outline, and offers three levels: L3 everything, L2 the services and stores with each message between them as one edge, L1 the domains and systems. Once the code ships, the page shows those parts as in the catalog.ac docs checkfails a design that does not hold together (arefthat resolves to nothing, a relation orparentnaming no resource), and/designslists them all.hotspots:flag what a reader should know first, each atitle, aseverity(critical,warningorinfo), an optionaldescriptionand theresourceit is on; the page lists them before the resources.Under
ac sitedev, a design page has Edit on canvas. It opens the design on EventCatalog Studio’s canvas: drag resources in from the palette, connect them, add sticky notes and hotspots. Beside the canvas, the design is checked against the catalog as it changes: what it adds, and each catalog resource a new relation touches, with its owner and who depends on it today, which is who the pull request needs to hear from. Save (⌘S) writesdesigns/<id>.mdwith one line per resource and relation, and keeps where things sit on the canvas underlayout:, so commit it and the change goes to review like code. A built site has no editor and writes nothing.On the canvas, what the catalog does not have yet has a dashed outline. When the repository has a runtime snapshot, each relation between a catalog service and a catalog event is drawn as the maps draw evidence (solid and thicker the more it ran, dashed when declared and never seen, the inferred colour when it ran though nobody declared it), and the panel names who ran on a drawn event that the design leaves off.
/designshas New design underac sitedev: a title and the repository it goes in, and it opens on an empty canvas asdesigns/<title>.md.When the file changes on disk (an agent writing it through its own tools, an editor, a pull), the open canvas takes it in place; when the canvas has unsaved changes of its own, it asks first. A file written so that it does not hold is named with why (a
refthat resolves to nothing, with the reference it probably meant), and the canvas keeps what it had. -
/glossary/holds each domain’s ubiquitous language. A repository defines a domain’s terms inglossary/<domain>.md, in EventCatalog’sdictionary:shape:---dictionary:- id: capturename: Capturesummary: Taking money a card issuer has already authorised.description: A capture can be for less than the authorised amount, never more.aliases: [charge, take payment]about: [service/PaymentService, event/payments/payment.captured]---aliasesare the other words teams use for the term, andaboutnames what implements it, in the form a document’sabout:takes. The glossary shows both, and/catalog.jsoncarries each term as atermresource thatdescribesthose services and events, so an agent asking about “charging a card” gets from the word to the code. Each term has an anchor,/glossary/#payments-capture, so a document can link to it, andllms.txtlists every term with its meaning, aliases and implementations.ac docs checkfails a term defined twice in one domain, including across two repositories, and warns of a glossary for a domain no event or service names. -
/entities/<repository>/<id>/describes a thing the domain talks about, fromentities/<id>.md. The frontmatter takes EventCatalog’s fields:name,summary,domains,services,aggregateRoot,identifierandproperties. Pointschema:at a JSON Schema in the repository and the page reads the properties from it instead:---name: PaymentaggregateRoot: trueidentifier: paymentIdschema: contracts/payment.schema.jsondomains: [payments]services: [PaymentService]---The page lists the properties, links the services that hold the entity, and lists the events whose schema carries the identifier, which the site finds by field name. A property with
references: <entity id>joins two entities, and the page draws them as an entity-relationship diagram./entitiesdraws every entity that way.ac docs checkfails an identifier that is not a property, a service the catalog lacks, a schema outside the repository, and an entity id another repository already defines. -
/data-products/<repository>/<id>/maps a data product fromdata-products/<id>.md: what feeds it on the left, what it publishes on the right, and the contract each output keeps. Inputs and outputs name resources asabout:writes them, or another data product asdata-product/<id>:---name: Daily revenuedomain: paymentsinputs: [event/payments/payment.captured]outputs:- { ref: service/LedgerService, contract: contracts/daily-revenue.schema.json }---ac docs checkfails a reference that resolves to nothing, with the nearest match, and a contract file that is not there. One repository defines each data product id, and the site leaves out a second file that claims it. -
Owner pages show what the directory says of a team or person:
teams/<id>.md(name,summary,email,slackDirectMessageUrl,msTeamsDirectMessageUrl,members) andusers/<id>.md(name,role,avatarUrland the same contact fields). A file matches an owner when its id or name is the owner as CODEOWNERS or frontmatter writes it, soteams/payments.mddescribes@acme/payments. Keep the directory in one repository, such as the handbook:ac docs checkfails a team or user described twice, and warns of a member with nousers/file. Any ofglossary/,entities/,data-products/,teams/,users/ordesigns/enrols a repository on its own, so a handbook can hold the directory alone. -
/data/<repository>/<model>/is one table or collection: where its definition was read, who owns it, its fields (type, required, primary key, unique, default, database name, enum values, the model a key column points at), its relations, its indexes, an entity-relationship diagram of the model and every model one relation away, and the services that read and write it./datalists every model with its datastore, repository, owner, field count, readers and writers. Maps and the Catalog link each table and collection to its page. Models come from each repository’s.autocatalog/resources.json, or, without one, from the site scanning the repository’s Prisma, Drizzle and Mongoose code; see brownfield resources. -
/schemaslists every event schema with its format, version, domain and owner. -
/fieldslists every field across every event, the services a change to it would reach, and the fields whose type or requiredness disagrees between events. -
/retirementslists each deprecated event, and each field a schema marksdeprecated: true, with where every consumer stands (retiring a field). It appears once there is one. -
/catalogshows the whole knowledge graph, and/catalog.jsonholds the same graph as JSON. -
/decisions/,/runbooks/and/incidents/list every ADR, runbook and incident across every repository, newest first, in a table of the columns the conventions file names. The status shows as a badge, and the table filters by status and by repository. The decisions page also draws each supersede chain, oldest first. A kind the organisation adds gets the same page at its ownpath, and a kind with no documents gets no page. The Catalog sidebar group and the home page’s Explore cards link each one, and the home page counts them. -
A decision, runbook or incident page is still the document, with its state as a badge under the title and its kind, date, other fields, what it applies to (its
about:targets, linked to their service and event pages) and what it supersedes or was superseded by. -
Service and event pages list the decisions, runbooks and incidents that name them in
about:, one card per kind, beside their other documents. -
/channelslists each queue and topic: the ones runtime evidence saw, with their messaging system, and the channels designs draw./systemslists the systems and external systems designs name. Each has its own page. -
On an event’s schema tree, each field lists the other events that carry the same path and the consumers that read it.
-
A draft, deprecated or superseded resource shows a banner above its body, and a deprecated one says since when. The home page counts what needs review, events with no producer or consumer, and resources with no owner; each count opens the Catalog filtered to them.
-
Pin a page with the star beside its title. Pins sit at the top of the sidebar, in this browser only. Drag the sidebar’s edge to resize it, or collapse it.
-
Add
?embed=1to any page to hide the header, sidebar and footer, for a frame in another tool. -
/contracts/docs-manifest.jsonlists every document and who links to it. Pointac docs check --manifestat it and each repository’s CI catches a link to a missing document, or a rename that would orphan a link from elsewhere.
Write in MDX
Section titled “Write in MDX”A document can be .mdx as well as .md. Four components draw from the
catalog, and you use them without importing anything:
<Owner name="Payments" /> takes a card payment in two steps: authorisation, then<Term id="capture" />.
<Map focus="service/PaymentService" />
<Schema event="event/payments/payment.captured" /><Map focus="…">draws the map around a resource written asabout:writes it, orflow/<id>,domain/<name>ordata-product/<id>. Leave outfocusfor the whole catalog.<Schema event="event/<domain>/<id>">shows the event’s schema, linked to its page.<Term id="…">links a glossary term and shows its meaning on hover. Adddomainwhen two domains define it.<Owner name="…">links the owner’s page.
In .md and .mdx alike, [[kind|id]] links a catalog page:
[[service|PaymentService]], [[event|payments/payment.captured]],
[[domain|payments]], [[owner|…]], [[channel|…]], [[system|…]],
[[doc|…]], [[flow|…]] or [[design|…]]. An event written as
domain/id opens the Catalog searching for it.
A focus, event or term that names nothing fails the build, with the nearest match. Import a component of your own as MDX always allows, and your import wins over one of the built-in names.
Make it yours
Section titled “Make it yours”The site carries your name, not ours. Keep one file beside your build and ac site reads it, so the command takes no flags:
{ "$schema": "https://unpkg.com/autocatalog-docs-core/contracts/autocatalog.site.schema.json", "title": "Acme Docs", "description": "Documentation from every Acme team.", "brand": "./brand", "theme": "teal", "navigation": { "groups": [ { "label": "Browse", "items": [ "catalog", "domains", { "id": "owners", "label": "Teams" }, { "label": "Status", "href": "https://status.acme.com" } ] } ] }}That is autocatalog.site.json: plain JSON, so reading it runs nothing, and it
fails the build on a field it does not know. A flag of the same name wins over
the file.
theme is an accent colour: teal, blue, orange, green or purple.
navigation.groups replaces the catalog’s group in the sidebar. An item is a
page by its path, optionally renamed, or a link of your own. A page your catalog
has nothing for is left out, so a group can list everything you might want. Each
repository’s documents follow, arranged by its
docs/meta.json.
brand is a folder of your files. Each one is optional:
brand/ logo.svg # replaces the title in the header (svg, png, webp, jpg, avif) logo-dark.svg # the same for the dark theme favicon.svg theme.csstheme.css sets the shadcn/ui tokens the
whole site reads, Starlight’s pages included. Set each one for both themes:
:root { --font-sans: 'Inter', system-ui, sans-serif; --sl-font-mono: ui-monospace, Menlo, monospace; --background: oklch(1 0 0); --foreground: oklch(0.15 0 0); --card: oklch(1 0 0); --sidebar: oklch(1 0 0); --secondary: oklch(0.97 0 0); --muted: oklch(0.97 0 0); --accent: oklch(0.96 0.02 300); --primary: oklch(0.45 0.2 300); --ring: oklch(0.45 0.2 300); --border: oklch(0.9 0 0); --input: oklch(0.9 0 0);}
[data-theme='dark'] { --background: oklch(0.16 0 0); /* and the rest, for the dark theme */}The provenance colours (declared, extracted, inferred) carry meaning on every page, so keep them.
To replace the home page, add index.astro to --pages. For more, such as your
own Markdown plugins or a replaced component, run the site as your own Astro
project with autocatalog-docs-astro, which takes theme and navigation as
options. Starlight’s logo, customCss, head and components keep working,
and your customCss loads after the theme. Use unified() as the Markdown
processor, with rehypeDocsBase so links reach the right pages, as
the bundled site
does.
Your own pages and API routes
Section titled “Your own pages and API routes”A site you run as your own Astro project already takes pages and API routes in
src/pages/. For the site ac site builds, keep them in a folder laid out the
same way:
ac site --pages ./site-pagessite-pages/team.astro publishes /team/, and site-pages/api/glossary.json.ts
publishes /api/glossary.json. An index.astro replaces the home page. Files
and folders that start with _ are left out, so a layout can sit beside the
pages. A page at a path the catalog publishes fails the build with the file to
rename. In your own project, pass the folder as autocatalogDocs({ pages }).
Pages import from the site’s dependencies. docsCatalog() hands a page the
catalog every page is built from:
import { docsCatalog } from 'autocatalog-docs-astro';
export async function GET() { return Response.json((await docsCatalog()).model.glossaries);}For agents
Section titled “For agents”Every document, event, service and data model page has a Markdown twin at <url>.md. Its
frontmatter adds updated, source and url, so an agent can cite who owns an
answer and when it last changed. Each page’s <head> links to its twin. Every
event publishes its contract at schema.json beside its page. /llms.txt lists
documents and events together, and /llms-full.txt holds the full text of
every document, event and data model. A decision, runbook or incident’s twin
also carries kind, even when its folder alone set it, and its llms.txt line
ends with its kind, status and date.
A ```mermaid fence in a document or an event’s index.mdx renders as a
diagram in both themes, and the twin keeps the source.
Deploy
Section titled “Deploy”The site action builds the site and leaves hosting to you. With topic and
owner in autocatalog.site.json, it needs no other settings:
name: siteon: push: { branches: [main] } schedule: [{ cron: '*/30 * * * *' }] workflow_dispatch:
permissions: { contents: read, pages: write, id-token: write }
jobs: deploy: runs-on: ubuntu-latest environment: { name: github-pages } steps: - id: site uses: jagreehal/autocatalog/actions/site@v1 with: changed-only: true site: https://acme.github.io base: /catalog/ - if: steps.site.outputs.built == 'true' uses: actions/upload-pages-artifact@v3 with: { path: dist } - if: steps.site.outputs.built == 'true' uses: actions/deploy-pages@v4changed-only skips the build when no enrolled repository has a new commit and
the site’s own repository has not changed, so a frequent schedule costs almost
nothing and a new repository still joins without anyone touching the site.
Keep the deploy in the job that builds, as here, and the next run retries a
failed deploy. Any host works: follow the action with cloudflare/wrangler-action
or your own step, gated on built.
To read private repositories, pass a token that can, such as one from a GitHub
App. The token clones and stops there: ac site keeps it out of the build,
where MDX from an enrolled repository runs. Enrol repositories you trust, and
keep other secrets out of the build step. On another CI, run ac site yourself; ac site --fingerprint prints what
the action compares.
Narrative alongside generated state
Section titled “Narrative alongside generated state”An event folder may contain an index.mdx. Its body renders on the event page
without changing any generated field, so a runbook step or a caveat lives next
to the contract. Regenerating the catalog leaves it alone.
Diagrams
Section titled “Diagrams”A diagram can tell its story. Add %% narrate <node>: <sentence> lines and the
page plays it step by step, reading each sentence aloud. Write
```mermaid interactive and the reader clicks through its branches instead:
```mermaidflowchart LR A[Submit batch] --> B[Validate] --> C[Pay out] %% narrate A: A client submits a batch of payments. %% narrate B: Each row is checked against its payee.```--eventcatalog has EventCatalog draw the
diagrams, inside this site rather than beside it:
npx autocatalog site --topic catalog --owner acme --base /catalog/ --eventcatalogThe command writes the composed catalog into an EventCatalog project, builds it
under <base>eventcatalog/, and puts the result in the same output, so it
deploys with the site. The Catalog group gains an Architecture page that hosts
EventCatalog whole, its own header and navigation included, inside the site’s
layout. /architecture/?at=<page> opens it at any EventCatalog page, and the
address follows the reader around EventCatalog, so a reload or a shared link
reopens the same diagram. EventCatalog takes the site’s light or dark theme and
switches with it. Each event and service page links to its diagram there.
Without JavaScript the Architecture page links to EventCatalog directly.
Without the flag, nothing about EventCatalog appears.
EventCatalog is installed with npm, once, into eventcatalog/ beside the
--cache directory, and installed again only when the pinned range changes.
The range is @eventcatalog/core@^4.10.7: EventCatalog 5 moves to the Business
Source License, so the site stays on 4. EventCatalog 4 is MIT apart from its
enterprise code, which is under EventCatalog’s commercial licence; its
LICENSE is published beside its output, at eventcatalog/LICENSE. The site
never works around EventCatalog’s licence key: without one, EventCatalog shows
its own navigation.
EventCatalog 4’s build reports anonymous usage to EventCatalog (organisation name, resource counts, a content hash) and has no setting to turn that off. Nothing else in autocatalog sends anything, so the build says so when it runs EventCatalog.
With EventCatalog’s licence key (EVENTCATALOG_LICENSE_KEY_BACKSTAGE, read
from the environment by EventCatalog itself), its embed mode is allowed, and
event and service pages show their diagram inline as well, without
EventCatalog’s navigation. The build fails if a page the site links to was not
built. The broken-link check covers the site’s own pages and leaves
eventcatalog/ to EventCatalog. Only a build draws diagrams, so
--eventcatalog does not combine with --dev.
EventCatalog on its own
Section titled “EventCatalog on its own”To keep a separate EventCatalog project, write the catalog into it:
ac to-eventcatalog ./eventcatalog --root ./eventsThe same export runs as an EventCatalog generator, so an existing EventCatalog
project adds one line to eventcatalog.config.js:
generators: [['autocatalog-cli/eventcatalog', { repositories: ['../payments-service'] }]];eventcatalog generate then scans each repository the way ac scan does and
writes what it finds. The plugins a repository installs come with it: set
allowCodeExecution: true for one that defines its events in Zod, and the
generator runs autocatalog-plugin-zod and any
plugin of your own in its isolated process. roots reads
events/ folders a repository already scanned and committed. When a scan
fails, eventcatalog generate exits 1, and your CI stops before it publishes.
Each event page in EventCatalog leads with where its contract was found. A
domain retired in source disappears on the next export. The export lists every
file it wrote in .autocatalog-export.json and removes only those next time, so
a page, diagram or asset added by hand stays, even inside a generated domain.
Each file is recorded before it is written, so a failed export can be run
again. If the manifest can’t be read, the export stops and deletes nothing.
Upgrading a project exported before the manifest existed removes only files
with proof that autocatalog wrote them. Anything it can’t be sure of, such as
an example or a service linked to a generated domain, stays and is reported.
A resource someone else already wrote under the same id is left alone and
reported.
As an EventCatalog Federation source
Section titled “As an EventCatalog Federation source”EventCatalog Federation (an EventCatalog Enterprise feature) combines many teams’ EventCatalog projects into one. A repository joins as a source by committing its export:
ac to-eventcatalog ./eventcatalog --root ./eventsand the central catalog names that folder:
federation: { sources: [{ id: 'payments', source: 'github:acme/payments-service', path: 'eventcatalog' }],}Federation indexes the folder with the EventCatalog SDK’s buildIndex, and
autocatalog’s tests build that index from every export. For autocatalog’s own
site, ac site reads the same repositories’ contracts.
With autotel
Section titled “With autotel”autotel’s autotel-eventcatalog can also write EventCatalog resources
(generate), but from a runtime snapshot: ids, versions and schemas inferred
from what the tests sent, with no owners, contracts or unexercised events. Keep
one writer. Generate with autocatalog, and let autotel stamp and check:
npm test # ArchitectureSnapshotSubscriber writes .autotel/snapshot.json; commit itac drift --base-ref origin/main --root . --snapshot .autotel/snapshot.json --fail-on suspiciousac site <root> --eventcatalog # evidence on the site's event pages, exported into EventCatalognpx autotel-eventcatalog stamp --snapshot .autotel/snapshot.json --catalog <project>stamp writes its evidence into each event page between
<!-- autotel:stamp-start --> and <!-- autotel:stamp-end -->. The export
rewrites the page but keeps that block where it was, so a later export,
including the generator inside an EventCatalog build, does not erase it, and
stamping again changes nothing. ac drift --snapshot is the runtime gate, in
the same report as the contract changes: events and fields the code sends that
the catalog does not declare, matched as autotel-eventcatalog drift matches
them (see Drift gating in CI).
Each event’s transport becomes an EventCatalog channel: one channel per
address, however many events share it, with the address and protocols. The
services that send and receive the event point at it (sends[].to,
receives[].from, where EventCatalog 4 keeps them, carrying the delivery
mode); an event no service sends or receives points at it itself. Channels are
owned through .autocatalog-export.json like every other page, and
ac from-eventcatalog reads them back as transport.
Experiment 05 runs this end to end.
EventCatalog gives each event id one page. If two repositories, or two domains, publish the same id, the export stops before writing anything and names both. Rename one, or export them to separate EventCatalog projects.