Configuration
HCL configuration for entrypoints, routers, services, middlewares, and providers
Configuration
A3S Gateway uses HCL (HashiCorp Configuration Language) exclusively for configuration. The format is auto-detected by .hcl file extension. Configuration supports hot reload via file watching.
GatewayConfig
pub struct GatewayConfig {
pub entrypoints: HashMap<String, EntrypointConfig>,
pub routers: HashMap<String, RouterConfig>,
pub services: HashMap<String, ServiceConfig>,
pub middlewares: HashMap<String, MiddlewareConfig>,
pub providers: ProviderConfig,
pub shutdown_timeout_secs: u64, // default: 30
}Loading Configuration
// Load from file
let config = GatewayConfig::from_file("gateway.hcl").await?;
// Parse from HCL string
let config = GatewayConfig::from_hcl(hcl_content)?;
// Validate (checks router→service, router→middleware, router→entrypoint references)
config.validate()?;Entrypoints
Entrypoints define network listeners.
pub struct EntrypointConfig {
pub address: String,
pub protocol: Protocol, // http (default), tcp, udp
pub tls: Option<TlsConfig>,
pub max_connections: Option<u32>, // TCP only
pub tcp_allowed_ips: Vec<String>, // TCP only, CIDR or single IP
pub udp_session_timeout_secs: Option<u64>, // UDP only
pub udp_max_sessions: Option<usize>, // UDP only
}# HTTP listener
entrypoints "web" {
address = "0.0.0.0:80"
}
# HTTPS listener with TLS
entrypoints "websecure" {
address = "0.0.0.0:443"
tls {
cert_file = "/etc/certs/cert.pem"
key_file = "/etc/certs/key.pem"
min_version = "1.3"
}
}
# TCP listener with connection limits
entrypoints "tcp" {
address = "0.0.0.0:5432"
protocol = "tcp"
max_connections = 1000
tcp_allowed_ips = ["10.0.0.0/8", "192.168.1.0/24"]
}
# UDP listener
entrypoints "dns" {
address = "0.0.0.0:53"
protocol = "udp"
udp_session_timeout_secs = 60
udp_max_sessions = 5000
}TLS Configuration
pub struct TlsConfig {
pub cert_file: String,
pub key_file: String,
pub min_version: String, // "1.2" (default) or "1.3"
pub acme: bool, // Let's Encrypt auto-cert
pub acme_email: Option<String>, // required when acme = true
pub acme_domains: Vec<String>, // defaults to Host rules if empty
pub acme_staging: bool, // use staging environment
pub acme_storage_path: Option<String>,// default: /etc/gateway/acme
}TLS is handled by rustls (pure Rust, no OpenSSL dependency). ACME support enables automatic certificate provisioning from Let's Encrypt.
entrypoints "websecure" {
address = "0.0.0.0:443"
tls {
cert_file = "/etc/certs/cert.pem"
key_file = "/etc/certs/key.pem"
acme = true
acme_email = "admin@example.com"
acme_domains = ["example.com", "api.example.com"]
acme_staging = false
acme_storage_path = "/etc/gateway/acme"
}
}Routers
Routers match incoming requests to services.
pub struct RouterConfig {
pub rule: String,
pub service: String,
pub entrypoints: Vec<String>,
pub middlewares: Vec<String>,
pub priority: i32, // lower = higher priority, default: 0
}routers "api" {
rule = "Host(`api.example.com`) && PathPrefix(`/v1`)"
service = "api-service"
entrypoints = ["websecure"]
middlewares = ["auth-jwt", "rate-limit"]
priority = 0
}
routers "web" {
rule = "Host(`www.example.com`)"
service = "web-service"
entrypoints = ["web", "websecure"]
priority = 10
}Lower priority values are matched first. See Routing for rule syntax details.
Services
Services define backend pools with load balancing.
pub struct ServiceConfig {
pub load_balancer: LoadBalancerConfig,
pub mirror: Option<MirrorConfig>,
pub failover: Option<FailoverConfig>,
}
pub struct LoadBalancerConfig {
pub strategy: Strategy, // round-robin, weighted, least-connections, random
pub servers: Vec<ServerConfig>,
pub health_check: Option<HealthCheckConfig>,
pub sticky: Option<StickyConfig>,
}services "api-service" {
load_balancer {
strategy = "weighted"
servers = [
{ url = "http://127.0.0.1:8001", weight = 3 },
{ url = "http://127.0.0.1:8002", weight = 1 }
]
health_check {
path = "/health"
interval = "10s"
timeout = "5s"
unhealthy_threshold = 3
healthy_threshold = 1
}
sticky {
cookie = "srv_id"
}
}
}Traffic Mirroring
Copy a percentage of live traffic to a shadow backend for testing.
services "api-service" {
load_balancer {
strategy = "round-robin"
servers = [{ url = "http://127.0.0.1:8001" }]
}
mirror {
service = "shadow-backend"
percentage = 10
}
}Failover
Automatic fallback when the primary service has zero healthy backends.
services "api-service" {
load_balancer {
strategy = "round-robin"
servers = [{ url = "http://127.0.0.1:8001" }]
}
failover {
service = "backup-pool"
}
}See Services for load balancing strategies and health check details.
Middlewares
Middlewares are defined globally and referenced by name in routers.
middlewares "auth-jwt" {
type = "jwt"
value = "${JWT_SECRET}"
}
middlewares "rate-limit" {
type = "rate-limit"
rate = 100
burst = 50
}
middlewares "cors" {
type = "cors"
allowed_origins = ["https://example.com"]
allowed_methods = ["GET", "POST", "PUT", "DELETE"]
allowed_headers = ["Content-Type", "Authorization"]
max_age = 3600
}
middlewares "compress" {
type = "compress"
}
middlewares "circuit-breaker" {
type = "circuit-breaker"
failure_threshold = 3
cooldown_secs = 60
success_threshold = 2
}See Middleware for all 15 built-in middleware types.
Environment Variable Substitution
Configuration values support environment variable references:
middlewares "auth-jwt" {
type = "jwt"
value = "${JWT_SECRET}"
}
entrypoints "websecure" {
address = "0.0.0.0:443"
tls {
cert_file = "${TLS_CERT_PATH}"
key_file = "${TLS_KEY_PATH}"
}
}Providers
Configuration providers supply dynamic configuration.
File Provider
providers {
file {
watch = true
directory = "/etc/gateway/conf.d/"
}
}When watch = true, the gateway monitors the config file and directory for changes using inotify (Linux) or kqueue (macOS), and hot-reloads without downtime.
Discovery Provider
Health-based service discovery that polls backend seed URLs for /.well-known/a3s-service.json metadata.
providers {
discovery {
poll_interval_secs = 30
timeout_secs = 5
seeds = [
{ url = "http://10.0.0.5:8080" },
{ url = "http://10.0.0.6:8080" }
]
}
}Docker Provider
Auto-discover services from container labels.
providers {
docker {
host = "/var/run/docker.sock"
label_prefix = "a3s"
poll_interval_secs = 10
}
}Kubernetes Provider
Watch Ingress and IngressRoute CRD resources.
providers {
kubernetes {
namespace = "default"
label_selector = "app=my-service"
watch_interval_secs = 30
ingress_route_crd = true
}
}Hot Reload
// Programmatic reload
let new_config = GatewayConfig::from_file("gateway.hcl").await?;
gateway.reload(new_config).await?;During reload:
- Gateway transitions to
Reloadingstate - New configuration is validated
- Routers, services, and middlewares are rebuilt
- Existing connections continue on old config until complete
- Gateway transitions back to
Running
Full Example
# Entrypoints
entrypoints "web" {
address = "0.0.0.0:80"
}
entrypoints "websecure" {
address = "0.0.0.0:443"
tls {
cert_file = "/etc/certs/cert.pem"
key_file = "/etc/certs/key.pem"
}
}
# Routers
routers "api" {
rule = "Host(`api.example.com`) && PathPrefix(`/v1`)"
service = "api-service"
entrypoints = ["websecure"]
middlewares = ["auth-jwt", "rate-limit", "cors"]
}
routers "web" {
rule = "Host(`www.example.com`)"
service = "web-service"
entrypoints = ["web", "websecure"]
}
# Services
services "api-service" {
load_balancer {
strategy = "least-connections"
servers = [
{ url = "http://127.0.0.1:8001" },
{ url = "http://127.0.0.1:8002" }
]
health_check {
path = "/health"
interval = "10s"
}
}
}
services "web-service" {
load_balancer {
strategy = "round-robin"
servers = [{ url = "http://127.0.0.1:3000" }]
}
}
# Middlewares
middlewares "auth-jwt" {
type = "jwt"
value = "${JWT_SECRET}"
}
middlewares "rate-limit" {
type = "rate-limit"
rate = 100
burst = 50
}
middlewares "cors" {
type = "cors"
allowed_origins = ["https://example.com"]
allowed_methods = ["GET", "POST"]
}
# Providers
providers {
file {
watch = true
}
}