Roadmap

Phase 3: Ollama RAG — Natural Language over Documentation

The next layer: ask questions in English, get answers sourced from your own documentation.

GET /ask?q=how do I configure 802.1X on a Catalyst switch?

{
  "answer": "Use the dot1x system-auth-control command globally...",
  "sources": [
    "education/certifications/ccnp/dot1x-config",
    "case-studies/changes/CR-2026-02-15-dot1x-rollout"
  ],
  "model": "qwen3:30b"
}

Architecture:

Component Design

services/embedding.py

On startup, vectorize all document titles + first 500 chars using Ollama /api/embeddings. Store as numpy array in memory (same pattern as DocumentCache — no external vector DB).

services/rag.py

Query embedding → cosine similarity against corpus → select top-K documents → assemble context → Ollama /api/generate with context injection. Response includes provenance (which documents informed the answer).

routes/ask.py

GET /ask?q=…​ — single endpoint. Returns answer, source documents, model name.

Config additions

ollama_url (default: localhost:11434), ollama_model (default: qwen3:30b), embedding_model (default: nomic-embed-text).

Hardware

RTX 5090 local inference. No cloud API. No data leaves the machine.

Why in-memory embeddings: The same reasoning as the DocumentCache. ~3,000 documents × 768-dim float32 = ~9 MB. Fits in RAM trivially. No Chroma, no Pinecone, no Weaviate. numpy cosine similarity over 3,000 vectors completes in microseconds.

Phase 4: Multi-Spoke — Unified Search Across 15 Repositories

The documentation system spans 15 spoke repositories. Today the API indexes only domus-captures. Multi-spoke makes the entire ecosystem queryable through one interface.

What changes:

Component Change

config.py

Add spoke_roots: list[Path] to Settings. Each entry points to an Antora module ROOT.

services/filesystem.py

DocumentCache accepts multiple roots. Each document gets a component field (e.g., domus-captures, domus-infra-ops, domus-ise-linux).

routes/search.py

Search gains a component query parameter. Default: search all. Filter: search one spoke.

New endpoint

GET /components — lists all loaded spokes with document counts per spoke.

Why this is a config change: The filesystem-as-database pattern means the data model is the directory structure. Adding a spoke is adding a directory. No schema migration, no index rebuild. The cache loads additional directories at startup.

Phase 5: Event Automation — Webhooks and Filesystem Watcher

The system becomes reactive. File changes trigger cache updates. Events trigger workflows.

Components:

Component Design

services/watcher.py

Background task using watchfiles (async-compatible). Monitors docs_root for .adoc file changes. On create/modify: cache.add_page(path). On delete: cache.remove_page(path). Runs as an asyncio.Task started in the lifespan context.

routes/webhooks.py

POST /webhooks — register a webhook (URL + event type). Dispatch HTTP callback on matching events. Registration stored as a JSON file in docs_root (filesystem-as-database pattern).

services/events.py

Event bus: document_created, document_modified, incident_resolved. When an incident’s status changes to "resolved", auto-scaffold an RCA via the scaffolder service.

systemd service

domus-api.service unit file. ExecStart=uv run uvicorn domus_api.main:app --host 0.0.0.0 --port 8080. Auto-restart on failure. Stowed via dots-quantum.

Tailscale ACL

Restrict API access to authorized devices. The API already binds to 0.0.0.0 — Tailscale ACLs control which machines can reach port 8080.

Dependency chain: Phase 5 requires the cache.add_page() / cache.add_partial() methods (already implemented) and the dependency injection pattern (already implemented). The foundation is in place.

Phase 6: Operational Deployment — Always-On API

Make domus-api and the association engine start on boot and stay running. No manual startup ritual.

systemd User Service

Create the service file
mkdir -p ~/.config/systemd/user

cat > ~/.config/systemd/user/domus-api.service << 'EOF'
[Unit]
Description=domus-api — REST API for the domus documentation ecosystem
After=network.target

[Service]
Type=simple
WorkingDirectory=/home/evanusmodestus/atelier/_projects/personal/domus-api
ExecStart=/home/evanusmodestus/atelier/_projects/personal/domus-api/.venv/bin/uvicorn domus_api.main:app --host 0.0.0.0 --port 8080
Restart=on-failure
RestartSec=5
Environment=PATH=/home/evanusmodestus/.local/bin:/usr/bin

[Install]
WantedBy=default.target
EOF
Enable and start (survives reboot)
systemctl --user daemon-reload
systemctl --user enable --now domus-api
Verify
systemctl --user status domus-api
curl -s localhost:8080/ | jq '{status, pages: .counts.pages}'
View logs
journalctl --user -u domus-api -f
Restart after code changes
systemctl --user restart domus-api
Stop temporarily
systemctl --user stop domus-api

Shell Aliases

Add to ~/.zshrc or a dots-quantum aliases file:

# domus-api shortcuts
alias api-start='systemctl --user start domus-api'
alias api-stop='systemctl --user stop domus-api'
alias api-restart='systemctl --user restart domus-api'
alias api-status='systemctl --user status domus-api'
alias api-logs='journalctl --user -u domus-api -f'

# Association engine
alias assoc-link='bash ~/atelier/_projects/personal/association-engine/scripts/assoc-link.sh'

# Quick queries
alias api-stats='curl -s localhost:8080/ | jq'
alias api-search='f() { curl -s "localhost:8080/search?q=$1&limit=${2:-10}" | jq "[.results[] | {title, component}]"; }; f'
alias api-assoc='f() { curl -s "localhost:8080/associations/$1" | jq; }; f'
Usage after aliases are loaded
api-status                          # Is it running?
api-search "802.1X"                 # Quick search
api-search "certificate" 20         # Search with limit
api-assoc CISSP                     # Association query
api-assoc python                    # What does Python connect to?
assoc-link codex bash traps --teaches error-handling

Stow Integration (dots-quantum)

The service file and aliases should be stowed via dots-quantum for reproducibility across machines:

dots-quantum/
├── systemd/
│   └── .config/systemd/user/
│       └── domus-api.service
└── zsh/
    └── .config/zsh/
        └── aliases-domus.zsh    # All domus aliases
Stow command
cd ~/atelier/_projects/personal/dots-quantum
stow -t ~ systemd
stow -t ~ zsh

Verification Checklist

  • systemctl --user enable --now domus-api — service starts

  • Reboot → curl -s localhost:8080/ returns operational

  • api-search "security" works from any terminal

  • api-assoc CISSP returns forward + reverse

  • assoc-link link test --enables test-target creates association

  • api-logs shows request activity

  • Service file stowed in dots-quantum

  • Aliases stowed in dots-quantum