A lightweight, high-performance service mesh sidecar proxy built with Go, designed for transparently securing and observing microservice traffic.
Charon acts as a "bodyguard" for your microservices, intercepting all incoming and outgoing traffic, verifying identities, logging activities, and ensuring messages reach their correct destination, even if the destination moves.
- Transparent TCP Proxy: Forward TCP traffic without application awareness
- Intelligent HTTP Proxy: Parse and understand HTTP traffic with metrics
- Service Discovery: File-based registry with dynamic routing and cache/watcher
- Advanced Routing: Host/path-based routing with multi-upstream support
- Health Checks: Active TCP probes and passive health monitoring
- Circuit Breaking: Per-upstream circuit breaker with configurable thresholds and timeouts
- Rate Limiting: Token bucket algorithm with configurable RPS and burst limits
- Retry Logic: Exponential backoff for idempotent requests
- Structured Logging: Zap logger with trace context and structured fields
- Distributed Tracing: OpenTelemetry integration with Jaeger exporter
- Secure Communication: Automatic mTLS with certificate generation and management
- Prometheus Metrics: Request metrics, latencies, health status, CB transitions, and rate limiting
- Go 1.21 or higher
# Clone the repository
git clone https://github.com/0xReLogic/Charon.git
cd Charon
# Build the project
go mod tidy
go build -o charon.exe ./cmd/charonCreate a config.yaml file:
listen_port: "8080"
target_service_name: "my-backend"
registry_file: "registry.yaml"
routes:
- host: "api.example.com"
path_prefix: "/v1"
service: "api-service"
- path_prefix: "/users"
service: "user-service"
# Circuit breaker settings
circuit_breaker:
failure_threshold: 5
open_duration: "30s"
# Rate limiting settings
rate_limit:
requests_per_second: 100
burst_size: 200
routes: [] # empty = all routes
logging:
level: "info"
format: "json"
environment: "production"
tracing:
enabled: false
jaeger_endpoint: "http://localhost:14268/api/traces"
service_name: "charon-proxy"
# TLS/mTLS Configuration
tls:
enabled: false
cert_dir: "./certs"
server_port: "8443"
upstream_tls: false# Start the proxy
./charon --config config.yaml # macOS/Linux
# or on Windows
./charon.exe --config config.yaml
# In another terminal, start the echo server for testing
go run ./test/cmd/echo_server --port 9091You can test the proxy using telnet or netcat:
# Connect to the proxy
telnet localhost 8080
# Type any message and it should be echoed backAlternatively, use the provided test clients:
# Smoke test (expects echo back)
go run ./test/cmd/smoke_client --addr localhost:8080 --msg "hello-through-proxy\n"
# Interactive client (type and see echo)
go run ./test/cmd/interactive_client --addr localhost:8080Start a simple HTTP backend, run Charon, then curl via the proxy:
# Terminal A: HTTP backend on :9091
go run ./test/cmd/http_backend --addr :9091
# Terminal B: Charon HTTP reverse proxy on :8080
./charon.exe --config config.yaml # or ./charon on Linux/macOS
# Terminal C: Send requests through proxy
curl -v http://localhost:8080/
curl -v -X POST http://localhost:8080/hello -d 'hi'You should see logs like:
http request method=GET path=/ -> status=200 bytes=... latency=...
Use a file-based registry to dynamically resolve upstream addresses per request (no restart needed).
- Create
registry.yaml:
services:
http-backend: localhost:9091- Start backend and Charon:
# Terminal A: backend on :9091
go run ./test/cmd/http_backend --addr :9091
# Terminal B: start Charon
./charon.exe --config config.yaml
# Terminal C: call via proxy
curl -v http://localhost:8080/hello- Change routing dynamically:
# edit registry.yaml
services:
http-backend: localhost:9092Restart backend on :9092:
go run ./test/cmd/http_backend --addr :9092Call again. The proxy will route to the new address without restart.
Charon exposes Prometheus metrics at /metrics on the same listen port.
Example:
curl -s http://localhost:8080/metrics | grep charon_Available metrics include:
charon_http_requests_total{method,status,upstream}charon_http_request_latency_seconds_bucket{method,upstream,...}(+ sum/count)charon_http_retries_total{method}charon_http_rate_limited_total{route}(counter)charon_upstream_health{service,upstream}(gauge 1=UP, 0=DOWN)charon_circuit_breaker_transitions_total{upstream,to_state}(counter)
You can configure Prometheus to scrape http://<charon-host>:8080/metrics.
Charon performs active health checks (TCP probe every 5s) and per-upstream circuit breaking.
- Circuit breaker: configurable failure threshold and open duration (defaults: 3 failures, 20s).
- Rate limiting: token bucket algorithm with configurable RPS and burst size.
- Metrics:
charon_upstream_health{service,upstream}: current health.charon_circuit_breaker_transitions_total{upstream,to_state}: transitions (open/half_open/closed).charon_http_rate_limited_total{route}: rate limited requests per route.
Test the circuit breaker locally:
# registry.yaml (temporary for deterministic testing)
services:
http-backend: localhost:9091# Terminal A: backend with failing endpoint
go run ./test/cmd/http_backend --addr :9091 # /fail returns 500
# Terminal B: start Charon
./charon.exe --config config.yaml
# Terminal C (PowerShell): trip breaker with 500s
1..10 | % { curl.exe -s http://localhost:8080/fail > $null }
# Check transitions metric
curl -s http://localhost:8080/metrics | findstr charon_circuit_breaker_transitions_total
# Wait ~21s (half-open), then send a success to close
Start-Sleep -Seconds 21; curl.exe -s http://localhost:8080/hello > $null
curl -s http://localhost:8080/metrics | findstr charon_circuit_breaker_transitions_totalCharon mendukung routing berbasis host/path melalui routes di config.yaml.
Aturan dievaluasi dari atas ke bawah, first match wins. Jika tidak ada yang match,
akan fallback ke target_service_name (jika ada) atau target_service_addr.
Contoh konfigurasi:
listen_port: "8080"
# Default service (fallback)
target_service_name: "http-backend"
registry_file: "registry.yaml"
# Routing rules (opsional)
routes:
- host: "api.local"
path_prefix: "/hello"
service: "http-backend" # nama service di registry.yaml
- path_prefix: "/admin"
service: "admin-backend"Contoh registry.yaml:
services:
http-backend: localhost:9091
admin-backend: localhost:9092Uji cepat:
curl -v http://localhost:8080/hello # -> http-backend
curl -v http://localhost:8080/admin # -> admin-backend
# atau dengan Host header spesifik:
curl -v -H "Host: api.local" http://localhost:8080/hellocharon/
├── cmd/
│ └── charon/ # Main application entry point
├── internal/
│ ├── config/ # Configuration handling
│ ├── proxy/ # Proxy implementation
│ │ ├── tcp.go # Phase 1: TCP transparent proxy
│ │ └── http.go # Phase 2: HTTP reverse proxy with basic metrics
│ ├── registry/ # Phase 3: file-based service discovery
│ └── ...
├── test/ # Test utilities and mock servers
│ ├── cmd/ # Standalone test binaries
│ │ ├── echo_server/ # TCP echo server for testing
│ │ ├── http_backend/ # HTTP backend server
│ │ └── interactive_client/ # Interactive test client
│ ├── simple_backend.go # Simple HTTP backend for testing
│ ├── echo_server.go # TCP echo server implementation
│ ├── smoke_client.go # Smoke test client
│ ├── tls_test.go # TLS functionality tests
│ └── test_proxy.go # Interactive proxy test client
├── .github/
│ └── workflows/
│ └── ci.yml # CI/CD pipeline (Linux, Windows, macOS)
├── config.yaml # Production-ready configuration
├── registry.yaml # Service registry with load balancing
├── Dockerfile # Multi-stage Docker build
└── README.md # This documentation
Allen Elzayn - Lead Developer - 0xReLogic
- Fork the project
- Create your feature branch (
git checkout -b feature/AmazingFeature) - Commit your changes (
git commit -m 'Add some AmazingFeature') - Push to the branch (
git push origin feature/AmazingFeature) - Open a Pull Request
This project is licensed under the MIT License - see the LICENSE file for details.
- Built with ❤️ by Allen Elzayn
- Inspired by modern service mesh architectures
- Thanks to the Go community for excellent libraries