Skip to content

Documentation Authoring and Publishing

Audiences and Source Roots

Source Published destination Content
docs/handbook/ /handbook/ Staff procedures, expected results, checks and escalation
docs/systems/ /systems/ Business rules, rationale, reports, designs and engineering
docs/notes/ Not published Research, working comments and unfinished drafts
docs/archive/ Not published Dated evidence and completed migration records
docs/local/ Not published or committed Private source evidence and captures
docs/assets/ Not published Retained attachments for repository notes
docs/handbook/assets/ /handbook/assets/ Approved Handbook attachments
docs/systems/assets/ /systems/assets/ Approved Systems attachments
docs/theme/ Both sites, under theme/ Explicitly included shared logo and CSS

The two sites share branding and Obsidian normalization through mkdocs.shared.yml and mkdocs_hooks.py. Each has its own navigation, search index and output directory. mkdocs.handbook.yml builds the Handbook; mkdocs.systems.yml builds Systems.

Specify the site's configuration explicitly when building or serving it; there is no default mkdocs.yml.

Folder-Based Navigation

Both configurations omit nav. MkDocs discovers Markdown pages from the source folders, puts each folder's index.md first, and sorts other entries alphanumerically by filename. A page's first heading supplies its title. The folder tree is the navigation structure; there is no separate menu manifest or navigation-generation hook to maintain.

The Handbook's processes/ folder contains staff procedures. Systems uses:

Folder Responsibility
business-rules/ Implemented rules, their rationale and the workflow register
design/ Intended models, proposals and delivery plans
engineering/ Development, architecture, integrations and documentation tooling
maintenance/ Reviewed migrations and administrative configuration
reference/ External API and platform reference material
reports/ Report interpretation, calculations and technical models

Use descriptive kebab-case filenames without ordering numbers. Create an index.md overview when adding a subject folder. Explain the useful reading paths there, and link directly between related procedures, rules and technical references. These links serve readers independently of the sidebar's order. Links between the Handbook and Systems live in the overview and subject pages. Adding a page requires no MkDocs configuration change.

Choose a Page's Responsibility

Staff procedures explain what to do, the expected result, what to review and when to ask the responsible process owner for help. Use the CRM labels staff see. Keep the explanation needed to act confidently on the procedure itself.

Business-rule pages own definitions, calculation rules, eligibility, exceptions and worked examples. Rationale pages explain consequential choices and tradeoffs. Do not invent historical motivation; distinguish an explanation of current behavior from an evidenced decision record.

Engineering pages own APIs, mappings, setup, commands, tests and troubleshooting. Maintenance references own reviewed migrations and administrative operations. Keep customer-specific evidence in the repository archive or private storage.

Design proposals identify intended behavior, implemented boundaries and open decisions explicitly. Keep them in design/, separate from current operating instructions. Working drafts remain outside the published source roots until they are suitable for their intended audience.

The MSP documentation demonstrates the pattern:

When behavior changes, update the owning rule page and affected procedure together, then update the workflow register if its trigger, scope or references changed. Link to the detailed rule instead of maintaining a second full specification in the handbook.

Within a site, use relative Markdown links. Between sites, use published paths such as /systems/business-rules/msp-billing/ and /handbook/processes/msp-invoicing/. Both sites must be mounted at those prefixes on the same host for these links to work. Repository-only references use explicit GitHub links and require repository access.

Open the repository root as the Obsidian vault. Put new notes in docs/notes until their destination is decided. Use three document-attachment folders:

  • docs/handbook/assets/ for approved Handbook attachments.
  • docs/systems/assets/ for approved Systems attachments.
  • docs/assets/ for unpublished notes and drafts.

An attachment in a site source root is published with it, even if no page links to it. Keep private evidence in docs/local/, not in the default attachment folder. Theme resources are not document attachments.

In Obsidian's Files and links settings, set the default location for new attachments to In the folder specified below, with docs/assets as the attachment folder path. Keep Automatically update internal links enabled. These local settings are ignored by Git; configure them when opening a new vault. Obsidian does not automatically route pastes to the owning site's assets folder. Before publishing a draft, move its approved attachments into that site's assets/ folder using Obsidian so existing links are updated. If an attachment is still needed by unpublished notes, update their links to the moved file too.

Wiki links and image embeds resolve within the current site's source root; use Markdown links for cross-site links. Callouts and nested list indentation are normalized during the build.

Shared branding is limited to docs/theme/peppermint-mark.svg and docs/theme/peppermint.css. The hook includes only those two files in both builds under theme/, without copying unrelated repository attachments.

Build and Preview

pip install -r requirements-docs.txt
python -m mkdocs build --strict --config-file mkdocs.handbook.yml
python -m mkdocs build --strict --config-file mkdocs.systems.yml
python -m http.server 8000 --bind 127.0.0.1 --directory site

Open http://127.0.0.1:8000/handbook/ or http://127.0.0.1:8000/systems/. Rebuild after editing when using the combined static preview. Each build cleans only its own output directory.

When replacing an older combined build, move its generated files out of site/ before building these two sites. A clean combined output contains only handbook/ and systems/; building either site does not remove stale files at the output root. The Docker build starts with a fresh output directory.

For live reload of one site, use python -m mkdocs serve --config-file mkdocs.handbook.yml or python -m mkdocs serve --config-file mkdocs.systems.yml. The other site's links require the combined preview above.

Deployment and Access

Dockerfile.docs builds both sites with strict validation, then copies only their generated output into Nginx. It copies only the two source roots and shared branding into its builder. The Compose docs service uses host port 8081. The docs Nginx redirects / to /handbook/.

Publish the middleware and documentation images with scripts/publish-images.sh, then run the digest-based Actions release. Both sites are validated before publishing and checked again from the selected image in CI. The server pulls the finished image. See deployment for the check-only run, production release and recovery commands.

The configured URLs are https://peppermint.fxbn.dev/handbook/ and https://peppermint.fxbn.dev/systems/. The repository prepares these routes; editing the files does not deploy them or configure authentication.

Apply staff authentication at the VPS reverse proxy for the Handbook, and the appropriate product/engineering group policy for Systems. These are separate publication and search boundaries, not access controls. Existing hosting notes describe temporary public access; verify and configure the host's access policy before publishing internal material. Actual staff costs and private source captures never belong in either source root.

Nginx redirects old maintained-page URLs to their owning site without publishing duplicate pages. docs.redirects.conf contains only legacy URL mappings; it does not control navigation. When moving a previously published page, update its incoming source links and add a redirect there. The combined Python preview serves current paths only; legacy redirects run in the Nginx container. Notes and archives have no published redirects. Links to MSP sections moved into maintenance need their new Systems URL: a server redirect cannot route on a browser's fragment identifier. Update shared bookmarks to the relevant new site paths. Historical repository links remain available through Git history.