claude.md
This commit is contained in:
228
CLAUDE.md
Normal file
228
CLAUDE.md
Normal file
@@ -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
|
||||||
Reference in New Issue
Block a user