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
- Variables
- func BaseURL(ctx context.Context, d *Deps) string
- func BaseURLOf(ctx context.Context, st *settings.Store, configured string) string
- func ExtendBootOrder(after string, names ...string)
- func Get[T any](d *Deps, name string) (T, bool)
- func InspectSchema(ctx context.Context, d *sql.DB, mods []Module) (db.SchemaInspection, error)
- func MigrateAll(ctx context.Context, d *sql.DB, mods []Module) error
- type APIContract
- type APIErrorResponse
- type APIOperation
- type APIParam
- type APIRegistry
- type AdminRegistry
- func (a *AdminRegistry) AddCanvasWidget(widget CanvasWidget)
- func (a *AdminRegistry) AddNav(item NavItem)
- func (a *AdminRegistry) AddNavProvider(fn func(ctx context.Context) []NavItem)
- func (a *AdminRegistry) AddWidget(id string, ...)
- func (a *AdminRegistry) CanvasWidgets() []CanvasWidget
- func (a *AdminRegistry) Nav(ctx context.Context) []NavItem
- func (a *AdminRegistry) SetNavFilter(fn func(ctx context.Context, items []NavItem) []NavItem)
- func (a *AdminRegistry) SetPermissionCheck(can func(ctx context.Context, perm string) bool)
- func (a *AdminRegistry) Widgets() []Widget
- type App
- type BootOptions
- type CanvasMetric
- type CanvasWidget
- type CanvasWidgetData
- type CanvasWidgetRow
- type CatalogProvider
- type DataLock
- type Deps
- type KindRequest
- type MCPRegistry
- type MCPTool
- type Module
- type NavItem
- type NavigationPins
- type OptionalModule
- type Permission
- type PermissionRegistry
- type Router
- type SchemaInspector
- type ServiceRegistry
- type ThemeRegistry
- type Widget
Constants ¶
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 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).
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.
const SettingBaseURL = "site.base_url"
SettingBaseURL is the admin-editable public base URL; it wins over Config.BaseURL (GORUN_BASE_URL).
Variables ¶
var BootOrder = []string{"db", "settings", "i18n", "auth", "audit", "jobs", "content", "taxonomy", "media", "menus", "redirects", "seo", "theme", "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.
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.
var LogOutput io.Writer = os.Stderr
LogOutput is where Boot's logger writes (tests set io.Discard).
Functions ¶
func BaseURL ¶
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 ¶
BaseURLOf is BaseURL for code that holds the settings store and the configured base URL instead of *Deps.
func ExtendBootOrder ¶
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 InspectSchema ¶
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.
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 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 APIRegistry ¶
type APIRegistry struct {
// contains filtered or unexported fields
}
APIRegistry collects REST operations (consumed by the api module for routing and OpenAPI generation).
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 ¶
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 ¶
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 CanvasWidget ¶
type CanvasWidget struct {
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 CatalogProvider ¶
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 ¶
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.
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 (*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 ¶
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 NavigationPins ¶
type NavigationPins interface {
}
NavigationPins provides personal dashboard placements without coupling auth to the optional admin module's tables. Pins remain hidden after preference reset.
type OptionalModule ¶
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 ¶
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.
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 ¶
Authenticator returns the name passed to SetAuthenticator, or "".
func (*Router) SetAuthenticator ¶
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.