Skip to content

CharlieHub TCP Routing Specifics

CharlieHub-specific implementation of the control plane standards.

Overview

All routing in CharlieHub (HTTP + TCP) is managed through a single control plane:

PostgreSQL domains table
         ↓
Domain Manager API (/api/domains/*)
         ↓
traefik_generator.py (v2.3+)
         ↓
/traefik/config/generated/routes.yml
         ↓
Traefik routers (HTTP + TCP)

TCP Routing Fields

When adding a TCP route via the API:

{
  "domain": "mqtt.verdegris.eu",           // FQDN (required)
  "protocol": "tcp",                       // Must be 'tcp' for TCP routes
  "service_type": "tcp-proxy",             // Metadata (tcp-proxy is suggested)
  "tcp_entrypoint": "mqtt",                // Traefik entrypoint name (required)
  "backend_host": "parking-mosquitto",     // Service hostname/IP (required)
  "backend_port": 1883,                    // Backend port (required)
  "environment": "production",              // Environment
  "status": "active",                      // active/disabled/draft
  "ssl_enabled": true,                     // Use ACME certificates
  "ddns_enabled": true,                    // Auto-update DNS
  "priority": 100,                         // Route priority
  "description": "MQTT broker for sensors" // Human description
}

Available TCP Entrypoints

Check /opt/charliehub/traefik/config/traefik.yml for available entrypoints:

grep -A 20 "^entryPoints:" /opt/charliehub/traefik/config/traefik.yml

Current entrypoints: - mqtt - MQTT broker (default port 8883) - ssh - SSH bastion (if configured) - postgres - PostgreSQL proxy (if configured)

To add a new entrypoint: 1. Update traefik.yml (infrastructure layer) 2. Restart Traefik 3. Use the API with new entrypoint

Constraints

TCP routes enforce these constraints (database-enforced):

-- TCP routes MUST have tcp_entrypoint
CHECK (protocol != 'tcp' OR tcp_entrypoint IS NOT NULL)

-- TCP routes MUST have backend
CHECK (protocol != 'tcp' OR (backend_host IS NOT NULL AND backend_port IS NOT NULL))

-- TCP routes CANNOT use HTTP-only features
CHECK (protocol != 'tcp' OR (cors_enabled = false AND default_path IS NULL))

Practical meaning:

Rule Example
TCP requires entrypoint ❌ Can't skip tcp_entrypoint field
TCP requires backend ❌ Can't have TCP route without backend_host:backend_port
TCP can't use CORS ❌ Can't set cors_enabled=true for TCP route
TCP can't use default_path ❌ Can't redirect root path for TCP route

Examples

Add MQTT Route

curl -X POST http://localhost:8001/api/domains \
  -H "X-API-Key: $API_KEY" \
  -d '{
    "domain": "mqtt.example.com",
    "protocol": "tcp",
    "service_type": "tcp-proxy",
    "tcp_entrypoint": "mqtt",
    "backend_host": "mosquitto-broker",
    "backend_port": 1883,
    "environment": "production",
    "status": "active",
    "description": "MQTT broker"
  }'

Add SSH Bastion

curl -X POST http://localhost:8001/api/domains \
  -H "X-API-Key: $API_KEY" \
  -d '{
    "domain": "ssh.example.com",
    "protocol": "tcp",
    "service_type": "tcp-proxy",
    "tcp_entrypoint": "ssh",
    "backend_host": "bastion.internal",
    "backend_port": 22,
    "environment": "production",
    "status": "active",
    "description": "SSH bastion access"
  }'

Disable Temporarily

curl -X PUT http://localhost:8001/api/domains/42 \
  -H "X-API-Key: $API_KEY" \
  -d '{"status": "disabled"}'

Verify Route Generated

# Check generator output
docker exec charliehub_domain_manager_v3 python3 /app/services/traefik_generator.py | grep tcp

# Check Traefik API
curl -s http://localhost:8091/api/tcp/routers | jq '.[] | select(.name | contains("mqtt"))'

Common Issues

Route Generated but Not Loading

# Check if entrypoint is defined
grep "mqtt:" /opt/charliehub/traefik/config/traefik.yml

# If not present:
# 1. Add to traefik.yml
# 2. Restart Traefik
# 3. Regenerate routes
# 4. Verify

API Rejects TCP Route

# Error: "TCP routes require tcp_entrypoint"
# → You didn't specify tcp_entrypoint field

# Error: "TCP routes cannot use CORS"
# → You set cors_enabled=true with protocol='tcp'
# → Either remove cors_enabled OR change to protocol='http'

# Error: "TCP routes require backend_host and backend_port"
# → You didn't specify both fields

Route Disappears After Generation

# Did you:
# 1. Check if status='active'? (disabled routes don't generate)
# 2. Verify backend_host and backend_port are present?
# 3. Check if tcp_entrypoint is a valid entrypoint?

docker exec -i charliehub-postgres psql -U charliehub -d charliehub_domains \
  -c "SELECT domain, protocol, status, tcp_entrypoint, backend_host FROM domains WHERE domain='your-domain.com'"

Testing

Test MQTT Connection

# Verify the route works
openssl s_client -connect mqtt.example.com:8883 -servername mqtt.example.com < /dev/null
# Should show certificate chain and successful connection

Test SSH Connection

ssh -v user@ssh.example.com
# Should connect to the bastion

Check Gateway Connectivity

# For MQTT, check if gateways are connected
docker logs parking-mosquitto --tail 20 | grep "New client"
# Should see connected clients

Future Extensions

This pattern supports adding more TCP services:

  1. Add entrypoint to traefik.yml
  2. Use API with appropriate tcp_entrypoint
  3. Same constraints, same API, same guarantees

Supported now: - MQTT (mqtt entrypoint)

Can add easily: - SSH (ssh entrypoint) - PostgreSQL (postgres entrypoint) - Redis (redis entrypoint) - gRPC (grpc entrypoint) - Custom (custom entrypoint)

See Also