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:
- Add entrypoint to
traefik.yml - Use API with appropriate
tcp_entrypoint - 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¶
- Homelab Control Plane Rules
- Common Patterns
- Emergency Procedures
- TCP_ROUTING_GUIDE.md - Quick reference
- Git commit a9b79b5 - TCP routing implementation details