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:
pip install mkdocs-material && mkdocs build
- Create symlink
cmd/docs/.kodata/site -> ../../../site
ko build ./cmd/docs → pushes image
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
Overview
A user-facing documentation site at
https://docs.docstore.dev. Markdown source lives indocs/(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 withko.Content
docs/already contains the source:getting-started.md,concepts.md,cli-reference.mdapi-reference.md,canonical-resource-uris.mdci.md,ci-architecture.md,ci-local.md,proposals-and-ci-triggers.mdevents.md,policy.md,sdk.md,cryptographic-verifiability.md,deployment.mdNo migration needed. Navigation and structure wired up in
mkdocs.yml.Site generator: MkDocs + Material
mkdocs.ymlat the repo root. Material theme gives search, dark mode, sidebar navigation, and mobile layout with zero custom code. Configured with:repo_url+edit_uripointing to the GitHub sourcedocs/layoutdocs/hooks/raw_link.py) that injects a "Raw markdown" link at the bottom of every page pointing tohttps://raw.githubusercontent.com/dlorenc/docstore/main/docs/{page}.md— agents can fetch this URL to get clean markdown without HTML parsingServer:
cmd/docsA small Go HTTP server:
os.Getenv("KO_DATA_PATH")(set automatically by ko at runtime)http.FileServer(http.Dir(filepath.Join(koDataPath, "site")))404.htmlfor unmatched pathsPORTenv var (Cloud Run requirement, default 8080)Local development:
Static assets:
cmd/docs/.kodata/cmd/docs/.kodata/is committed to the repo (with a.gitkeep). The builtsite/directory is gitignored inside it. During CI,mkdocs buildoutputs tosite/at the repo root, then a symlinkcmd/docs/.kodata/site -> ../../../site(or a copy) is created beforeko buildruns. Ko resolves the symlink and packages the site files into the image alongside the binary. No Dockerfile needed.Build and deploy: ko
kobuilds thecmd/docsbinary and packages it with.kodata/contents into a minimal distroless image — no Dockerfile..ko.yaml(or workflow env vars) configures:gcr.io/distroless/staticus-central1-docker.pkg.dev/dlorenc-chainguard/images/docsGitHub Actions:
.github/workflows/deploy-docs.ymlTriggers on push to
maintouchingdocs/**,mkdocs.yml,cmd/docs/**.Steps:
pip install mkdocs-material && mkdocs buildcmd/docs/.kodata/site -> ../../../siteko build ./cmd/docs→ pushes imagegcloud run deploy docs --image=...Deployment
docs, public ingress (no IAP — docs are public)docs.docstore.devvia Cloud Run domain mapping or existing load balancerImplementation tasks
mkdocs.yml— navigation, Material theme config, repo URL, edit URIdocs/hooks/raw_link.py— inject raw markdown link on every pagecmd/docs/main.go— Go server reading fromKO_DATA_PATH, cache headers, 404 handling, security headerscmd/docs/.kodata/.gitkeep+.gitignoreentry forcmd/docs/.kodata/site.ko.yaml— base image and registry config.github/workflows/deploy-docs.yml— mkdocs build + ko + Cloud Run deployCONTRIBUTING.mdwith local preview instructions (mkdocs serve)