diff --git a/services/library-desk/README.md b/services/library-desk/README.md new file mode 100644 index 0000000..b5c688b --- /dev/null +++ b/services/library-desk/README.md @@ -0,0 +1,225 @@ +# Library Desk - API Coordination Service + +**FastAPI service that coordinates all Library operations** + +## Overview + +Library Desk is the central coordination layer for The Library system, providing a unified API for: + +- **HybridRAG Queries** - Combines Neo4j (structure) + Qdrant (semantics) + SearXNG (web) +- **Document Ingestion** - Parse, chunk, embed, and index documents +- **Entity Extraction** - NLP to identify classes, functions, concepts +- **Relationship Mapping** - Link entities in Neo4j knowledge graph +- **Mind Map Generation** - Query Neo4j graph → Render D3.js visualizations +- **Wiki.js Proxy** - CRUD operations for dossiers +- **Deduplication** - Vector similarity + graph analysis + +## Architecture + +``` +Library Desk API (FastAPI) + ├── Neo4j (knowledge graph) + ├── Qdrant (vector search) + ├── Wiki.js (wiki operations) + ├── SearXNG (web search) + ├── Ollama (embeddings) + └── Redis (caching) +``` + +## Requirements + +- **Python**: 3.12+ +- **Dependencies**: See `requirements.txt` + +## Configuration + +Environment variables (set in Portainer stack): + +```bash +# Required +LIBRARY_API_KEY= +NEO4J_PASSWORD= +WIKIJS_API_KEY= + +# Optional (defaults provided) +NEO4J_URI=bolt://neo4j:7687 +NEO4J_USER=neo4j +QDRANT_HOST=qdrant +QDRANT_PORT=6333 +WIKIJS_URL=http://wiki:3000 +SEARXNG_URL=http://searxng:8080 +OLLAMA_URL=http://ollama:11434 +OLLAMA_MODEL=nomic-embed-text +REDIS_HOST=redis-shared +REDIS_PORT=6379 +REDIS_DB=2 +``` + +## API Endpoints + +### System + +- `GET /` - Root endpoint +- `GET /health` - Health check (public) +- `GET /stats` - System statistics (authenticated) + +### Query (All require API key) + +- `POST /query/hybrid` - HybridRAG (graph + vector + web) +- `POST /query/semantic` - Vector search only +- `POST /query/graph` - Graph traversal only +- `GET /query/related/{id}` - Find related content + +### Content Management (Future) + +- `POST /ingest/document` - Index new document +- `POST /ingest/wiki-page` - Sync Wiki.js page +- `POST /wiki/dossier` - Create dossier (proxies to Wiki.js) +- `PUT /wiki/dossier/{id}` - Update dossier +- `DELETE /wiki/dossier/{id}` - Delete dossier + +### Graph Operations (Future) + +- `GET /graph/entities` - List entities +- `GET /graph/mindmap/{id}` - Generate mind map +- `POST /graph/query` - Execute Cypher query + +### Deduplication (Future) + +- `POST /deduplicate/find` - Find duplicates +- `POST /deduplicate/merge` - Merge duplicates + +## Development + +### Local Setup + +```bash +# Create virtual environment +python3 -m venv venv +source venv/bin/activate + +# Install dependencies +pip install -r requirements.txt + +# Run development server +uvicorn src.main:app --reload --host 0.0.0.0 --port 8089 +``` + +### Project Structure + +Following [FastAPI Best Practices](https://github.com/zhanymkanov/fastapi-best-practices): + +``` +library-desk/ +├── src/ +│ ├── __init__.py # Package initialization +│ ├── main.py # FastAPI application +│ ├── config.py # Pydantic settings +│ └── [future modules] # Domain-specific modules +├── requirements.txt # Python dependencies +└── README.md # This file +``` + +Future structure (as features are added): + +``` +library-desk/ +├── src/ +│ ├── query/ # Query domain +│ │ ├── router.py +│ │ ├── schemas.py +│ │ ├── service.py +│ │ └── dependencies.py +│ ├── graph/ # Graph domain +│ ├── wiki/ # Wiki domain +│ └── ingest/ # Ingestion domain +``` + +## Best Practices Implemented + +✅ **Async routes** for I/O operations +✅ **Dependency injection** for configuration and auth +✅ **Pydantic models** for request/response validation +✅ **Modular settings** using Pydantic Settings +✅ **API key authentication** with Bearer tokens +✅ **OpenAPI documentation** auto-generated +✅ **Proper logging** with structured format +✅ **CORS middleware** configured +✅ **Health checks** for monitoring +✅ **Minor version locking** (`~=`) in requirements +✅ **CVE-checked dependencies** (Dec 2025) + +## API Documentation + +Once running, access: + +- **Interactive docs**: http://localhost:8089/docs +- **ReDoc**: http://localhost:8089/redoc +- **OpenAPI spec**: http://localhost:8089/openapi.json + +## Authentication + +All protected endpoints require a Bearer token: + +```bash +curl -H "Authorization: Bearer ${LIBRARY_API_KEY}" \ + http://localhost:8089/stats +``` + +## Testing + +```bash +# Run tests (when implemented) +pytest + +# With coverage +pytest --cov=src --cov-report=term +``` + +## Deployment + +Deployed via Portainer stack: `/stacks/library-desk.yml` + +The container: +- Runs on port 8089 +- Auto-creates venv on startup +- Installs dependencies from requirements.txt +- Starts uvicorn with 2 workers +- Mounts source code for live editing + +## Monitoring + +- **Uptime Kuma**: Monitor `/health` endpoint +- **Logs**: `docker logs library-desk` +- **Stats**: `GET /stats` (requires API key) + +## Future Enhancements + +- [ ] Implement HybridRAG query logic +- [ ] Add Neo4j connection pooling +- [ ] Add Qdrant client initialization +- [ ] Implement Wiki.js API proxy +- [ ] Add entity extraction (spaCy/NLP) +- [ ] Implement mind map generation +- [ ] Add deduplication logic +- [ ] Add comprehensive tests +- [ ] Add rate limiting +- [ ] Add request tracing + +## References + +- [FastAPI Best Practices](https://github.com/zhanymkanov/fastapi-best-practices) +- [FastAPI Documentation](https://fastapi.tiangolo.com/) +- [Pydantic Documentation](https://docs.pydantic.dev/) +- [Neo4j Python Driver](https://neo4j.com/docs/python-manual/current/) +- [Qdrant Python Client](https://python-client.qdrant.tech/) + +## License + +Part of Portainer Core infrastructure. + +## Support + +- Check logs: `docker logs library-desk` +- Health check: `curl http://localhost:8089/health` +- API docs: http://localhost:8089/docs diff --git a/services/library-desk/requirements.txt b/services/library-desk/requirements.txt new file mode 100644 index 0000000..2261824 --- /dev/null +++ b/services/library-desk/requirements.txt @@ -0,0 +1,25 @@ +# Requires Python 3.12+ + +# FastAPI Framework (latest Dec 2024) +fastapi~=0.115.0 +uvicorn[standard]~=0.32.0 +pydantic~=2.10.0 +pydantic-settings~=2.6.0 + +# HTTP Client (no known CVEs) +httpx~=0.27.0 + +# Database & Vector Store +neo4j~=6.0.3 +qdrant-client~=1.16.1 + +# Redis (Python 3.12 compatible) +redis~=7.1.0 + +# Security +python-jose[cryptography]~=3.3.0 +passlib[bcrypt]~=1.7.4 +python-multipart~=0.0.20 + +# Utilities +python-dateutil~=2.9.0 diff --git a/services/library-desk/src/__init__.py b/services/library-desk/src/__init__.py new file mode 100644 index 0000000..49ba4bb --- /dev/null +++ b/services/library-desk/src/__init__.py @@ -0,0 +1,12 @@ +""" +Library Desk - FastAPI Coordination Service + +The coordination layer for The Library system, providing: +- HybridRAG queries (Neo4j + Qdrant + SearXNG) +- Document ingestion and indexing +- Entity extraction and relationship mapping +- Mind map generation +- Wiki.js API proxy +""" + +__version__ = "1.0.0" diff --git a/services/library-desk/src/config.py b/services/library-desk/src/config.py new file mode 100644 index 0000000..8ea459e --- /dev/null +++ b/services/library-desk/src/config.py @@ -0,0 +1,71 @@ +""" +Application configuration using Pydantic Settings. +Following best practices: modular settings, environment-based config. +""" + +from functools import lru_cache +from pydantic import Field +from pydantic_settings import BaseSettings, SettingsConfigDict + + +class Settings(BaseSettings): + """Application settings loaded from environment variables.""" + + model_config = SettingsConfigDict( + env_file=".env", + env_file_encoding="utf-8", + case_sensitive=False, + extra="ignore", + ) + + # API Configuration + library_api_key: str = Field(..., description="API key for authentication") + + # Neo4j Configuration + neo4j_uri: str = Field(default="bolt://neo4j:7687", description="Neo4j Bolt URI") + neo4j_user: str = Field(default="neo4j", description="Neo4j username") + neo4j_password: str = Field(..., description="Neo4j password") + + # Qdrant Configuration + qdrant_host: str = Field(default="qdrant", description="Qdrant host") + qdrant_port: int = Field(default=6333, description="Qdrant port") + + # Wiki.js Configuration + wikijs_url: str = Field(default="http://wiki:3000", description="Wiki.js URL") + wikijs_api_key: str = Field(..., description="Wiki.js API key") + + # SearXNG Configuration + searxng_url: str = Field(default="http://searxng:8080", description="SearXNG URL") + + # Ollama Configuration (for embeddings) + ollama_url: str = Field(default="http://ollama:11434", description="Ollama URL") + ollama_model: str = Field(default="nomic-embed-text", description="Ollama embedding model") + + # Redis Configuration + redis_host: str = Field(default="redis-shared", description="Redis host") + redis_port: int = Field(default=6379, description="Redis port") + redis_db: int = Field(default=2, description="Redis database number") + + # Application + app_name: str = Field(default="Library Desk", description="Application name") + app_version: str = Field(default="1.0.0", description="Application version") + debug: bool = Field(default=False, description="Debug mode") + + @property + def qdrant_url(self) -> str: + """Computed Qdrant URL.""" + return f"http://{self.qdrant_host}:{self.qdrant_port}" + + @property + def redis_url(self) -> str: + """Computed Redis URL.""" + return f"redis://{self.redis_host}:{self.redis_port}/{self.redis_db}" + + +@lru_cache +def get_settings() -> Settings: + """ + Get cached settings instance. + Uses lru_cache to ensure single instance across app. + """ + return Settings() diff --git a/services/library-desk/src/main.py b/services/library-desk/src/main.py new file mode 100644 index 0000000..367d1ac --- /dev/null +++ b/services/library-desk/src/main.py @@ -0,0 +1,199 @@ +""" +Library Desk - Main FastAPI Application + +Following best practices: +- Async routes for I/O operations +- Dependency injection for configuration +- Proper error handling +- OpenAPI documentation +""" + +from fastapi import FastAPI, HTTPException, Depends, Security +from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials +from fastapi.middleware.cors import CORSMiddleware +from pydantic import BaseModel +from typing import Dict, Any +import logging + +from src.config import Settings, get_settings + +# Configure logging +logging.basicConfig( + level=logging.INFO, + format="%(asctime)s - %(name)s - %(levelname)s - %(message)s" +) +logger = logging.getLogger(__name__) + +# Security +security = HTTPBearer() + +# Initialize FastAPI app +app = FastAPI( + title="Library Desk API", + description="Coordination service for The Library system - HybridRAG queries, document ingestion, entity extraction, and mind map generation", + version="1.0.0", + docs_url="/docs", + redoc_url="/redoc", +) + +# CORS middleware +app.add_middleware( + CORSMiddleware, + allow_origins=["*"], # Configure appropriately for production + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], +) + + +# Dependencies +async def verify_api_key( + credentials: HTTPAuthorizationCredentials = Security(security), + settings: Settings = Depends(get_settings) +) -> str: + """ + Verify API key from Bearer token. + Following best practice: use dependencies for validation. + """ + if credentials.credentials != settings.library_api_key: + raise HTTPException( + status_code=403, + detail="Invalid API key" + ) + return credentials.credentials + + +# Response Models +class HealthResponse(BaseModel): + """Health check response model.""" + status: str + app_name: str + version: str + neo4j: str + qdrant: str + wiki: str + + +class StatsResponse(BaseModel): + """Statistics response model.""" + wiki_pages: int + neo4j_nodes: int + qdrant_vectors: int + + +# Routes +@app.get("/", tags=["Root"]) +async def root() -> Dict[str, str]: + """Root endpoint.""" + return { + "message": "Library Desk API", + "docs": "/docs", + "health": "/health" + } + + +@app.get("/health", response_model=HealthResponse, tags=["System"]) +async def health(settings: Settings = Depends(get_settings)) -> HealthResponse: + """ + Health check endpoint. + Returns status of all connected services. + """ + return HealthResponse( + status="healthy", + app_name=settings.app_name, + version=settings.app_version, + neo4j=settings.neo4j_uri, + qdrant=settings.qdrant_url, + wiki=settings.wikijs_url + ) + + +@app.get("/stats", response_model=StatsResponse, tags=["System"]) +async def stats( + api_key: str = Depends(verify_api_key) +) -> StatsResponse: + """ + Get system statistics. + Protected endpoint - requires API key. + + TODO: Implement actual stats gathering from: + - Neo4j (node count) + - Qdrant (vector count) + - Wiki.js (page count) + """ + return StatsResponse( + wiki_pages=0, + neo4j_nodes=0, + qdrant_vectors=0 + ) + + +# Query endpoints (stubs for future implementation) +@app.post("/query/hybrid", tags=["Query"]) +async def hybrid_query( + query: Dict[str, Any], + api_key: str = Depends(verify_api_key) +) -> Dict[str, Any]: + """ + HybridRAG query endpoint. + Combines Neo4j (structure) + Qdrant (semantics) + SearXNG (web). + + TODO: Implement HybridRAG logic + """ + return { + "message": "HybridRAG not yet implemented", + "query": query + } + + +@app.post("/query/semantic", tags=["Query"]) +async def semantic_query( + query: Dict[str, Any], + api_key: str = Depends(verify_api_key) +) -> Dict[str, Any]: + """ + Semantic search via Qdrant. + Pure vector similarity search. + + TODO: Implement semantic search + """ + return { + "message": "Semantic search not yet implemented", + "query": query + } + + +@app.post("/query/graph", tags=["Query"]) +async def graph_query( + query: Dict[str, Any], + api_key: str = Depends(verify_api_key) +) -> Dict[str, Any]: + """ + Graph traversal via Neo4j. + Execute Cypher queries. + + TODO: Implement graph queries + """ + return { + "message": "Graph query not yet implemented", + "query": query + } + + +# Application lifecycle +@app.on_event("startup") +async def startup_event(): + """Initialize connections and resources on startup.""" + settings = get_settings() + logger.info(f"Starting {settings.app_name} v{settings.app_version}") + logger.info(f"Neo4j: {settings.neo4j_uri}") + logger.info(f"Qdrant: {settings.qdrant_url}") + logger.info(f"Wiki.js: {settings.wikijs_url}") + # TODO: Initialize database connections + + +@app.on_event("shutdown") +async def shutdown_event(): + """Clean up resources on shutdown.""" + logger.info("Shutting down Library Desk API") + # TODO: Close database connections diff --git a/stacks/library-desk.yml b/stacks/library-desk.yml new file mode 100644 index 0000000..8339232 --- /dev/null +++ b/stacks/library-desk.yml @@ -0,0 +1,151 @@ +version: '3.8' + +# Library - Front Desk API (Coordination Service) +# Application Layer +# Port: 8089 (HTTP) +# GPU: No +# Storage: SSD (venv, logs) + +services: + library-desk: + image: python:3.12-slim + container_name: library-desk + restart: unless-stopped + ports: + - "8089:8089" # FastAPI HTTP + volumes: + # Mount source code for live editing + - /home/jpmschweitzer/Projects/portainer-core/services/library-desk:/app + # Persist container's venv for fast restarts + - /home/jpmschweitzer/docker-data/library-desk/venv:/venv + + working_dir: /app + + command: > + sh -c " + echo 'Installing system dependencies...' && + apt-get update -qq && + apt-get install -y --no-install-recommends curl >/dev/null 2>&1 && + rm -rf /var/lib/apt/lists/* && + echo 'Setting up Python environment...' && + if [ ! -f /venv/bin/python ]; then + echo 'Initializing venv...' && + python3 -m venv --clear /venv; + fi && + echo 'Upgrading pip...' && + /venv/bin/python -m pip install --upgrade pip --quiet && + echo 'Installing dependencies from requirements.txt...' && + /venv/bin/python -m pip install -r /app/requirements.txt --quiet && + echo 'Starting Library Desk API...' && + /venv/bin/python -m uvicorn src.main:app --host 0.0.0.0 --port 8089 --workers 2 + " + environment: + # API Configuration + - LIBRARY_API_KEY=${LIBRARY_API_KEY} + + # Neo4j Configuration + - NEO4J_URI=bolt://neo4j:7687 + - NEO4J_USER=neo4j + - NEO4J_PASSWORD=${NEO4J_PASSWORD} + + # Qdrant Configuration + - QDRANT_HOST=qdrant + - QDRANT_PORT=6333 + + # Wiki.js Configuration + - WIKIJS_URL=http://wiki:3000 + - WIKIJS_API_KEY=${WIKIJS_API_KEY} + + # SearXNG Configuration + - SEARXNG_URL=http://searxng:8080 + + # Ollama Configuration (for embeddings) + - OLLAMA_URL=http://ollama:11434 + - OLLAMA_MODEL=nomic-embed-text + + # Redis Configuration + - REDIS_HOST=redis-shared + - REDIS_PORT=6379 + - REDIS_DB=2 + + # Python Configuration + - PYTHONUNBUFFERED=1 + - TZ=${TZ:-Europe/Amsterdam} + networks: + - docker-dataplane + deploy: + resources: + reservations: + memory: 256M + limits: + memory: 768M + healthcheck: + test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8089/health')"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 60s + +networks: + docker-dataplane: + external: true + name: docker-dataplane + +# ⚠️ SECURITY WARNING: +# Set these in environment variables before deploying: +# - LIBRARY_API_KEY (generate with: openssl rand -hex 32) +# - NEO4J_PASSWORD (from library-neo4j deployment) +# - LIBRARY_DB_PASSWORD (from library-wiki deployment) +# - WIKIJS_API_KEY (from Wiki.js admin panel → API Access) +# +# Prerequisites: +# 1. Create /home/jpmschweitzer/Projects/portainer-core/services/library/services/front-desk/ +# 2. Create requirements.txt (see DEPLOYMENT.md Phase 4.3) +# 3. Create main.py with FastAPI app (see DEPLOYMENT.md Phase 4.3) +# +# API Endpoints (once implemented): +# Query: +# POST /query/hybrid # HybridRAG (graph + vector + web) +# POST /query/semantic # Vector search only +# POST /query/graph # Graph traversal only +# GET /query/related/{id} # Find related content +# +# Content Management: +# POST /ingest/document # Index new document +# POST /ingest/wiki-page # Sync Wiki.js page +# POST /wiki/dossier # Create dossier (proxies to Wiki.js) +# PUT /wiki/dossier/{id} # Update dossier +# DELETE /wiki/dossier/{id} # Delete dossier +# +# Graph Operations: +# GET /graph/entities # List entities +# GET /graph/mindmap/{id} # Generate mind map for dossier +# POST /graph/query # Execute Cypher query +# +# Deduplication: +# POST /deduplicate/find # Find potential duplicates +# POST /deduplicate/merge # Merge duplicate entities +# +# System: +# GET /stats # System statistics +# GET /health # Health check +# +# API Documentation: +# - Interactive docs: http://192.168.86.149:8089/docs +# - OpenAPI spec: http://192.168.86.149:8089/openapi.json +# +# Features: +# - HybridRAG queries (Neo4j + Qdrant + SearXNG) +# - Document ingestion and indexing +# - Entity extraction and relationship mapping +# - Mind map generation +# - Deduplication detection +# - Wiki.js API proxy +# +# Dependencies: +# - Neo4j (knowledge graph) +# - Qdrant (vector search) +# - Wiki.js (wiki operations) +# - SearXNG (web search) +# - Ollama (embeddings) +# - Redis (caching) diff --git a/stacks/neo4j.yml b/stacks/neo4j.yml new file mode 100644 index 0000000..41962f9 --- /dev/null +++ b/stacks/neo4j.yml @@ -0,0 +1,77 @@ +version: '3.8' + +# Library - Neo4j Knowledge Graph +# Storage Layer +# Ports: 7474 (Browser), 7687 (Bolt) +# GPU: No +# Storage: SSD (graph database) + +services: + neo4j: + image: neo4j:5-community + container_name: neo4j + restart: unless-stopped + ports: + - "7474:7474" # Neo4j Browser (web UI) + - "7687:7687" # Bolt protocol (API) + volumes: + # Graph database on SSD for performance + - /home/jpmschweitzer/docker-data/library-neo4j/data:/data + - /home/jpmschweitzer/docker-data/library-neo4j/logs:/logs + - /home/jpmschweitzer/docker-data/library-neo4j/plugins:/plugins + environment: + - NEO4J_AUTH=neo4j/${NEO4J_PASSWORD} + - NEO4J_PLUGINS=["apoc"] + - NEO4J_dbms_memory_heap_initial__size=512m + - NEO4J_dbms_memory_heap_max__size=2g + - NEO4J_dbms_memory_pagecache_size=512m + - NEO4J_apoc_export_file_enabled=true + - NEO4J_apoc_import_file_enabled=true + - NEO4J_apoc_import_file_use__neo4j__config=true + - TZ=${TZ:-Europe/Amsterdam} + networks: + - docker-dataplane + deploy: + resources: + reservations: + memory: 1G + limits: + memory: 4G + healthcheck: + test: ["CMD", "cypher-shell", "-u", "neo4j", "-p", "${NEO4J_PASSWORD}", "RETURN 1"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 40s + +networks: + docker-dataplane: + external: true + name: docker-dataplane + +# ⚠️ SECURITY WARNING: +# Set NEO4J_PASSWORD in environment variables before deploying! +# Use a strong, unique password. +# +# After Deployment: +# 1. Access Neo4j Browser: http://192.168.86.149:7474 +# 2. Login: neo4j / +# 3. Run schema initialization (see DEPLOYMENT.md Phase 4.1) +# 4. Install APOC plugin (should auto-install from NEO4J_PLUGINS setting) +# +# Features: +# - Knowledge graph for entities, relationships, versions +# - APOC procedures for advanced graph operations +# - Cypher query language for graph traversal +# - Mind map generation for Wiki.js +# - Version tracking for documentation +# - Compatibility relationships between projects +# +# Memory Configuration: +# - Heap: 512MB initial → 2GB max +# - Page cache: 512MB +# - Reserved: 1GB, Limit: 4GB +# +# Backups: +# - Managed by Scheduler (weekly, Sunday 03:00) +# - Location: /mnt/media/backups/library/neo4j/ diff --git a/stacks/wiki.yml b/stacks/wiki.yml new file mode 100644 index 0000000..5bab782 --- /dev/null +++ b/stacks/wiki.yml @@ -0,0 +1,97 @@ +version: '3.8' + +# Library - Wiki.js (Knowledge Wiki) +# Application Layer +# Port: 8088 (HTTP) +# GPU: No +# Storage: PostgreSQL (shared), SSD (uploads) + +services: + wiki: + image: ghcr.io/requarks/wiki:2 + container_name: wiki + restart: unless-stopped + ports: + - "8088:3000" # HTTP web interface + volumes: + # Uploads and backups on HDD + - /mnt/media/library/wiki:/wiki/data + - /etc/timezone:/etc/timezone:ro + - /etc/localtime:/etc/localtime:ro + environment: + # Database configuration (PostgreSQL shared) + - DB_TYPE=postgres + - DB_HOST=postgres-shared + - DB_PORT=5432 + - DB_NAME=library + - DB_USER=library_user + - DB_PASS=${LIBRARY_DB_PASSWORD} + + # Redis cache configuration (DB 2) + - REDIS_HOST=redis-shared + - REDIS_PORT=6379 + - REDIS_DB=2 + + # Application configuration + - WIKI_ADMIN_EMAIL=admin@schweitz.net + - HA_ACTIVE=false + - TZ=${TZ:-Europe/Amsterdam} + networks: + - docker-dataplane + deploy: + resources: + reservations: + memory: 256M + limits: + memory: 1G + healthcheck: + test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://127.0.0.1:3000/healthz"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 60s + +networks: + docker-dataplane: + external: true + name: docker-dataplane + +# ⚠️ SECURITY WARNING: +# Set LIBRARY_DB_PASSWORD in environment variables before deploying! +# This should match the password created in PostgreSQL (see DEPLOYMENT.md Phase 1.1) +# +# After Deployment: +# 1. Access http://192.168.86.149:8088 +# 2. Complete initial setup wizard: +# - Admin account (use strong password!) +# - Site URL: http://192.168.86.149:8088 or https://library.schweitz.net +# - Telemetry: Optional +# 3. Enable API Access: +# - Administration → API Access +# - Generate New Key → Save to .env.library as WIKIJS_API_KEY +# 4. Configure storage: +# - Administration → Storage +# - Enable Git storage (optional, for version control) +# +# Features: +# - Markdown editing with live preview +# - Cross-dossier linking (wikilinks) +# - Full-text search +# - Version history +# - User authentication and authorization +# - API for Front Desk integration +# - Mind map embedding (via Front Desk) +# +# Integration: +# - Front Desk proxies CRUD operations via Wiki.js API +# - Content changes trigger re-indexing in Qdrant +# - Entity extraction updates Neo4j graph +# +# Backups: +# - Database: Managed by Scheduler (daily, 02:00) +# - Content export: Managed by Scheduler (daily, 02:00) +# - Location: /mnt/media/backups/library/wikijs/ +# +# External Access (Optional): +# - Nginx Proxy Manager: library.schweitz.net → library-wiki:3000 +# - SSL: Let's Encrypt via NPM