Skip to content

docs-kit

docs-kit is a reusable CLI utility for generating beautiful documentation websites from Markdown.

It is intentionally small. A project should be able to keep its documentation in normal Markdown files, define a tiny configuration file, and run:

docs-kit serve
docs-kit build

Version 0.1.0 focuses on one job: generate a static documentation site from an existing Markdown project using MkDocs and Material for MkDocs.

Philosophy

docs-kit favors convention over configuration. It should feel like a thin, dependable layer around excellent documentation tools rather than a new documentation framework.

The project is generic by design. It should work for libraries, services, tools, games, internal systems, and personal projects without carrying assumptions from any one codebase.

Why MkDocs?

Markdown rendering, search, navigation, responsive layout, syntax highlighting, and theme behavior are deep product surfaces. MkDocs already handles them well, and Material for MkDocs provides a polished default experience with a strong accessibility and usability baseline.

docs-kit orchestrates MkDocs instead of replacing it. That keeps this project focused on project discovery, sensible defaults, repeatable builds, and future publishing workflows.

Requirements

Install the Node.js dependencies for docs-kit:

npm install

Install MkDocs and Material for MkDocs in your Python environment:

pip install mkdocs mkdocs-material

Configuration

Create a docs.config.ts file in the project you want to document:

export default {
  siteName: "Example Project",
  siteDescription: "Optional short description for shared documentation indexes.",
  repositoryUrl: "https://example.com/project",
  basePath: "/example/",
  sources: [
    "README.md",
    "ROADMAP.md",
    "DECISIONS.md",
    "AGENTS.md",
    "docs/**/*.md"
  ],
  publish: {
    type: "filesystem",
    retainDays: 10
  }
};

This configuration object is the intended public API for docs-kit. The core fields describe the site and Markdown sources. The optional publish block enables filesystem release publication.

siteDescription and repositoryUrl are optional. Filesystem publishing uses siteDescription in the shared documentation-root index and manifest. repositoryUrl is recorded in the manifest for future tooling.

Usage

Build a static site:

docs-kit build

Serve the documentation locally:

docs-kit serve

Publish an already-built site:

docs-kit publish

Build and publish in one command:

docs-kit deploy

Roll back to the previous release, or to a specific release:

docs-kit rollback
docs-kit rollback 20260713-191532

During local development in this repository, use:

npm run docs:build
npm run docs:serve

The build command copies configured Markdown files into a generated workspace, writes a MkDocs configuration, invokes MkDocs, and copies the resulting static site into site/.

Markdown Sources and README Files

A root-level README.md remains the conventional site homepage and is labeled Home in generated navigation.

Nested README files, such as docs/README.md or packages/foo/README.md, remain available in the generated documentation, but they are not labeled Home. Their navigation label is derived from the parent directory, such as Docs or Foo. If labels collide, docs-kit appends source-path context so the labels remain deterministic and each source can still be distinguished.

Ordinary non-README Markdown files keep the existing basename-derived labels unless a collision requires disambiguation.

Filesystem Publishing

One docs-kit installation owns one shared documentation host and one shared deployment root. Projects choose their URL location with basePath; they normally do not set publish.root.

Filesystem publishing creates immutable complete snapshots under the deployment root and atomically advances a current symlink:

<publish-root>/
|-- releases/
|   `-- <release-id>/
|       |-- index.html
|       |-- 404.html
|       |-- docs-kit/
|       |   `-- index.html
|       `-- screeps/
|           `-- index.html
|-- shared/
|   `-- sites/
|       `-- <site-id>.json
|-- .docs-kit-publish.lock/
`-- current -> releases/<release-id>

Configure the installation once by creating a local .env file beside the installed docs-kit package. For this repository during local development:

cp .env.example .env

Then set:

DOCS_PUBLISH_ROOT=/var/lib/docs-kit

Publish-root precedence is:

  1. publish.root in the consuming project's docs.config.ts
  2. exported DOCS_PUBLISH_ROOT
  3. installation-scoped .env beside docs-kit
  4. a clear configuration error

The project owns basePath. When basePath is /screeps/, docs-kit publishes the built site into <release>/screeps/, so a web server rooted at current can serve the site at /screeps.

Each publish clones the active release into a new release directory, removes only the configured basePath subtree, copies the newly built site into that subtree, validates index.html, and then atomically repoints current. That means publishing /screeps/ preserves /docs-kit/ and every other unrelated path from the previous active snapshot.

Each publish also writes or updates a site manifest under <publish-root>/shared/sites/. The manifest is owned by the publishing site and contains:

  • schemaVersion
  • id
  • siteName
  • basePath
  • optional description
  • publishedAt
  • optional docsKitVersion
  • optional repositoryUrl

The manifest filename is a URL-safe identifier derived from basePath, and manifests are stored outside release directories so release retention does not unregister sites. Republishing the same basePath updates the same manifest instead of creating duplicates. Malformed or unsupported manifests are skipped when rendering shared pages and reported in CLI output.

For non-root basePath sites, docs-kit renders a shared documentation hub at <release>/index.html and a custom <release>/404.html from all valid manifests. These standalone pages use safe HTML escaping, deterministic ordering, no JavaScript requirement, and no external runtime dependencies. A failed shared-page render prevents activation of the new release, leaving the previous active release untouched.

Publishing with basePath: "/" is allowed only for the first release. After a release is active, root-path publishing is rejected because it would ambiguously replace the whole hosted documentation tree.

When the first publish uses basePath: "/", docs-kit writes the site manifest but skips shared root page generation because that site owns /index.html.

Rollback activates a whole previous release snapshot. Rolling back after a /screeps/ publish restores all hosted project paths to their state in that release, not just /screeps/.

publish.retainDays defaults to 10. Set it to 0 to disable age-based cleanup. Retention only runs after successful activation, never removes the active release, and preserves the most recent rollback candidate.

Publish and rollback take a deployment lock under .docs-kit-publish.lock so only one mutation of a deployment root happens at a time. If a lock appears stale, inspect it before removing it unless docs-kit reports that it safely removed a stale lock itself.

The same lock covers manifest updates and shared index/404 rendering, so concurrent publishes to one deployment root are serialized. Writes use temporary files followed by rename where docs-kit updates shared-root files.

To intentionally remove a site from the shared index today, stop publishing that site, remove its manifest from <publish-root>/shared/sites/, and publish any remaining site to regenerate index.html and 404.html. Do not delete another site's release subtree by hand. A future docs-kit command may make unregistering a site explicit.

docs-kit does not configure nginx, reload nginx, manage DNS, manage TLS, invoke Certbot, use sudo, or change file ownership. A web server can point at the active release through a stable symlink, for example:

/var/www/docs.mc-lane.com/current -> /var/lib/docs-kit/current

To use the generated custom 404 page with nginx, configure nginx once for the documentation host using the publish root already chosen for docs-kit. Conceptually:

error_page 404 /404.html;

location = /404.html {
    root /path/to/docs/current;
    internal;
}

Use the actual path your server exposes for the current symlink. docs-kit only generates 404.html; it does not install or reload nginx configuration.

Manual production test workflow:

git switch feature/shared-release-snapshots
npm install
npm run build
cp .env.example .env
npm run dev -- build
npm run dev -- publish
readlink -f /var/lib/docs-kit/current
ls -la /var/lib/docs-kit/releases
curl -I https://docs.mc-lane.com
curl -I https://docs.mc-lane.com/docs-kit
npm run dev -- rollback
readlink -f /var/lib/docs-kit/current
curl -I https://docs.mc-lane.com/docs-kit
latest="$(ls -1 /var/lib/docs-kit/releases | sort | tail -n 1)"
npm run dev -- rollback "$latest"

Current Scope

v0.1 includes:

  • Markdown source discovery from configured glob patterns
  • Generated MkDocs workspace
  • Material for MkDocs as the default theme
  • Search, syntax highlighting, responsive layout, and dark/light mode
  • docs-kit build
  • docs-kit serve
  • docs-kit publish
  • docs-kit deploy
  • docs-kit rollback

nginx management, TLS automation, DNS changes, and GitHub integration are intentionally out of scope.

Roadmap

Planned milestones include richer project configuration, automatic navigation generation, publishing workflows, GitHub integration, edit links, live reload improvements, incremental builds, and a stable public API.

See ROADMAP.md for the working roadmap and DECISIONS.md for architectural decisions.