Skip to content

feat: docs.docstore.dev — MkDocs + Material site served by Go Cloud Run service #401

Description

@dlorenc

Overview

A user-facing documentation site at https://docs.docstore.dev. Markdown source lives in docs/ (already populated with 14 pages). The site is built with MkDocs + Material theme and served by a small Go HTTP server on Cloud Run, built with ko.

Content

docs/ already contains the source:

  • getting-started.md, concepts.md, cli-reference.md
  • api-reference.md, canonical-resource-uris.md
  • ci.md, ci-architecture.md, ci-local.md, proposals-and-ci-triggers.md
  • events.md, policy.md, sdk.md, cryptographic-verifiability.md, deployment.md

No migration needed. Navigation and structure wired up in mkdocs.yml.

Site generator: MkDocs + Material

mkdocs.yml at the repo root. Material theme gives search, dark mode, sidebar navigation, and mobile layout with zero custom code. Configured with:

  • repo_url + edit_uri pointing to the GitHub source
  • Navigation structure matching the existing docs/ layout
  • A custom hook (docs/hooks/raw_link.py) that injects a "Raw markdown" link at the bottom of every page pointing to https://raw.githubusercontent.com/dlorenc/docstore/main/docs/{page}.md — agents can fetch this URL to get clean markdown without HTML parsing

Server: cmd/docs

A small Go HTTP server:

  • Reads static site from os.Getenv("KO_DATA_PATH") (set automatically by ko at runtime)
  • Serves with http.FileServer(http.Dir(filepath.Join(koDataPath, "site")))
  • Adds cache-control headers (long TTL for hashed assets, short for HTML)
  • Serves MkDocs' generated 404.html for unmatched paths
  • Adds security headers (X-Content-Type-Options, X-Frame-Options, etc.)
  • Reads PORT env var (Cloud Run requirement, default 8080)

Local development:

mkdocs build
KO_DATA_PATH=cmd/docs/.kodata go run ./cmd/docs

Static assets: cmd/docs/.kodata/

cmd/docs/.kodata/ is committed to the repo (with a .gitkeep). The built site/ directory is gitignored inside it. During CI, mkdocs build outputs to site/ at the repo root, then a symlink cmd/docs/.kodata/site -> ../../../site (or a copy) is created before ko build runs. Ko resolves the symlink and packages the site files into the image alongside the binary. No Dockerfile needed.

Build and deploy: ko

ko builds the cmd/docs binary and packages it with .kodata/ contents into a minimal distroless image — no Dockerfile.

.ko.yaml (or workflow env vars) configures:

  • Base image: gcr.io/distroless/static
  • Image repository: us-central1-docker.pkg.dev/dlorenc-chainguard/images/docs

GitHub Actions: .github/workflows/deploy-docs.yml

Triggers on push to main touching docs/**, mkdocs.yml, cmd/docs/**.

Steps:

  1. pip install mkdocs-material && mkdocs build
  2. Create symlink cmd/docs/.kodata/site -> ../../../site
  3. ko build ./cmd/docs → pushes image
  4. gcloud run deploy docs --image=...

Deployment

  • Cloud Run service docs, public ingress (no IAP — docs are public)
  • Custom domain docs.docstore.dev via Cloud Run domain mapping or existing load balancer

Implementation tasks

  • mkdocs.yml — navigation, Material theme config, repo URL, edit URI
  • docs/hooks/raw_link.py — inject raw markdown link on every page
  • cmd/docs/main.go — Go server reading from KO_DATA_PATH, cache headers, 404 handling, security headers
  • cmd/docs/.kodata/.gitkeep + .gitignore entry for cmd/docs/.kodata/site
  • .ko.yaml — base image and registry config
  • .github/workflows/deploy-docs.yml — mkdocs build + ko + Cloud Run deploy
  • Cloud Run service + custom domain setup
  • Update CONTRIBUTING.md with local preview instructions (mkdocs serve)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions