Bastion

Routes

Route configuration, path matching, protocol selection, and per-route overrides.

A route defines how incoming requests are matched and forwarded to upstream targets. Each route has a path pattern, one or more targets, a protocol, and optional overrides for load balancing, rate limiting, authentication, and caching.

RouteConfig

The RouteConfig struct is the primary way to define routes programmatically:

type RouteConfig struct {
    // Path is the URL pattern to match (e.g., "/users/*", "/api/v1/orders/*").
    Path string

    // Targets is the list of upstream targets for this route.
    Targets []TargetConfig

    // StripPrefix removes the matched path prefix before forwarding.
    StripPrefix bool

    // Protocol specifies the proxy protocol (HTTP, WebSocket, SSE, gRPC).
    Protocol Protocol

    // Enabled controls whether this route is active.
    Enabled bool

    // LoadBalancer overrides the global load balancing strategy for this route.
    LoadBalancer LoadBalancerStrategy

    // RateLimit overrides the global rate limit for this route.
    RateLimit *RateLimitConfig

    // Auth sets route-specific authentication requirements.
    Auth *AuthConfig

    // Cache sets route-specific response caching policy.
    Cache *CacheConfig

    // Timeout overrides the global upstream timeout for this route.
    Timeout time.Duration

    // RetryPolicy overrides the global retry configuration for this route.
    RetryPolicy *RetryConfig

    // Metadata is a free-form map for custom route annotations.
    Metadata map[string]string
}

Registering routes

Routes are registered via the WithRoute option function:

bastion.NewExtension(
    bastion.WithRoute(bastion.RouteConfig{
        Path:    "/users/*",
        Targets: []bastion.TargetConfig{
            {URL: "http://user-service:8080", Weight: 1},
        },
        StripPrefix: true,
        Protocol:    bastion.ProtocolHTTP,
        Enabled:     true,
    }),
)

Path matching

Bastion uses path patterns to match incoming requests to routes. Patterns support these forms:

PatternMatchesExample
/users/*Any path starting with /users//users/42, /users/42/profile
/api/v1/orders/*Any path starting with /api/v1/orders//api/v1/orders/123
/healthExact match only/health

The * wildcard matches one or more path segments. Routes are matched in registration order -- the first matching route wins.

Path conflicts

When multiple routes could match the same request, the most specific route wins. Exact matches take precedence over wildcard matches. If two wildcard routes overlap, the one registered first wins.

Protocol selection

Each route specifies a protocol that determines how requests are proxied:

const (
    ProtocolHTTP      Protocol = "http"
    ProtocolWebSocket Protocol = "websocket"
    ProtocolSSE       Protocol = "sse"
    ProtocolGRPC      Protocol = "grpc"
)
ProtocolUse case
ProtocolHTTPStandard HTTP/HTTPS API traffic. Also handles WebSocket upgrades automatically.
ProtocolWebSocketDedicated WebSocket routes where all traffic is WebSocket.
ProtocolSSEServer-Sent Events streaming endpoints. Disables response buffering.
ProtocolGRPCgRPC services. Forwards HTTP/2 frames including trailers.

For most services, ProtocolHTTP is the right choice. It handles both regular HTTP requests and WebSocket upgrade requests transparently.

Strip prefix

When StripPrefix is true, Bastion removes the matched path prefix before forwarding the request to the upstream.

// Route: /users/* with StripPrefix: true
// Request:  GET /users/42/profile
// Upstream: GET /42/profile

// Route: /users/* with StripPrefix: false
// Request:  GET /users/42/profile
// Upstream: GET /users/42/profile

This is useful when the upstream service does not expect the gateway's path prefix. Most microservice deployments use StripPrefix: true.

Enable and disable

Routes have an Enabled field that controls whether they accept traffic:

bastion.RouteConfig{
    Path:    "/users/*",
    Enabled: true,  // route is active
}

Disabled routes return 404 Not Found as if they do not exist. You can enable and disable routes at runtime through the admin API:

POST /bastion/api/routes/:id/enable
POST /bastion/api/routes/:id/disable

Static vs discovered routes

Bastion supports two sources of routes:

Static routes are defined in code (via WithRoute) or in YAML configuration. They are loaded at startup and persist across restarts.

Discovered routes are created automatically by the FARP service discovery integration. When a new service registers itself via FARP, Bastion creates a route for it. When the service deregisters, the route is removed.

Static routes take precedence over discovered routes when paths overlap. Discovered routes are tagged with metadata indicating their source.

Per-route overrides

Several global settings can be overridden at the route level:

Rate limiting

bastion.RouteConfig{
    Path: "/uploads/*",
    RateLimit: &bastion.RateLimitConfig{
        Enabled:        true,
        RequestsPerSec: 10,
        Burst:          5,
    },
}

Load balancing

bastion.RouteConfig{
    Path:         "/users/*",
    LoadBalancer: bastion.LoadBalancerLeastConn,
}

Authentication

bastion.RouteConfig{
    Path: "/admin/*",
    Auth: &bastion.AuthConfig{
        Type:   bastion.AuthBearer,
        Secret: "my-admin-token",
    },
}

Response caching

bastion.RouteConfig{
    Path: "/products/*",
    Cache: &bastion.CacheConfig{
        Enabled: true,
        TTL:     5 * time.Minute,
    },
}

Timeout

bastion.RouteConfig{
    Path:    "/reports/*",
    Timeout: 60 * time.Second,
}

Retry policy

bastion.RouteConfig{
    Path: "/payments/*",
    RetryPolicy: &bastion.RetryConfig{
        MaxRetries:  3,
        Strategy:    bastion.RetryExponential,
        BaseDelay:   100 * time.Millisecond,
        MaxDelay:    5 * time.Second,
        Jitter:      true,
    },
}

YAML route configuration

Routes can also be defined in YAML:

bastion:
  routes:
    - path: "/users/*"
      targets:
        - url: "http://user-service:8080"
          weight: 1
      stripPrefix: true
      protocol: http
      enabled: true
      loadBalancer: round-robin
      timeout: 30s
      rateLimit:
        enabled: true
        requestsPerSec: 100
        burst: 20
      cache:
        enabled: true
        ttl: 5m

On this page