---
title: Forge Extension
description: Integrate Bastion as a Forge extension with auto-configuration and DI container binding.
---

Bastion ships a first-class [Forge](https://github.com/xraph/forge) extension that handles gateway initialization, FARP discovery wiring, dashboard registration, health checks, and graceful shutdown automatically. This guide covers setup via programmatic options and YAML configuration.

## Quick Start

```go
import (
    "github.com/xraph/forge"
    "github.com/xraph/bastion/extension"
)

app := forge.New()
app.Register(extension.New(
    bastion.WithBasePath("/api"),
    bastion.WithDashboardEnabled(true),
))
app.Run()
```

That single call registers Bastion into the Forge lifecycle:

1. Loads configuration from YAML (merged with programmatic options).
2. Initializes the proxy engine, route manager, circuit breakers, and health monitor.
3. Auto-resolves the `discovery` extension from the DI container for FARP service discovery.
4. Registers the admin REST API and dashboard routes.
5. Provides the `*bastion.Gateway` instance to the DI container via [Vessel](https://github.com/xraph/vessel).
6. Starts the WebSocket hub for real-time dashboard updates.

## Extension Lifecycle

The extension participates in the full Forge lifecycle:

| Method | When Called | What Happens |
|--------|------------|--------------|
| `Register(app)` | App startup | Load config, initialize components, register with DI, set up admin routes |
| `Start(ctx)` | After all extensions registered | Wire callbacks, load manual and persisted routes, start discovery, start health monitor |
| `Health(ctx)` | Health check requests | Verify routes exist and upstream targets are reachable |
| `Stop(ctx)` | Graceful shutdown | Set draining mode, stop discovery, stop health monitor |

The gateway declares a dependency on the `discovery` extension, ensuring it is always registered and started first.

## YAML Configuration

Bastion reads configuration from your Forge config file under the `bastion` key:

```yaml
# config.yaml
bastion:
  enabled: true
  base_path: "/api"

  routes:
    - path: /users
      targets:
        - url: http://user-service:8080
          weight: 1
      strip_prefix: true
      enabled: true

    - path: /orders
      targets:
        - url: http://order-service:8080
          weight: 1
      strip_prefix: true
      enabled: true

  discovery:
    enabled: true
    poll_interval: 30s
    watch_mode: true
    auto_prefix: true
    prefix_template: "/{{.ServiceName}}"
    strip_prefix: true

  circuit_breaker:
    enabled: true
    failure_threshold: 5
    failure_window: 60s
    reset_timeout: 30s

  rate_limiting:
    enabled: false
    requests_per_sec: 1000
    burst: 100

  health_check:
    enabled: true
    interval: 10s
    timeout: 5s
    path: "/_/health"

  load_balancing:
    strategy: roundRobin

  dashboard:
    enabled: true
    base_path: /gateway
    title: "API Gateway"
    realtime: true

  openapi:
    enabled: true
    path: /openapi.json
    title: "My API Gateway"
```

### Config Resolution Order

The extension loads configuration in this order:

1. YAML file (`bastion` key).
2. Programmatic options (`ConfigOption` functions passed to `New()`).
3. Defaults for any zero-valued fields via `DefaultConfig()`.

YAML values and programmatic options are merged. Programmatic options override YAML when both set the same field.

## Programmatic Options

All configuration can be set via `ConfigOption` functions:

```go
import (
    "time"
    "github.com/xraph/bastion"
    "github.com/xraph/bastion/extension"
)

ext := extension.New(
    bastion.WithBasePath("/api"),
    bastion.WithDiscoveryEnabled(true),
    bastion.WithDiscoveryPollInterval(30 * time.Second),
    bastion.WithLoadBalancing(bastion.LoadBalancingConfig{
        Strategy: bastion.LBWeightedRoundRobin,
    }),
    bastion.WithCircuitBreaker(bastion.CircuitBreakerConfig{
        Enabled:          true,
        FailureThreshold: 5,
        ResetTimeout:     30 * time.Second,
    }),
    bastion.WithRateLimiting(bastion.RateLimitConfig{
        Enabled:        true,
        RequestsPerSec: 1000,
        Burst:          100,
    }),
    bastion.WithDashboardEnabled(true),
    bastion.WithOpenAPIEnabled(true),
)
```

### Key Options

| Option | Description |
|--------|-------------|
| `WithBasePath(path)` | URL prefix for all proxied routes |
| `WithRoute(rc)` | Add a single manual route |
| `WithRoutes(routes)` | Set all manual routes |
| `WithServiceRoute(name, path, url, specURL)` | Add a route with OpenAPI spec |
| `WithDiscoveryEnabled(bool)` | Enable/disable FARP auto-discovery |
| `WithDiscoveryPollInterval(d)` | Set discovery polling interval |
| `WithDiscoveryWatchMode(bool)` | Use event-driven watch instead of polling |
| `WithLoadBalancing(cfg)` | Set load balancing strategy |
| `WithCircuitBreaker(cfg)` | Configure circuit breaker |
| `WithRateLimiting(cfg)` | Configure global rate limiting |
| `WithHealthCheck(cfg)` | Configure upstream health checks |
| `WithAuth(cfg)` | Configure gateway authentication |
| `WithCaching(cfg)` | Configure response caching |
| `WithDashboardEnabled(bool)` | Enable/disable the admin dashboard |
| `WithOpenAPI(cfg)` | Configure OpenAPI aggregation |
| `WithCORS(cfg)` | Configure gateway-level CORS |
| `WithTLS(cfg)` | Configure upstream TLS/mTLS |
| `WithConfig(cfg)` | Set the complete config struct |

## DI Container Integration

After registration, the `*bastion.Gateway` instance is available from the DI container:

```go
import "github.com/xraph/vessel"

// In another extension or AfterStart callback
gw, err := vessel.Inject[*bastion.Gateway](app.Container())
if err != nil {
    log.Fatal("bastion not registered:", err)
}

// Access gateway components
routes := gw.RouteManager().ListRoutes()
stats := gw.Snapshot()
hooks := gw.Hooks()
```

## Auto-Resolution of Discovery Extension

Bastion automatically resolves Forge's built-in discovery extension to power FARP-based service discovery. When a Forge application includes both the discovery extension and Bastion, the gateway automatically:

1. Resolves the `discovery.Service` from the DI container.
2. Wraps it in an adapter that implements `bastion.DiscoveryService`.
3. Injects it into the gateway via `SetDiscoveryService()`.

No manual wiring is needed. If the discovery extension is not registered, Bastion logs a warning and operates with only manual routes.

## Health Check Integration

The extension implements `forge.Extension`'s `Health(ctx)` method. Forge automatically includes this in its aggregated health endpoint:

```go
func (e *Gateway) Health(ctx context.Context) error {
    if e.routeManager == nil || e.routeManager.RouteCount() == 0 {
        return fmt.Errorf("no routes configured")
    }
    return e.healthMon.Health(ctx)
}
```

The health check verifies:

- At least one route is configured.
- The upstream health monitor reports healthy (at least one target is reachable).

## Persistent Store Support

The extension supports optional persistent storage for routes, circuit breaker state, health history, and audit logs. Configure a store using Grove database integration:

```go
ext := extension.New(
    bastion.WithDashboardEnabled(true),
).Configure(
    extension.WithGroveDatabase(""),  // Use default Grove DB
)
```

The store backend is auto-detected from the Grove driver (PostgreSQL, SQLite, or MongoDB). Routes created via the admin API are persisted and restored on restart.

## Dashboard Integration

When Bastion is used alongside the Forge dashboard extension, it automatically registers itself as a dashboard contributor. The dashboard provides pages for Overview, Routes, Upstreams, Services, Traffic, Health, Circuits, API Explorer, and Config.

```go
import (
    "github.com/xraph/forge"
    "github.com/xraph/bastion/extension"
    dashext "github.com/xraph/forge/extensions/dashboard"
)

app := forge.New()
app.Register(dashext.New())
app.Register(extension.New(
    bastion.WithDashboardEnabled(true),
))
app.Run()
```

## Complete Example

```go
package main

import (
    "github.com/xraph/forge"
    "github.com/xraph/bastion"
    "github.com/xraph/bastion/extension"
    "github.com/xraph/forge/extensions/discovery"
    dashext "github.com/xraph/forge/extensions/dashboard"
)

func main() {
    app := forge.New()

    // Register discovery for FARP service auto-discovery.
    app.Register(discovery.New())

    // Register the dashboard extension.
    app.Register(dashext.New())

    // Register Bastion API gateway.
    app.Register(extension.New(
        bastion.WithBasePath("/api"),
        bastion.WithRoute(bastion.RouteConfig{
            Path: "/users",
            Targets: []bastion.TargetConfig{
                {URL: "http://user-service:8080", Weight: 1},
            },
            StripPrefix: true,
            Enabled:     true,
        }),
        bastion.WithDiscoveryEnabled(true),
        bastion.WithDashboardEnabled(true),
        bastion.WithOpenAPIEnabled(true),
    ))

    app.Run()
}
```

With the corresponding YAML config:

```yaml
# config.yaml
bastion:
  enabled: true
  base_path: "/api"
  routes:
    - path: /users
      targets:
        - url: http://user-service:8080
          weight: 1
      strip_prefix: true
      enabled: true
  discovery:
    enabled: true
  dashboard:
    enabled: true
    base_path: /gateway
  openapi:
    enabled: true
```
