Ctrl Plane

Forge Extension

Mount Ctrl Plane into a Forge application with shared auth and routing.

Ctrl Plane can run as a Forge extension. This lets you embed instance management into a larger Forge-based application and share authentication, middleware, and the HTTP server.

Basic setup

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)
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:

CategoryPrecedence
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
AuthProviderAlways 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:

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

OptionPurpose
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 for authentication, connect it through the adapter:

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:

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

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

On this page