Skip to content

MkDocs Material for Forge: Build Handoff

Date: 2026-04-28 Owner: the Opus worker spawned to do this build Parent session: n8n-telegram_Opus47 (Building The Brain conversation) Estimated effort: ~45 min Related handoff: Building The Brain


The Goal

Justin needs a web-based markdown viewer for everything in /home/justinwieb/forge so he can read his memory/handoffs/docs/nexus from his phone or any browser without fighting VS Code. Built with MkDocs Material, exposed at forge.justinkrystal.com behind Cloudflare Access. Auto-rebuilds when files change. Mobile-responsive. Full-text search.

When done, he'll be able to tap a link in chat like https://forge.justinkrystal.com/memory/handoffs/building-the-brain-life-os-inbox-2026-04-27/ from his phone and read it instantly.


Why MkDocs Material (the decision is locked, don't redebate)

  • Static site generator, no DB, no live process risk
  • Material theme = beautiful, mobile-responsive, dark mode toggle
  • Built-in full-text search (lunr.js, ranked)
  • Free + open-source
  • ~45 min total to set up
  • Auto-rebuild on file change via mkdocs serve watcher OR a small systemd service

Scope, what the site indexes

Include: | Folder/file | Purpose | |---|---| | memory/daily/*.md | Daily logs | | memory/general/*.md | Topical knowledge (ideas.md, etc.) | | memory/handoffs/*.md | All handoff docs | | memory/sessions/*.md | Captured session output | | system-map/*.md | Fleet, architecture, steering | | docs/**/*.md | Infra docs, playbooks | | brands/**/*.md | Brand notes (where md exists) | | infra/n8n/*.md | Outbound pattern, n8n README | | CLAUDE.md | Bootstrap doc, root level |

Exclude: - logs/ (operational logs) - tasks/ (queue JSON) - data/ (SQLite DBs) - comms/ (file-based bus) - dashboard/ (HTML/CSS for fleet dashboard, served separately on :8099) - assets/ (binaries) - sites/ (deployable sites, they have their own URLs) - scripts/ (code, not docs, except top-level READMEs if any) - .claude/, .git/, node_modules/, ~/.forge-secrets/, anything dotfile


Likely approach

1. Install MkDocs Material on UDev

# Use a dedicated venv to keep deps isolated
python3 -m venv ~/.forge-venvs/mkdocs
source ~/.forge-venvs/mkdocs/bin/activate
pip install mkdocs-material mkdocs-monorepo-plugin pymdown-extensions

2. Build the mkdocs config

Decision: rather than restructuring forge to look like a standard docs/ tree, use mkdocs-monorepo-plugin OR docs_dir: . with nav overrides OR a small build-time symlink generator.

Cleanest: write infra/mkdocs/mkdocs.yml at this shape:

site_name: Forge
site_url: https://forge.justinkrystal.com
docs_dir: ../../          # forge root
site_dir: /var/www/forge-mkdocs/   # build output

theme:
  name: material
  palette:
    - scheme: default
      toggle: { icon: material/weather-sunny, name: dark }
    - scheme: slate
      toggle: { icon: material/weather-night, name: light }
  features:
    - navigation.instant
    - navigation.tracking
    - navigation.sections
    - navigation.expand
    - navigation.top
    - search.suggest
    - search.highlight
    - content.code.copy

plugins:
  - search
  - exclude:
      glob:
        - logs/*
        - tasks/*
        - data/*
        - comms/*
        - dashboard/*
        - assets/*
        - sites/*
        - scripts/*
        - .claude/*
        - .git/*
        - node_modules/*
        - infra/mkdocs/*    # don't index ourselves
        - "*.json"
        - "*.py"
        - "*.sh"
        - "*.html"
        - "*.css"
        - "*.png"
        - "*.jpg"
        - "*.webp"

markdown_extensions:
  - admonition
  - codehilite
  - pymdownx.superfences
  - pymdownx.tabbed
  - pymdownx.highlight
  - toc:
      permalink: true

nav:
  - Home: CLAUDE.md
  - Memory:
      - Daily: memory/daily/
      - General: memory/general/
      - Handoffs: memory/handoffs/
  - Nexus:
      - Fleet: system-map/fleet.md
      - Architecture: system-map/architecture.md
      - Steering: system-map/steering.md
  - Docs: docs/
  - Brands: brands/

(Hand-tune the nav once you see what builds.)

3. Serve it

Two options, pick whichever works:

Option A: Static build + nginx-style serve (recommended for prod) - mkdocs build outputs to site_dir - Serve via python3 -m http.server on port 8000 (simple) OR caddy / nginx - Wrap in a systemd service forge-mkdocs.service that runs the server - Add a file watcher (mkdocs serve itself, OR inotifywait + rebuild) for auto-update

Option B, mkdocs serve directly (dev mode, has live reload built-in) - Run mkdocs serve --dev-addr 0.0.0.0:8000 as systemd service - Auto-rebuilds on file change - Slightly heavier than static + http.server but zero extra plumbing - Recommended for v1, simpler, gets shipped tonight

4. Cloudflare Tunnel ingress

Use the existing media-server tunnel (id a55b7768-b42a-4d78-82b5-4a427bdbb9b7, catch-all for *.justinkrystal.com). Use the /cf-add skill:

/cf-add forge justinkrystal.com http://192.168.86.50:8000
Verify by curling https://forge.justinkrystal.com/ from outside the LAN.

5. Cloudflare Access in front

Pattern matches what's on n8n.justinkrystal.com: - Cloudflare Zero Trust → Access → Applications - Add a self-hosted app for forge.justinkrystal.com - Policy: email = [email protected] (Justin's hardened account) - Justin authenticates via email OTP / Google sign-in - Anyone hitting forge.justinkrystal.com without auth → 302 to Cloudflare login

6. Test

  • curl https://forge.justinkrystal.com/ → 302 to CF Access (proves Access works)
  • After login, browse from phone
  • Use search bar, verify it returns results
  • Tap a daily log link, confirms file viewing
  • Confirm the Building The Brain handoff renders properly

Files you'll work with

File Purpose
infra/mkdocs/mkdocs.yml MkDocs config (NEW)
infra/mkdocs/build.sh Optional rebuild trigger (NEW)
infra/systemd/forge-mkdocs.service systemd unit (NEW)
~/.forge-venvs/mkdocs/ Isolated Python venv
~/.forge-secrets/cloudflare.env Already exists, scoped CF token
scripts/cloudflare/cf.py Existing CF API wrapper for /cf-add
MEMORY.md Add a new "Tools & Pipelines" entry pointing to the new reference doc
~/.claude/projects/-home-justinwieb-forge/memory/reference_mkdocs.md NEW, register the tool

Don't do

  • Don't expose without Cloudflare Access. Forge contains credentials, infra docs, daily logs, secrets references. Public exposure = catastrophic. Verify Access is active before declaring done.
  • Don't index logs/, tasks/, data/, comms/, dashboard/, assets/, sites/, scripts/, .claude/, secrets. Confirm with find site_dir -name '*.html' | wc -l makes sense (a few hundred, not thousands).
  • Don't break the existing media-server tunnel. Use /cf-add (which is non-destructive), don't hand-edit the tunnel config in a way that loses other ingress rules.
  • Don't put the venv inside the forge repo. Use ~/.forge-venvs/mkdocs/ like the other forge venvs.
  • Don't use Opus for the worker's own internal subtasks. You ARE Opus, but if you spawn helpers, keep them Sonnet/Haiku. (Likely no helpers needed for a build this small.)
  • Don't skip the systemd unit. Justin will reboot UDev someday; the viewer needs to come back automatically.

Deliverables

  1. Working https://forge.justinkrystal.com/ behind Cloudflare Access
  2. Auto-rebuild on file change (mkdocs serve OR a watcher), verify by editing a file and seeing it update without restart
  3. systemd unit forge-mkdocs.service enabled + active
  4. infra/mkdocs/mkdocs.yml checked into the repo
  5. Reference doc ~/.claude/projects/-home-justinwieb-forge/memory/reference_mkdocs.md written
  6. MEMORY.md "Tools & Pipelines" section updated with a one-line pointer
  7. Push back to the parent session with: live URL + brief proof it works (e.g. screenshot of search returning results, OR curl output): Justin is in the parent session waiting

Done when

  • forge.justinkrystal.com returns content from outside the LAN, behind Access
  • Search works (try query "Building The Brain", confirm the new handoff shows up)
  • At least 5 daily logs are browseable
  • Justin can tap https://forge.justinkrystal.com/memory/handoffs/building-the-brain-life-os-inbox-2026-04-27/ on his phone and it renders
  • Service survives a systemctl restart forge-mkdocs
  • Documented in MEMORY.md

Conversation pointers

  • Parent conversation: tonight's n8n-telegram_Opus47 session
  • Daily log: memory/daily/2026-04-28.md (today) and memory/daily/2026-04-27.md (yesterday's checkpoints have full context)
  • Master vision doc: memory/handoffs/building-the-brain-life-os-inbox-2026-04-27.md
  • CF tunnel reference: MEMORY.md ## External Service Access → Cloudflare API
  • Existing CF Access pattern: applied to n8n.justinkrystal.com, copy that
  • /cf-add skill at .claude/skills/cf-add/SKILL.md
  • /decrypt-n8n skill at .claude/skills/decrypt-n8n/SKILL.md (not needed but adjacent)

Final note

Justin is on Remote Control (Claude Code Web) on his phone, watching this build happen. When done, push the live URL back to the parent session by writing the URL + status to a file or by notify skill, he'll see it and pick up the Notion redesign from there.