Charon

Charon


Project Status: Active – The project is being actively developed. Docker Pulls Release
Code Coverage License: MIT Security: Audited
E2E Tests Cerberus Integration
CrowdSec Integration WAF Integration Rate Limit Integration


Your server, your rulesβ€”without the headaches.

Simply manage multiple websites and self-hosted applications. Click, save, done. No code, no config files, no PhD required.


Why Charon?

You want your apps accessible online. You don't want to become a networking expert first.

The problem: Managing reverse proxies usually means editing config files, memorizing cryptic syntax, and hoping you didn't break everything.

Charon's answer: A web interface where you click boxes and type domain names. That's it.


πŸ• Cerberus Security Suite

πŸ•΅οΈβ€β™‚οΈ CrowdSec Integration

πŸ” Access Control Lists (ACLs)

🧱 Web Application Firewall (WAF)

⏱️ Rate Limiting


✨ Top 10 Features

🎯 Point & Click Management

No config files. No terminal commands. Just click, type your domain name, and you're live. If you can use a website, you can run Charon.

πŸ” Automatic HTTPS Certificates

Free SSL certificates that request, install, and renew themselves. Your sites get the green padlock without you lifting a finger.

🌐 DNS Challenge for Wildcard Certificates

Secure all your subdomains with a single *.example.com certificate. Supports 15+ DNS providers including Cloudflare, Route53, DigitalOcean, and Google Cloud DNS. Credentials are encrypted and automatically rotated.

πŸ›‘οΈ Enterprise-Grade Security Built In

Web Application Firewall, rate limiting, geographic blocking, access control lists, and intrusion detection via CrowdSec. Protection that "just works."

πŸ” Supply Chain Security

Verifiable builds with cryptographic signatures, SLSA provenance attestation, and comprehensive SBOMs. Verify what you run with transparent, tamper-proof evidence.

🌐 Smart Proxy Headers

Automatically adds standard headers (X-Real-IP, X-Forwarded-Proto, etc.) so your backend applications see real client IPs, enforce HTTPS correctly, and log accuratelyβ€”with full backward compatibility for existing hosts.

🐳 Instant Docker Discovery

Already running apps in Docker? Charon finds them automatically and offers one-click proxy setup. No manual configuration required.

πŸ“Š Real-Time Monitoring & Logs

See exactly what's happening with live request logs, uptime monitoring, and instant notifications when something goes wrong.

πŸ“₯ Migration Made Easy

Import your existing configurations with one click:

Already invested in another reverse proxy? Bring your work with you.

⚑ Live Configuration Changes

Update domains, add security rules, or modify settings instantlyβ€”no container restarts needed.* Your sites stay up while you make changes.

🌍 Multi-App Management

Run dozens of websites, APIs, or services from a single dashboard. Perfect for homelab enthusiasts and small teams managing multiple projects.

πŸš€ Zero-Dependency Deployment

One Docker container. No databases to install. No external services required. No complexityβ€”just pure simplicity.

πŸ’― 100% Free & Open Source

No premium tiers. No feature paywalls. No usage limits. Everything you see is yours to use, forever, backed by the MIT license.

* Note: Initial security engine setup (CrowdSec) requires a one-time container restart to initialize the protection layer. All subsequent changes happen live.

Explore All Features β†’


Quick Start

Container Registries

Charon is available from two container registries:

Docker Hub (Recommended):

docker pull wikid82/charon:latest

GitHub Container Registry:

docker pull ghcr.io/wikid82/charon:latest

Docker Compose (Recommended)

Save this as docker-compose.yml:

services:
  charon:
    # Docker Hub (recommended)
    image: wikid82/charon:latest
    # Alternative: GitHub Container Registry
    # image: ghcr.io/wikid82/charon:latest
    container_name: charon
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
      - "443:443/udp"
      - "8080:8080"
    volumes:
      - ./charon-data:/app/data
      - /var/run/docker.sock:/var/run/docker.sock:ro
    environment:
      - CHARON_ENV=production
      # Generate with: openssl rand -base64 32
      - CHARON_ENCRYPTION_KEY=your-32-byte-base64-key-here

Using Nightly Builds:

To test the latest nightly build (automated daily at 02:00 UTC):

services:
  charon:
    # Docker Hub
    image: wikid82/charon:nightly
    # Alternative: GitHub Container Registry
    # image: ghcr.io/wikid82/charon:nightly
    # ... rest of configuration

Note: Nightly builds are for testing and may contain experimental features. Use latest for production.

Then run:

docker-compose up -d

Docker Run (One-Liner)

Stable Release (Docker Hub):

docker run -d \
  --name charon \
  -p 80:80 \
  -p 443:443 \
  -p 443:443/udp \
  -p 8080:8080 \
  -v ./charon-data:/app/data \
  -v /var/run/docker.sock:/var/run/docker.sock:ro \
  -e CHARON_ENV=production \
  -e CHARON_ENCRYPTION_KEY=your-32-byte-base64-key-here \
  wikid82/charon:latest

Stable Release (GitHub Container Registry):

docker run -d \
  --name charon \
  -p 80:80 \
  -p 443:443 \
  -p 443:443/udp \
  -p 8080:8080 \
  -v ./charon-data:/app/data \
  -v /var/run/docker.sock:/var/run/docker.sock:ro \
  -e CHARON_ENV=production \
  -e CHARON_ENCRYPTION_KEY=your-32-byte-base64-key-here \
  ghcr.io/wikid82/charon:latest

Nightly Build (Testing - Docker Hub):

docker run -d \
  --name charon \
  -p 80:80 \
  -p 443:443 \
  -p 443:443/udp \
  -p 8080:8080 \
  -v ./charon-data:/app/data \
  -v /var/run/docker.sock:/var/run/docker.sock:ro \
  -e CHARON_ENV=production \
  -e CHARON_ENCRYPTION_KEY=your-32-byte-base64-key-here \
  wikid82/charon:nightly

Note: Nightly builds include the latest development features and are rebuilt daily at 02:00 UTC. Use for testing only. Also available via GHCR: ghcr.io/wikid82/charon:nightly

What Just Happened?

  1. Charon downloaded and started
  2. The web interface opened on port 8080
  3. Your websites will use ports 80 (HTTP) and 443 (HTTPS)

Open http://localhost:8080 and start adding your websites!

Requirements

Server:

Browser:

Note: If you encounter errors after upgrading, try a hard refresh (Ctrl+Shift+R) or clearing your browser cache. See Troubleshooting Guide for details.

Development Setup

Requirements:

Install golangci-lint (for contributors): go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest

GORM Security Scanner: Charon includes an automated security scanner that detects GORM vulnerabilities (ID leaks, exposed secrets, DTO embedding issues). Runs automatically in CI on all PRs. Run locally via:

# VS Code: Command Palette β†’ "Lint: GORM Security Scan"
# Or via pre-commit:
pre-commit run --hook-stage manual gorm-security-scan --all-files
# Or directly:
./scripts/scan-gorm-security.sh --report

See GORM Security Scanner Documentation for details.

See CONTRIBUTING.md for complete development environment setup.

Note: GitHub Actions CI uses GOTOOLCHAIN: auto to automatically download and use Go 1.25.6, even if your system has an older version installed. For local development, ensure you have Go 1.25.6+ installed.

Environment Configuration

Before running Charon or E2E tests, configure required environment variables:

  1. Copy the example environment file:

    cp .env.example .env
    
  2. Configure required secrets:

    # Generate encryption key (32 bytes, base64-encoded)
    openssl rand -base64 32
    
    # Generate emergency token (64 characters hex)
    openssl rand -hex 32
    
  3. Add to .env file:

    CHARON_ENCRYPTION_KEY=<paste_encryption_key_here>
    CHARON_EMERGENCY_TOKEN=<paste_emergency_token_here>
    
  4. Verify configuration:

    # Encryption key should be ~44 chars (base64)
    grep CHARON_ENCRYPTION_KEY .env | cut -d= -f2 | wc -c
    
    # Emergency token should be 64 chars (hex)
    grep CHARON_EMERGENCY_TOKEN .env | cut -d= -f2 | wc -c
    

⚠️ Security: Never commit actual secret values to the repository. The .env file is gitignored.

πŸ“– More Info: See Getting Started Guide for detailed setup instructions.

Upgrading? Run Migrations

If you're upgrading from a previous version with persistent data:

docker exec charon /app/charon migrate
docker restart charon

This ensures security features (especially CrowdSec) work correctly.

Important: If you had CrowdSec enabled before the upgrade, it will automatically restart after migration. You don't need to manually re-enable it via the GUI. See Migration Guide for details.


πŸ”” Smart Notifications

Stay informed about your infrastructure with flexible notification support.

Supported Services

Charon integrates with popular notification platforms using JSON templates for rich formatting:

JSON Template Examples

Discord Rich Embed:

{
  "embeds": [{
    "title": "🚨 {{.Title}}",
    "description": "{{.Message}}",
    "color": 15158332,
    "timestamp": "{{.Timestamp}}",
    "fields": [
      {"name": "Host", "value": "{{.HostName}}", "inline": true},
      {"name": "Event", "value": "{{.EventType}}", "inline": true}
    ]
  }]
}

Slack Block Kit:

{
  "blocks": [
    {
      "type": "header",
      "text": {"type": "plain_text", "text": "πŸ”” {{.Title}}"}
    },
    {
      "type": "section",
      "text": {"type": "mrkdwn", "text": "*Event:* {{.EventType}}\n*Message:* {{.Message}}"}
    }
  ]
}

Available Template Variables

All JSON templates support these variables:

Variable Description Example
{{.Title}} Event title "SSL Certificate Renewed"
{{.Message}} Event details "Certificate for example.com renewed"
{{.EventType}} Type of event "ssl_renewal", "uptime_down"
{{.Severity}} Severity level "info", "warning", "error"
{{.HostName}} Affected host "example.com"
{{.Timestamp}} ISO 8601 timestamp "2025-12-24T10:30:00Z"

πŸ“– Complete Notification Guide β†’


🚨 Emergency Break Glass Access

Charon provides a 3-Tier Break Glass Protocol for emergency lockout recovery when security modules (ACL, WAF, CrowdSec) block access to the admin interface.

Emergency Recovery Quick Reference

Tier 1 (Preferred): Use emergency token via main endpoint

curl -X POST https://charon.example.com/api/v1/emergency/security-reset \
  -H "X-Emergency-Token: $CHARON_EMERGENCY_TOKEN"

Tier 2 (If Tier 1 blocked): Use emergency server via SSH tunnel

ssh -L 2019:localhost:2019 admin@server
curl -X POST http://localhost:2019/emergency/security-reset \
  -H "X-Emergency-Token: $CHARON_EMERGENCY_TOKEN" \
  -u admin:password

Tier 3 (Catastrophic): Direct SSH access - see Emergency Runbook

Tier 1: Emergency Token (Layer 7 Bypass)

Use when: The application is accessible but security middleware is blocking you.

# Set emergency token (generate with: openssl rand -hex 32)
export CHARON_EMERGENCY_TOKEN=your-64-char-hex-token

# Use token to disable security
curl -X POST https://charon.example.com/api/v1/emergency/security-reset \
  -H "X-Emergency-Token: $CHARON_EMERGENCY_TOKEN"

Response:

{
  "success": true,
  "message": "All security modules have been disabled",
  "disabled_modules": [
    "feature.cerberus.enabled",
    "security.acl.enabled",
    "security.waf.enabled",
    "security.rate_limit.enabled",
    "security.crowdsec.enabled"
  ]
}

Tier 2: Emergency Server (Sidecar Port)

Use when: Caddy/CrowdSec is blocking at the reverse proxy level, or you need a separate entry point.

Prerequisites:

Setup:

# docker-compose.yml
environment:
  - CHARON_EMERGENCY_SERVER_ENABLED=true
  - CHARON_EMERGENCY_BIND=127.0.0.1:2019  # Localhost only
  - CHARON_EMERGENCY_USERNAME=admin
  - CHARON_EMERGENCY_PASSWORD=your-strong-password

Usage:

# 1. SSH to server and create tunnel
ssh -L 2019:localhost:2019 admin@server.example.com

# 2. Access emergency endpoint (from local machine)
curl -X POST http://localhost:2019/emergency/security-reset \
  -H "X-Emergency-Token: $CHARON_EMERGENCY_TOKEN" \
  -u admin:your-strong-password

Tier 3: Direct System Access (Physical Key)

Use when: All application-level recovery methods have failed.

Prerequisites:

Emergency Procedures:

# SSH to host
ssh admin@docker-host.example.com

# Clear CrowdSec bans
docker exec charon cscli decisions delete --all

# Disable security via database
docker exec charon sqlite3 /app/data/charon.db \
  "UPDATE settings SET value='false' WHERE key LIKE 'security.%.enabled';"

# Restart container
docker restart charon

When to Use Each Tier

Scenario Tier Solution
ACL blocked your IP Tier 1 Emergency token via main port
Caddy/CrowdSec blocking at Layer 7 Tier 2 Emergency server on separate port
Complete system failure Tier 3 Direct SSH + database access

Security Considerations

⚠️ Emergency Server Security:

πŸ” Emergency Token Security:

οΏ½ API Key & Credential Management:

For detailed security practices, see:

οΏ½πŸ“ Management Network Configuration:

# Restrict emergency access to trusted networks only
environment:
  - CHARON_MANAGEMENT_CIDRS=10.0.0.0/8,172.16.0.0/12,192.168.0.0/16

Default: RFC1918 private networks + localhost

Complete Documentation

πŸ“– Emergency Lockout Recovery Runbook β€” Complete procedures for all 3 tiers πŸ”„ Emergency Token Rotation Guide β€” Token rotation procedures βš™οΈ Configuration Examples β€” Docker Compose and secrets manager integration πŸ›‘οΈ Security Documentation β€” Break glass protocol architecture


Getting Help

πŸ“– Full Documentation β€” Everything explained simply πŸš€ 5-Minute Guide β€” Your first website up and running πŸ” Supply Chain Security β€” Verify signatures and build provenance οΏ½ Maintenance β€” Keeping Charon running smoothly οΏ½πŸ› οΈ Troubleshooting β€” Common issues and solutions πŸ’¬ Ask Questions β€” Friendly community help πŸ› Report Problems β€” Something broken? Let us know