---
title: Forge Extension
description: Use Warden as a Forge extension with DI, routes, and lifecycle management.
---

Warden provides a first-class Forge extension that handles lifecycle, dependency injection, and API route registration.

## Registration

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

app := forge.New()
app.Use(wardenext.New(
    wardenext.WithConfig(wardenext.Config{
        DisableRoutes:  false,  // Set true to skip API route registration
        DisableMigrate: false,  // Set true to skip auto-migration
    }),
))
app.Start(context.Background())
```

## What the Extension Does

### Register Phase

1. Resolves a `store.Store` from the Forge DI container
2. Creates a `warden.Engine` with the store and any configured options
3. Registers the engine in the DI container via `vessel.Provide()`
4. If routes are enabled, registers REST API endpoints on `forge.Router`

### Start Phase

1. Runs database migrations (unless `DisableMigrate` is true)
2. Starts the engine

### Stop Phase

1. Gracefully shuts down the engine
2. Fires `Shutdown` hooks on all plugins

## Extension options

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `WithStore(s)` | `store.Store` | -- | Sets the persistence backend directly |
| `WithConfig(cfg)` | `Config` | defaults | Extension configuration |
| `WithEngineOptions(opts...)` | `...warden.Option` | -- | Pass engine-level options |
| `WithPlugin(x)` | `plugin.Plugin` | -- | Register a lifecycle plugin (repeatable) |
| `WithDisableRoutes()` | -- | `false` | Skip HTTP route registration |
| `WithDisableMigrate()` | -- | `false` | Skip migrations on Start |
| `WithBasePath(path)` | `string` | `""` | URL prefix for warden routes |
| `WithGroveDatabase(name)` | `string` | `""` | Named grove.DB to resolve from DI |
| `WithRequireConfig()` | -- | `false` | Require config in YAML files |

## Accessing the Engine

After registration, resolve the engine from any Forge handler:

```go
func myHandler(ctx forge.Context) error {
    eng := forge.Inject[*warden.Engine](ctx)
    result, _ := eng.Check(ctx.Context(), &warden.CheckRequest{
        Subject: warden.Subject{Kind: "user", ID: ctx.Get("userID")},
        Action:  "read",
        ResourceType: "document",
    })
    if !result.Allowed {
        return forge.Forbidden("access denied")
    }
    return ctx.JSON(200, data)
}
```

## Grove database integration

When your Forge app uses the [Grove extension](https://github.com/xraph/grove) to manage database connections, Warden can automatically resolve a `grove.DB` from the DI container and construct the correct store backend based on the driver type.

### Using the default grove database

If the Grove extension registers a single database (or a default in multi-DB mode), use `WithGroveDatabase` with an empty name:

```go
ext := wardenext.New(
    wardenext.WithGroveDatabase(""),
)
```

### Using a named grove database

In multi-database setups, reference a specific database by name:

```go
ext := wardenext.New(
    wardenext.WithGroveDatabase("warden"),
)
```

This resolves the grove.DB named `"warden"` from the DI container and auto-constructs the matching store. The driver type is detected automatically -- you do not need to import individual store packages.

### Store resolution order

The extension resolves its store in this order:

1. **Explicit store** -- if `WithStore(s)` was called, it is used directly and grove is ignored.
2. **Grove database** -- if `WithGroveDatabase(name)` was called, the named or default `grove.DB` is resolved from DI.
3. **In-memory fallback** -- if neither is configured, an in-memory store is used.

## File-based configuration (YAML)

When running as a Forge extension, Warden automatically loads configuration from YAML config files. The extension looks for config under the following keys (in order):

1. `extensions.warden` -- standard Forge extension config namespace
2. `warden` -- top-level shorthand

### Example

```yaml
# forge.yaml
extensions:
  warden:
    disable_routes: false
    disable_migrate: false
    base_path: "/warden"
    max_graph_depth: 10
    grove_database: ""
```

### Config fields

| YAML Key | Type | Default | Description |
|----------|------|---------|-------------|
| `disable_routes` | `bool` | `false` | Skip HTTP route registration |
| `disable_migrate` | `bool` | `false` | Skip migrations on Start |
| `base_path` | `string` | `""` | URL prefix for all routes |
| `max_graph_depth` | `int` | `10` | Max depth for ReBAC graph traversal |
| `grove_database` | `string` | `""` | Named grove.DB from DI |

### Merge behaviour

File-based configuration is merged with programmatic options. Programmatic boolean flags (`DisableRoutes`, `DisableMigrate`) always win when set to `true`. For other fields, YAML values take precedence, then programmatic values, then defaults.

## REST API Endpoints

When routes are enabled, the extension registers these endpoints:

| Method | Path | Description |
|--------|------|-------------|
| `POST` | `/v1/authz/check` | Authorization check |
| `POST` | `/v1/authz/enforce` | Authorization enforce |
| `POST` | `/v1/authz/batch-check` | Batch authorization check |
| `POST/GET/PUT/DELETE` | `/v1/roles/*` | Role management |
| `POST/GET/DELETE` | `/v1/permissions/*` | Permission management |
| `POST/GET/DELETE` | `/v1/assignments/*` | Assignment management |
| `POST/GET` | `/v1/relations/*` | Relation tuple management |
| `POST/GET/PUT/DELETE` | `/v1/policies/*` | Policy management |
| `POST/GET/PUT/DELETE` | `/v1/resource-types/*` | Resource type management |
| `GET` | `/v1/check-logs` | Query check audit logs |

All endpoints include OpenAPI metadata for automatic documentation generation.
