Docs-Hub β Auto-Generated Documentation Portal β
Internal documentation portal that auto-renders all Markdown files in the monorepo, with an integrated AI chat assistant powered by the context-engine.
Problem β
The predecessor docs-assistant (studio/docs-assistant, now removed) provided a chat-only interface. Team members had to already know what to ask. There was no way to browse, discover, or navigate the documentation organically. The docs-hub solves this by providing a full browsable documentation portal.
Vision β
A single internal documentation portal that:
- Auto-generates a browsable docs site from the monorepo's folder structure β zero manual curation
- Integrates an AI chat assistant (slide-over drawer) powered by the context-engine for semantic search and Q&A
- Supports role-based content visibility so different team roles see different documentation scopes
Critical Decision: Framework Choice β
Option A: VitePress (Recommended) β
VitePress is purpose-built for documentation sites. It auto-generates sidebar navigation from folder structure, includes built-in local search, and supports custom Vue components.
Strengths:
- Auto-sidebar from file system β the core requirement β works out of the box
- Built-in full-text search (MiniSearch) β no extra infra needed
- Theme customization via Vue component slots (
layout-bottom,nav-bar-title-after) - Extremely fast: instant HMR, sub-second builds
- Vue-based β aligns with the monorepo's ecosystem
- Chat drawer integrates cleanly via custom theme layout extension
Weaknesses:
- Static Site Generator only β no server-side rendering or server API routes
- The existing Nuxt server API (
/api/chat,/api/doc) cannot live inside VitePress β the chat backend must be served separately (from a standalone lightweight Node.js server) - Role-based content filtering requires a build-per-role strategy or a runtime content-gate (since VitePress has no server middleware)
Chat integration pattern:
// .vitepress/theme/index.ts
import DefaultTheme from 'vitepress/theme'
import { h } from 'vue'
import ChatDrawer from './components/ChatDrawer.vue'
export default {
...DefaultTheme,
Layout() {
return h(DefaultTheme.Layout, null, {
'layout-bottom': () => h(ChatDrawer)
})
}
}Option B: Nuxt Content v3 β
Nuxt Content is a module for Nuxt that treats Markdown files as queryable data. It provides <ContentNavigation> and <ContentRenderer> components.
Strengths:
- Full server-side capabilities β server API routes, middleware, SSR
- Role-based filtering can happen server-side via middleware (check auth, filter content)
- The existing Nuxt server code (
/api/chat, session management, RAG) can stay in-place <ContentNavigation>auto-generates sidebar from folder structure
Weaknesses:
- More complex setup β full Nuxt framework overhead for what is essentially a static docs viewer
- Nuxt Content v3 is still maturing and has breaking changes from v2
- Slower builds compared to VitePress
- Content must live in a
content/directory (or be symlinked from the monorepo root)
Option C: Custom Nuxt App (Original Plan β Rejected) β
The original implementation plan proposed building a custom build-time manifest generator, a file-serving API, and a hand-rolled sidebar. This reinvents what VitePress and Nuxt Content already solve.
Why this is wrong:
- The manifest generator is a worse version of VitePress's built-in file routing
- The
/api/docendpoint serving raw files at runtime doesn't work on Vercel serverless (no access to monorepo at runtime) - Hand-rolling sidebar navigation, search, and markdown rendering is weeks of work for a solved problem
- No built-in search, no table of contents, no heading anchors β all must be manually built
Recommendation β
Start with VitePress. It solves the primary requirement (auto-generated browsable docs) with near-zero config. The chat drawer integrates via custom Vue components. The chat backend runs on the context-engine β a standalone lightweight Node.js server. Role-based visibility can start as build-time content filtering and evolve later.
If future requirements demand server-side auth middleware or dynamic content filtering at runtime, migrate to Nuxt Content v3 at that point. VitePress's Markdown files need zero changes for such a migration β both consume standard Markdown with frontmatter.
Why Not Extend a Nuxt App with @nuxt/content? β
A previous recommendation suggested extending a Nuxt app with @nuxt/content, arguing it reuses auth, RAG, and i18n. This approach couples the docs browser and the AI engine into a single Nuxt monolith. That contradicts the architecture goals:
- The context-engine must be a reusable package β it powers not only the docs-hub chat but also the future Copilot, Dispatch Assistant, and Customer Support Agent. Embedding it inside a Nuxt app makes it non-portable.
- VitePress is purpose-built for docs. Nuxt Content adds unnecessary framework overhead (SSR, middleware, routing) for what is fundamentally a static content viewer.
- Separation of concerns. The docs-hub = static site (VitePress). The AI brain = standalone server (
packages/context-engine). Different deployment lifecycles, different scaling needs, cleanly decoupled. - Auth and i18n are trivial to add. Password auth can be implemented as a simple client-side gate in VitePress. The original auth and i18n code took ~50 lines combined β not enough to justify an entire framework choice.
Architecture β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β docs-hub (VitePress) β
β β
β ββββββββββββββββββββββββββ βββββββββββββββββββββββββββββ β
β β Auto-Generated Docs β β Chat Drawer (Vue) β β
β β β β β β
β β β’ Sidebar from dirs β β β’ Floating chat button β β
β β β’ Rendered markdown β β β’ Slide-over panel β β
β β β’ Built-in search β β β’ SSE streaming β β
β β β’ Table of contents β β β’ Source citations β β
β ββββββββββββββββββββββββββ βββββββββββ¬ββββββββββββββββββ β
β β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β HTTP (SSE)
βΌ
ββββββββββββββββββββββββββββββββββββββββ
β context-engine (Node.js server) β
β β
β β’ Gemini embeddings β
β β’ Vector search β
β β’ Claude streaming β
β β’ Role-aware index β
ββββββββββββββββββββββββββββββββββββββββDeployment Strategy β
| Component | Phase 1 (Now) | Long Term |
|---|---|---|
| VitePress static site | Vercel (free tier) | Hetzner (self-hosted, aligns with infra strategy) |
| Context-engine server | Vercel serverless | Hetzner Docker Swarm |
| Authentication | General password (current approach) | Nhost auth with role detection |
Build Trigger & Incremental Builds β
The docs-hub rebuilds on every commit to main via CI/CD pipeline, with an optimization threshold:
- Documentation changes (any
.mdfile modified) β always rebuild - Code-only changes (< 50 total lines changed, no
.mdfiles) β skip rebuild
Ideal future optimization: incremental builds β VitePress rebuilds only changed pages. This requires investigating VitePress's caching behavior and potentially a custom build script that detects changed files via git diff and invalidates only affected pages.
Content Pipeline β
- Build time: VitePress reads all
.mdfiles from configured source directories. Symlinks or file copies bring monorepo docs into VitePress's content root. - Sidebar: VitePress auto-generates sidebar navigation from the directory tree. A
sidebarconfig in.vitepress/config.tscan customize grouping and ordering. - Search: VitePress's built-in MiniSearch provides instant client-side full-text search across all pages.
- Chat: A custom
ChatDrawer.vuecomponent calls the context-engine's separate Node.js server for RAG-powered Q&A.
Content Sources β
The following directories feed into the docs-hub at build time:
| Source Directory | Content Type | Audience |
|---|---|---|
docs/product/ | Product specs, domain model | All roles |
docs/architecture/ | Technical architecture | Developers, Lead |
docs/schemas/ | Database schemas (ERD) | Developers |
docs/protocols/ | Protocol specifications | Developers |
docs/supporting/ | ADRs, guidelines | Developers |
apps/*/docs/ | App-specific docs | Developers |
packages/*/README.md | Package documentation | Developers |
Context-Engine Package (packages/context-engine/) β
The context-engine is a standalone reusable package β the AI brain behind the docs-hub chat and future product features. It runs as its own lightweight Node.js server, separate from both the VitePress static site and the main NestJS backend.
Package Structure β
packages/context-engine/
βββ src/
β βββ indexer/ # Document indexing pipeline
β β βββ walker.ts # Walks monorepo dirs, reads .md files
β β βββ chunker.ts # Splits docs by heading into embeddable chunks
β β βββ embedder.ts # Gemini embedding-001 integration
β βββ search/ # Semantic search
β β βββ vector-store.ts # Vectra/Qdrant abstraction
β β βββ similarity.ts # Cosine similarity, threshold filtering
β βββ chat/ # LLM answer generation
β β βββ rag.ts # Retrieved context β system prompt builder
β β βββ stream.ts # Claude SSE streaming
β βββ auth/ # Simple password auth (MVP)
β β βββ password.ts # Shared password validation
β βββ server.ts # Standalone HTTP server (Fastify)
βββ scripts/
β βββ index-docs.ts # CLI: index all docs β vector store
βββ package.json
βββ tsconfig.jsonAPI Surface β
| Endpoint | Method | Purpose |
|---|---|---|
POST /api/auth | POST | Validate password, return session token |
POST /api/chat | POST | RAG-powered Q&A with SSE streaming |
POST /api/search | POST | Semantic search, return ranked results |
GET /api/health | GET | Health check |
What Moved from docs-assistant (Completed) β
| Previous Location | Current Location | Component |
|---|---|---|
studio/docs-assistant/scripts/index-docs.ts | packages/context-engine/scripts/index-docs.ts | Document indexer |
studio/docs-assistant/scripts/upload-index.ts | packages/context-engine/scripts/upload-index.ts | Index uploader |
studio/docs-assistant/server/utils/rag.ts | packages/context-engine/src/chat/rag.ts | RAG pipeline |
studio/docs-assistant/server/api/chat.post.ts | packages/context-engine/src/server.ts | Chat endpoint |
studio/docs-assistant/server/utils/session.ts | packages/context-engine/src/auth/password.ts | Auth logic |
Consumers β
| Consumer | How it connects | When |
|---|---|---|
| docs-hub (chat drawer) | HTTP β context-engine server | MVP |
| Customer Support Copilot | Import as library | Phase 2 |
| Dispatch Assistant | Import as library | Phase 2 |
| CI/CD doc validation agent | CLI via scripts/ | Future |
MVP Chat Features β
The chat drawer in the docs-hub provides RAG-powered Q&A with minimal infrastructure.
Password Authentication β
| Aspect | Implementation |
|---|---|
| Mechanism | Client-side password gate in VitePress |
| Flow | User enters password β validated against context-engine POST /api/auth β session token stored in localStorage |
| Token | Simple JWT or opaque token with expiry (24h) |
| Scope | Protects only the chat endpoint, not the docs pages (docs are statically deployed) |
| Future | Replaced by Nhost auth with role detection when the product goes live |
Chat History Persistence (localStorage) β
Chat conversations persist locally in the browser β zero server-side storage for MVP.
// Stored in localStorage under 'docs-hub-chats'
interface ChatSession {
id: string // UUID
title: string // Auto-generated from first user message
messages: Message[] // Full message history
createdAt: string // ISO timestamp
updatedAt: string // ISO timestamp
}| Feature | Detail |
|---|---|
| Storage key | docs-hub-chats |
| New chat | Floating "+ New Chat" button, creates a new session |
| Continue | Sidebar or dropdown lists previous sessions by title |
| Auto-title | First user message truncated to ~50 chars becomes the session title |
| Size limit | Cap at 50 sessions; oldest auto-pruned when exceeded |
| Export | Not in MVP; future option to export as markdown |
| Cross-device | No sync β localStorage is per-browser. Acceptable for internal team tool |
Role-Based Content Visibility β
Phase 1: Build-Time Filtering (MVP) β
Use the existing frontmatter type field (abstract vs concrete) to generate separate builds:
| Role Tier | Visible Content | Build Command |
|---|---|---|
| Developer | All content | vitepress build (unfiltered) |
| Management / Product | type: abstract only | vitepress build --filter abstract |
A pre-build script filters the content directory based on frontmatter before VitePress processes it. Deployed as two separate Vercel projects (or preview URLs).
Phase 2: Runtime Role Filtering (Future β with Nhost Auth) β
When the product reaches production and Nhost auth is integrated:
- Auth middleware checks session role on every content request
- Content queries filter by
visibilityfrontmatter field - Context-engine indexes documents per role scope (separate embedding indices)
Frontmatter Extension β
Add a visibility field to Markdown files:
---
type: concrete
ai-permission: read-write
visibility: [developer] # New field
---Visibility values: developer, management, dispatcher, stakeholder.
Files without a visibility field default to all roles (public within the org).
Phase 2: Knowledge Graph & Network View β
Extend the context-engine from flat vector search into a relationship-aware knowledge graph, and surface it as an interactive network visualization in the docs-hub.
Document Relationship Graph β
Build a graph of document relationships at index time:
| Relationship Type | How it's extracted | Example |
|---|---|---|
| Cross-references | Parse markdown links between docs | schema-commerce.md β event-contracts-commerce.md |
| Shared entities | NER extraction (table names, enum values, domain terms) | BOOKING appears in 12 docs |
| Heading co-occurrence | Same heading text across files | "State Machine" in 4 schemas |
| ADR impact | ADR files reference affected schemas/protocols | adr-006 β kalkulations-engine.md |
| Frontmatter tags | type, visibility, domain context | All type: concrete ops docs cluster together |
The context-engine's indexer extracts these relationships during pnpm run index and stores them as edges alongside the embedding vectors.
Interactive Network Visualization β
An Obsidian-like graph view embedded as a VitePress page (/graph):
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β π Documentation Network π Filter βΎ β
β β
β ββββββββββββ β
β β±β schema- ββ² ββββββββββββββββ β
β β± β commerce β β²βββββΆβ event- β β
β β± ββββββββββββ β² β contracts- β β
β β± β β² β commerce β β
β βΌ βΌ βΌ ββββββββββββββββ β
β ββββββ ββββββββββ ββββββββββ β
β βadr β βkalkul- β βPRODUCT β β
β β006 β βations β βpaymentsβ β
β ββββββ ββββββββββ ββββββββββ β
β β
β Nodes: 84 β Edges: 312 β Clusters: 6 β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββTechnology choice: D3.js force-directed graph (or @antv/g6 for richer interaction). Renders in the browser as an interactive canvas β zoom, pan, node click navigates to the doc page.
| Feature | Detail |
|---|---|
| Node = document | Sized by number of connections ("god nodes" are visually larger) |
| Edge = relationship | Color-coded by type (cross-ref, shared entity, ADR impact) |
| Clusters | Auto-detected via community detection (Leiden/Louvain), colored by domain |
| Click | Navigates to the doc page in the docs-hub |
| Search | Type to highlight a node and its neighbors |
| Filters | Toggle relationship types, filter by domain (Product, Architecture, Schemas) |
| Local graph | Each doc page gets a mini-graph showing its direct neighbors (like Obsidian's local graph) |
Graphify Integration β
The existing Graphify integration (docs/architecture/ai.md) builds a code-level knowledge graph (call graphs, cross-module dependencies). Phase 2 connects both graphs:
| Layer | Source | Graph Type | Purpose |
|---|---|---|---|
| Documentation graph | context-engine indexer | Doc-to-doc relationships | Navigate documentation structure |
| Code graph | Graphify | AST-extracted code relationships | Navigate code architecture |
| Bridge edges | Frontmatter implements field | Doc β code, code β doc | Trace from spec to implementation |
The bridge layer requires adding an implements frontmatter field to protocol/schema docs:
---
type: concrete
implements:
- apps/api/src/modules/commerce/booking.service.ts
- apps/api/src/modules/commerce/payment.service.ts
---This enables the network view to show: "This protocol spec is implemented by these code modules" β and vice versa when exploring code via Graphify.
Context-Engine Extensions β
The context-engine package evolves to support graph-aware search:
| Capability | MVP (Vector-only) | Phase 2 (Graph-enriched) |
|---|---|---|
| Search | Cosine similarity on flat chunks | Graph-walk neighbors to expand context |
| Retrieval | Top-8 most similar chunks | Top-8 + their graph neighbors (BFS depth 1) |
| System prompt | Flat context injection | Context + relationship context ("this doc references X, which says...") |
| Indexer | Chunks by heading | Chunks by heading + edge extraction |
Migration Path from docs-assistant (Completed) β
| Step | Action | Status |
|---|---|---|
| 1 | Create studio/docs-hub/ with VitePress scaffold | β Done |
| 2 | Configure sidebar, search, branding | β Done |
| 3 | Build packages/context-engine/ standalone server | β Done |
| 4 | Move RAG logic from docs-assistant β context-engine | β Done |
| 5 | Implement chat drawer component in VitePress theme | β Done |
| 6 | Add password auth gate + localStorage chat persistence | β Done |
| 7 | Deploy docs-hub (static) to Hetzner Swarm | Todo |
| 8 | Deploy context-engine server to Hetzner Swarm | Todo |
| 9 | Remove studio/docs-assistant/ | β Done |
| 10 | Phase 2: Knowledge graph extraction in indexer | Future |
| 11 | Phase 2: Interactive network view (/graph page) | Future |
| 12 | Phase 2: Graphify bridge + graph-enriched RAG | Future |