Valkyrian Labs logo

Payload Markdown Docs

@valkyrianlabs/payload-markdown-docs turns a repo-local /docs tree into Payload-native documentation that AI can generate, humans can review, CI can publish, and your Next/Payload site can render.

GitHub Workflow Status npm npm downloads license

No hosted docs platform lock-in.
No AI-generated Markdown sludge with no structure.
No babysitting a CMS every time the code changes.

Just plain Markdown docs, native agent skills, trusted publishing, and Payload-owned output.

1pnpm add @valkyrianlabs/payload-markdown-docs @valkyrianlabs/payload-markdown
The pitch

Payload Markdown Docs is the delivery pipeline for teams who want documentation to move as fast as the code it describes. AI gets the speed. Humans keep the control. Payload owns the output.


Documentation that lives with the code

Most documentation systems eventually drift into one of three bad worlds:

Manual docs rot

The code changes. The docs do not. Eventually the documentation becomes archaeological evidence instead of operational truth.

AI slop

Agents can generate Markdown quickly, but without structure, validation, route rules, and renderer guidance, the output becomes a pile of plausible nonsense.

Hosted platform gravity

External docs platforms can be convenient, but they pull your content, routes, rendering, auth, publishing, and ownership away from your app.

payload-markdown-docs takes the fourth path:

1analyze codebase2  -> generate docs with repo-local AI skills3  -> write plain Markdown into /docs4  -> validate and plan the docs tree5  -> publish through GitHub OIDC or Ed255196  -> render inside your Payload/Next site

The docs stay in your repo. The CLI validates them. CI publishes them. Payload stores them. Your site renders them.


What you get

Repo-local Markdown source

Keep human documentation in /docs as plain Markdown files that can be reviewed, edited, versioned, and generated like normal source.

Native AI skill installer

Install Codex and Claude workflow packs so agents know your docs structure, frontmatter rules, validation flow, and renderer syntax.

Payload-native docs records

Sync documentation into Payload collections for docs sets, groups, access records, keys, assets, and rendered content.

Trusted publishing

Publish through GitHub Actions OIDC or Ed25519 signed local pushes with branch, owner, repository, and docs-set validation.

Next.js rendering helpers

Resolve docs routes, render docs pages, build nav trees, generate metadata, and integrate docs into your app frontend.

AI discovery routes

Generate and serve llms.txt, llms-full.txt, docs-set AI files, and native agent skill artifacts from synced docs and skills.

Docs navigation

Use drop-in nav components or headless builders for docs sets, docs groups, and custom site headers.

Validation and planning

Validate docs, generate manifests, preview sync plans, and catch problems before publishing into Payload.

Renderer powered by Payload Markdown

Render structured Markdown through @valkyrianlabs/payload-markdown for cards, callouts, buttons, tabs, steps, code blocks, and docs layouts.


AI-first, not AI-only

This plugin is built for AI-assisted documentation from the ground up.

Give agents a real workflow

Install native skill packs into the repo so Codex and Claude know how to generate docs for your actual pipeline instead of freestyle hallucinating a docs structure.

1pnpm exec payload-markdown-docs install skill --agent codex2pnpm exec payload-markdown-docs install skill --agent claude

Keep humans in command

AI can create the first pass, maintain large sections, and keep documentation moving with the codebase.

Humans still review, edit, tune, and ship plain Markdown like any other source-controlled project asset.

The magic trick

The AI does not just write random docs. It writes docs for a known renderer, known route model, known frontmatter format, known CLI, and known publishing pipeline.

A typical agent prompt after installing the skill:

1Use the installed payload-markdown-docs skill.2 3Analyze this repository and generate a complete documentation tree under /docs.4Use route-aware frontmatter and payload-markdown-compatible Markdown. Validate5the tree with the payload-markdown-docs CLI when finished.

The workflow

  1. 1

    Generate or edit docs in the repo

    Write documentation by hand or ask an agent to generate a complete tree under /docs.

  2. 2

    Validate before publishing

    Run the CLI locally or in CI to validate docs structure, metadata, routes, assets, and sync behavior.

  3. 3

    Publish through a trusted channel

    Use GitHub Actions OIDC for normal CI publishing or Ed25519 signed pushes for local/operator workflows.

  4. 4

    Store and render in Payload

    Payload receives the docs, stores the records, resolves docs sets and groups, and exposes renderable content to your Next app.

  5. 5

    Serve human and AI-facing routes

    Render human docs pages while also serving AI-readable llms.txt, docs-set discovery files, and native skill artifacts.


Install

Install the plugin and its renderer dependency:

1pnpm add @valkyrianlabs/payload-markdown-docs @valkyrianlabs/payload-markdown

Install the CLI package in repositories that publish documentation:

1pnpm add -D @valkyrianlabs/payload-markdown-docs

Configure Payload

Add the plugin to your Payload config:

1import { payloadMarkdownDocs } from '@valkyrianlabs/payload-markdown-docs'2import { buildConfig } from 'payload'3 4export default buildConfig({5  plugins: [6    payloadMarkdownDocs({7      auth: {8        githubOidc: true,9      },10      target: {11        enableDrafts: true,12      },13      sync: {14        allowWrites: true,15        allowPublish: true,16      },17    }),18  ],19})

This adds the docs admin surface, sync endpoint, publishing controls, and Payload-native records needed to receive documentation from your repo.


Public API surface

Use the root package for Payload plugin configuration:

1import { payloadMarkdownDocs } from '@valkyrianlabs/payload-markdown-docs'

This surface is for Payload config and server-side plugin setup.


Create your docs model

Create a docs set in Payload Admin:

1title: Payload Markdown Docs2slug: payload-markdown-docs3branch: main4group: plugins

With the plugins group, the generated route becomes:

1/plugins/payload-markdown-docs

Create a trusted GitHub owner:

1owner: valkyrianlabs2limitRepos: false

When limitRepos is disabled, any repository owned by that GitHub owner can publish to a matching docs set from the configured branch.

Enable limitRepos when you want explicit repository allowlists.


Create a docs tree

A minimal project can look like this:

1docs/2  index.md3  getting-started/4    quick-start.md5  configuration/6    plugin-config.md7  workflow/8    ci-github-actions.md9  reference/10    cli.md11skills/12  payload-markdown-docs/13    codex/14      SKILL.md15    claude/16      SKILL.md
Source split by design

/docs contains human documentation. /skills contains agent-native workflow packages. The plugin syncs both, but treats them as different kinds of source material.

Generated llms.txt, llms-full.txt, and docs-set AI files are built from synced docs, docs set metadata, dependencies, and skills.

You do not need generated-only docs, hidden storage, or a hosted documentation service.


Validate locally

Before publishing, validate the docs tree:

1pnpm exec payload-markdown-docs validate --source payload-markdown-docs

Generate a manifest:

1pnpm exec payload-markdown-docs manifest \2  --source payload-markdown-docs \3  --pretty

Preview the sync plan:

1pnpm exec payload-markdown-docs plan --source payload-markdown-docs

From this package source checkout, use the local source CLI instead:

1pnpm cli validate --source payload-markdown-docs
CI-friendly by default

In GitHub Actions, --source can be omitted when the docs set slug matches the repository name. The CLI can infer it from GITHUB_REPOSITORY.


Publish from GitHub Actions

GitHub Actions is the standard publishing path.

GitHub signs the publish request with OIDC. Payload verifies the trusted owner, repository, branch, and docs set before accepting the sync.

1permissions:2  contents: read3  id-token: write4 5steps:6  - uses: actions/checkout@v47 8  - uses: pnpm/action-setup@v49 10  - uses: actions/setup-node@v411    with:12      node-version: 2213      cache: pnpm14 15  - run: pnpm install --frozen-lockfile16 17  - run: pnpm exec payload-markdown-docs validate --source payload-markdown-docs18 19  - run: |20      pnpm exec payload-markdown-docs push \21        --endpoint "$DOCS_SYNC_ENDPOINT" \22        --repository "$GITHUB_REPOSITORY" \23        --branch "$GITHUB_REF_NAME" \24        --commit "$GITHUB_SHA" \25        --github-oidc \26        --publish

Sync writes require:

1sync: {2  allowWrites: true,3}

Publishing also requires:

1sync: {2  allowPublish: true,3},4target: {5  enableDrafts: true,6}

That is the normal docs CI path:

1commit docs2  -> validate docs3  -> push docs4  -> publish docs

Push locally with Ed25519

For advanced operator workflows, use Ed25519 signed pushes from your editor, local machine, internal tooling, or non-GitHub environments.

Generate a keypair:

1pnpm exec payload-markdown-docs keygen --out .docs-sync

Add the public key in Payload Admin:

1Docs Globals > Access

Push with the private key:

1pnpm exec payload-markdown-docs push \2  --endpoint "$DOCS_SYNC_ENDPOINT" \3  --source payload-markdown-docs \4  --key-id local-docs \5  --private-key-file .docs-sync/docs-sync-private.pem

For immediate publishing:

1pnpm exec payload-markdown-docs push \2  --endpoint "$DOCS_SYNC_ENDPOINT" \3  --source payload-markdown-docs \4  --key-id local-docs \5  --private-key-file .docs-sync/docs-sync-private.pem \6  --publish
Operator mode

Edit locally, validate locally, push directly, and review the rendered docs on your Payload site.


Render docs in Next

The plugin does not mutate your Pages collection and does not register public frontend routes for you.

Add route handlers where you want docs to render:

1import config from '@payload-config'2import {3  PayloadMarkdownDocsPage,4  resolvePayloadMarkdownDocsRoute,5} from '@valkyrianlabs/payload-markdown-docs/next'6import { notFound } from 'next/navigation'7import { getPayload } from 'payload'8 9export default async function Page({ params }: { params: Promise<{ slug?: string[] }> }) {10  const { slug } = await params11  const payload = await getPayload({ config })12 13  const resolved = await resolvePayloadMarkdownDocsRoute({14    payload,15    slug,16  })17 18  if (resolved) {19    return <PayloadMarkdownDocsPage resolved={resolved} />20  }21 22  notFound()23}

Your app decides where documentation lives. The plugin handles route resolution and rendering.


Drop-in navbar

Use PayloadMarkdownDocsNavbar when you want the plugin to own the docs menu UI.

Header adapter

Append docs groups and docs sets to an existing site header without rewriting your navigation model.

Headless nav builder

Use getPayloadMarkdownDocsNavItems when you want full control over rendering, routing, analytics, and layout.

Drop-in navbar example:

1import { PayloadMarkdownDocsNavbar } from '@valkyrianlabs/payload-markdown-docs/next'2import type { Payload } from 'payload'3 4export async function HeaderDocsNav({ payload }: { payload: Payload }) {5  return (6    <PayloadMarkdownDocsNavbar currentPath="/plugins/payload-markdown-docs" payload={payload} />7  )8}

Header adapter example:

1import { appendPayloadMarkdownDocsHeaderNavItems } from '@valkyrianlabs/payload-markdown-docs/next'2 3const navItems = await appendPayloadMarkdownDocsHeaderNavItems({4  existingItems: header.navItems ?? [],5  maxItems: headerNavItemsMaxRows,6  payload,7})

Headless nav example:

1import { getPayloadMarkdownDocsNavItems } from '@valkyrianlabs/payload-markdown-docs/next'2 3const docsNav = await getPayloadMarkdownDocsNavItems({4  availableSlots: 4,5  payload,6})

Serve raw AI assets

The canonical agent artifacts are normal files under skills/.

push syncs them as raw asset records by convention. Skill files are not docs records and do not need docs frontmatter.

Payload owns the asset storage and handlers, but a Next App Router site still needs route files so public root URLs reach those handlers instead of being swallowed by the frontend catch-all.

Install public Next route files once:

1pnpm exec payload-markdown-docs install routes --payload-app "src/app/(payload)"

Use this for apps without src/:

1pnpm exec payload-markdown-docs install routes --payload-app "app/(payload)"

Useful stable paths include:

1/llms.txt2/llms-full.txt3/plugins/payload-markdown-docs/llms.txt4/plugins/payload-markdown-docs/llms-full.txt5/plugins/payload-markdown-docs/skills/codex6/plugins/payload-markdown-docs/skills/codex/SKILL.md7/plugins/payload-markdown-docs/skills/claude8/plugins/payload-markdown-docs/skills/claude/SKILL.md9/plugins/payload-markdown-docs/skills/codex/reference/workflow.md
Sitemaps and AI routes are not the same thing

sitemap.xml is crawler discovery. llms.txt is an AI-readable entrypoint. Skills are native agent workflow artifacts. Keep those surfaces separate on purpose.


Advanced security

You do not need advanced workflow restrictions for normal docs publishing.

Each docs set can optionally enforce exact GitHub workflow refs.

Leave it disabled to allow any workflow from a trusted owner/repository on the configured branch.

When enabled, add every allowed workflow ref explicitly. An empty list rejects all workflow publishing for that docs set.


Built on Payload Markdown

payload-markdown-docs depends on @valkyrianlabs/payload-markdown for structured Markdown rendering.

That means your generated documentation can use the same directive system as the rest of your Payload Markdown content:

Callouts

Highlight warnings, tips, notes, and important documentation context.

Cards and grids

Create feature grids, reference groups, and landing-page style documentation sections.

Buttons and CTAs

Link readers to install guides, repositories, examples, and related pages.

Tabs and steps

Document multi-path workflows, setup flows, and progressive implementation guides.

Code blocks

Render technical examples through the same Shiki-powered code pipeline.

AI-friendly source

Let agents edit readable Markdown instead of opaque CMS state.


Philosophy

The repo is the source of truth

Documentation should live next to the code it explains.

AI should have rules

Agents should receive repo-local instructions, renderer syntax, validation expectations, and publishing constraints.

Humans should keep control

Generated docs should still be plain Markdown that developers can review, edit, and own.

Publishing should be trusted

CI and local pushes should prove who they are before Payload accepts documentation writes.

Payload should own the output

Your app should control routes, rendering, navigation, metadata, and final presentation.

Docs should move with code

A docs pipeline should make maintenance faster, not add another side quest to every release.


Generate fast. Validate locally. Publish safely. Render in Payload.

Payload Markdown Docs is for teams that want AI-accelerated documentation without giving up plain Markdown, source control, trusted publishing, or ownership of the rendered site.