Version: 1.0 Last Updated: 2026-01-30 Status: Living Document
- Overview
- System Architecture
- Technology Stack
- Directory Structure
- Core Components
- Security Architecture
- Data Flow
- Deployment Architecture
- Development Workflow
- Testing Strategy
- Build & Release Process
- Extensibility
- Known Limitations
- Maintenance & Updates
Charon is a self-hosted reverse proxy manager with a web-based user interface designed to simplify website and application hosting for home users and small teams. It eliminates the need for manual configuration file editing by providing an intuitive point-and-click interface for managing multiple domains, SSL certificates, and enterprise-grade security features.
"Your server, your rules—without the headaches."
Charon bridges the gap between simple solutions (like Nginx Proxy Manager) and complex enterprise proxies (like Traefik/HAProxy) by providing a balanced approach that is both user-friendly and feature-rich.
- Web-Based Proxy Management: No config file editing required
- Automatic HTTPS: Let's Encrypt and ZeroSSL integration with auto-renewal
- DNS Challenge Support: 15+ DNS providers for wildcard certificates
- Docker Auto-Discovery: One-click proxy setup for Docker containers
- Cerberus Security Suite: WAF, ACL, CrowdSec, Rate Limiting
- Real-Time Monitoring: Live logs, uptime tracking, and notifications
- Configuration Import: Migrate from Caddyfile or Nginx Proxy Manager
- Supply Chain Security: Cryptographic signatures, SLSA provenance, SBOM
Charon follows a monolithic architecture with an embedded reverse proxy, packaged as a single Docker container. This design prioritizes simplicity, ease of deployment, and minimal operational overhead.
graph TB
User[User Browser] -->|HTTPS :8080| Frontend[React Frontend SPA]
Frontend -->|REST API /api/v1| Backend[Go Backend + Gin]
Frontend -->|WebSocket /api/v1/logs| Backend
Backend -->|Configures| CaddyMgr[Caddy Manager]
CaddyMgr -->|JSON API| Caddy[Caddy Server]
Backend -->|CRUD| DB[(SQLite Database)]
Backend -->|Query| DockerAPI[Docker Socket API]
Caddy -->|Proxy :80/:443| UpstreamServers[Upstream Servers]
Backend -->|Security Checks| Cerberus[Cerberus Security Suite]
Cerberus -->|IP Bans| CrowdSec[CrowdSec Bouncer]
Cerberus -->|Request Filtering| WAF[Coraza WAF]
Cerberus -->|Access Control| ACL[Access Control Lists]
Cerberus -->|Throttling| RateLimit[Rate Limiter]
subgraph Docker Container
Frontend
Backend
CaddyMgr
Caddy
DB
Cerberus
CrowdSec
WAF
ACL
RateLimit
end
subgraph Host System
DockerAPI
UpstreamServers
end
| Source | Target | Protocol | Purpose |
|---|---|---|---|
| Frontend | Backend | HTTP/1.1 | REST API calls for CRUD operations |
| Frontend | Backend | WebSocket | Real-time log streaming |
| Backend | Caddy | HTTP/JSON | Dynamic configuration updates |
| Backend | SQLite | SQL | Data persistence |
| Backend | Docker Socket | Unix Socket/HTTP | Container discovery |
| Caddy | Upstream Servers | HTTP/HTTPS | Reverse proxy traffic |
| Cerberus | CrowdSec | HTTP | Threat intelligence sync |
| Cerberus | WAF | In-process | Request inspection |
- Simplicity First: Single container, minimal external dependencies
- Security by Default: All security features enabled out-of-the-box
- User Experience: Web UI over configuration files
- Modularity: Pluggable DNS providers, notification channels
- Observability: Comprehensive logging and metrics
- Reliability: Graceful degradation, atomic config updates
| Component | Technology | Version | Purpose |
|---|---|---|---|
| Language | Go | 1.26.0 | Primary backend language |
| HTTP Framework | Gin | Latest | Routing, middleware, HTTP handling |
| Database | SQLite | 3.x | Embedded database |
| ORM | GORM | Latest | Database abstraction layer |
| Reverse Proxy | Caddy Server | 2.11.2 | Embedded HTTP/HTTPS proxy |
| WebSocket | gorilla/websocket | Latest | Real-time log streaming |
| Crypto | golang.org/x/crypto | Latest | Password hashing, encryption |
| Metrics | Prometheus Client | Latest | Application metrics |
| Notifications | Notify (Discord-first) | Current | Discord notifications now; additional services in phased rollout |
| Docker Client | Docker SDK | Latest | Container discovery |
| Logging | Logrus + Lumberjack | Latest | Structured logging with rotation |
| Component | Technology | Version | Purpose |
|---|---|---|---|
| Framework | React | 19.2.3 | UI framework |
| Language | TypeScript | 6.x | Type-safe JavaScript |
| Build Tool | Vite | 8.0.0-beta.18 | Fast bundler and dev server |
| CSS Framework | Tailwind CSS | 4.2.1 | Utility-first CSS |
| Routing | React Router | 7.x | Client-side routing |
| HTTP Client | Fetch API | Native | API communication |
| State Management | React Hooks + Context | Native | Global state |
| Internationalization | i18next | Latest | 5 language support |
| Unit Testing | Vitest | 4.1.0-beta.6 | Fast unit test runner |
| E2E Testing | Playwright | 1.58.2 | Browser automation |
| Component | Technology | Version | Purpose |
|---|---|---|---|
| Containerization | Docker | 24+ | Application packaging |
| Base Image | Debian Trixie Slim | Latest | Security-hardened base |
| CI/CD | GitHub Actions | N/A | Automated testing and deployment |
| Registry | Docker Hub + GHCR | N/A | Image distribution |
| Security Scanning | Trivy + Grype | Latest | Vulnerability detection |
| SBOM Generation | Syft | Latest | Software Bill of Materials |
| Signature Verification | Cosign | Latest | Supply chain integrity |
/projects/Charon/
├── backend/ # Go backend source code
│ ├── cmd/ # Application entrypoints
│ │ ├── api/ # Main API server
│ │ ├── migrate/ # Database migration tool
│ │ └── seed/ # Database seeding tool
│ ├── internal/ # Private application code
│ │ ├── api/ # HTTP handlers and routes
│ │ │ ├── handlers/ # Request handlers
│ │ │ ├── middleware/ # HTTP middleware
│ │ │ └── routes/ # Route definitions
│ │ ├── services/ # Business logic layer
│ │ │ ├── proxy_service.go
│ │ │ ├── certificate_service.go
│ │ │ ├── docker_service.go
│ │ │ └── mail_service.go
│ │ ├── caddy/ # Caddy manager and config generation
│ │ │ ├── manager.go # Dynamic config orchestration
│ │ │ └── templates.go # Caddy JSON templates
│ │ ├── cerberus/ # Security suite
│ │ │ ├── acl.go # Access Control Lists
│ │ │ ├── waf.go # Web Application Firewall
│ │ │ ├── crowdsec.go # CrowdSec integration
│ │ │ └── ratelimit.go # Rate limiting
│ │ ├── models/ # GORM database models
│ │ ├── database/ # DB initialization and migrations
│ │ └── utils/ # Helper functions
│ ├── pkg/ # Public reusable packages
│ ├── integration/ # Integration tests
│ ├── go.mod # Go module definition
│ └── go.sum # Go dependency checksums
│
├── frontend/ # React frontend source code
│ ├── src/
│ │ ├── pages/ # Top-level page components
│ │ │ ├── Dashboard.tsx
│ │ │ ├── ProxyHosts.tsx
│ │ │ ├── Certificates.tsx
│ │ │ └── Settings.tsx
│ │ ├── components/ # Reusable UI components
│ │ │ ├── forms/ # Form inputs and validation
│ │ │ ├── modals/ # Dialog components
│ │ │ ├── tables/ # Data tables
│ │ │ └── layout/ # Layout components
│ │ ├── api/ # API client functions
│ │ ├── hooks/ # Custom React hooks
│ │ ├── context/ # React context providers
│ │ ├── locales/ # i18n translation files
│ │ ├── App.tsx # Root component
│ │ └── main.tsx # Application entry point
│ ├── public/ # Static assets
│ ├── package.json # NPM dependencies
│ └── vite.config.ts # Vite configuration
│
├── .docker/ # Docker configuration
│ ├── compose/ # Docker Compose files
│ │ ├── docker-compose.yml # Production setup
│ │ ├── docker-compose.dev.yml
│ │ └── docker-compose.test.yml
│ ├── docker-entrypoint.sh # Container startup script
│ └── README.md # Docker documentation
│
├── .github/ # GitHub configuration
│ ├── workflows/ # CI/CD pipelines
│ │ ├── *.yml # GitHub Actions workflows
│ ├── agents/ # GitHub Copilot agent definitions
│ │ ├── Management.agent.md
│ │ ├── Planning.agent.md
│ │ ├── Backend_Dev.agent.md
│ │ ├── Frontend_Dev.agent.md
│ │ ├── QA_Security.agent.md
│ │ ├── Doc_Writer.agent.md
│ │ ├── DevOps.agent.md
│ │ └── Supervisor.agent.md
│ ├── instructions/ # Code generation instructions
│ │ ├── *.instructions.md # Domain-specific guidelines
│ └── skills/ # Automation scripts
│ └── scripts/ # Task automation
│
├── scripts/ # Build and utility scripts
│ ├── go-test-coverage.sh # Backend coverage testing
│ ├── frontend-test-coverage.sh
│ └── docker-*.sh # Docker convenience scripts
│
├── tests/ # End-to-end tests
│ ├── *.spec.ts # Playwright test files
│ └── fixtures/ # Test data and helpers
│
├── docs/ # Documentation
│ ├── features/ # Feature documentation
│ ├── guides/ # User guides
│ ├── api/ # API documentation
│ ├── development/ # Developer guides
│ ├── plans/ # Implementation plans
│ └── reports/ # QA and audit reports
│
├── configs/ # Runtime configuration
│ └── crowdsec/ # CrowdSec configurations
│
├── data/ # Persistent data (gitignored)
│ ├── charon.db # SQLite database
│ ├── backups/ # Database backups
│ ├── caddy/ # Caddy certificates
│ └── crowdsec/ # CrowdSec local database
│
├── Dockerfile # Multi-stage Docker build
├── Makefile # Build automation
├── go.work # Go workspace definition
├── package.json # Frontend dependencies
├── playwright.config.js # E2E test configuration
├── codecov.yml # Code coverage settings
├── README.md # Project overview
├── CONTRIBUTING.md # Contribution guidelines
├── CHANGELOG.md # Version history
├── LICENSE # MIT License
├── SECURITY.md # Security policy
└── ARCHITECTURE.md # This file
internal/: Private code that should not be imported by external projectspkg/: Public libraries that can be reusedcmd/: Application entrypoints (each subdirectory is a separate binary).docker/: All Docker-related files (prevents root clutter)docs/implementation/: Archived implementation documentationdocs/plans/: Active planning documents (current_spec.md)test-results/: Test artifacts (gitignored)
Purpose: RESTful API server, business logic orchestration, Caddy management
Key Modules:
- Handlers: Process HTTP requests, validate input, return responses
- Middleware: CORS, GZIP, authentication, logging, metrics, panic recovery
- Routes: Route registration and grouping (public vs authenticated)
Example Endpoints:
GET /api/v1/proxy-hosts- List all proxy hostsPOST /api/v1/proxy-hosts- Create new proxy hostPUT /api/v1/proxy-hosts/:id- Update proxy hostDELETE /api/v1/proxy-hosts/:id- Delete proxy hostWS /api/v1/logs- WebSocket for real-time logs
- ProxyService: CRUD operations for proxy hosts, validation logic
- CertificateService: ACME certificate provisioning and renewal
- DockerService: Container discovery and monitoring
- MailService: Email notifications for certificate expiry
- SettingsService: Application settings management
Design Pattern: Services contain business logic and call multiple repositories/managers
- Manager: Orchestrates Caddy configuration updates
- Config Builder: Generates Caddy JSON from database models
- Reload Logic: Atomic config application with rollback on failure
- Security Integration: Injects Cerberus middleware into Caddy pipelines
Responsibilities:
- Generate Caddy JSON configuration from database state
- Validate configuration before applying
- Trigger Caddy reload via JSON API
- Handle rollback on configuration errors
- Integrate security layers (WAF, ACL, Rate Limiting)
- ACL (Access Control Lists): IP-based allow/deny rules, GeoIP blocking
- WAF (Web Application Firewall): Coraza engine with OWASP CRS
- CrowdSec: Behavior-based threat detection with global intelligence
- Rate Limiter: Per-IP request throttling
Integration Points:
- Middleware injection into Caddy request pipeline
- Database-driven rule configuration
- Metrics collection for security events
- Migrations: Automatic schema versioning with GORM AutoMigrate
- Seeding: Default settings and admin user creation
- Connection Management: SQLite with WAL mode and connection pooling
Schema Overview:
- ProxyHost: Domain, upstream target, SSL config
- RemoteServer: Upstream server definitions
- CaddyConfig: Generated Caddy configuration (audit trail)
- SSLCertificate: Certificate metadata and renewal status
- AccessList: IP whitelist/blacklist rules
- User: Authentication and authorization
- Setting: Key-value configuration storage
- ImportSession: Import job tracking
Purpose: Web-based user interface for proxy management
Component Architecture:
- Dashboard: System overview, recent activity, quick actions
- ProxyHosts: List, create, edit, delete proxy configurations
- Certificates: Manage SSL/TLS certificates, view expiry
- Settings: Application settings, security configuration
- Logs: Real-time log viewer with filtering
- Users: User management (admin only)
- Forms: Reusable form inputs with validation
- Modals: Dialog components for CRUD operations
- Tables: Data tables with sorting, filtering, pagination
- Layout: Header, sidebar, navigation
- Centralized API calls with error handling
- Request/response type definitions
- Authentication token management
Example:
export const getProxyHosts = async (): Promise<ProxyHost[]> => {
const response = await fetch('/api/v1/proxy-hosts', {
headers: { Authorization: `Bearer ${getToken()}` }
});
if (!response.ok) throw new Error('Failed to fetch proxy hosts');
return response.json();
};- React Context: Global state for auth, theme, language
- Local State: Component-specific state with
useState - Custom Hooks: Encapsulate API calls and side effects
Example Hook:
export const useProxyHosts = () => {
const [hosts, setHosts] = useState<ProxyHost[]>([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
getProxyHosts().then(setHosts).finally(() => setLoading(false));
}, []);
return { hosts, loading, refresh: () => getProxyHosts().then(setHosts) };
};Purpose: High-performance reverse proxy with automatic HTTPS
Integration:
- Embedded as a library in the Go backend
- Configured via JSON API (not Caddyfile)
- Listens on ports 80 (HTTP) and 443 (HTTPS)
Features Used:
- Dynamic configuration updates without restarts
- Automatic HTTPS with Let's Encrypt and ZeroSSL
- DNS challenge support for wildcard certificates
- HTTP/2 and HTTP/3 (QUIC) support
- Request logging and metrics
Configuration Flow:
- User creates proxy host via frontend
- Backend validates and saves to database
- Caddy Manager generates JSON configuration
- JSON sent to Caddy via
/config/API endpoint - Caddy validates and applies new configuration
- Traffic flows through new proxy route
Route Pattern: Emergency + Main
For each proxy host, Charon generates two routes with the same domain:
-
Emergency Route (with path matchers):
- Matches:
/api/v1/emergency/*paths - Purpose: Bypass security features for administrative access
- Priority: Evaluated first (more specific match)
- Handlers: No WAF, ACL, or Rate Limiting
- Matches:
-
Main Route (without path matchers):
- Matches: All other paths for the domain
- Purpose: Normal application traffic with full security
- Priority: Evaluated second (catch-all)
- Handlers: Full Cerberus security suite
This pattern is intentional and valid:
- Emergency route provides break-glass access to security controls
- Main route protects application with enterprise security features
- Caddy processes routes in order (emergency matches first)
- Validator allows duplicate hosts when one has paths and one doesn't
Example:
// Emergency Route (evaluated first)
{
"match": [{"host": ["app.example.com"], "path": ["/api/v1/emergency/*"]}],
"handle": [/* Emergency handlers - no security */],
"terminal": true
}
// Main Route (evaluated second)
{
"match": [{"host": ["app.example.com"]}],
"handle": [/* Security middleware + proxy */],
"terminal": true
}Purpose: Persistent data storage
Why SQLite:
- Embedded (no external database server)
- Serverless (perfect for single-user/small team)
- ACID compliant with WAL mode
- Minimal operational overhead
- Backup-friendly (single file)
Configuration:
- WAL Mode: Allows concurrent reads during writes
- Foreign Keys: Enforced referential integrity
- Pragma Settings: Performance optimizations
Backup Strategy:
- Automated daily backups to
data/backups/ - Retention: 7 daily, 4 weekly, 12 monthly backups
- Backup during low-traffic periods
Migrations:
- GORM AutoMigrate for schema changes
- Manual migrations for complex data transformations
- Rollback support via backup restoration
Charon implements multiple security layers (Cerberus Suite) to protect against various attack vectors:
graph LR
Internet[Internet] -->|HTTP/HTTPS| RateLimit[Rate Limiter]
RateLimit -->|Throttled| CrowdSec[CrowdSec Bouncer]
CrowdSec -->|Threat Intel| ACL[Access Control Lists]
ACL -->|IP Whitelist| WAF[Web Application Firewall]
WAF -->|OWASP CRS| Caddy[Caddy Proxy]
Caddy -->|Proxied| Upstream[Upstream Server]
style RateLimit fill:#f9f,stroke:#333,stroke-width:2px
style CrowdSec fill:#bbf,stroke:#333,stroke-width:2px
style ACL fill:#bfb,stroke:#333,stroke-width:2px
style WAF fill:#fbb,stroke:#333,stroke-width:2px
Purpose: Prevent brute-force attacks and API abuse
Implementation:
- Per-IP request counters with sliding window
- Configurable thresholds (e.g., 100 req/min, 1000 req/hour)
- HTTP 429 response when limit exceeded
- Admin whitelist for monitoring tools
Purpose: Behavior-based threat detection
Features:
- Local log analysis (brute-force, port scans, exploits)
- Global threat intelligence (crowd-sourced IP reputation)
- Automatic IP banning with configurable duration
- Decision management API (view, create, delete bans)
- IP whitelist management: operators add/remove IPs and CIDRs via the management UI; entries are persisted in SQLite and regenerated into a
crowdsecurity/whitelistsparser YAML on every mutating operation and at startup
Modes:
- Local Only: No external API calls
- API Mode: Sync with CrowdSec cloud for global intelligence
Purpose: IP-based access control
Features:
- Per-proxy-host allow/deny rules
- CIDR range support (e.g.,
192.168.1.0/24) - Geographic blocking via GeoIP2 (MaxMind)
- Admin whitelist (emergency access)
Evaluation Order:
- Check admin whitelist (always allow)
- Check deny list (explicit block)
- Check allow list (explicit allow)
- Default action (configurable allow/deny)
Purpose: Inspect HTTP requests for malicious payloads
Engine: Coraza with OWASP Core Rule Set (CRS)
Detection Categories:
- SQL Injection (SQLi)
- Cross-Site Scripting (XSS)
- Remote Code Execution (RCE)
- Local File Inclusion (LFI)
- Path Traversal
- Command Injection
Modes:
- Monitor: Log but don't block (testing)
- Block: Return HTTP 403 for violations
Additional Protections:
- SSRF Prevention: Block requests to private IP ranges in webhooks/URL validation
- HTTP Security Headers: CSP, HSTS, X-Frame-Options, X-Content-Type-Options
- Input Validation: Server-side validation for all user inputs
- SQL Injection Prevention: Parameterized queries with GORM
- XSS Prevention: React's built-in escaping + Content Security Policy
- Credential Encryption: AES-GCM with key rotation for stored credentials
- Password Hashing: bcrypt with cost factor 12
3-Tier Recovery System:
- Admin Dashboard: Standard access recovery via web UI
- Recovery Server: Localhost-only HTTP server on port 2019
- Direct Database Access: Manual SQLite update as last resort
Emergency Token:
- 64-character hex token set via
CHARON_EMERGENCY_TOKEN - Grants temporary admin access
- Rotated after each use
Charon operates with two distinct traffic flows on separate ports, each with different security characteristics:
Purpose: Admin UI and REST API for Charon configuration
- Protocol: HTTPS (via Gin HTTP server)
- Frontend: React SPA served by Gin
- Backend: REST API at
/api/v1/* - Middleware: Standard HTTP middleware (CORS, GZIP, auth, logging, metrics, panic recovery)
- Security: JWT authentication, CSRF protection, input validation
- NO Cerberus Middleware: Rate limiting, ACL, WAF, and CrowdSec are NOT applied to management interface
- Testing: Playwright E2E tests verify UI/UX functionality on this port
Why No Middleware?
- Management interface must remain accessible even when security modules are misconfigured
- Emergency endpoints (
/api/v1/emergency/*) require unrestricted access for system recovery - Separation of concerns: admin access control is handled by JWT, not proxy-level security
Purpose: User-configured reverse proxy hosts with full security enforcement
- Protocol: HTTP/HTTPS (via Caddy server)
- Routes: User-defined proxy configurations (e.g.,
app.example.com → http://localhost:3000) - Middleware: Full Cerberus Security Suite
- Rate Limiting (Cerberus)
- IP Reputation (CrowdSec Bouncer)
- Access Control Lists (ACL)
- Web Application Firewall (Coraza WAF)
- Security: All middleware enforced in order (Rate Limit → CrowdSec → ACL → WAF)
- Testing: Integration tests in
backend/integration/verify middleware behavior
Traffic Separation Example:
┌─────────────────────────────────────────────────────────────┐
│ Charon Container │
│ │
│ Port 8080 (Management) Port 80/443 (Proxy) │
│ ┌─────────────────────┐ ┌──────────────────────┐ │
│ │ React UI │ │ Caddy Proxy │ │
│ │ REST API │ │ + Cerberus │ │
│ │ NO middleware │ │ - Rate Limiting │ │
│ │ │ │ - CrowdSec │ │
│ │ Used by: │ │ - ACL │ │
│ │ - Admins │ │ - WAF │ │
│ │ - E2E tests │ │ │ │
│ └─────────────────────┘ │ Used by: │ │
│ ▲ │ - End users │ │
│ │ │ - Integration tests │ │
│ │ └──────────────────────┘ │
│ │ ▲ │
└───────────┼─────────────────────────────┼─────────────────┘
│ │
Admin access Public traffic
(localhost:8080) (example.com:80/443)
sequenceDiagram
participant U as User Browser
participant F as Frontend (React)
participant B as Backend (Go)
participant S as Service Layer
participant D as Database (SQLite)
participant C as Caddy Manager
participant P as Caddy Proxy
U->>F: Click "Add Proxy Host"
F->>U: Show creation form
U->>F: Fill form and submit
F->>F: Client-side validation
F->>B: POST /api/v1/proxy-hosts
B->>B: Authenticate user
B->>B: Validate input
B->>S: CreateProxyHost(dto)
S->>D: INSERT INTO proxy_hosts
D-->>S: Return created host
S->>C: TriggerCaddyReload()
C->>C: BuildConfiguration()
C->>D: SELECT all proxy hosts
D-->>C: Return hosts
C->>C: Generate Caddy JSON
C->>P: POST /config/ (Caddy API)
P->>P: Validate config
P->>P: Apply config
P-->>C: 200 OK
C-->>S: Reload success
S-->>B: Return ProxyHost
B-->>F: 201 Created + ProxyHost
F->>F: Update UI (optimistic)
F->>U: Show success notification
sequenceDiagram
participant C as Client
participant P as Caddy Proxy
participant RL as Rate Limiter
participant CS as CrowdSec
participant ACL as Access Control
participant WAF as Web App Firewall
participant U as Upstream Server
C->>P: HTTP Request
P->>RL: Check rate limit
alt Rate limit exceeded
RL-->>P: 429 Too Many Requests
P-->>C: 429 Too Many Requests
else Rate limit OK
RL-->>P: Allow
P->>CS: Check IP reputation
alt IP banned
CS-->>P: Block
P-->>C: 403 Forbidden
else IP OK
CS-->>P: Allow
P->>ACL: Check access rules
alt IP denied
ACL-->>P: Block
P-->>C: 403 Forbidden
else IP allowed
ACL-->>P: Allow
P->>WAF: Inspect request
alt Attack detected
WAF-->>P: Block
P-->>C: 403 Forbidden
else Request safe
WAF-->>P: Allow
P->>U: Forward request
U-->>P: Response
P-->>C: Response
end
end
end
end
sequenceDiagram
participant F as Frontend (React)
participant B as Backend (Go)
participant L as Log Buffer
participant C as Caddy Proxy
F->>B: WS /api/v1/logs (upgrade)
B-->>F: 101 Switching Protocols
loop Every request
C->>L: Write log entry
L->>B: Notify new log
B->>F: Send log via WebSocket
F->>F: Append to log viewer
end
F->>B: Close WebSocket
B->>L: Unsubscribe
Rationale: Simplicity over scalability - target audience is home users and small teams
Container Contents:
- Frontend static files (Vite build output)
- Go backend binary
- Embedded Caddy server
- SQLite database file
- Caddy certificates
- CrowdSec local database
# Stage 1: Build frontend
FROM node:23-alpine AS frontend-builder
WORKDIR /app/frontend
COPY frontend/package*.json ./
RUN npm ci --only=production
COPY frontend/ ./
RUN npm run build
# Stage 2: Build backend
FROM golang:1.26-bookworm AS backend-builder
WORKDIR /app/backend
COPY backend/go.* ./
RUN go mod download
COPY backend/ ./
RUN CGO_ENABLED=1 go build -o /app/charon ./cmd/api
# Stage 3: Install gosu for privilege dropping
FROM debian:trixie-slim AS gosu
RUN apt-get update && \
apt-get install -y --no-install-recommends gosu && \
rm -rf /var/lib/apt/lists/*
# Stage 4: Final runtime image
FROM debian:trixie-slim
RUN apt-get update && \
apt-get install -y --no-install-recommends \
ca-certificates \
libsqlite3-0 && \
rm -rf /var/lib/apt/lists/*
COPY --from=gosu /usr/sbin/gosu /usr/sbin/gosu
COPY --from=backend-builder /app/charon /app/charon
COPY --from=frontend-builder /app/frontend/dist /app/frontend/dist
COPY .docker/docker-entrypoint.sh /docker-entrypoint.sh
RUN chmod +x /docker-entrypoint.sh
EXPOSE 8080 80 443 443/udp
ENTRYPOINT ["/docker-entrypoint.sh"]
CMD ["/app/charon"]| Port | Protocol | Purpose | Bind |
|---|---|---|---|
| 8080 | HTTP | Web UI + REST API | 0.0.0.0 |
| 80 | HTTP | Caddy reverse proxy | 0.0.0.0 |
| 443 | HTTPS | Caddy reverse proxy (TLS) | 0.0.0.0 |
| 443 | UDP | HTTP/3 QUIC (optional) | 0.0.0.0 |
| 2019 | HTTP | Emergency recovery (localhost only) | 127.0.0.1 |
| Container Path | Purpose | Required |
|---|---|---|
/app/data |
Database, certificates, backups | Yes |
/var/run/docker.sock |
Docker container discovery | Optional |
| Variable | Purpose | Default | Required |
|---|---|---|---|
CHARON_ENV |
Environment (production/development) | production |
No |
CHARON_ENCRYPTION_KEY |
32-byte base64 key for credential encryption | Auto-generated | No |
CHARON_EMERGENCY_TOKEN |
64-char hex for break-glass access | None | Optional |
CHARON_CADDY_CONFIG_ROOT |
Caddy autosave config root | /config |
No |
CHARON_CADDY_LOG_DIR |
Caddy log directory | /var/log/caddy |
No |
CHARON_CROWDSEC_LOG_DIR |
CrowdSec log directory | /var/log/crowdsec |
No |
CHARON_PLUGINS_DIR |
DNS provider plugin directory | /app/plugins |
No |
CHARON_SINGLE_CONTAINER_MODE |
Enables permission repair endpoints | true |
No |
CROWDSEC_API_KEY |
CrowdSec cloud API key | None | Optional |
SMTP_HOST |
SMTP server for notifications | None | Optional |
SMTP_PORT |
SMTP port | 587 |
Optional |
SMTP_USER |
SMTP username | None | Optional |
SMTP_PASS |
SMTP password | None | Optional |
services:
charon:
image: wikid82/charon:latest
container_name: charon
restart: unless-stopped
ports:
- "8080:8080"
- "80:80"
- "443:443"
- "443:443/udp"
volumes:
- ./data:/app/data
- /var/run/docker.sock:/var/run/docker.sock:ro
environment:
- CHARON_ENV=production
- CHARON_ENCRYPTION_KEY=${CHARON_ENCRYPTION_KEY}
healthcheck:
test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:8080/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40sCurrent Limitations:
- SQLite does not support clustering
- Single point of failure (one container)
- Not designed for horizontal scaling
Future Options:
- PostgreSQL backend for HA deployments
- Read replicas for load balancing
- Container orchestration (Kubernetes, Docker Swarm)
-
Prerequisites:
- Go 1.26+ (backend development) - Node.js 23+ and npm (frontend development) - Docker 24+ (E2E testing) - SQLite 3.x (database)
-
Clone Repository:
git clone https://github.com/Wikid82/Charon.git cd Charon -
Backend Development:
cd backend go mod download go run cmd/api/main.go # API server runs on http://localhost:8080
-
Frontend Development:
cd frontend npm install npm run dev # Vite dev server runs on http://localhost:5173
-
Full-Stack Development (Docker):
docker-compose -f .docker/compose/docker-compose.dev.yml up # Frontend + Backend + Caddy in one container
Branch Strategy:
main: Stable production branchfeature/*: New feature developmentfix/*: Bug fixeschore/*: Maintenance tasks
Commit Convention:
feat:New user-facing featurefix:Bug fix in application codechore:Infrastructure, CI/CD, dependenciesdocs:Documentation-only changesrefactor:Code restructuring without functional changestest:Adding or updating tests
Example:
feat: add DNS-01 challenge support for Cloudflare
Implement Cloudflare DNS provider for automatic wildcard certificate
provisioning via Let's Encrypt DNS-01 challenge.
Closes #123
-
Automated Checks (CI):
- Linters (golangci-lint, ESLint)
- Unit tests (Go test, Vitest)
- E2E tests (Playwright)
- Security scans (Trivy, CodeQL, Grype)
- Coverage validation (85% minimum)
-
Human Review:
- Code quality and maintainability
- Security implications
- Performance considerations
- Documentation completeness
-
Merge Requirements:
- All CI checks pass
- At least 1 approval
- No unresolved review comments
- Branch up-to-date with base
/\ E2E (Playwright) - 10%
/ \ Critical user flows
/____\
/ \ Integration (Go) - 20%
/ \ Component interactions
/__________\
/ \ Unit (Go + Vitest) - 70%
/______________\ Pure functions, models
Purpose: Validate critical user flows in a real browser
Scope:
- User authentication
- Proxy host CRUD operations
- Certificate provisioning
- Security feature toggling
- Real-time log streaming
Execution:
# Run against Docker container
npx playwright test --project=chromium
# Run with coverage (Vite dev server)
.github/skills/scripts/skill-runner.sh test-e2e-playwright-coverage
# Debug mode
npx playwright test --debugCoverage Modes:
- Docker Mode: Integration testing, no coverage (0% reported)
- Vite Dev Mode: Coverage collection with V8 inspector
Why Two Modes?
- Playwright coverage requires source maps and raw source files
- Docker serves pre-built production files (no source maps)
- Vite dev server exposes source files for coverage instrumentation
Purpose: Test individual functions and methods in isolation
Framework: Go's built-in testing package
Coverage Target: 85% minimum
Execution:
# Run all tests
go test ./...
# With coverage
go test -cover ./...
# VS Code task
"Test: Backend with Coverage"Test Organization:
*_test.gofiles alongside source code- Table-driven tests for comprehensive coverage
- Mocks for external dependencies (database, HTTP clients)
Example:
func TestCreateProxyHost(t *testing.T) {
tests := []struct {
name string
input ProxyHostDTO
wantErr bool
}{
{
name: "valid proxy host",
input: ProxyHostDTO{Domain: "example.com", Target: "http://localhost:8000"},
wantErr: false,
},
{
name: "invalid domain",
input: ProxyHostDTO{Domain: "", Target: "http://localhost:8000"},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := CreateProxyHost(tt.input)
if (err != nil) != tt.wantErr {
t.Errorf("CreateProxyHost() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}Purpose: Test React components and utility functions
Framework: Vitest + React Testing Library
Coverage Target: 85% minimum
Execution:
# Run all tests
npm test
# With coverage
npm run test:coverage
# VS Code task
"Test: Frontend with Coverage"Test Organization:
*.test.tsxfiles alongside components- Mock API calls with MSW (Mock Service Worker)
- Snapshot tests for UI consistency
Purpose: Test component interactions (e.g., API + Service + Database)
Location: backend/integration/
Scope:
- API endpoint end-to-end flows
- Database migrations
- Caddy manager integration
- CrowdSec API calls
Execution:
go test ./integration/...Automated Hooks (via .pre-commit-config.yaml):
Fast Stage (< 5 seconds):
- Trailing whitespace removal
- EOF fixer
- YAML syntax check
- JSON syntax check
- Markdown link validation
Manual Stage (run explicitly):
- Backend coverage tests (60-90s)
- Frontend coverage tests (30-60s)
- TypeScript type checking (10-20s)
Why Manual?
- Coverage tests are slow and would block commits
- Developers run them on-demand before pushing
- CI enforces coverage on pull requests
Workflow Triggers:
pushtomain,feature/*,fix/*pull_requesttomain
CI Jobs:
- Lint: golangci-lint, ESLint, markdownlint, hadolint
- Test: Go tests, Vitest, Playwright
- Security: Trivy, CodeQL, Grype, Govulncheck
- Build: Docker image build
- Coverage: Upload to Codecov (85% gate)
- Supply Chain: SBOM generation, Cosign signing
Semantic Versioning: MAJOR.MINOR.PATCH-PRERELEASE
- MAJOR: Breaking changes (e.g., API contract changes)
- MINOR: New features (backward-compatible)
- PATCH: Bug fixes (backward-compatible)
- PRERELEASE:
-beta.1,-rc.1, etc.
Examples:
1.0.0- Stable release1.1.0- New feature (DNS provider support)1.1.1- Bug fix (GORM query fix)1.2.0-beta.1- Beta release for testing
Version File: VERSION.md (single source of truth)
Platforms Supported:
linux/amd64linux/arm64
Build Process:
-
Frontend Build:
cd frontend npm ci --only=production npm run build # Output: frontend/dist/
-
Backend Build:
cd backend go build -o charon cmd/api/main.go # Output: charon binary
-
Docker Image Build:
docker buildx build \ --platform linux/amd64,linux/arm64 \ --tag wikid82/charon:latest \ --tag wikid82/charon:1.2.0 \ --push .
Automated Release (GitHub Actions):
- Trigger: Push tag
v1.2.0 - Build: Multi-platform Docker images
- Test: Run E2E tests against built image
- Security: Scan for vulnerabilities (block if Critical/High)
- SBOM: Generate Software Bill of Materials (Syft)
- Sign: Cryptographic signature with Cosign
- Provenance: Generate SLSA provenance attestation
- Publish: Push to Docker Hub and GHCR
- Release Notes: Generate changelog from commits
- Notify: Send release notification (Discord, email)
Mandatory rollout gates (sign-off block):
- Digest freshness and index digest parity across GHCR and Docker Hub
- Per-arch digest parity across GHCR and Docker Hub
- SBOM and vulnerability scans against immutable refs (
image@sha256:...) - Artifact freshness timestamps after push
- Evidence block with required rollout verification fields
Components:
-
SBOM (Software Bill of Materials):
- Generated with Syft (CycloneDX format)
- Lists all dependencies (Go modules, NPM packages, OS packages)
- Attached to release as
sbom.cyclonedx.json
-
Container Scanning:
- Trivy: Fast vulnerability scanning (filesystem)
- Grype: Deep image scanning (layers, dependencies)
- CodeQL: Static analysis (Go, JavaScript)
-
Cryptographic Signing:
- Cosign signs Docker images with keyless signing (OIDC)
- Signature stored in registry alongside image
- Verification:
cosign verify wikid82/charon:latest
-
SLSA Provenance:
- Attestation of build process (inputs, outputs, environment)
- Proves image was built by trusted CI pipeline
- Level: SLSA Build L3 (hermetic builds)
Verification Example:
# Verify image signature
cosign verify \
--certificate-identity-regexp="https://github.com/Wikid82/Charon" \
--certificate-oidc-issuer="https://token.actions.githubusercontent.com" \
wikid82/charon:latest
# Inspect SBOM
syft ghcr.io/wikid82/charon@sha256:<index-digest> -o json
# Scan for vulnerabilities
grype ghcr.io/wikid82/charon@sha256:<index-digest>Container Rollback:
# List available versions
docker images wikid82/charon
# Roll back to previous version
docker-compose down
docker-compose up -d --pull always wikid82/charon:1.1.1Database Rollback:
# Restore from backup
docker exec charon /app/scripts/restore-backup.sh \
/app/data/backups/charon-20260127.dbCurrent State: Monolithic design (no plugin system)
Planned Extensibility Points:
-
DNS Providers:
- Interface-based design for DNS-01 challenge providers
- Current: 15+ built-in providers (Cloudflare, Route53, etc.)
- Future: Dynamic plugin loading for custom providers
-
Notification Channels:
- Current rollout is Discord-only for notifications
- Additional services are enabled later in validated phases
-
Authentication Providers:
- Current: Local database authentication
- Future: OAuth2, LDAP, SAML integration
-
Storage Backends:
- Current: SQLite (embedded)
- Future: PostgreSQL, MySQL for HA deployments
REST API Design:
- Version prefix:
/api/v1/ - Future versions:
/api/v2/(backward-compatible) - Deprecation policy: 2 major versions supported
WebHooks (Future):
- Event notifications for external systems
- Triggers: Proxy host created, certificate renewed, security event
- Payload: JSON with event type and data
Current: Cerberus security middleware injected into Caddy pipeline
Future:
- User-defined middleware (rate limiting rules, custom headers)
- JavaScript/Lua scripting for request transformation
- Plugin marketplace for community contributions
-
Single Point of Failure:
- Monolithic container design
- No horizontal scaling support
- Mitigation: Container restart policies, health checks
-
Database Scalability:
- SQLite not designed for high concurrency
- Write bottleneck for > 100 concurrent users
- Mitigation: Optimize queries, consider PostgreSQL for large deployments
-
Memory Usage:
- All proxy configurations loaded into memory
- Caddy certificates cached in memory
- Mitigation: Monitor memory usage, implement pagination
-
Embedded Caddy:
- Caddy version pinned to backend compatibility
- Cannot use standalone Caddy features
- Mitigation: Track Caddy releases, update dependencies regularly
-
GORM Struct Reuse:
- Fixed in v1.2.0 (see docs/implementation/gorm_security_scanner_complete.md)
- Prior versions had ID leakage in Settings queries
-
Docker Discovery:
- Requires
docker.sockmount (security trade-off) - Only discovers containers on same Docker host
- Mitigation: Use remote Docker API or Kubernetes
- Requires
-
Certificate Renewal:
- Let's Encrypt rate limits (50 certificates/week per domain)
- No automatic fallback to ZeroSSL
- Mitigation: Implement fallback logic, monitor rate limits
When to Update:
-
Major Feature Addition:
- New components (e.g., API gateway, message queue)
- New external integrations (e.g., cloud storage, monitoring)
-
Architectural Changes:
- Change from SQLite to PostgreSQL
- Introduction of microservices
- New deployment model (Kubernetes, Serverless)
-
Technology Stack Updates:
- Major version upgrades (Go, React, Caddy)
- Replacement of core libraries (e.g., GORM to SQLx)
-
Security Architecture Changes:
- New security layers (e.g., API Gateway, Service Mesh)
- Authentication provider changes (OAuth2, SAML)
Update Process:
- Developer: Update relevant sections when making changes
- Code Review: Reviewer validates architecture docs match implementation
- Quarterly Audit: Architecture team reviews for accuracy
- Version Control: Track changes via Git commit history
GitHub Copilot Instructions:
All agents (Planning, Backend_Dev, Frontend_Dev, DevOps) must reference ARCHITECTURE.md when:
- Creating new components
- Modifying core systems
- Changing integration points
- Updating dependencies
CI Checks:
- Validate directory structure matches documented conventions
- Check technology versions against
ARCHITECTURE.md - Ensure API endpoints follow documented patterns
Metrics to Track:
- Code Complexity: Cyclomatic complexity per module
- Coupling: Dependencies between components
- Technical Debt: TODOs, FIXMEs, HACKs in codebase
- Test Coverage: Maintain 85% minimum
- Build Time: Frontend + Backend + Docker build duration
- Container Size: Track image size bloat
Tools:
- SonarQube: Code quality and technical debt
- Codecov: Coverage tracking and trend analysis
- Grafana: Runtime metrics and performance
- GitHub Insights: Contributor activity and velocity
graph TB
subgraph "User Interface"
Browser[Web Browser]
end
subgraph "Docker Container"
subgraph "Frontend"
React[React SPA]
Vite[Vite Dev Server]
end
subgraph "Backend"
Gin[Gin HTTP Server]
API[API Handlers]
Services[Service Layer]
Models[GORM Models]
end
subgraph "Data Layer"
SQLite[(SQLite DB)]
Cache[Memory Cache]
end
subgraph "Proxy Layer"
CaddyMgr[Caddy Manager]
Caddy[Caddy Server]
end
subgraph "Security (Cerberus)"
RateLimit[Rate Limiter]
CrowdSec[CrowdSec]
ACL[Access Lists]
WAF[WAF/Coraza]
end
end
subgraph "External Systems"
Docker[Docker Daemon]
ACME[Let's Encrypt]
DNS[DNS Providers]
Upstream[Upstream Servers]
CrowdAPI[CrowdSec Cloud API]
end
Browser -->|HTTPS :8080| React
React -->|API Calls| Gin
Gin --> API
API --> Services
Services --> Models
Models --> SQLite
Services --> CaddyMgr
CaddyMgr --> Caddy
Services --> Cache
Caddy --> RateLimit
RateLimit --> CrowdSec
CrowdSec --> ACL
ACL --> WAF
WAF --> Upstream
Services -.->|Container Discovery| Docker
Caddy -.->|ACME Protocol| ACME
Caddy -.->|DNS Challenge| DNS
CrowdSec -.->|Threat Intel| CrowdAPI
SQLite -.->|Backups| Backups[Backup Storage]
- README.md - Project overview and quick start
- CONTRIBUTING.md - Contribution guidelines
- docs/features.md - Detailed feature documentation
- docs/api.md - REST API reference
- docs/database-schema.md - Database structure
- docs/cerberus.md - Security suite documentation
- docs/getting-started.md - User guide
- SECURITY.md - Security policy and vulnerability reporting
Maintained by: Charon Development Team Questions? Open an issue on GitHub or join our community.