---
title: Forge Extension
description: Mount Weave into a Forge application as a first-class extension with automatic route registration and migrations.
---

Weave ships a ready-made Forge extension in the `extension` package. It wires the engine, HTTP API, and lifecycle management into Forge's extension system with one call.

## Installation

```go
import "github.com/xraph/weave/extension"
```

## Registering the extension

```go
package main

import (
    "github.com/xraph/forge"
    "github.com/xraph/weave/extension"
)

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

    weaveExt := extension.New(
        extension.WithGroveDatabase(""), // resolve default grove.DB from DI
    )

    app.RegisterExtension(weaveExt)
    app.Run()
}
```

Or with explicit stores:

```go
weaveExt := extension.New(
    extension.WithStore(pgstore.New(db)),
    extension.WithVectorStore(pgvec.New(db)),
    extension.WithEmbedder(embedder.NewOpenAI(
        embedder.WithOpenAIKey(os.Getenv("OPENAI_API_KEY")),
    )),
)
```

## What the extension does

| Lifecycle event | Behaviour |
|----------------|-----------|
| `Register` | Creates the engine from the provided options |
| `Start` | Runs `store.Migrate` and `vectorstore.Migrate` (unless disabled) |
| `RegisterRoutes` | Mounts all Weave HTTP endpoints under the base path |
| `Stop` | Calls `engine.Stop` -- emits `OnShutdown` to all plugins |

## Extension options

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `WithStore(s)` | `store.Store` | -- | MetadataStore (auto-resolved from grove if not set) |
| `WithVectorStore(v)` | `vectorstore.VectorStore` | -- | VectorStore |
| `WithExtension(x)` | `plugins.Extension` | -- | Lifecycle hook plugin (repeatable) |
| `WithEngineOption(opt)` | `engine.Option` | -- | Pass engine option directly |
| `WithConfig(cfg)` | `Config` | defaults | Full config struct |
| `WithDisableRoutes()` | -- | `false` | Skip HTTP route registration |
| `WithDisableMigrate()` | -- | `false` | Skip migrations on Start |
| `WithBasePath(path)` | `string` | `"/weave"` | URL prefix for all weave routes |
| `WithGroveDatabase(name)` | `string` | `""` | Name of the grove.DB to resolve from DI |
| `WithRequireConfig(b)` | `bool` | `false` | Require config in YAML files |

## File-based configuration (YAML)

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

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

### Example YAML config

```yaml
# forge.yaml
extensions:
  weave:
    disable_routes: false
    disable_migrate: false
    base_path: "/weave"
    default_chunk_size: 512
    default_chunk_overlap: 50
    default_embedding_model: "text-embedding-3-small"
    default_chunk_strategy: "recursive"
    default_top_k: 10
    shutdown_timeout: "30s"
    ingest_concurrency: 4
    grove_database: ""
```

Or using the top-level shorthand:

```yaml
# forge.yaml
weave:
  default_chunk_size: 1024
  default_embedding_model: "text-embedding-3-large"
  ingest_concurrency: 8
```

### 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` | `"/weave"` | URL prefix for all weave routes |
| `default_chunk_size` | `int` | `512` | Default tokens per chunk |
| `default_chunk_overlap` | `int` | `50` | Default token overlap between chunks |
| `default_embedding_model` | `string` | `"text-embedding-3-small"` | Default embedding model |
| `default_chunk_strategy` | `string` | `"recursive"` | Default chunking strategy |
| `default_top_k` | `int` | `10` | Default number of similarity search results |
| `shutdown_timeout` | `duration` | `"30s"` | Max graceful shutdown wait time |
| `ingest_concurrency` | `int` | `4` | Parallel ingest operations |
| `grove_database` | `string` | `""` | Named grove.DB to resolve from DI |

### Merge behaviour

File-based configuration is merged with programmatic options. Programmatic options set via `extension.New(...)` take precedence over YAML values. This lets you set sensible defaults in YAML while overriding specific values in code.

### Requiring configuration

If your deployment requires YAML config to be present, use `WithRequireConfig`:

```go
ext := extension.New(
    extension.WithRequireConfig(true), // error if no YAML config found
)
```

## Disabling auto-migration

If you manage migrations separately (e.g. with a migration tool), disable auto-migration:

```go
weaveExt := extension.New(
    extension.WithStore(store),
    extension.WithDisableMigrate(),
)
```

## Accessing the engine from other extensions

After `Register` is called by Forge, `weaveExt.Engine()` returns the fully initialised engine:

```go
eng := weaveExt.Engine()
// use eng.Ingest, eng.Retrieve, etc. from another extension or handler
```

## Tenant middleware

In a Forge app, tenant scope is typically set by middleware that reads the JWT or API key. Implement a Forge middleware that calls `weave.WithTenant` and `weave.WithApp` before passing the request to the Weave handlers:

```go
func tenantMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        tenantID := r.Header.Get("X-Tenant-ID")
        appID := r.Header.Get("X-App-ID")
        ctx := weave.WithTenant(r.Context(), tenantID)
        ctx = weave.WithApp(ctx, appID)
        next.ServeHTTP(w, r.WithContext(ctx))
    })
}

router.Use(tenantMiddleware)
```

## Grove database integration

When your Forge app uses the [Grove extension](https://github.com/xraph/grove) to manage database connections, Weave 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 := extension.New(
    extension.WithGroveDatabase(""),
    extension.WithVectorStore(pgvec.New(pgxPool)),
    extension.WithEmbedder(embedder.NewOpenAI(
        embedder.WithOpenAIKey(os.Getenv("OPENAI_API_KEY")),
    )),
)
```

### Using a named grove database

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

```go
ext := extension.New(
    extension.WithGroveDatabase("weave"),
    extension.WithVectorStore(pgvec.New(pgxPool)),
    extension.WithEmbedder(embedder.NewOpenAI(
        embedder.WithOpenAIKey(os.Getenv("OPENAI_API_KEY")),
    )),
)
```

This resolves the grove.DB named `"weave"` 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.

## Adding metrics

Register the observability extension alongside the Weave engine extension:

```go
import "github.com/xraph/weave/observability"

metricsExt := observability.NewMetricsExtensionWithFactory(fapp.Metrics())

weaveExt := extension.New(
    extension.WithStore(store),
    extension.WithVectorStore(vecStore),
    extension.WithExtension(metricsExt), // passed to engine.New internally
)
```
