Forge Extension
Integrate Bastion as a Forge extension with auto-configuration and DI container binding.
Bastion ships a first-class 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
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:
- Loads configuration from YAML (merged with programmatic options).
- Initializes the proxy engine, route manager, circuit breakers, and health monitor.
- Auto-resolves the
discoveryextension from the DI container for FARP service discovery. - Registers the admin REST API and dashboard routes.
- Provides the
*bastion.Gatewayinstance to the DI container via Vessel. - 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:
# 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:
- YAML file (
bastionkey). - Programmatic options (
ConfigOptionfunctions passed toNew()). - 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:
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:
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:
- Resolves the
discovery.Servicefrom the DI container. - Wraps it in an adapter that implements
bastion.DiscoveryService. - 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:
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:
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.
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
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:
# 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