A3S Docs
A3S Gateway

Configuration

ACL configuration for entrypoints, routers, services, middlewares, and providers

Configuration

A3S Gateway uses ACL (Agent Configuration Language) exclusively for configuration. The format is auto-detected by .acl 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 management: ManagementConfig,
    pub shutdown_timeout_secs: u64, // default: 30
}

Loading Configuration

// Load from file
let config = GatewayConfig::from_file("gateway.acl").await?;

// Parse from ACL string
let config = GatewayConfig::from_acl(acl_content)?;

// Validate (checks router→service, router→middleware, router→entrypoint references)
config.validate()?;

Management

The Dashboard API is optional and runs on a dedicated listener. It never intercepts traffic entrypoints. Requests must match allowed_ips and, when auth_token_env is set, include Authorization: Bearer <token>. Enable the nested tls block when remote management needs HTTPS or client certificate validation. Rejected management requests and TLS handshake failures are retained in an in-memory audit ring buffer and exposed at /events.

pub struct ManagementConfig {
    pub enabled: bool,                  // default: false
    pub address: String,                // default: "127.0.0.1:9090"
    pub path_prefix: String,            // default: "/api/gateway"
    pub auth_token_env: Option<String>, // default: "A3S_GATEWAY_ADMIN_TOKEN"
    pub allowed_ips: Vec<String>,       // default: ["127.0.0.1", "::1"]
    pub tls: Option<ManagementTlsConfig>,
}

pub struct ManagementTlsConfig {
    pub cert_file: String,
    pub key_file: String,
    pub client_ca_file: Option<String>,
    pub require_client_cert: bool, // default: false
    pub min_version: String,       // "1.2" (default) or "1.3"
}
management {
  enabled        = true
  address        = "127.0.0.1:9090"
  path_prefix    = "/api/gateway"
  auth_token_env = "A3S_GATEWAY_ADMIN_TOKEN"
  allowed_ips    = ["127.0.0.1", "::1"]

  tls {
    cert_file           = "/etc/a3s/admin/server.crt"
    key_file            = "/etc/a3s/admin/server.key"
    client_ca_file      = "/etc/a3s/admin/client-ca.crt"
    require_client_cert = true
    min_version         = "1.3"
  }
}

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 = env("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 = env("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.acl").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. If HTTP/TCP entrypoints are unchanged, the runtime snapshot is swapped without rebinding traffic ports
  5. If HTTP/TCP listeners are added or moved, changed entrypoints are reconciled while unchanged listeners stay active on bind failure
  6. If a UDP entrypoint is involved, affected entrypoints restart explicitly
  7. 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 = env("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