Cortex

Forge Extension

Mount Cortex into a Forge application as a first-class extension with automatic route registration, migrations, and DI.

Cortex 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

import "github.com/xraph/cortex/extension"

Registering the extension

package main

import (
    "github.com/xraph/forge"
    "github.com/xraph/cortex/extension"
    pgstore "github.com/xraph/cortex/store/postgres"
)

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

    cortexExt := extension.New(
        extension.WithStore(pgstore.New(bunDB)),
    )

    app.RegisterExtension(cortexExt)
    app.Run()
}

What the extension does

Lifecycle eventBehaviour
RegisterCreates the engine, builds the API handler, registers the engine in the DI container via vessel.Provide, mounts all HTTP routes (unless disabled)
StartRuns store.Migrate(ctx) (unless disabled), then calls engine.Start(ctx)
StopCalls engine.Stop(ctx) which emits OnShutdown to all plugins
HealthCalls store.Ping(ctx) to verify database connectivity

Extension options

OptionTypeDefaultDescription
WithStore(s)store.StoreRequired. Composite persistence store
WithExtension(x)plugin.ExtensionRegister a Cortex plugin (lifecycle hooks)
WithEngineOption(o)engine.OptionPass any engine option through
WithConfig(c)extension.ConfigSet the full extension config
WithDisableRoutes()falseSkip HTTP route registration
WithDisableMigrate()falseSkip auto-migration on Start
WithBasePath(p)string""URL prefix for all Cortex routes
WithLogger(l)*slog.Loggerslog.Default()Structured logger

Extension config struct

type Config struct {
    DisableRoutes  bool   // Skip HTTP route registration
    DisableMigrate bool   // Skip auto-migration on start
    BasePath       string // URL prefix for all cortex routes
}

Disabling auto-migration

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

cortexExt := extension.New(
    extension.WithStore(store),
    extension.WithDisableMigrate(),
)

Disabling routes

If you only need the engine (e.g. using Cortex programmatically from another extension), disable route registration:

cortexExt := extension.New(
    extension.WithStore(store),
    extension.WithDisableRoutes(),
)

Accessing the engine from other extensions

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

eng := cortexExt.Engine()
// Use eng.CreateAgent, eng.ListSkills, etc. from another extension

The engine is also available via Forge's DI container (powered by vessel):

import "github.com/xraph/cortex/engine"

// Inside another extension's Register:
var eng *engine.Engine
if err := vessel.Resolve(fapp.Container(), &eng); err != nil {
    return err
}

Tenant middleware

In a Forge app, tenant scope is typically set by middleware that reads the JWT or API key:

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 := cortex.WithTenant(r.Context(), tenantID)
        ctx = cortex.WithApp(ctx, appID)
        next.ServeHTTP(w, r.WithContext(ctx))
    })
}

router.Use(tenantMiddleware)

Adding plugins

Register Cortex plugins (observability, audit hooks, etc.) alongside the extension:

import (
    "github.com/xraph/cortex/extension"
    "github.com/xraph/cortex/observability"
    audithook "github.com/xraph/cortex/audit_hook"
)

metricsPlugin := observability.NewMetricsExtension()
auditPlugin := audithook.New(myRecorder)

cortexExt := extension.New(
    extension.WithStore(store),
    extension.WithExtension(metricsPlugin),
    extension.WithExtension(auditPlugin),
)

Health checks

The extension implements forge.Extension's Health method by pinging the store:

func (e *Extension) Health(ctx context.Context) error {
    return e.eng.Store().Ping(ctx)
}

Forge surfaces this automatically in its health endpoint.

Complete example

package main

import (
    "log/slog"
    "os"

    "github.com/uptrace/bun"
    "github.com/xraph/forge"
    "github.com/xraph/cortex/extension"
    "github.com/xraph/cortex/observability"
    pgstore "github.com/xraph/cortex/store/postgres"
)

func main() {
    logger := slog.New(slog.NewJSONHandler(os.Stdout, nil))
    app := forge.New()

    db := connectDB() // your *bun.DB connection
    store := pgstore.New(db)

    cortexExt := extension.New(
        extension.WithStore(store),
        extension.WithExtension(observability.NewMetricsExtension()),
        extension.WithLogger(logger),
    )

    app.RegisterExtension(cortexExt)
    app.Run()
}

This gives you a production-ready Cortex deployment with:

  • All 36 REST endpoints mounted
  • Auto-migration on startup
  • Health checks via store.Ping
  • Prometheus-compatible metrics
  • Graceful shutdown with plugin notification

On this page