Files
mesh/infra/CLAUDE.md
Gilles Soulier 1d177e96a6 first
2026-01-05 13:20:54 +01:00

8.2 KiB

CLAUDE.md — Mesh Infrastructure

This file provides infrastructure-specific guidance for Mesh deployment and operations.

Infrastructure Role

The infrastructure layer provides:

  • Docker containers for all services
  • Reverse proxy with TLS termination
  • TURN server for NAT traversal fallback
  • Gotify for notifications
  • Monitoring and logging (optional)

Components

Mesh Server

  • FastAPI application
  • WebSocket support
  • Database (SQLite or PostgreSQL)
  • Health check endpoint

Coturn (TURN Server)

  • NAT traversal fallback for WebRTC
  • Temporary credentials
  • Rate limiting and quotas
  • Ports: 3478 (UDP/TCP), 49160-49200 (UDP range)

Gotify

  • Push notification server
  • Web UI on port 80/443
  • Volume for persistent data

Reverse Proxy (Caddy or Nginx)

  • TLS termination
  • HTTP to HTTPS redirect
  • WebSocket upgrade support
  • Static file serving for client

Directory Structure

infra/
├── docker-compose.yml      # Production compose file
├── docker-compose.dev.yml  # Development compose file
├── .env.example            # Environment variables template
├── nginx/
│   ├── nginx.conf          # Nginx configuration
│   └── ssl/                # SSL certificates
├── caddy/
│   └── Caddyfile           # Caddy configuration
└── CLAUDE.md

Environment Variables

Create .env file in infra/ directory:

# Mesh Server
MESH_PUBLIC_URL=https://mesh.example.com
MESH_JWT_SECRET=your-secret-key-change-this
MESH_JWT_ALGORITHM=HS256
MESH_JWT_ACCESS_TOKEN_EXPIRE_MINUTES=120

# Gotify
GOTIFY_URL=https://gotify.example.com
GOTIFY_TOKEN=your-gotify-token
GOTIFY_DEFAULTUSER_NAME=admin
GOTIFY_DEFAULTUSER_PASS=change-this-password

# TURN Server
TURN_HOST=turn.example.com
TURN_PORT=3478
TURN_EXTERNAL_IP=your-server-public-ip
TURN_REALM=mesh.example.com
TURN_USER=mesh
TURN_PASS=change-this-password

# STUN
STUN_URL=stun:stun.l.google.com:19302

# Database
DATABASE_URL=sqlite:///./mesh.db
# Or for PostgreSQL:
# DATABASE_URL=postgresql://user:pass@postgres:5432/mesh

# Logging
LOG_LEVEL=INFO

Docker Compose

Development

cd infra
cp .env.example .env
# Edit .env
docker-compose -f docker-compose.dev.yml up -d

Production

cd infra
cp .env.example .env
# Edit .env with production values
docker-compose up -d

Network Ports

Required open ports:

  • 80/tcp - HTTP (redirect to HTTPS)
  • 443/tcp - HTTPS (web client + API)
  • 3478/tcp - TURN (TCP mode)
  • 3478/udp - TURN (UDP mode)
  • 49160-49200/udp - TURN relay range

Optional:

  • 8080/tcp - Gotify web UI (if exposed separately)

Reverse Proxy Configuration

mesh.example.com {
    reverse_proxy /api/* mesh-server:8000
    reverse_proxy /ws mesh-server:8000
    reverse_proxy /health mesh-server:8000

    root * /var/www/mesh-client
    file_server
    try_files {path} /index.html
}

gotify.example.com {
    reverse_proxy gotify:80
}

Nginx

server {
    listen 443 ssl http2;
    server_name mesh.example.com;

    ssl_certificate /etc/nginx/ssl/cert.pem;
    ssl_certificate_key /etc/nginx/ssl/key.pem;

    # API endpoints
    location /api/ {
        proxy_pass http://mesh-server:8000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
    }

    # WebSocket
    location /ws {
        proxy_pass http://mesh-server:8000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
    }

    # Client static files
    location / {
        root /var/www/mesh-client;
        try_files $uri /index.html;
    }
}

SSL/TLS Certificates

With Caddy: Automatic

With Nginx + Certbot:

certbot --nginx -d mesh.example.com -d gotify.example.com

Self-Signed (Development Only)

openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
  -keyout infra/nginx/ssl/key.pem \
  -out infra/nginx/ssl/cert.pem

TURN Server Configuration

Coturn configuration for NAT traversal:

# In docker-compose.yml, coturn command:
-n                              # Use config from CLI
--log-file=stdout               # Log to stdout
--external-ip=${TURN_EXTERNAL_IP}
--realm=${TURN_REALM}
--user=${TURN_USER}:${TURN_PASS}
--listening-port=3478
--min-port=49160
--max-port=49200
--fingerprint
--lt-cred-mech
--no-multicast-peers
--no-cli

Important:

  • Monitor TURN bandwidth usage
  • Implement rate limiting
  • Use temporary credentials (V1+)
  • TURN should be fallback only, not primary

Database

SQLite (Development)

Default option, file-based:

DATABASE_URL=sqlite:///./mesh.db

Data persists in volume: mesh_db

Add to docker-compose.yml:

postgres:
  image: postgres:16
  environment:
    POSTGRES_USER: mesh
    POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
    POSTGRES_DB: mesh
  volumes:
    - postgres_data:/var/lib/postgresql/data
  restart: unless-stopped

volumes:
  postgres_data:

Update DATABASE_URL:

DATABASE_URL=postgresql://mesh:${POSTGRES_PASSWORD}@postgres:5432/mesh

Monitoring & Logging

Logs

View service logs:

docker-compose logs -f mesh-server
docker-compose logs -f coturn
docker-compose logs -f gotify

Health Checks

Mesh Server health:

curl https://mesh.example.com/health

Expected response:

{"status": "healthy"}

Metrics (V2)

Consider adding:

  • Prometheus for metrics collection
  • Grafana for visualization
  • Alert manager for notifications

Backup Strategy

Database Backup

SQLite:

docker exec mesh-server sqlite3 /app/mesh.db ".backup /app/backup.db"
docker cp mesh-server:/app/backup.db ./backup-$(date +%Y%m%d).db

PostgreSQL:

docker exec postgres pg_dump -U mesh mesh > backup-$(date +%Y%m%d).sql

Gotify Data

docker cp gotify:/app/data ./gotify-backup-$(date +%Y%m%d)

Backup Schedule

Recommended:

  • Daily database backups
  • Weekly full backups
  • 30-day retention

Security Checklist

  • HTTPS with valid certificates
  • Strong JWT secret (32+ characters)
  • Firewall rules configured (only required ports open)
  • TURN credentials changed from defaults
  • Gotify admin password changed
  • Database credentials secured
  • Regular security updates (container images)
  • Logs rotated and retained appropriately
  • Rate limiting on auth endpoints
  • TURN bandwidth monitoring

Scaling Considerations

Current architecture: Single server, 2-4 users

If scaling needed:

  • Load balancer for multiple server instances
  • Shared session store (Redis)
  • Separate database server
  • Multiple TURN servers (geographic distribution)

Troubleshooting

WebSocket Connection Failed

Check:

  1. Reverse proxy WebSocket upgrade headers
  2. Firewall allows WebSocket traffic
  3. Server logs for connection errors

TURN Not Working

Check:

  1. UDP ports 3478 and 49160-49200 open
  2. External IP correctly configured
  3. Credentials valid
  4. Check coturn logs: docker-compose logs coturn

File Upload/Download Slow

Agent QUIC connections bypass server. Check:

  1. Agent logs on both machines
  2. Firewall rules for UDP traffic
  3. NAT configuration
  4. Network latency between peers

Database Connection Error

Check:

  1. DATABASE_URL format correct
  2. Database container running
  3. Database initialized (migrations run)
  4. Credentials valid

Deployment Workflow

  1. Setup server with Docker and docker-compose
  2. Clone repository to server
  3. Configure environment (copy and edit .env)
  4. Build client (cd client && npm run build)
  5. Copy client dist to reverse proxy volume
  6. Start services (docker-compose up -d)
  7. Check health endpoints
  8. Configure DNS (point domain to server)
  9. Setup SSL (Let's Encrypt or custom)
  10. Test end-to-end (login, join room, send message, call)

Remember: The infrastructure should be minimal. The server is control plane only - it never handles media or large data transfers.