Skip to content

Oracle Setup

Problem

Documentation is scattered across multiple GitHub repositories. It's difficult to find docs, there's no unified search, and API documentation is hard to discover.

Taking inspiration from Backstage proposal in Tech Proposal.

Solution

A static documentation portal using MkDocs that aggregates docs from multiple repos.

Why not Backstage?

Backstage is a full internal developer platform (service catalog, infrastructure tooling, workflows), not just a documentation portal, so it's much heavier than what we need. Since our goal is just multi-repo docs with search and PR-based editing, a MkDocs-based static portal is much simpler and faster to build.

Architecture

GitHub Actions (scheduled every 30min)
  ↓
Clone repos → Copy docs + OpenAPI specs → Build MkDocs → Deploy
  ↓
Static Site on Cloudflare Pages (behind Cloudflare Access)

Stack

Component Choice Rationale
Static site generator MkDocs + Material theme Simple, fast, built-in search, beautiful
Repo access GitHub App (read-only) Secure access to private repos
Build automation GitHub Actions Scheduled builds, no webhooks
Search MkDocs built-in Client-side, no backend needed
Hosting Cloudflare Pages Free, CDN, built-in basic auth
Authentication Cloudflare Access (One-Time PIN) Simple email-based access, no auth server needed
API docs Swagger UI (embedded) Interactive, static
Edit workflow GitHub's native editor Link to github.com/.../edit/...
Configuration repos.yml Version-controlled, no database

Out of Scope (For Now)

Not building the below initially, but can add later if needed:

  • Backend server / database: Add if we need fine-grained permissions or custom workflows
  • Custom search engine (Meilisearch): Add if MkDocs search becomes too slow
  • Webhooks: Add if 30-min delay becomes unacceptable
  • Custom editor: Add if need edit-in-place functionality
  • Expose for LLMs: The same documentation can serve LLMs (Claude Code, Cursor, etc.). Since MkDocs already produces structured markdown files, we can expose them via an MCP server for coding assistants to consume.

How It Works

Build Process

GitHub Actions runs every 30 minutes:

  1. Read repos.yml for list of repos
  2. Clone/pull each repo into _repos/{name}/
  3. Copy docs to docs/{repo-name}/ based on docs_path, docs_paths, or docs_pattern
  4. Copy OpenAPI specs to docs/{output} (e.g., docs/api/main-api.json)
  5. Inject source metadata into each markdown file (for edit links)
  6. Run mkdocs build → outputs to site/
  7. Deploy site/ to Cloudflare Pages

Editing Docs

Each page has an "Edit on GitHub" button that links to https://github.com/{org}/{repo}/edit/{branch}/{path}. User edits in GitHub's native editor, creates PR. Next build picks up changes.

MkDocs generates search_index.json at build time. Browser loads this JSON and searches client-side using Lunr.js. No backend needed.

API Documentation

OpenAPI specs live in the source repos. The fetch script copies them to the portal. Swagger UI is embedded in markdown pages and loads the local JSON spec.

For backend repo owners: To have your API docs appear in the portal:

  1. Generate an OpenAPI spec file in your repo (e.g., openapi.json or swagger.yaml)
  2. Add an entry to repos.yml in the docs-portal repo pointing to your spec
  3. The portal will automatically fetch and display it with Swagger UI

Repository Structure

oracle/
├── .github/workflows/
│   └── build-deploy.yml          # CI: scheduled build + deploy to Cloudflare Pages
├── docs/
│   ├── index.md                  # Portal homepage
│   ├── team-guides/              # Org-level docs (checked into this repo)
│   │   └── pr-guidelines.md
│   ├── backend-api/              # Fetched from backend-api repo
│   ├── frontend-app/             # Fetched from frontend-app repo
│   └── api/                      # API doc pages with Swagger UI
├── overrides/
│   └── main.html                 # Edit on GitHub button customization
├── scripts/
│   └── fetch_repos.py            # Clone repos, copy docs, inject frontmatter
├── mkdocs.yml                    # MkDocs configuration
├── repos.yml                     # Source repos + API specs config
├── requirements.txt              # Python dependencies
└── .gitignore
  • Team/org-level docs: Live in docs/ (e.g., docs/index.md, docs/team-guides/) — version-controlled in this repo
  • Fetched repo docs: Go into docs/{repo-name}/ — prevents file conflicts, clear ownership per repo

Configuration

repos.yml

Three ways to specify what to fetch per repo:

  • docs_path: Single folder
  • docs_paths: List of folders
  • docs_pattern: Glob pattern (e.g., **/*.md for all markdown files)
repos:
  - name: backend-api
    url: https://github.com/yourorg/backend-api
    branch: main
    docs_path: docs/ # Single folder

  - name: frontend-app
    url: https://github.com/yourorg/frontend-app
    branch: main
    docs_paths: # Multiple folders
      - docs/
      - guides/

  - name: mobile-app
    url: https://github.com/yourorg/mobile-app
    branch: main
    docs_pattern: "**/*.md" # Glob all markdown files

# OpenAPI specs (optional)
openapi_specs:
  - name: main-api
    repo: backend-api
    path: openapi.json
    output: api/main-api.json

GitHub App Setup

Create a GitHub App with:

  • Permissions: Contents (Read), Metadata (Read)
  • Webhooks: Disabled (not needed)
  • Install on organization, store App ID and private key as GitHub Actions secrets

Authentication

Cloudflare Access with One-Time PIN. Add allowed email addresses or an email domain (e.g., *@yourcompany.com). Users get a PIN via email to access the site. Simple, no auth server integration needed.