The single source of truth for all ScottyLabs documentation.
Replaces Notion, Discord pins, Google Drive docs, and scattered README files with a unified, searchable, automatically-updated documentation platform. Integrates with ScottyLabs governance to automatically pull documentation from projects marked with the docs flag.
Every ScottyLabs project, guide, process, and resource in one place:
- Project Documentation: Automatically aggregated from repos with
docs: truein governance - Org-Level Documentation: Central repository for organization-wide guides, processes, and resources
- API References: Interactive documentation for all APIs (OpenAPI/Scalar)
- Code Documentation: Auto-generated rustdoc for Rust projects
- Institutional Knowledge: Onboarding, meeting notes, decision records - everything previously scattered across Notion/Discord
- Governance integration: Projects marked with
docs: trueflag are automatically included - Multi-repo aggregation: Clone and merge documentation from all flagged projects
- Central org docs: Dedicated repository for ScottyLabs-wide documentation
- OpenAPI support: Interactive API documentation with Scalar
- Rustdoc integration: Automatic rustdoc generation and hosting
- Full-text search: Find anything across all projects (powered by Pagefind)
- AI agent access: Pages serve Markdown via
Accept: text/markdown(Accept Markdown) - CI/CD ready: Rebuilds automatically when any project updates docs
- Nix-powered: Reproducible builds and deployment via Nix flake
The docs site supports Accept Markdown content negotiation. AI agents can read any page as clean Markdown from the same URL browsers use for HTML:
# Canonical page (preferred)
curl -sI -H "Accept: text/markdown" https://docs.scottylabs.org/scottylabs/onboarding/contributing/
# Content-Type: text/markdown; charset=utf-8
# Vary: Accept
curl -s -H "Accept: text/markdown" https://docs.scottylabs.org/scottylabs/onboarding/contributing/Legacy URLs (e.g. /scottylabs/contributing/) return Markdown redirect stubs pointing to the canonical path.
At build time, the site exports a Markdown counterpart for every HTML page. Caddy on infra-01 must negotiate Accept: text/markdown at the edge and rewrite requests to the matching .md file in Garage. The docs CI upload alone is not enough.
Verify negotiation is live (both checks should pass after infra deploy):
# Should include: Vary: Accept
curl -sI -H "Accept: text/html" https://docs.scottylabs.org/scottylabs/onboarding/contributing/
# Should include: Content-Type: text/markdown and Vary: Accept (not text/html)
curl -sI -H "Accept: text/markdown" https://docs.scottylabs.org/scottylabs/onboarding/contributing/If the second request still returns content-type: text/html with no Vary: Accept, apply the docs.scottylabs.org Caddy config in infrastructure/hosts/infra-01/garage.nix on infra-01 (nixos-rebuild switch).
Markdown files are always available at the sibling index.md path as a fallback, e.g. https://docs.scottylabs.org/scottylabs/onboarding/contributing/index.md.
flowchart TB
governance[Governance YAML<br/>docs: true flag] --> discover[Project Discovery]
central[Central Docs Repo<br/>org-wide content] --> discover
discover --> manifest[projects.toml]
manifest --> build[Build Script]
repos[Project Repos] --> build
build --> starlight[Starlight Pages]
build --> scalar[Scalar API Docs]
build --> rustdoc[Rustdoc Sites]
starlight --> site[Unified Site<br/>Single Source of Truth]
scalar --> site
rustdoc --> site
site --> deploy[Garage S3]
notion[❌ Notion] -.replaced by.-> site
discord[❌ Discord] -.replaced by.-> site
gdrive[❌ Google Drive] -.replaced by.-> site
-
Central Org Docs (
scottylabs-docsrepo)- Onboarding guides
- Organization processes and policies
- Meeting notes and decision records
- Event planning guides
- Infrastructure documentation
-
Project Docs (from repos with
docs: truein governance)- Starlight - Markdown documentation that integrates into main navigation
- Rust - Runs
cargo doc, hosts at/{slug}/api/ - OpenAPI - Generates Scalar-rendered interactive API reference
The documentation hub automatically rebuilds when governance changes. See .forgejo/README.md for setup instructions.
What triggers a rebuild:
- Changes to
data/in the governance repository - Direct pushes to this repository
- Manual workflow dispatch
Setup required in governance repository:
- Add access token secret:
DOCS_TRIGGER_TOKEN - Add workflow file:
.forgejo/workflows/trigger-docs-rebuild.yml(see.forgejo/examples/trigger-docs-rebuild.yml)
Once configured, any change to governance (adding/removing docs = true flags, updating descriptions, etc.) will automatically trigger a documentation rebuild and deployment.
# Clone the repository
git clone https://codeberg.org/scottylabs/documentation.git
cd documentation
# Install dependencies
bun install
# Enter development shell (Nix users)
nix developProjects are automatically discovered from the ScottyLabs governance repository. When a repository has docs = true in its governance entry (same pattern as kennel and sentry flags), it's included in the documentation hub.
To add your project's documentation:
-
In the governance repository (
data/directory), adddocs = trueto your repository entry:# data/my-team.toml [[team.projects]] name = "My Project" slug = "my-project" [[team.projects.repos]] name = "my-project-backend" description = "Backend for My Project" kennel = true docs = true # <-- Add this flag (same level as kennel/sentry)
-
Ensure your repository has a
docs/directory with markdown files -
The documentation hub will automatically pick it up on the next build
Optional configuration:
[[team.projects.repos]]
name = "my-api"
docs = true
docs_type = "openapi" # or "rust" or "starlight" (default)
docs_dir = "documentation" # custom docs directory
openapi_spec = "openapi.json" # for OpenAPI projects
export_command = "cargo run --bin export-openapi"Manual override: You can also manually add projects to projects.toml:
[[project]]
slug = "my-project"
name = "My Project"
repo = "https://codeberg.org/scottylabs/my-project"
type = "starlight"
docs_dir = "docs"
description = "Documentation for My Project"[[project]]
slug = "guides"
name = "User Guides"
repo = "https://codeberg.org/scottylabs/guides"
type = "starlight"
docs_dir = "docs"
description = "Comprehensive guides for all ScottyLabs services"[[project]]
slug = "common-lib"
name = "Common Library"
repo = "https://codeberg.org/scottylabs/common-lib"
type = "rust"
docs_dir = "docs"
description = "Shared Rust utilities and types"[[project]]
slug = "courses-api"
name = "Courses API"
repo = "https://codeberg.org/scottylabs/courses-backend"
type = "openapi"
docs_dir = "docs"
openapi_spec = "openapi.json"
export_command = "cargo run --bin export-openapi"
description = "Course scheduling and registration API"# Build documentation from all projects
bun run build
# Start development server
bun run dev
# Clean build artifacts
bun run scripts/build.ts cleanThe build process follows these steps:
- Parse manifest - Read
projects.tomlto get project list - Clone repos - Parallel
git cloneinto.repos/{slug}/ - Process by type:
- Starlight: Copy markdown to
src/content/docs/{slug}/ - Rust: Run
cargo doc, copy topublic/{slug}/api/ - OpenAPI: Export spec, generate Scalar page
- Starlight: Copy markdown to
- Generate nav - Build dynamic Starlight sidebar
- Build site - Run
astro build
documentation/
├── astro.config.mjs # Starlight configuration
├── package.json # Dependencies
├── projects.toml # Project manifest
├── flake.nix # Nix development environment
├── .forgejo/
│ ├── README.md # Forgejo integration (governance + diagram triggers)
│ ├── workflows/
│ │ └── deploy.yml # CI/CD pipeline
│ ├── examples/
│ │ ├── trigger-docs-rebuild.yml # Copy to governance repo
│ │ └── trigger-docs-diagrams.yml # Copy to project repos
│ └── scripts/
│ └── dispatch-rebuild.sh
├── scripts/
│ ├── build.ts # Main build orchestrator
│ ├── manifest.ts # TOML parsing
│ ├── clone-repos.ts # Git operations
│ ├── aggregate-docs.ts # Content aggregation
│ ├── scalar-integration.ts # OpenAPI handling
│ ├── rustdoc.ts # Rust documentation
│ └── generate-nav.ts # Navigation generation
├── src/
│ ├── content/
│ │ ├── config.ts # Content collections
│ │ └── docs/ # Documentation pages
│ ├── pages/
│ │ └── [slug]/
│ │ └── api.astro # Dynamic API pages
│ └── styles/
│ └── scalar-theme.css
└── .repos/ # Cloned repos (gitignored)
The documentation hub rebuilds automatically on:
- Direct commits to the documentation repository
- Governance changes via repository dispatch (when governance
data/changes) - Manual triggers via workflow dispatch
To enable automatic rebuilds when governance changes, add the trigger workflow to the governance repository. See .forgejo/README.md for complete setup instructions.
Quick setup:
# In governance repository
mkdir -p .forgejo/workflows
cp /path/to/documentation/.forgejo/examples/trigger-docs-rebuild.yml \
.forgejo/workflows/trigger-docs-rebuild.yml
# Add secret DOCS_TRIGGER_TOKEN to governance repo
# (see .forgejo/README.md for details)The included workflow automatically:
- Checks out the repository
- Installs dependencies with Bun
- Runs the build script
- Uploads artifacts
- Deploys to Garage S3 (on main branch)
Configure these in your Forgejo repository settings:
GARAGE_ENDPOINT- S3 endpoint URLGARAGE_ACCESS_KEY- S3 access keyGARAGE_SECRET_KEY- S3 secret key
The bucket name is configured in the workflow: scottylabs-docs
# Using Nix
nix run .#upload-garage
# Or directly with environment variables
export GARAGE_ENDPOINT="https://s3.example.com"
export GARAGE_ACCESS_KEY="your-access-key"
export GARAGE_SECRET_KEY="your-secret-key"
export GARAGE_BUCKET="scottylabs-docs"
nix run .#upload-garageFor projects contributing Starlight documentation:
your-project/
└── docs/
├── index.md # Landing page
├── getting-started.md
├── guides/
│ ├── installation.md
│ └── configuration.md
└── api/
└── reference.md
Standard Starlight frontmatter is supported:
---
title: Page Title
description: Page description for SEO
---
# Page Title
Content here...The build system automatically adds:
project: The project slugprojectType: The project type (starlight/rust/openapi)
For OpenAPI projects, ensure your export command:
- Runs without starting a server
- Writes to the path specified in
openapi_spec - Generates valid OpenAPI 3.0+ JSON
Example Rust implementation with utoipa:
// bin/export-openapi.rs
use utoipa::OpenApi;
use std::fs;
#[tokio::main]
async fn main() {
let doc = ApiDoc::openapi();
fs::write(
"openapi.json",
serde_json::to_string_pretty(&doc).unwrap()
).unwrap();
}Before:
- Notion: Onboarding guides, meeting notes, processes (hard to search, requires account)
- Discord: Pinned messages, FAQs (ephemeral, poor discoverability)
- Google Drive: Shared docs (siloed, inconsistent permissions)
- README files: Scattered across 30+ repos (no central search)
- Tribal knowledge: In people's heads or DMs
After:
- One URL:
docs.scottylabs.org - Full-text search: Find anything across all projects
- Always up-to-date: Rebuilds on every commit
- No account needed: Public, accessible, linkable
- Git-based: Version controlled, reviewable, forkable
Projects use a simple docs: true flag (same pattern as kennel: true):
- Automatic discovery: No manual manifest maintenance
- Consistent with existing workflows: Same governance system
- Self-service: Project maintainers control their own docs
- Audit trail: Changes tracked in governance repo
No mature multi-repo plugin exists for Starlight (unlike mkdocs-monorepo-plugin). A ~200 LOC build script provides:
- Full control over navigation structure
- Integration with governance system
- Better build caching
- Type-safe TypeScript implementation
- Equivalent UX to established plugins
Rustdoc generates a complete static site with its own theme, search, and navigation. Embedding would require:
- Fragile iframe hacks
- JSON-to-markdown conversion (lossy)
- Custom theming to match (high maintenance)
The sibling pattern (/{slug}/api/) is the industry standard (docs.rs, tokio.rs, axum.rs, etc.)
Compared to Swagger UI and Redoc:
- Better UX: Modern design, fast rendering
- More features: Try It, code generation, dark mode
- Better integration: First-party Astro component
- Active development: 14K+ stars, regular releases
Check that all required fields are present in projects.toml:
slug,name,repo,type,docs_dir,description
For OpenAPI projects, also ensure:
openapi_specis setexport_commandis provided (if spec isn't pre-generated)
Ensure:
- Project
typeis set to"rust" - Repository contains a valid Cargo workspace/package
cargo docruns successfully in the project
Check build logs for cargo errors.
The navigation is regenerated on each build. If changes aren't appearing:
- Clean build artifacts:
bun run scripts/build.ts clean - Rebuild:
bun run build - Check that markdown files have correct file extensions (
.mdor.mdx)
- Fork this repository
- Add your project to
projects.toml - Ensure your project has documentation in the specified
docs_dir - Test locally:
bun run build && bun run dev - Submit a pull request
Contributions to the documentation hub itself are welcome:
- Build script improvements
- Theme enhancements
- Additional project type support
- Documentation improvements
MIT License - see LICENSE file for details
For questions or issues:
- Open an issue on Codeberg
- Ask in the ScottyLabs Discord
- Email: tech@scottylabs.org