---
title: Forge Extension
description: Mount Ctrl Plane into a Forge application with shared auth and routing.
---

Ctrl Plane can run as a [Forge](https://github.com/xraph/forge) extension. This lets you embed instance management into a larger Forge-based application and share authentication, middleware, and the HTTP server.

## Basic setup

```go
package main

import (
    "github.com/xraph/forge"
    cpext "github.com/xraph/ctrlplane/extension"
    "github.com/xraph/ctrlplane/provider/docker"
)

func main() {
    dockerProv, _ := docker.New(docker.Config{
        Host: "unix:///var/run/docker.sock",
    })

    app := forge.New()
    app.Use(cpext.New(
        cpext.WithProvider("docker", dockerProv),
    ))
    app.Run()
}
```

The extension registers itself with Forge and mounts the Ctrl Plane HTTP API at the configured base path.

## File-based configuration

When running inside Forge, Ctrl Plane automatically loads configuration from the application's YAML config file. The extension looks for settings under two keys (in order):

1. `extensions.ctrlplane` (namespaced, recommended)
2. `ctrlplane` (legacy shorthand)

```yaml title="forge.yaml"
extensions:
  ctrlplane:
    database_url: "postgres://localhost:5432/ctrlplane"
    default_provider: "k8s"
    base_path: "/ctrlplane"
    health_interval: "30s"
    telemetry_flush_interval: "10s"
    max_instances_per_tenant: 100
    audit_enabled: true
    disable_routes: false
    disable_migrate: false
```

### Merge behavior

File-based and programmatic configuration are merged together with these rules:

| Category | Precedence |
|----------|------------|
| Boolean flags (`disable_routes`, `disable_migrate`) | Programmatic `true` wins |
| String fields (`base_path`, `database_url`, `default_provider`) | YAML takes precedence; programmatic fills gaps |
| Duration/int fields (`health_interval`, `max_instances_per_tenant`, etc.) | YAML takes precedence; programmatic fills gaps |
| `AuthProvider` | Always programmatic (cannot be set from YAML) |

Any remaining zero-valued fields are filled from `DefaultConfig()`.

### Requiring file config

If your deployment relies on the config file being present, use `WithRequireConfig`:

```go
app.Use(cpext.New(
    cpext.WithRequireConfig(true),
))
```

When `RequireConfig` is `true` and no config is found under either key, `Register` returns an error.

## Extension options

| Option | Purpose |
|--------|---------|
| `WithAuthProvider(p)` | Set the auth provider |
| `WithProvider(name, p)` | Register a cloud provider |
| `WithBasePath(path)` | Set the URL prefix (default: `/ctrlplane`) |
| `WithConfig(cfg)` | Set extension configuration directly |
| `WithStore(opt)` | Pass a store option to the underlying Ctrl Plane |
| `WithExtension(x)` | Register a plugin extension (lifecycle hooks) |
| `WithDisableRoutes()` | Disable automatic route registration |
| `WithDisableMigrate()` | Disable automatic database migration on Register |
| `WithRequireConfig(bool)` | Require config to be present in YAML files |

## With Authsome

If you're using [Authsome](https://github.com/xraph/authsome) for authentication, connect it through the adapter:

```go
package main

import (
    "github.com/xraph/forge"
    "github.com/xraph/authsome"
    cpext "github.com/xraph/ctrlplane/extension"
    authadapter "github.com/xraph/ctrlplane/auth/authsome"
)

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

    // Mount Authsome first
    authProvider := authsome.New(/* ... */)
    app.Use(authProvider.Extension())

    // Mount Ctrl Plane with the Authsome adapter
    app.Use(cpext.New(
        cpext.WithAuthProvider(authadapter.New(authProvider)),
        cpext.WithProvider("k8s", kubernetesProvider),
    ))

    app.Run()
}
```

The Authsome adapter translates Authsome's user and session model into Ctrl Plane's `auth.Claims` format.

## Lifecycle

The extension implements the `forge.Extension` interface:

1. **`Register(fapp)`** -- Loads configuration from YAML files (or programmatic defaults), initializes the Ctrl Plane instance, runs migrations, registers routes, and provides the instance to the DI container.
2. **`Start(ctx)`** -- Starts background workers.
3. **`Stop(ctx)`** -- Gracefully shuts down background workers.
4. **`Health(ctx)`** -- Returns an error if the extension is not initialized.

Forge calls these methods in order during its own startup and shutdown sequence.

## Accessing the Ctrl Plane

If you need to call Ctrl Plane services from other parts of your Forge application:

```go
ext := cpext.New(/* ... */)
app.Use(ext)

// After init, access the underlying Ctrl Plane
cp := ext.CtrlPlane()
instance, err := cp.Instances.Get(ctx, instanceID)
```
