Skip to content

Repository files navigation

title Documentation Server

doc

Matrix License

A lightweight, read-only documentation aggregation service. It discovers markdown documents across your GitHub organization, syncs them automatically, and serves them through a simple filesystem-like HTTP API.

Why this exists

Documentation lives scattered across repositories. Rather than manually curating a docs site, this service treats your org's repos as the source of truth. It discovers README.md and files under docs/ automatically, parses YAML frontmatter, and exposes everything through a stable URL structure. The API is designed around a filesystem metaphor because developers already think in paths.

API

The API is organized as a virtual filesystem rooted at each repository:

GET /                          → list all public repos
GET /{repo}/                   → list documents in a repo
GET /{repo}/__tree__           → recursive tree of all documents
GET /{repo}/{path}             → fetch a specific document
GET /__index__                 → global search index across all repos
GET /__search__?q=…            → full-text search with snippets
GET /sitemap.xml               → SEO sitemap for crawlers
GET /health                    → health check

OpenAPI / Swagger

The full API schema is available at runtime:

  • GET /openapi.json — OpenAPI 3.1 JSON
  • GET /openapi.yaml — OpenAPI 3.1 YAML
  • GET /swagger-ui/ — Interactive Swagger UI

Raw markdown

Append ?raw=1 or send Accept: text/markdown to get the raw markdown body without JSON wrapping.

Search

GET /__search__ runs a full-text query across every public document — titles, tags, and body content — using an embedded tantivy index that lives in server memory:

GET /__search__?q=<query>&repo=<optional repo name>&limit=<default 12, max 50>
{
  "query": "benchma",
  "hits": [
    {
      "repo": "hyperlight",
      "repo_full_name": "sunbeamdotpt/hyperlight",
      "path": "/benchmarks.md",
      "title": "Benchmarks",
      "snippet": "…best-matching plain-text fragment (≤ 300 chars)…",
      "score": 12.34
    }
  ]
}
  • path is the normalized path (same form the portal routes on).
  • Typo tolerance: terms match with edit distance 1; the last term also prefix-matches, so partial input (benchmabenchmarks) works.
  • Title matches rank above tag matches, which rank above body matches.
  • repo scopes results to a single repository (exact name).
  • The index is rebuilt on boot and after every successful sync run — no external search service, nothing to back up.

File names with spaces

Paths may contain spaces (e.g. docs/guide with spaces/intro.md). Request such documents with percent-encoded segments (/test-repo/my%20file.md); responses keep normalized_path raw while url fields and the sitemap are percent-encoded.

# Spaces in file names: encode the request, read the raw path back
curl https://doc.example.com/backrooms/guide%20with%20spaces/intro.md?raw=1

Example

# List all repositories
curl https://doc.example.com/

# Get a document as JSON (frontmatter + body)
curl https://doc.example.com/backrooms/getting-started

# Get raw markdown
curl https://doc.example.com/backrooms/getting-started?raw=1

Document discovery

Documents are discovered via a GitHub App with read access to your organization's repositories. On each sync cycle:

  1. Lists all public repositories
  2. Extracts README.md (mapped to / within the repo)
  3. Extracts all files under docs/ (mapped without the docs/ prefix)
  4. Parses YAML frontmatter into structured metadata
  5. Soft-deletes documents that no longer exist upstream

Private repositories are never indexed or exposed.

Frontmatter

Documents may include YAML frontmatter:

---
title: Getting Started
description: How to enter the Backrooms
author: sienna
tags: [guide, safety]
---

# Getting Started

All frontmatter fields are optional. The title field is used for display; description and tags power search and indexing.

Running

With Docker

docker run -p 8080:8080 \
  -e DOCS__GITHUB_APP_ID=4017767 \
  -e DOCS__GITHUB_PRIVATE_KEY="$(cat key.pem)" \
  -e DOCS__DATABASE_URL="postgres://user:pass@host/db" \
  ghcr.io/sunbeamdotpt/doc:latest

Configuration

Environment Variable Description
DOCS__GITHUB_APP_ID GitHub App ID
DOCS__GITHUB_PRIVATE_KEY GitHub App private key (PEM)
DOCS__DATABASE_URL PostgreSQL connection string
DOCS__BIND_ADDRESS HTTP bind address (default: 0.0.0.0:8080)
DOCS__BASE_URL External base URL for sitemap/links
DOCS__ALLOWED_ORIGINS Comma-separated CORS origin allowlist (default: https://sunbeam.pt)
DOCS__POLL_INTERVAL_SECONDS Sync interval (default: 300)

Helm

The chart is published as an OCI artifact to GitHub Container Registry:

# Install directly from GHCR
helm install doc oci://ghcr.io/sunbeamdotpt/doc/chart --version 0.2.0

# Or pull it locally first
helm pull oci://ghcr.io/sunbeamdotpt/doc/chart --version 0.2.0 --untar
helm install doc ./doc

You can also use the local source at helm/doc/:

helm install doc ./helm/doc

See values.yaml for configuration options including Kubernetes secrets support and an optional 1Gi persistent volume (persistence.*).

Architecture at a glance

  • Rust + Axum — async HTTP API
  • PostgreSQL + sqlx — strongly-typed queries with compile-time checking
  • tantivy (in-memory) — full-text search rebuilt after each sync
  • moka LRU cache — hot paths cached with TTL invalidation
  • Background sync — tokio task polls GitHub on a configurable interval
  • Multi-arch container — ~15-20MB distroless image for linux/amd64 and linux/arm64

License

MIT — Copyright (c) 2026 Sunbeam Studios

About

Auto-organized documentation server for hand-managed documentation.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages