Help us shape the future of Go! Take the 2026 Go Developer Survey

kernel

package
v0.11.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 20, 2026 License: AGPL-3.0 Imports: 33 Imported by: 0

Documentation

Overview

Package kernel defines the Module contract and boots GoRun: it opens the database, runs each module's migrations, lets modules register into shared registries, and assembles the HTTP handler.

Index

Constants

View Source
const (
	ServiceAuth           = "auth"            // auth.Service
	ServiceAPIKeys        = "auth.keys"       // auth.KeyService
	ServiceRoles          = "auth.roles"      // auth.RoleService
	ServiceAdminTemplates = "admin.templates" // *httpx.Templates (admin renderer)

	ServiceContent   = "content"   // content.Service
	ServiceTaxonomy  = "taxonomy"  // taxonomy.Service
	ServiceMedia     = "media"     // media.Service
	ServiceMenus     = "menus"     // menus.Service
	ServiceRedirects = "redirects" // redirects.Service
	ServiceSEO       = "seo"       // seo.Service
	ServiceTheme     = "theme"     // theme.Service
	ServicePattern   = "pattern"   // pattern.Service (layout packages)
	ServiceCommerce  = "commerce"  // commerce service (wave 2)
	ServiceWebhooks  = "webhooks"  // webhooks.Service

	ServiceSchema = "kernel.schema" // kernel.SchemaInspector (set by the kernel after every module migrated)
)

Names under which modules publish their services in Deps.Services (Services.Set in Register; Get[T](d, name) to consume).

View Source
const DataLockFile = "gorun.lock"

DataLockFile is the lock file inside the data directory. The process that runs job workers (gorun serve, gorun mcp --jobs, reindex without --no-jobs) holds an exclusive OS lock on it for its whole lifetime.

View Source
const ServiceNavigationPins = "admin.navigation_pins"
View Source
const SettingBaseURL = "site.base_url"

SettingBaseURL is the admin-editable public base URL; it wins over Config.BaseURL (GORUN_BASE_URL).

Variables

View Source
var BootOrder = []string{"db", "settings", "i18n", "auth", "audit", "jobs", "content", "taxonomy", "media", "menus", "redirects", "seo", "theme", "pattern", "builder", "site", "api", "admin", "webhooks", "commerce", "saas", "membership", "launch", "workagents", "wpmigrate", "mcp", "ext"}

BootOrder is the canonical module order. A module may only import modules earlier in this list (enforced by importorder_test.go); Boot rejects module lists that violate it. Packages not listed (kernel, config, hooks, httpx…) and the kernel infrastructure (db, settings, i18n, audit, jobs) are importable by every module but never import domain modules. A downstream build adds its own modules with ExtendBootOrder.

View Source
var ErrDataDirLocked = errors.New("data directory is in use by another GoRun process")

ErrDataDirLocked is returned (wrapped) when another process holds the data-directory lock.

View Source
var LogOutput io.Writer = os.Stderr

LogOutput is where Boot's logger writes (tests set io.Discard).

Functions

func BaseURL

func BaseURL(ctx context.Context, d *Deps) string

BaseURL is the one source of the site's public base URL (no trailing slash): the site.base_url setting when set, else Config.BaseURL. Use it for absolute links and the redirects own-host check. Never for cookie Secure flags or HSTS: those follow the request (httpx.SecureCookie, httpx.RequestIsHTTPS), so a wrong setting cannot lock admins out.

func BaseURLOf

func BaseURLOf(ctx context.Context, st *settings.Store, configured string) string

BaseURLOf is BaseURL for code that holds the settings store and the configured base URL instead of *Deps.

func ExtendBootOrder

func ExtendBootOrder(after string, names ...string)

ExtendBootOrder inserts names into BootOrder right after the module named after (at the end when after is empty or unknown), keeping their order. Names already in BootOrder are skipped, so calling it again is harmless. Call it before Boot, typically from a binary's main.

func Get

func Get[T any](d *Deps, name string) (T, bool)

Get returns the service registered under name if it has type T.

func InspectSchema

func InspectSchema(ctx context.Context, d *sql.DB, mods []Module) (db.SchemaInspection, error)

InspectSchema reports applied migrations unknown to this binary (the schema is ahead of it) and applied files whose content changed. mods is the module list as given to Boot or MigrateAll. Rows of a BootOrder module missing from mods are not reported: a partial list (CLI commands that boot only settings) cannot judge them.

func MigrateAll

func MigrateAll(ctx context.Context, d *sql.DB, mods []Module) error

MigrateAll validates the module list like Boot, then applies kernel infrastructure migrations (audit, jobs, diagnostics) and every module's migrations, in order. Used by `gorun migrate`.

Types

type APIContract

type APIContract struct {
	SuccessStatuses    []int
	RequestContentType string
	MaxBodyBytes       int64
	Limits             map[string]any
	Errors             []APIErrorResponse
}

APIContract describes transport details that cannot be inferred from Go types.

type APIErrorResponse

type APIErrorResponse struct {
	Status            int
	Code, Description string
}

type APIOperation

type APIOperation struct {
	Method, Path, Summary string
	Tags                  []string
	Scopes                []string
	Request, Response     any
	Params                []APIParam
	Handler               http.HandlerFunc
	Contract              *APIContract
}

APIOperation is one REST operation; Path is relative to the API mount ("/v1/content"). Request/Response are example values for OpenAPI schemas.

type APIParam

type APIParam struct {
	Name, In, Type, Description string
	Required                    bool
}

APIParam documents a path/query/header parameter.

type APIRegistry

type APIRegistry struct {
	// contains filtered or unexported fields
}

APIRegistry collects REST operations (consumed by the api module for routing and OpenAPI generation).

func NewAPIRegistry

func NewAPIRegistry() *APIRegistry

NewAPIRegistry returns an empty registry.

func (*APIRegistry) Add

func (r *APIRegistry) Add(op APIOperation)

Add registers op; Method+Path must be unique.

func (*APIRegistry) Operations

func (r *APIRegistry) Operations() []APIOperation

Operations returns the operations in registration order.

type AdminRegistry

type AdminRegistry struct {
	// contains filtered or unexported fields
}

AdminRegistry collects admin navigation and dashboard widgets.

func NewAdminRegistry

func NewAdminRegistry() *AdminRegistry

NewAdminRegistry returns an empty registry.

func (*AdminRegistry) AddCanvasWidget

func (a *AdminRegistry) AddCanvasWidget(widget CanvasWidget)

func (*AdminRegistry) AddNav

func (a *AdminRegistry) AddNav(item NavItem)

AddNav adds a navigation item; IDs must be unique.

func (*AdminRegistry) AddNavProvider

func (a *AdminRegistry) AddNavProvider(fn func(ctx context.Context) []NavItem)

AddNavProvider adds a function evaluated on every Nav call (each admin request) for entries that change at runtime, e.g. one per content type. Its items go through the same permission filter and sort; an item whose ID is already registered with AddNav is skipped.

func (*AdminRegistry) AddWidget

func (a *AdminRegistry) AddWidget(id string, render func(ctx context.Context, r *http.Request) (template.HTML, error))

AddWidget adds a dashboard widget; IDs must be unique.

func (*AdminRegistry) CanvasWidgets

func (a *AdminRegistry) CanvasWidgets() []CanvasWidget

func (*AdminRegistry) Nav

func (a *AdminRegistry) Nav(ctx context.Context) []NavItem

Nav returns the items visible in ctx (static and provided), sorted by Position then ID.

func (*AdminRegistry) SetNavFilter added in v0.4.0

func (a *AdminRegistry) SetNavFilter(fn func(ctx context.Context, items []NavItem) []NavItem)

SetNavFilter installs a post-permission filter (e.g. workspace mode). The filter runs after permission checks and before sorting is finalized; it may drop items but should not invent IDs already filtered out.

func (*AdminRegistry) SetPermissionCheck

func (a *AdminRegistry) SetPermissionCheck(can func(ctx context.Context, perm string) bool)

SetPermissionCheck installs the function Nav uses to hide items whose Permission the current principal lacks (set by the auth module).

func (*AdminRegistry) Widgets

func (a *AdminRegistry) Widgets() []Widget

Widgets returns the dashboard widgets in registration order.

type App

type App struct {
	Deps    *Deps
	Handler http.Handler
	Close   func()
}

App is a booted GoRun instance. Close stops the job workers, waits for them, then closes the database; it is safe to call more than once.

func Boot

func Boot(ctx context.Context, cfg config.Config, mods []Module) (*App, error)

Boot opens the database and wires GoRun in this order: migrate infrastructure → settings module migrate+register → load i18n (core catalog plus every CatalogProvider module) → remaining modules migrate+register in the given order → mount routers → start job workers. mods must contain the settings module and follow BootOrder.

func BootWithOptions

func BootWithOptions(ctx context.Context, cfg config.Config, mods []Module, opts BootOptions) (*App, error)

BootWithOptions is Boot with options (see BootOptions).

type BootOptions

type BootOptions struct {
	// StartJobs starts the job workers and cron scheduler (Boot does). CLI
	// commands that may run beside a live server (reindex --no-jobs) leave
	// it false: workers would requeue the server's running jobs as stale
	// and duplicate its cron ticks. Jobs enqueued meanwhile wait for the
	// server's workers.
	//
	// A process that starts job workers first takes the exclusive
	// data-directory lock (<data>/gorun.lock, see LockDataDir) and refuses
	// to boot when another process holds it; App.Close releases it.
	StartJobs bool
}

BootOptions tunes BootWithOptions.

type CanvasMetric

type CanvasMetric struct {
	Label string `json:"label"`
	Value string `json:"value"`
}

type CanvasWidget

type CanvasWidget struct {
	ID, Href, Permission, NavID string
	Load                        func(*http.Request) (CanvasWidgetData, error)
}

CanvasWidget is an optional module-owned read-only projection. The admin validates board ownership, permission and visible navigation before Load.

type CanvasWidgetData

type CanvasWidgetData struct {
	ID        string            `json:"id"`
	Title     string            `json:"title"`
	Href      string            `json:"href"`
	Metrics   []CanvasMetric    `json:"metrics"`
	Rows      []CanvasWidgetRow `json:"rows"`
	UpdatedAt string            `json:"updated_at"`
}

type CanvasWidgetRow

type CanvasWidgetRow struct {
	Title  string `json:"title"`
	Detail string `json:"detail"`
	Href   string `json:"href"`
}

type CatalogProvider

type CatalogProvider interface {
	Catalogs() fs.FS
}

CatalogProvider is optionally implemented by modules that ship translations. The FS holds "catalog/<module>.<locale>.json" files; they are loaded before any non-settings module registers.

type DataLock

type DataLock struct {
	// contains filtered or unexported fields
}

DataLock is a held data-directory lock. The OS releases it when the process exits, even after a crash, so a leftover gorun.lock file never blocks a restart.

func LockDataDir

func LockDataDir(dir string) (*DataLock, error)

LockDataDir takes the exclusive, non-blocking lock on <dir>/gorun.lock, creating dir and the file when needed, and records this process (pid, host, start time) in the file for the error message another process sees. It fails with ErrDataDirLocked when the lock is already held.

func (*DataLock) Path

func (l *DataLock) Path() string

Path is the lock file.

func (*DataLock) Release

func (l *DataLock) Release() error

Release clears the holder record and releases the lock. The file is kept: removing it could let two processes lock different inodes. Safe to call more than once and on a nil lock.

type Deps

type Deps struct {
	DB       *sql.DB
	Hooks    *hooks.Registry
	Jobs     *jobs.Queue
	Settings *settings.Store
	I18n     *i18n.Bundle // nil only while the settings module registers
	Log      *slog.Logger
	Config   config.Config
	Router   *Router
	Admin    *AdminRegistry
	Theme    *ThemeRegistry
	API      *APIRegistry
	MCP      *MCPRegistry
	Perms    *PermissionRegistry
	Audit    *audit.Writer
	Services *ServiceRegistry // Set(name, v) / Get[T](d, name): reach earlier modules' services without import cycles
}

Deps is the set of shared services handed to every module's Register.

type KindRequest

type KindRequest struct{ Kind, Type, Slug, Template, Taxonomy, Term string }

KindRequest describes what the theme should render; candidate functions map it to an ordered list of template names (most specific first).

type MCPRegistry

type MCPRegistry struct {
	// contains filtered or unexported fields
}

MCPRegistry collects MCP tools.

func NewMCPRegistry

func NewMCPRegistry() *MCPRegistry

NewMCPRegistry returns an empty registry.

func (*MCPRegistry) AddTool

func (r *MCPRegistry) AddTool(t MCPTool)

AddTool registers t; names must be unique.

func (*MCPRegistry) Tools

func (r *MCPRegistry) Tools() []MCPTool

Tools returns the tools in registration order.

type MCPTool

type MCPTool struct {
	Name, Description string
	Input             any
	Scopes            []string
	Sensitive         bool
	Handler           func(ctx context.Context, args json.RawMessage) (any, error)
}

MCPTool is one MCP tool. Sensitive tools require human approval.

type Module

type Module interface {
	Name() string
	Migrations() fs.FS
	Register(d *Deps) error
}

Module is a pluggable domain. Migrations returns an FS whose files live at "migrations/NNNN_name.sql" (nil when the module has no tables). Register wires the module into Deps; it runs once, in BootOrder.

type NavItem struct {
	ID, LabelKey, Icon, Href, Permission, Group string
	Position                                    int
}

NavItem is an admin navigation entry. LabelKey is an i18n key.

type NavigationPins interface {
	PinnedNavigation(context.Context) ([]string, error)
}

NavigationPins provides personal dashboard placements without coupling auth to the optional admin module's tables. Pins remain hidden after preference reset.

type OptionalModule

type OptionalModule interface {
	Enabled(context.Context, *sql.DB) (bool, error)
}

OptionalModule is evaluated before any module migrations, catalog loading or route registration. Disabled modules retain their existing data untouched. Enabled must only read the database; an empty database is valid.

type Permission

type Permission struct {
	Name  string
	Label map[string]string
}

Permission is a declared permission with labels by locale.

type PermissionRegistry

type PermissionRegistry struct {
	// contains filtered or unexported fields
}

PermissionRegistry collects permissions declared by modules.

func NewPermissionRegistry

func NewPermissionRegistry() *PermissionRegistry

NewPermissionRegistry returns an empty registry.

func (*PermissionRegistry) All

func (p *PermissionRegistry) All() []Permission

All returns every permission sorted by name.

func (*PermissionRegistry) Declare

func (p *PermissionRegistry) Declare(perm string, label map[string]string)

Declare adds perm; declaring it again merges the labels.

type Router

type Router struct {
	Public, Admin, API, Webhooks chi.Router
	// contains filtered or unexported fields
}

Router holds the chi sub-routers modules register on during Register. Use may be called on any of them at any point during Register, even after earlier modules added routes: middlewares are collected and wrap every route of that router, in Use order, from the first request on (Use after serving starts panics). Boot mounts the routers on the root mux after every module has registered.

Mount prefixes: Public "/", Admin "/admin", API "/api" (modules add "/v1/..."; the spec's "/api/docs" sits outside v1), Webhooks "/webhooks". Session/CSRF/permission middleware on Admin and key-or-session on API are installed by the auth module via Admin.Use / API.Use; Webhooks stays raw. Admin and API deny every request (503) until SetAuthenticator is called. The root mux also serves GET /healthz (liveness), GET /readyz (DB ping), and optionally GET /metrics (Prometheus text when GORUN_METRICS is on), outside every sub-router.

func (*Router) Authenticator

func (r *Router) Authenticator() string

Authenticator returns the name passed to SetAuthenticator, or "".

func (*Router) SetAuthenticator

func (r *Router) SetAuthenticator(name string)

SetAuthenticator records that a module (the auth module, in its Register) has installed authentication middleware on Admin and API. Until it is called, Boot's guard answers every Admin and API request with 503 "authentication module not installed" (deny by default).

type SchemaInspector

type SchemaInspector func(context.Context) (db.SchemaInspection, error)

SchemaInspector compares the live schema_migrations table with the migrations this binary ships. Boot publishes one under ServiceSchema; System Health calls it per request, so it sees a newer binary migrating the database while this one runs.

type ServiceRegistry

type ServiceRegistry struct {
	// contains filtered or unexported fields
}

ServiceRegistry lets later modules reach earlier modules' services by name (e.g. "content" → content.Service) without import cycles.

func NewServiceRegistry

func NewServiceRegistry() *ServiceRegistry

NewServiceRegistry returns an empty registry.

func (*ServiceRegistry) Set

func (s *ServiceRegistry) Set(name string, v any)

Set registers v under name, replacing any previous value.

type ThemeRegistry

type ThemeRegistry struct {
	// contains filtered or unexported fields
}

ThemeRegistry collects template funcs and template-hierarchy kinds.

func NewThemeRegistry

func NewThemeRegistry() *ThemeRegistry

NewThemeRegistry returns an empty registry.

func (*ThemeRegistry) AddFunc

func (t *ThemeRegistry) AddFunc(name string, fn any)

AddFunc adds a template func; fn must be a function and name unique.

func (*ThemeRegistry) AddKind

func (t *ThemeRegistry) AddKind(kind string, candidates func(req KindRequest) []string)

AddKind registers the candidate-template function for kind.

func (*ThemeRegistry) Candidates

func (t *ThemeRegistry) Candidates(req KindRequest) []string

Candidates returns the template names for req.Kind, or nil if the kind is not registered.

func (*ThemeRegistry) Funcs

func (t *ThemeRegistry) Funcs() template.FuncMap

Funcs returns a copy of the registered template funcs.

type Widget

type Widget struct {
	ID     string
	Render func(ctx context.Context, r *http.Request) (template.HTML, error)
}

Widget is an admin dashboard widget.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL