A3S Docs
A3S Gateway

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:

  1. Gateway transitions to Reloading state
  2. New configuration is validated
  3. Routers, services, and middlewares are rebuilt
  4. Existing connections continue on old config until complete
  5. 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
  }
}

On this page