📄

Technical Writer Guide — Docs-Site

Writing and publishing documentation for the Registry Platform

Sign in to see your personalized configuration examples Sign In

Technical Writer Guide — Docs-Site

This guide is written for technical writers and documentation specialists contributing to the Registry Platform documentation. It covers content strategy, conventions, the review workflow, and how to ensure your content works across both rendered sites.

Table of Contents


Content Strategy

The docs-site is built on one principle: one canonical source, multiple rendered outputs.

flowchart TD
    subgraph strategy ["Content Strategy"]
        one["One canonical .md file\nper topic"]
        two["docs-sync.py generates\nservice-specific roots"]
        three["Two sites read from\n.service-specific/"]
    end

    one --> two --> three

    three --> vp["VitePress\n(registry-docs-vitepress)\nAll pages"]
    three --> nx["Next.js\n(registry-docs-nextjs)\nAll pages — same content"]

This means:

  • Write once. There is no separate copy for VitePress and Next.js. A single edit propagates to both sites after a sync.
  • No drift. Both sites always reflect the same canonical content. There is no "VitePress version" or "Next.js version" of a document.
  • Git is the audit trail. Every content change is a git commit. History, attribution, and rollback are handled by the repository.

Directory Conventions

docs/docs-site/
├── index.md              ← Platform landing page
│
├── guides/               ← CROSS-CUTTING — appears in BOTH sites
│   ├── quickstart.md
│   ├── authentication.md
│   ├── architecture.md
│   ├── troubleshooting.md
│   ├── docker.md         ← "How to use Docker registries" (user perspective)
│   ├── maven.md
│   ├── npm.md
│   └── pypi.md
│
├── docker/               ← REGISTRY-SPECIFIC — VitePress only
│   ├── index.md          ← Registry overview
│   ├── developer.md      ← Developer guide for Docker Registry
│   └── devops.md         ← Operations guide for Docker Registry
│
├── maven/                ← Same structure
├── npm/                  ← Same structure
├── pypi/                 ← Same structure
│
└── features/             ← DOCS-SITE SYSTEM — documentation about this system
    ├── index.md          ← Platform overview
    ├── developer.md      ← Developer guide
    ├── devops.md         ← DevOps/operations guide
    ├── technical-writer.md ← This file
    └── project-manager.md

Decision guide:

Content typeWhere it goes
Relevant to all registry users (auth, quickstart, architecture)guides/
Specific to one registry technologydocker/, maven/, npm/, or pypi/
About the docs platform itselffeatures/
Platform landing pageindex.md (root)

Markdown Conventions

Frontmatter

Every canonical file must have at minimum title and description:

---
title: Authentication Guide
description: How to authenticate with the Registry Platform using personal access tokens
---

title is used as the HTML <title> tag and the page heading in navigation. description is used for the meta description in search results and link previews.

If these fields are absent, docs-sync.py auto-derives them (title from filename, description as empty string) and writes a real file instead of a symlink — creating unnecessary content duplication in the repository. Always include them.

Heading structure

Use ATX headings (#, ##, ###). Every document should have exactly one # H1 matching or closely paraphrasing the frontmatter title:

---
title: Docker Registry — Developer Guide
description: …
---

# Docker Registry — Developer Guide    ← matches title

## Prerequisites                        ## for major sections

### Installing the CLI                  ### for subsections within a section

Do not skip heading levels (no jumping from ## to ####).

Code blocks

Always tag fenced code blocks with the appropriate language:

```bash
uv run scripts/docs-sync.py sync
```

```yaml
services:
  vitepress:
    output_dir: .service-specific/vitepress
```

```markdown
---
title: My Document
---
```

Use bash for shell commands, yaml for config files, markdown for Markdown examples, python for Python, json for JSON.

Inline code

Use backtick inline code for:

  • File paths: `docs/docs-site/guides/auth.md`
  • Command names: `docs-sync.py`
  • Frontmatter field names: `title`, `description`
  • Environment variable names: `CONTENT_DIR`
  • Directory names: `.service-specific/`

Document Structure Pattern

Each service directory follows a consistent structure for organizing content by function:

FilePurpose
index.mdOverview: what the service is, why it exists, quick start
developer.mdContributing: day-to-day workflow, adding docs, integration patterns
devops.mdOperating: container management, deployment, troubleshooting

Optional additional files for the docs-site-platform/ service:

  • technical-writer.md — style guide and authoring conventions (this file)
  • guides/content-structure.md — canonical directory layout reference

This pattern keeps the index.md concise and scannable, while putting operational depth into the appropriate topic file. Every topic stays at the same path across both rendered sites — there is no site-specific routing.


Cross-Cutting Guides vs Registry-Specific Docs

Cross-cutting guides (in guides/) answer "how do I work with the Registry Platform?" from the user's perspective. They are agnostic to the underlying technology:

  • quickstart.md — I want to pull my first image / package
  • authentication.md — I need to log in to a registry
  • troubleshooting.md — something is broken, how do I diagnose it?

These appear in both the VitePress technical reference and the Next.js guided experience. Write them accessibly, with step-by-step instructions, and without assuming knowledge of the underlying registry implementation.

Registry-specific docs (in docker/, maven/, npm/, pypi/) answer "how does this specific registry work?" They appear only in VitePress. Write them at a deeper technical level and reference implementation details freely.

Avoid duplicating content between the two types. If a cross-cutting guide covers Docker authentication, the docker/developer.md should link to guides/authentication.md rather than re-explaining the same steps.


Review Workflow

flowchart LR
    edit["Edit canonical .md"] --> sync["uv run docs-sync.py sync"]
    sync --> check["uv run docs-sync.py check"]
    check --> preview["curl localhost:5173\nor localhost:3000"]
    preview --> commit["git add docs/docs-site/\ngit commit"]
    commit --> serve["Containers auto-serve\nupdated content"]
  1. Edit the canonical .md file in docs/docs-site/.
  2. Sync to regenerate .service-specific/:
    uv run scripts/docs-sync.py sync
  3. Check that all symlinks are valid:
    uv run scripts/docs-sync.py check
    # ✓  All symlinks are valid.
  4. Preview the rendered output (see Previewing Changes).
  5. Commit both the canonical file and the generated outputs:
    git add docs/docs-site/
    git commit -m "docs: update authentication guide"
  6. The running containers serve the updated content immediately (Next.js) or after a container restart (VitePress).

Writing for Both Rendered Sites

Because all content under services/ is served by both VitePress and Next.js, the following authoring constraints apply.

Admonition containers

Both sites support VitePress-style container syntax for callouts:

::: tip Title text here
Tip content.
:::

::: warning
Warning without a title.
:::

::: note
::: info
::: danger

These render as styled callout boxes in both VitePress (via markdown-it-container) and Next.js (via content preprocessing into <Admonition> components).

Mermaid diagrams

Both sites render Mermaid diagrams. Use fenced code blocks with mermaid as the language:

```mermaid
flowchart LR
    A --> B --> C
```

VitePress renders diagrams via vitepress-plugin-mermaid. Next.js renders them client-side via the MermaidDiagram component injected during content preprocessing.

Extension-agnostic links

Do not include .md or .mdx in cross-page links. Write:

[Authentication Guide](authentication)

Not:

[Authentication Guide](authentication.md)    ← avoid
[Authentication Guide](authentication.mdx)   ← avoid

Previewing Changes

Use task to manage the docs services:

# Start both docs sites (and all other registry services)
task docs:up

# Follow logs from both docs containers
task docs:logs

# Rebuild both docs sites after container changes
task docs:rebuild

For direct access by container IP (useful when running without Traefik):

VIP=$(podman inspect registry-docs-vitepress --format '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}')
NIP=$(podman inspect registry-docs-nextjs    --format '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}')
echo "VitePress: http://$VIP:5173    Next.js: http://$NIP:43198"

After a content change, both containers serve updates immediately (content is volume-mounted). A container rebuild is only needed when changing the site's source code (components, config, Dockerfile).


Verifying Sync with the Status Command

After creating a new file, use status to confirm it was picked up by all services:

uv run scripts/docs-sync.py status

Look for your new file in the status table. A healthy entry shows symlink in the Kind column and ✅ ok in the Status column.

If the Status column shows ❌ missing, the file is in the canonical source but hasn't been synced yet — run uv run scripts/docs-sync.py sync.

If the Status column shows ⚠️ stale link, a symlink exists but points to a canonical file that no longer exists — this usually means the canonical file was renamed or deleted without re-running sync. Run uv run scripts/docs-sync.py sync to clean up stale entries.

# Full status check for the nextjs service
uv run scripts/docs-sync.py status --service nextjs

# Verify symlinks after sync
uv run scripts/docs-sync.py check
# ✓  All symlinks are valid.

Best Practices

One concept, one file. Avoid multi-topic pages. A file called docker-auth-and-setup.md is harder to link to, harder to update, and harder to place in the navigation than two separate focused files.

Write the H1 first, then outline with H2s. Start each document with the title as # H1, then stub out the major sections as ## H2 before filling in content. This enforces a clear structure before you start writing.

Avoid future tense for current functionality. "The system will support X" means X isn't implemented yet — say so explicitly or remove the claim.

Test your links. After adding a new page, grep for references to files you renamed or deleted to catch broken links before they reach production:

grep -r "old-filename" docs/docs-site/ --include="*.md"

Keep frontmatter complete. Files with complete title and description stay as symlinks in .service-specific/ — no content duplication. Files without these fields get real copies with injected frontmatter. Always write both fields.

Commit canonical + symlinks together. Always stage the entire docs/docs-site/ directory. Committing only the canonical file without the updated symlinks will leave the repository in an inconsistent state.