kernel

package module
v1.0.0 Latest Latest
Warning

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

Go to latest
Published: Oct 11, 2026 License: MIT Imports: 10 Imported by: 0

Documentation

Overview

Package kernel is the public contract of Lesstruct v1 ("everything is a module").

Kernel owns the Module lifecycle, the Principal identity shape, the module registry, and the database driver/dialect contracts. It imports nothing from Lesstruct modules or internals, so dependencies always point one way: modules import kernel, the composition root imports everything.

Versioning: the kernel is an independent Go module tagged kernel/vX.Y.Z. Semver rules, stability classes, and the deprecation flow live in docs/kernel-versioning.md. The one rule to internalize: adding a method to an exported interface is a breaking change — prefer new narrow interfaces over widening existing ones.

Terminology: "modules" are compile-time Go modules (Tier 1). "Plugins" are the sandboxed WASM extensions (Tier 2) and live outside this package.

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrDuplicateModule    = errors.New("kernel: duplicate module")
	ErrMissingDependency  = errors.New("kernel: missing module dependency")
	ErrCircularDependency = errors.New("kernel: circular module dependency")
	ErrNilKernel          = errors.New("kernel: host cannot be nil")
	ErrNilModule          = errors.New("kernel: module cannot be nil")
)
View Source
var ErrInvalidPrincipalID = errors.New("kernel: invalid principal ID")

ErrInvalidPrincipalID marks a principal whose ID is not a decimal user ID.

Functions

func Capabilities

func Capabilities[T any](r *Registry) []T

Capabilities collects every registered module implementing capability T in registry name order. It is the discovery mechanism modules use instead of a service locator.

func ContextWithPrincipal

func ContextWithPrincipal(
	ctx context.Context,
	principal Principal,
) context.Context

ContextWithPrincipal stores the principal in the context.

Types

type AdminExtensionManifest

type AdminExtensionManifest struct {
	MenuEntries      []AdminMenuEntry
	SettingsSections []string
	EditorExtensions []string
}

AdminExtensionManifest is the declarative UI a feature module contributes to the admin shell: navigation, settings sections, and editor extensions. One shell renders everything; modules never ship their own mini-SPA.

func (AdminExtensionManifest) Validate

func (m AdminExtensionManifest) Validate() error

Validate ensures a manifest's entries are well-formed: menu entries need a label, an absolute path, and no duplicate paths. It mirrors the shell's own checks so module authors catch malformed manifests in unit tests (see kernel/conformance CheckAdminExtension for the third-party fixture).

type AdminExtensionProvider

type AdminExtensionProvider interface {
	Module
	AdminManifest() AdminExtensionManifest
}

AdminExtensionProvider contributes UI to the admin shell via manifest.

type AdminMenuEntry

type AdminMenuEntry struct {
	Label string
	Path  string
	Icon  string
}

AdminMenuEntry is one navigation item contributed to the admin shell.

type CapabilityResolver

type CapabilityResolver func(principal Principal, capability string) bool

CapabilityResolver maps a principal plus a capability name (for example "content.publish") to an allow/deny decision.

Implemented by the users module. The kernel never resolves roles itself.

type ContentTypeProvider

type ContentTypeProvider interface {
	Module
	PostTypes() []PostTypeDef
}

ContentTypeProvider registers content types (post, page, product, ...).

type DroppedModule

type DroppedModule struct {
	// Name is the dropped module's registry key.
	Name string
	// Direct is true when the module itself was named in disabled, false
	// when it cascaded from a disabled requirement.
	Direct bool
	// Cause names the disabled module that forced the drop: the module
	// itself when Direct, otherwise the unavailable requirement.
	Cause string
}

DroppedModule names one module Prune removed from the candidate list.

type Host

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

Host is the default Kernel implementation handed to Module.Init.

func NewHost

func NewHost(logger Logger) *Host

NewHost creates a Kernel host with a fresh bus and registry.

func (*Host) Logger

func (h *Host) Logger() Logger

Logger returns the shared logger.

func (*Host) Registry

func (h *Host) Registry() *Registry

Registry returns the module registry for capability discovery.

type Kernel

type Kernel interface {
	Logger() Logger
	Registry() *Registry
}

Kernel is the minimal host surface a module receives at Init.

type Logger

type Logger interface {
	Info(format string, args ...any)
	Error(format string, args ...any)
}

Logger is the printf-style logging contract modules use.

Core adapts *util.Logger to it; tests and fixtures may supply any sink. Messages use fmt-style verbs, matching the util.Logger convention.

type Migrator

type Migrator interface {
	Module
	Migrations() fs.FS
}

Migrator ships namespaced migrations as an embedded filesystem.

Layout convention: {sqlite,postgresql,mysql}/NNN_name.{up,down}.sql. The core migration runner selects the subdir via Driver.MigrationDir.

type Module

type Module interface {
	Name() string
	Version() string
	Requires() []string
	Optional() []string
	Init(kernel Kernel) error
	Shutdown() error
}

Module is the only interface every Lesstruct module must implement.

Name is the stable registry key ("users", "comments", "site-ssr"). Requires lists hard dependencies resolved topologically at startup; Optional lists soft dependencies the module degrades without. Init wires the module (routes, subscriptions, services); Shutdown releases it.

type PostTypeDef

type PostTypeDef struct {
	Slug      string
	Name      string
	Protected bool
}

PostTypeDef is the minimal post-type registration the kernel understands. Richer definitions (fields, templates) live in the posttypes module. Protected marks slugs reserved by built-in modules (post, page, media, comment): the kernel index records them so later phases can refuse overrides, and config entries may not hide them.

type Principal

type Principal struct {
	ID       string
	Username string
	Roles    []string
}

Principal is the minimal identity every auth module injects into requests.

The kernel owns this shape — never a concrete user type — so identity stays a module concern (users/auth) while route protection stays declarative.

func PrincipalFromContext

func PrincipalFromContext(ctx context.Context) (principal Principal, ok bool)

PrincipalFromContext reads the principal back. ok is false on anonymous requests, where no auth module ran.

func (Principal) HasRole

func (p Principal) HasRole(role string) bool

HasRole reports whether the principal carries the given role.

func (Principal) IsAnonymous

func (p Principal) IsAnonymous() bool

IsAnonymous reports whether no identity module populated the principal.

type PruneResult

type PruneResult struct {
	Kept    []Module
	Dropped []DroppedModule
}

PruneResult is the outcome of Prune: the modules to register plus every dropped module with its reason.

func Prune

func Prune(candidates []Module, disabled []string) PruneResult

Prune removes disabled modules and their transitive dependents from the candidate list. It is a pure function over Requires edges: no registry mutation, no I/O, deterministic output in candidate order.

A module named in disabled drops as Direct. Any other module whose Requires names a dropped or disabled module drops as a cascade, naming the unavailable requirement as Cause. Modules whose hard dependency is merely absent (not disabled) are KEPT: the registry still fails fast on those at InitAll, unchanged. Nil candidates are skipped.

type PublicRouteProvider

type PublicRouteProvider interface {
	Module
	RegisterPublicRoutes(router chi.Router)
}

PublicRouteProvider mounts HTTP routes onto the shared Chi router. Core passes the public router (no auth chain), so everything mounted here is anonymous. Prefer RouteProvider unless the endpoint is genuinely public.

The method is deliberately named differently from RouteProvider's so one module can serve both surfaces and core can discover each independently: a single RegisterRoutes method would satisfy both interfaces at once and mount the same routes twice.

type Registry

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

Registry is the module graph: it stores modules, resolves their Requires edges into a deterministic init order, and runs lifecycle transitions. Register order never matters; InitAll always topologically sorts.

func NewRegistry

func NewRegistry() *Registry

NewRegistry creates an empty module registry.

func (*Registry) Get

func (r *Registry) Get(name string) (Module, bool)

Get returns the module registered under name.

func (*Registry) InitAll

func (r *Registry) InitAll(kernel Kernel) error

InitAll resolves the dependency graph and calls Init on every module in topological order. A hard Requires edge to an unregistered module fails fast; an Optional edge to one is silently skipped. The first Init error aborts the transition and leaves Initialized false; already-initialized modules are NOT rolled back — the caller owns that (at startup the process exits, so there is nothing to roll back to). InitAll is a single-shot startup transition, not safe for concurrent use.

func (*Registry) InitOrder

func (r *Registry) InitOrder() []string

InitOrder returns the topological init order from the last InitAll, or nil when InitAll has not run.

func (*Registry) Initialized

func (r *Registry) Initialized() bool

Initialized reports whether InitAll completed successfully.

func (*Registry) Names

func (r *Registry) Names() []string

Names returns every registered module name in sorted order.

func (*Registry) Register

func (r *Registry) Register(module Module) error

Register adds a module under its Name. Duplicate names are rejected.

func (*Registry) ShutdownAll

func (r *Registry) ShutdownAll() error

ShutdownAll calls Shutdown in reverse init order, collecting every error with errors.Join. Modules never initialized are skipped.

type RoleProvider

type RoleProvider interface {
	Module
	Capabilities() []string
}

RoleProvider contributes capability strings to the permission registry.

type RouteProvider

type RouteProvider interface {
	Module
	RegisterRoutes(router chi.Router)
}

RouteProvider mounts HTTP routes onto the shared Chi router. Core passes the protected router (auth middleware pre-chained), so everything mounted here requires authentication. For anonymous surfaces, implement PublicRouteProvider instead.

type ThemeProvider

type ThemeProvider interface {
	Module
	ThemeFiles() fs.FS
	ThemeFuncs() template.FuncMap
}

ThemeProvider registers a base theme consumed by the site-ssr engine: embedded layouts and CSS, template funcs, and site-root files.

Directories

Path Synopsis
Package capability defines the Published Language of authorization: the capability names every module checks and the users module resolves.
Package capability defines the Published Language of authorization: the capability names every module checks and the users module resolves.
Package conformance ships embeddable test suites that third-party module authors run against their kernel-contract implementations.
Package conformance ships embeddable test suites that third-party module authors run against their kernel-contract implementations.
Package httpx is the kernel's HTTP response envelope: the one wire format every module writes.
Package httpx is the kernel's HTTP response envelope: the one wire format every module writes.
Package posttype is the kernel's content-type discovery index.
Package posttype is the kernel's content-type discovery index.
Package storage defines the file-storage contract for media-like resources.
Package storage defines the file-storage contract for media-like resources.

Jump to

Keyboard shortcuts

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