Files
lovdata-chat/CLAUDE.md
2026-02-01 19:40:55 +01:00

6.7 KiB

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

# 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)

# 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)

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/:

# 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

# 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):

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):

# 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