Skip to main content
This page defines what content belongs on docs.optimism.io and, for every content type, which source is canonical. It exists so that “where does this live?” is settled by citing a rule, not re-argued in every pull request. Reviewers should link the relevant section of this page when requesting changes. The approach is adapted from the Kubernetes content guide, which governs kubernetes.io with the same two ideas: a short allowlist test for what the site hosts, and a strict preference for linking canonical sources over restating them.

What’s allowed: the three-clause test

Content belongs on docs.optimism.io only if at least one of the following is true (adapted from the Kubernetes content guide’s third-party content rules):
  1. It documents first-party OP Stack software — software whose source of truth lives in Optimism repositories, such as the components listed on the Releases page.
  2. It documents third-party software that the OP Stack needs to function — for example, an L1 execution client or key-management tooling that an OP Stack chain cannot run without. Such content must be marked with the <ThirdPartyContent> component.
  3. It routes to canonical content that lives elsewhere — a selection, orientation, or hub page whose job is to send readers to the right canonical home (for example, a curated matrix of SDKs that links each SDK’s own documentation).
Content that satisfies none of the three clauses — tooling promotion, project-specific marketing, or documentation for software that is neither first-party nor required by the OP Stack — belongs on the third party’s own site, not here. Wherever a canonical source already exists, link it — never restate it. The Kubernetes content guide states the reason plainly: dual-sourced content “requires double the effort to maintain and grows stale more quickly.” In practice:
  • Never paraphrase normative protocol text. Explain the concept in your own words at explanation depth, then deep-link the exact section of the OP Stack specifications for the normative definition.
  • Never copy reference material from another living document. If a component’s book, README, or upstream API reference already documents something, link to it.
  • Never fork a table of facts (versions, addresses, activation times, flag lists) that another system maintains. Render from the source of truth or link it.

Canonical homes

One home per thing. The matrix below assigns a canonical home to each content type across the three layers of the OP Stack documentation surface — the protocol, the components, and the periphery — and states what docs.optimism.io holds for each. Precedents for the matrix, clause by clause:
  • Spec joined, never mirrored. Kubernetes documents feature lifecycles through its structured feature gates reference rather than copying design documents into prose.
  • One identity page per component. Cloudflare publishes a uniform per-product content strategy so every product’s documentation set has the same shape.
  • A curated matrix over the periphery. Stripe’s SDK page differentiates its client surfaces in one table; ethereum.org publishes written listing criteria so curation is policy application rather than per-PR debate.
  • The component declares its docs home. Each component’s README should point at its canonical documentation, following the op-deployer README model.
When two pages could both claim a topic, the matrix decides. If the matrix does not cover the case, raise it in the docs PR and propose a new row — amendments to this page go through the same review as any other docs change.

Component reference

Every first-party component has one reference in these docs, covering its command-line interface, its JSON-RPC API, and its metrics, and every page of it is generated. This section is the convention that makes those references uniform across components; the generator and its lint enforce it, and reviewers cite it when a change would break it. Components covered: op-node, op-batcher, op-proposer, op-challenger, op-conductor, op-supernode, op-deployer, op-reth, and kona-node. A component joins the list by being added to the generator, never by a hand-written page.

Source of truth

Every surface is generated from the component at a finalized release tag, never from a pre-release, and never from develop except into the develop version described under Versions.
  • Command line: the --help output of the published release artifact (the Docker image published for the tag, or the release binary where one is published). --help is the one surface every binary exposes the same way, whether it is a Go program built on urfave/cli or a Rust program built on clap, and it is by definition what the released binary accepts, so it cannot drift from the release the way a source parser run on the wrong commit can. geth and reth document their CLIs from the same output.
  • JSON-RPC: the method registrations in the component’s source at the tag: the rpc.API namespaces the Go services register, and the #[rpc] and #[method] attributes on kona-node’s jsonrpsee traits. A component that inherits an upstream client’s RPC (op-reth) points at the upstream reference for the inherited namespaces and generates only the OP Stack additions.
  • Metrics: the metric definitions (name, type, labels, help string) in the component’s source at the tag, with the same upstream rule.
  • Configuration schemas that have no --help, such as the deploy config, are generated from the source at the same tag.

Rendering

One generator, one output shape, whatever the framework. Every flag renders as a table row (flag, environment variable, default, description), and the verbatim --help text follows in an expandable block so an operator can match what their terminal shows. RPC pages render one section per namespace with a table of methods (method, parameters, result, description). Metrics pages render one table (name, type, labels, description).

Page tree

  • One page per top-level command.
  • Subcommand families collapse onto their parent page as one heading per subcommand, so a subcommand stays deep-linkable by anchor (db/checksum#mdbx) without a page of its own.
  • Option blocks that every command repeats (logging, metrics, profiling, RPC) hoist to one global-options page per component.
  • One rpc page and one metrics page per component, beside the command pages.
  • URLs are reference/<component>/<version>/<page> and never deeper.

Versions

  • One version per minor release line (v1.19, v2.4), generated from the latest finalized patch in that line. The provenance header names the exact tag.
  • The newest line is the default and is tagged Latest. The newest three lines are published; older lines are removed, with redirects into the newest line.
  • A patch release that changes a surface regenerates its line in place.
  • One further version named develop, tagged Unreleased, generated from the develop branch on every merge that touches the component. Its provenance names the commit, and its pages carry noindex: true, so unreleased behavior never appears in site search or search engines. It exists so that a reader who meets an <Unreleased> callout can see what is coming.

Provenance and no hand edits

  • Every generated page opens with a DO NOT EDIT comment, before any prose and after any imports, naming the exact tag (or commit, for the develop version), linking the GitHub release, and naming the artifact it was generated from. The generator’s manifest records the tag, artifact, and content hash per component per version.
  • Release notes are linked, never copied. The provenance link is the pointer to what changed in that release; the generated release history pages remain the home of the notes.
  • Nothing under the top-level reference/ directory is hand-written. Orientation (“how the CLI is organized”), advice on choosing values, and notes on selected flags live in guides in the persona tabs and link into the reference. pnpm lint:reference fails any page under reference/ without the generated header.
  • A hand edit to a generated page fails the generator’s drift check on the next run; regenerate instead.
Transition. The component references published today live inside the persona tabs (for example chain-operators/reference/ and node-operators/op-reth/cli/), some generated and some hand-maintained. They move into the top-level reference/ tree as each component joins the generator, and the rules above apply to a component once it has moved. Until then, do not add a new hand-maintained flag table anywhere; add the component to the generator instead.

Regeneration and automation

Nobody runs the generator by hand, and nothing depends on an agent: every job below is a deterministic script in monorepo CI, and its output is verifiable by the generator’s own --check.
  • On every finalized release tag, a CI job runs the generator against that release’s artifact and opens a docs pull request with the regenerated version, its manifest, and any redirects. The docs team reviews and merges it. A tag that has been public for a week with no such pull request is a pipeline bug; file it.
  • On every merge to develop that touches a covered component, the same job regenerates that component’s develop version and merges it without review, since it carries no editorial content and is not indexed.
  • On every docs pull request, CI runs the docs lints: navigation, redirects, reference, and link policy.
  • Weekly, CI regenerates the release history pages from GitHub Releases into one rolling pull request, and runs the reference lint in strict mode, which fails on an <Unreleased> callout whose release has since been tagged and lists the callouts that name no version, so a maintainer can confirm what has shipped.
Each job lands together with the generator it serves; until a job exists, its step is done by hand and the convention still applies.

Documenting unreleased changes

docs.optimism.io deploys from develop; components ship from release tags. A component pull request that changes behavior should update the docs in the same pull request, as follows:
  • Generated reference: do nothing. The pipeline regenerates the component’s reference at the next finalized tag.
  • Hand-written guides and explainers: edit the prose in the same pull request and wrap the changed statement in the <Unreleased> component, naming the component. The release that will carry the change is usually not known when the change merges, so the version is optional; give it when you know it:
    Which renders: The callout is temporary. When it names a version, pnpm lint:reference warns on every run once that tag exists, and the weekly CI sweep, which runs the lint in strict mode, fails until the callout is removed. The check lives in the weekly job rather than in pull-request CI so that a release tag appearing cannot turn an unrelated pull request red. When the callout names no version, the weekly sweep lists it, and a maintainer removes it after confirming the change has shipped. The lint also rejects a callout that names a component outside the covered list, or a version that is not a full vX.Y.Z, since such a callout could never become stale.
  • Removing a feature: add the callout, or set deprecated: true in the page’s frontmatter, in the same pull request. Delete the page only after the release ships, with a redirect.
  • Embargoed content that must not go live before a release uses the flag:merge-pending-release pull request label instead.

Marking third-party content

This section documents the <ThirdPartyContent> component, following the Kubernetes thirdparty-content shortcode pattern: every third-party mention is stamped the same way, so third-party content stays greppable and auditable.
Pages or sections that document or list third-party software (clauses 2 and 3 of the three-clause test) must open with the <ThirdPartyContent> component:
Which renders:
Items on this page refer to third-party projects or products that are not maintained by Optimism. They are provided for convenience; refer to each project’s own documentation as the source of truth.
When an entire page is about a single third-party project or product, use the single-project variant instead:
Which renders:
This page refers to a third-party project or product that is not maintained by Optimism. It is provided for convenience; refer to the project’s own documentation as the source of truth.

Citing the normative spec

This section documents the <NormativeSpec> component, the standing callout that applies the dual-sourcing ban to protocol pages: every page whose subject is normatively defined in the OP Stack specifications is stamped the same way, so spec citations stay uniform and greppable.
The component is reserved for explainer pages — pages whose frontmatter declares diataxis: explanation, primarily the concept pages in the OP Stack section. An explainer page whose subject is normatively defined in the spec must open with the <NormativeSpec> component, deep-linking the exact spec section that defines its subject:
Which renders: All four variables are required: what names the subject the spec defines, title and href name and deep-link the governing spec section (a rendered specs.optimism.io URL on its current path, per the link policy), and note states what the page does instead of restating the spec. Guides, tutorials, and reference pages do not open with the component. They should not go deep into protocol workings at all — that depth belongs on an explainer page or in the spec itself. Where a guide, tutorial, or reference page needs to touch a spec-defined concept, link the relevant spec section inline at the point of use instead. One deliberate exception: the hardfork registry pages are reference pages, but the registry’s purpose is the spec pointer, so they carry the same component with the spec URL from their structured frontmatter.

Next steps