opsdocsConsole

Ops platform / Publish

Publish docs

Keep docs beside your code as Markdown; ops stores every version and the docs site shows it.

Updated 13 Oct 2026View as Markdown

Docs live in a docs folder beside the code they describe. Each version of a package or each production deploy of a service publishes the folder as a new version of its doc set, and the docs site shows it within seconds. Nothing is rebuilt for it.

Doc sets are public: anyone can read them without logging in. Never put anything private in a docs folder.

The folder

Pages are Markdown, and the folder's own layout is the navigation. index.md is the first page of a folder.

text
docs/
  index.md
  install.md
  guides/
    index.md
    streaming.md
    images/flow.png

Every page starts with front matter. title is required; description and order are optional, and nothing else is allowed:

md
---
title: Streaming bodies
description: Read and write objects without buffering them.
order: 2
---

order is a whole number and sorts the pages of a folder. Write each value on one line.

What is checked

Nothing is published when a check fails, and the answer names every page that broke it.

  • Every page has a title, and its front matter uses only the three keys.
  • Every relative link and image points at a file in the same folder; a link may leave out .md or point at a folder.
  • Images are PNG, JPEG, WebP or SVG, 2 MB at most, and an SVG holds no script.
  • Every other file is refused. Hidden files, such as .DS_Store, are left out.
  • The folder fits the size limit, 64 MB unless your ops sets another.

Links to other sites, #anchors and absolute paths are not checked.

Publishing

A package publishes its docs in its publish run. A module's docs folder, beside its crate and layer, becomes the version the tag published. The docs folder beside a crate or an npm package that is no module, such as crates/billing/docs or packages/ui/docs, becomes a version of that package's set, named like the package without its scope, at the package's own version. A broken folder refuses the tag before anything publishes. A package without a folder publishes without docs, and the run says so. Pages changed under a version that has its docs already wait for the next version.

Guides that are no package's go in the folder the project file names. A tag's publish run publishes it at the tag's version:

ts
export default defineProject({
  services: {},
  docs: { set: 'rungholt-guides', path: 'docs' },
})

A service names its folder the same way:

ts
export default defineProject({
  services: { api: { dockerfile: 'Dockerfile', port: 8080 } },
  docs: { set: 'rungholt-api', path: 'docs' },
})

Every production deploy then publishes the folder after the service is healthy, as the version named by the deployed commit. A broken folder fails that docs step and notifies you; the deploy itself stands.

Anything else pushes with the CLI and a token that holds package.publish in the project. The Project admin and Developer roles hold it:

sh
ops docs push ./docs --set rungholt-api --version 1.4.0

A set name is lowercase letters, digits and dashes. A set named like a module or a package is that package's set, and its versions must be versions the package has published. Any other set is a service set and names its project the first time: pass --project rungholt, or --kind service to use the project chosen with ops use.

Versions

A version is published once. The same folder pushed again is accepted and changes nothing; other content under the same version is refused, so publish a new version instead.

Yanking a version hides it from readers and keeps its archive. The newest version that is not yanked is the one the docs site shows first.

sh
ops docs                          # every doc set, with its latest version
ops docs rungholt-api             # one set's versions, newest first
ops docs yank rungholt-api 1.4.0

Yanking needs package.publish in the set's project, like a push. Reading needs no login.

In the console

Library, Docs lists every doc set with its latest version, and each reader with the revision it serves and how long it took to pick it up. A set's page lists its versions, with Yank from the site in each version's menu, and the pages of the latest. A project's own sets are under Docs in its sidebar, and a deployment or publish run shows its docs step.

Showing doc sets on your own site

A project that renders doc sets declares itself a reader. Ops then calls it on every new version or yank, so it never has to rebuild:

ts
docsReader: { service: 'web', path: '/api/docs/refresh', secret: 'OPS_DOCS_SECRET' },

Ops sends POST <path> to the production service with {"revision": n} and the value of OPS_DOCS_SECRET from the service's secret group in the x-ops-docs-secret header. The service answers within 60 seconds, with a 2xx status and {"revision": n}, the revision it now serves.

A call that fails is not repeated, so the service also asks on a timer: GET /api/v1/docs/revision?since=<n> answers what changed, and the service sends x-ops-docs-reader (the project slug), x-ops-docs-secret and x-ops-docs-served (the revision it serves) with it, so ops always knows what each reader shows and how long it took to catch up.