diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..3594509 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,228 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Architecture Overview + +**Container-per-session architecture** for Norwegian legal research chat interface: + +- **session-manager**: FastAPI service managing OpenCode container lifecycles (one per user session) +- **OpenCode containers**: Isolated chat environments with MCP integration +- **Lovdata MCP server**: External Norwegian legal research server (15+ tools for law search, provisions, cross-references) +- **Caddy**: Reverse proxy with dynamic session-based routing + +### Session Manager Components + +The session-manager is built with a layered architecture: + +``` +main.py → FastAPI endpoints, session lifecycle orchestration +docker_service.py → Docker abstraction layer (testable, mockable) +async_docker_client.py → Async Docker operations +database.py → PostgreSQL session persistence with asyncpg +session_auth.py → Token-based session authentication +container_health.py → Health monitoring and auto-recovery +resource_manager.py → CPU/memory limits, throttling +http_pool.py → Connection pooling for container HTTP requests +host_ip_detector.py → Docker host IP detection (host.docker.internal fallback) +logging_config.py → Structured JSON logging with context +``` + +**Key design patterns:** +- **Dependency injection**: SessionManager receives DockerService via constructor (enables testing with MockDockerService) +- **Service abstraction**: Clean separation between business logic (main.py) and infrastructure (docker_service.py) +- **Async-first**: All I/O operations use asyncio (Docker, HTTP, database) +- **Database persistence**: Sessions survive manager restarts via PostgreSQL + +### MCP Integration + +OpenCode containers connect to two MCP servers: +1. **lovdata MCP server**: Norwegian legal tools (configured via `MCP_SERVER` env var) +2. **sequential-thinking**: Local MCP for reasoning (optional) + +Configuration: `config_opencode/opencode.jsonc` + +Skills: `config_opencode/skills/norwegian-legal-queries/` + +## Development Commands + +### Running the stack + +```bash +# Start all services (session-manager, docker-daemon, caddy) +docker-compose up --build + +# Start in background +docker-compose up -d --build + +# View logs +docker-compose logs -f session-manager + +# Stop services +docker-compose down +``` + +### Session management (API) + +```bash +# Create session +curl http://localhost/api/sessions -X POST + +# List sessions +curl http://localhost/api/sessions + +# Get session info +curl http://localhost/api/sessions/{session_id} + +# Delete session +curl http://localhost/api/sessions/{session_id} -X DELETE + +# Manual cleanup +curl http://localhost/api/cleanup -X POST + +# Health check +curl http://localhost/api/health +``` + +### Running session-manager locally (without Docker) + +```bash +cd session-manager + +# Install dependencies +pip install -r requirements.txt + +# Run directly (requires Docker socket or TLS connection) +uvicorn main:app --reload --host 0.0.0.0 --port 8000 +``` + +### Testing + +Test scripts in `docker/scripts/`: + +```bash +# Test Docker service abstraction +python docker/scripts/test-docker-service.py + +# Test async Docker operations +python docker/scripts/test-async-docker.py + +# Test resource limits +python docker/scripts/test-resource-limits.py + +# Test session authentication +python docker/scripts/test-session-auth.py + +# Test database persistence +python docker/scripts/test-database-persistence.py + +# Test container health monitoring +python docker/scripts/test-container-health.py + +# Test HTTP connection pooling +python docker/scripts/test-http-connection-pool.py + +# Test host IP detection +python docker/scripts/test-host-ip-detection.py + +# Test structured logging +python docker/scripts/test-structured-logging.py +``` + +All test scripts are self-contained and can run independently. + +### Building the OpenCode image + +```bash +# Build with custom MCP server +make build MCP_SERVER=http://your-lovdata-server:8001 + +# Run container interactively +make run + +# Clean up image +make clean +``` + +## Environment Configuration + +Required environment variables (see `.env.example`): + +```bash +MCP_SERVER=http://localhost:8001 # External Lovdata MCP server URL + +# Docker TLS (if using TLS instead of socket) +DOCKER_TLS_VERIFY=1 +DOCKER_CERT_PATH=/etc/docker/certs +DOCKER_HOST=tcp://host.docker.internal:2376 + +# Optional LLM keys (at least one required for chat functionality) +OPENAI_API_KEY=... +ANTHROPIC_API_KEY=... +GOOGLE_API_KEY=... +``` + +## Security + +**Current setup uses Docker socket mounting** (`/var/run/docker.sock`) which grants root access. TLS-based Docker API access is implemented but not enabled by default. + +To enable TLS (recommended for production): + +```bash +# Generate certificates +cd docker +DOCKER_ENV=production ./scripts/generate-certs.sh + +# Configure Docker daemon +./scripts/setup-docker-tls.sh + +# Update docker-compose.yml to use TLS instead of socket +``` + +**Session isolation:** +- Each session gets dedicated container +- Resource limits: 4GB RAM, 1 CPU core per container +- Max 3 concurrent sessions (configurable in resource_manager.py) +- Auto-cleanup after 60 minutes inactivity + +## Session Data Persistence + +Sessions are stored in PostgreSQL with the following schema: +- Session ID, container ID, status, timestamps +- Survives session-manager restarts +- Health monitoring tracks container state + +Session working directories: `./sessions/` (bind-mounted into containers) + +## Container Health Monitoring + +Automatic health checks run every 30 seconds: +- Restart unhealthy containers (max 3 attempts) +- Mark failed sessions for cleanup +- Track health history for debugging + +## Implementation Documentation + +Detailed implementation guides in `docker/`: +- `ASYNC_DOCKER_IMPLEMENTATION.md` - Async Docker client +- `CONTAINER_HEALTH_MONITORING_IMPLEMENTATION.md` - Health checks +- `DATABASE_PERSISTENCE_IMPLEMENTATION.md` - Session database +- `HOST_IP_IMPLEMENTATION.md` - Host IP detection +- `HTTP_CONNECTION_POOLING_IMPLEMENTATION.md` - HTTP pooling +- `RESOURCE_LIMITS_IMPLEMENTATION.md` - Resource management +- `SESSION_AUTHENTICATION_IMPLEMENTATION.md` - Auth tokens +- `STRUCTURED_LOGGING_IMPLEMENTATION.md` - JSON logging + +## Common Pitfalls + +1. **MCP_SERVER must point to external server**: The lovdata-ai MCP server is NOT part of this stack, configure URL in `.env` + +2. **Docker socket permissions**: Session-manager needs access to Docker socket or TLS certificates + +3. **Port conflicts**: Session-manager uses ports 8000, Caddy uses 80/443, docker-daemon uses 2376 + +4. **Container cleanup**: Failed containers may linger, use `/api/cleanup` to force cleanup + +5. **Resource exhaustion**: Default limit is 3 concurrent sessions, increase in resource_manager.py if needed + +6. **Database connection**: PostgreSQL connection configured in database.py, defaults to localhost:5432