Vaulthalla Logo

Dedicated Docs Sync Workflow

Complete setup guide for syncing Git-backed docs into Payload docs sets.

Dedicated Docs Sync Workflow

This guide covers the default workflow for syncing a Git-backed docs package into Payload-managed docs sets, generated docs records, and static AI-facing asset records.

The client sends docs content. Payload docs sets decide the source slug and branch; global Keys and Trusted records decide which credentials are allowed. Plugin config decides collections, fields, lifecycle behavior, and publishing modes.

Install

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

Server Configuration

Configure the plugin in payload.config.ts with a draft-enabled dedicated docs collection and explicit sync permissions. Add docs packages and trust records in Payload Admin.

1import { payloadMarkdownDocs } from '@valkyrianlabs/payload-markdown-docs'2 3payloadMarkdownDocs({4  auth: {5    githubOidc: true,6  },7  target: {8    enableDrafts: true,9  },10 11  sync: {12    allowWrites: true,13    allowPublish: true,14    allowHardDelete: false,15    deleteBehavior: 'archive',16  },17})

The default endpoint is exposed at:

1/api/payload-markdown-docs/sync

By default, the plugin adds Sets, Groups, Keys, and Trusted collections under Docs Globals. Generated docs records are linked to docs sets and remain the internal records used for routing, search, and sync correctness.

Create a docs set in Payload Admin before the first push:

1title: Main Docs2slug: main-docs3branch: main

The native route adapter can resolve and render generated docs routes from a Next/Payload catch-all route without creating one Page per Markdown file. It is read-only and does not mutate Pages.

The docs set edit view includes a generated docs manager. Use it to review generated docs for the set, inspect sync/archive/draft state, publish draft generated docs when publishing is enabled, see which docs have overrides, and open generated docs records when per-doc override editing is needed.

Docs Source Tree

Keep project documentation in the conventional local package layout:

1docs/2  index.md3  getting-started/4    installation.md5  configuration/6    sync.md7skills/8  main-docs/9    codex/10      SKILL.md11    claude/12      SKILL.md13llms.txt14llms-full.txt

Supported files are .md only. Paths must be relative, must not contain traversal, and must remain inside the docs root passed to the CLI.

Agent workflow packs are separate from human docs. Keep native skill artifacts under skills/<source>/<agent>/ or install them into the target project with payload-markdown-docs install skill --agent codex|claude. During manifest generation, human docs become files; skills, llms.txt, and llms-full.txt become assets.

Key Generation

Generate an Ed25519 key pair:

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

Use the generated keys this way:

  • docs-sync-public.pem goes into Docs Globals > Keys in Payload Admin.
  • docs-sync-private.pem goes into a CI secret such as DOCS_SYNC_PRIVATE_KEY.
  • Do not commit the private key.

Local Validation

Validate the docs tree before any upload:

1pnpm exec payload-markdown-docs validate --source main-docs2pnpm exec payload-markdown-docs manifest --source main-docs --pretty3pnpm exec payload-markdown-docs plan --source main-docs

validate catches path, frontmatter, hash, and manifest issues. manifest prints the JSON payload that will be signed. plan shows what would be created, updated, archived, drafted, deleted, or left unchanged against an optional existing-record input.

Dry-Run Upload

Use dry-run before applying changes:

1pnpm exec payload-markdown-docs push \2  --endpoint "$DOCS_SYNC_ENDPOINT" \3  --source main-docs \4  --key-id github-actions-main \5  --private-key-file .docs-sync/docs-sync-private.pem \6  --dry-run

The endpoint verifies the request signature, timestamp, nonce, body hash, manifest source, and server-side lifecycle policy. It returns a plan summary without changing docs records.

Sync Upload

Apply docs changes only after server config enables writes:

1pnpm exec payload-markdown-docs push \2  --endpoint "$DOCS_SYNC_ENDPOINT" \3  --source main-docs \4  --key-id github-actions-main \5  --private-key-env DOCS_SYNC_PRIVATE_KEY

Sync writes require:

1sync: {2  allowWrites: true,3}

The server can create, update, reactivate, archive, draft, or hard-delete dedicated docs records according to server-owned config. It checks for manual edit conflicts before writing and records sync-run audit data.

Public AI Asset Routes

Docs records render through the route adapter. Raw AI-facing assets use asset route handlers and committed Next route files.

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

The generated files must be committed and deployed. They expose public canonical routes outside /api, including:

1/llms.txt2/llms-full.txt3/<docsSet.routeBase>/llms.txt4/<docsSet.routeBase>/llms-full.txt5/<docsSet.routeBase>/skills/<agent>6/<docsSet.routeBase>/skills/<agent>/SKILL.md

If public asset route files are missing, /api/... asset URLs can work while the public URLs return rendered HTML 404 responses from the frontend catch-all.

Publish

Publishing is a request from the client and a server-owned decision.

1pnpm exec payload-markdown-docs push \2  --endpoint "$DOCS_SYNC_ENDPOINT" \3  --source main-docs \4  --key-id github-actions-main \5  --private-key-env DOCS_SYNC_PRIVATE_KEY \6  --publish

--publish only works when:

1target: {2  type: 'docsCollection',3  enableDrafts: true,4},5sync: {6  allowPublish: true,7}

Without those settings, the server rejects publish requests.

Hard Delete

Archive is the safer default because it preserves missing docs records and sync metadata.

Hard delete requires explicit server configuration:

1sync: {2  allowHardDelete: true,3  deleteBehavior: 'delete',4}

Hard delete applies only to managed dedicated docs records after conflict checks pass. It is not enabled by request body alone.

GitHub Actions

See examples/github-actions/publish-docs.yml for a CI workflow that:

  • validates docs
  • dry-runs signed sync on pull requests
  • syncs and publishes on pushes to main

Required secret:

  • DOCS_SYNC_ENDPOINT

The matching docs set slug must match the CLI source. For GitHub OIDC, add a Trusted owner in Payload Admin. For Ed25519, add the public key in Docs Globals > Keys.

Native Route Adapter

Use the /next export from an existing route layer to resolve docs routes before falling back to your normal Pages collection route.

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

The route adapter can resolve exact docs records, docs set index routes, and docs group index routes where serveIndex is enabled. It returns null for normal Page routes so your app can fall back to existing Page rendering.

Docs Set Admin Manager

Open a docs set in Payload Admin to review its generated docs records from one central place. The manager shows:

  • route base
  • sync summary
  • total, archived, draft, published, hidden-from-nav, and override counts
  • generated docs grouped by source path
  • route, title, status, and override summary for each generated doc
  • links to generated docs records for deeper editing
  • a publish action for draft generated docs when draft publishing is enabled

Per-doc override editing is currently done by opening the generated doc record. Inline override editing from the docs set manager is deferred to a later phase.

The manager does not sync docs, mutate Pages, or create one Page per Markdown file.

Current Boundaries

Implemented for this workflow:

  • dedicated docs collection
  • docs groups and docs sets
  • signed sync endpoint
  • GitHub Actions OIDC auth
  • local CLI validation, manifest, plan, keygen, and push
  • native route adapter, dynamic sitemap helper, and frontend rendering helpers
  • static asset storage for skills, llms.txt, and llms-full.txt
  • public asset route file installer for Next App Router apps
  • docs set admin manager
  • agent skill installer
  • sync writes behind sync.allowWrites
  • publish behind sync.allowPublish
  • hard delete behind sync.allowHardDelete
  • nonce replay protection
  • sync-run audit records

Not implemented yet:

  • existing collection targets
  • block targets