plugin

package
v0.6.5 Latest Latest
Warning

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

Go to latest
Published: Feb 9, 2026 License: Apache-2.0 Imports: 20 Imported by: 0

Documentation

Overview

Package plugin defines the unified interface for GoatKit plugins.

Plugins can be implemented as either:

  • WASM modules (portable, sandboxed, via wazero)
  • gRPC services (native, for I/O-heavy workloads, via go-plugin)

The host doesn't care which runtime backs a plugin - both implement this interface and are managed uniformly by the plugin manager.

Index

Constants

This section is empty.

Variables

View Source
var PluginCallerKey = pluginCallerKeyType{}

PluginCallerKey is the context key for tracking which plugin is making a call.

View Source
var PluginLanguageKey = pluginLangKeyType{}

Functions

func RegisterPluginJobs

func RegisterPluginJobs(mgr *Manager, sched *scheduler.Service) int

RegisterPluginJobs registers all plugin-defined jobs with the scheduler. Call this after plugins are loaded and the scheduler is created.

func SetTemplateOverrides

func SetTemplateOverrides(registry *TemplateOverrideRegistry)

SetTemplateOverrides sets the global template override registry.

func SetTemplatePluginManager

func SetTemplatePluginManager(mgr *Manager)

SetTemplatePluginManager sets the plugin manager for template tags. Call this during app initialization after creating the plugin manager.

Types

type DefaultHostAPI

type DefaultHostAPI struct {
}

DefaultHostAPI provides a basic implementation of HostAPI. In production, this would be wired to actual database, cache, etc.

func NewDefaultHostAPI

func NewDefaultHostAPI() *DefaultHostAPI

NewDefaultHostAPI creates a new default host API.

func (*DefaultHostAPI) CacheDelete

func (h *DefaultHostAPI) CacheDelete(ctx context.Context, key string) error

CacheDelete removes a value from cache.

func (*DefaultHostAPI) CacheGet

func (h *DefaultHostAPI) CacheGet(ctx context.Context, key string) ([]byte, bool, error)

CacheGet retrieves a value from cache.

func (*DefaultHostAPI) CacheSet

func (h *DefaultHostAPI) CacheSet(ctx context.Context, key string, value []byte, ttlSeconds int) error

CacheSet stores a value in cache.

func (*DefaultHostAPI) CallPlugin

func (h *DefaultHostAPI) CallPlugin(ctx context.Context, pluginName, fn string, args json.RawMessage) (json.RawMessage, error)

CallPlugin calls a function in another plugin.

func (*DefaultHostAPI) ConfigGet

func (h *DefaultHostAPI) ConfigGet(ctx context.Context, key string) (string, error)

ConfigGet retrieves a configuration value.

func (*DefaultHostAPI) DBExec

func (h *DefaultHostAPI) DBExec(ctx context.Context, query string, args ...any) (int64, error)

DBExec executes a statement and returns affected rows.

func (*DefaultHostAPI) DBQuery

func (h *DefaultHostAPI) DBQuery(ctx context.Context, query string, args ...any) ([]map[string]any, error)

DBQuery executes a query and returns rows as maps.

func (*DefaultHostAPI) HTTPRequest

func (h *DefaultHostAPI) HTTPRequest(ctx context.Context, method, url string, headers map[string]string, body []byte) (int, []byte, error)

HTTPRequest makes an outbound HTTP request.

func (*DefaultHostAPI) Log

func (h *DefaultHostAPI) Log(ctx context.Context, level, message string, fields map[string]any)

Log writes a log entry.

func (*DefaultHostAPI) SendEmail

func (h *DefaultHostAPI) SendEmail(ctx context.Context, to, subject, body string, html bool) error

SendEmail sends an email.

func (*DefaultHostAPI) Translate

func (h *DefaultHostAPI) Translate(ctx context.Context, key string, args ...any) string

Translate translates a key to the current locale.

type ErrorCodeSpec

type ErrorCodeSpec struct {
	Code       string `json:"code"`        // error code without prefix, e.g. "export_failed"
	Message    string `json:"message"`     // default English message
	HTTPStatus int    `json:"http_status"` // suggested HTTP status code
}

ErrorCodeSpec defines an API error code provided by the plugin. The plugin name is automatically prefixed to the code by the host (e.g., code "export_failed" in plugin "stats" becomes "stats:export_failed").

type GKRegistration

type GKRegistration struct {
	// Identity
	Name        string `json:"name"`        // unique identifier, e.g. "stats"
	Version     string `json:"version"`     // semver, e.g. "1.0.0"
	Description string `json:"description"` // human-readable description
	Author      string `json:"author"`      // author or organization
	License     string `json:"license"`     // SPDX identifier, e.g. "Apache-2.0"
	Homepage    string `json:"homepage"`    // URL to plugin docs/repo

	// Capabilities - what the plugin exposes to the host
	Routes     []RouteSpec     `json:"routes,omitempty"`      // HTTP routes to register
	MenuItems  []MenuItemSpec  `json:"menu_items,omitempty"`  // navigation menu entries
	Widgets    []WidgetSpec    `json:"widgets,omitempty"`     // dashboard widgets
	Jobs       []JobSpec       `json:"jobs,omitempty"`        // scheduled/cron tasks
	Templates  []TemplateSpec  `json:"templates,omitempty"`   // template overrides/additions
	I18n       *I18nSpec       `json:"i18n,omitempty"`        // translations provided by plugin
	ErrorCodes []ErrorCodeSpec `json:"error_codes,omitempty"` // API error codes provided by plugin

	// Requirements
	MinHostVersion string   `json:"min_host_version,omitempty"` // minimum GoatFlow version
	Permissions    []string `json:"permissions,omitempty"`      // required host permissions
}

GKRegistration describes what a plugin provides to the host. This is returned by GKRegister() - the self-describing plugin protocol.

type HostAPI

type HostAPI interface {
	// Database
	DBQuery(ctx context.Context, query string, args ...any) ([]map[string]any, error)
	DBExec(ctx context.Context, query string, args ...any) (int64, error)

	// Cache
	CacheGet(ctx context.Context, key string) ([]byte, bool, error)
	CacheSet(ctx context.Context, key string, value []byte, ttlSeconds int) error
	CacheDelete(ctx context.Context, key string) error

	// HTTP (outbound)
	HTTPRequest(ctx context.Context, method, url string, headers map[string]string, body []byte) (int, []byte, error)

	// Email
	SendEmail(ctx context.Context, to, subject, body string, html bool) error

	// Logging
	Log(ctx context.Context, level, message string, fields map[string]any)

	// Config
	ConfigGet(ctx context.Context, key string) (string, error)

	// i18n
	Translate(ctx context.Context, key string, args ...any) string

	// Plugin-to-plugin calls
	// Allows one plugin to call functions in another plugin
	CallPlugin(ctx context.Context, pluginName, fn string, args json.RawMessage) (json.RawMessage, error)
}

HostAPI is the interface plugins use to access host services. Passed to Plugin.Init() - plugins store this for later use.

type I18nSpec

type I18nSpec struct {
	// Namespace prefix for plugin translations (e.g., "stats" -> "stats.dashboard.title")
	Namespace string `json:"namespace,omitempty"`
	// Languages supported by this plugin
	Languages []string `json:"languages,omitempty"`
	// Inline translations (language -> key -> value)
	// For small plugins, translations can be embedded in the manifest
	Translations map[string]map[string]string `json:"translations,omitempty"`
}

I18nSpec defines internationalization resources provided by the plugin.

type JobSpec

type JobSpec struct {
	ID          string `json:"id"`                    // unique identifier
	Handler     string `json:"handler"`               // plugin function to call
	Schedule    string `json:"schedule"`              // cron expression, e.g. "0 * * * *"
	Description string `json:"description,omitempty"` // human-readable description
	Enabled     bool   `json:"enabled"`               // whether job runs by default
	Timeout     string `json:"timeout,omitempty"`     // max execution time, e.g. "5m"
}

JobSpec defines a scheduled/cron task.

type LazyLoader

type LazyLoader interface {
	EnsureLoaded(ctx context.Context, name string) error
	Discovered() []string
}

LazyLoader is the interface for lazy-loading plugins on demand.

type LogBuffer

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

LogBuffer is a ring buffer for plugin logs.

func GetLogBuffer

func GetLogBuffer() *LogBuffer

GetLogBuffer returns the global plugin log buffer.

func NewLogBuffer

func NewLogBuffer(maxSize int) *LogBuffer

NewLogBuffer creates a new log buffer with the given max size.

func (*LogBuffer) Add

func (b *LogBuffer) Add(entry LogEntry)

Add adds a log entry to the buffer.

func (*LogBuffer) Clear

func (b *LogBuffer) Clear()

Clear removes all entries from the buffer.

func (*LogBuffer) Count

func (b *LogBuffer) Count() int

Count returns the number of entries in the buffer.

func (*LogBuffer) GetAll

func (b *LogBuffer) GetAll() []LogEntry

GetAll returns all log entries, newest first.

func (*LogBuffer) GetByLevel

func (b *LogBuffer) GetByLevel(minLevel string) []LogEntry

GetByLevel returns log entries at or above the given level, newest first.

func (*LogBuffer) GetByPlugin

func (b *LogBuffer) GetByPlugin(pluginName string) []LogEntry

GetByPlugin returns log entries for a specific plugin, newest first.

func (*LogBuffer) GetRecent

func (b *LogBuffer) GetRecent(n int) []LogEntry

GetRecent returns the most recent n entries, newest first.

func (*LogBuffer) Log

func (b *LogBuffer) Log(plugin, level, message string, fields map[string]any)

Log adds a log entry with the given parameters.

type LogEntry

type LogEntry struct {
	Timestamp time.Time      `json:"timestamp"`
	Plugin    string         `json:"plugin"`
	Level     string         `json:"level"` // debug, info, warn, error
	Message   string         `json:"message"`
	Fields    map[string]any `json:"fields,omitempty"`
}

LogEntry represents a single plugin log entry.

type Manager

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

Manager handles plugin lifecycle: loading, registration, and invocation.

func NewManager

func NewManager(host HostAPI) *Manager

NewManager creates a plugin manager with the given host API.

func (*Manager) AllWidgets

func (m *Manager) AllWidgets(location string) []PluginWidget

AllWidgets returns widgets from all plugins (including lazy-loaded) for a location. This triggers lazy loading for all discovered plugins to ensure complete widget list.

func (*Manager) Call

func (m *Manager) Call(ctx context.Context, pluginName, fn string, args []byte) ([]byte, error)

Call invokes a function on a specific plugin. If lazy loading is enabled and the plugin isn't loaded yet, it will be loaded first.

func (*Manager) CallFrom

func (m *Manager) CallFrom(ctx context.Context, callerPlugin, targetPlugin, fn string, args []byte) ([]byte, error)

CallFrom invokes a function on a plugin, with caller context for better errors. If lazy loading is enabled and the plugin isn't loaded yet, it will be loaded first.

func (*Manager) Disable

func (m *Manager) Disable(name string) error

Disable disables a plugin without unloading it.

func (*Manager) Discovered

func (m *Manager) Discovered() []string

Discovered returns the names of discovered but not necessarily loaded plugins.

func (*Manager) Enable

func (m *Manager) Enable(name string) error

Enable enables a previously disabled plugin. If the plugin is lazy-loaded and not yet registered, it will be loaded first.

func (*Manager) Get

func (m *Manager) Get(name string) (Plugin, bool)

Get returns a plugin by name.

func (*Manager) IsEnabled

func (m *Manager) IsEnabled(name string) bool

IsEnabled returns whether a plugin is enabled.

func (*Manager) Jobs

func (m *Manager) Jobs() []PluginJob

Jobs returns all jobs from all enabled plugins.

func (*Manager) List

func (m *Manager) List() []GKRegistration

List returns all registered plugin manifests.

func (*Manager) MenuItems

func (m *Manager) MenuItems(location string) []PluginMenuItem

MenuItems returns all menu items from all enabled plugins.

func (*Manager) Register

func (m *Manager) Register(ctx context.Context, p Plugin) error

Register loads and initializes a plugin.

func (*Manager) Routes

func (m *Manager) Routes() []PluginRoute

Routes returns all routes from all enabled plugins.

func (*Manager) SetLazyLoader

func (m *Manager) SetLazyLoader(loader LazyLoader)

SetLazyLoader sets the lazy loader for on-demand plugin loading.

func (*Manager) ShutdownAll

func (m *Manager) ShutdownAll(ctx context.Context) error

ShutdownAll shuts down all plugins gracefully.

func (*Manager) Unregister

func (m *Manager) Unregister(ctx context.Context, name string) error

Unregister shuts down and removes a plugin.

func (*Manager) Widgets

func (m *Manager) Widgets(location string) []PluginWidget

Widgets returns all widgets from all enabled plugins for a location.

type MenuItemSpec struct {
	ID       string         `json:"id"`                 // unique identifier
	Label    string         `json:"label"`              // display text (can be i18n key)
	Icon     string         `json:"icon,omitempty"`     // icon name or SVG
	Path     string         `json:"path"`               // URL path when clicked
	Location string         `json:"location"`           // where to insert: "admin", "agent", "customer"
	Parent   string         `json:"parent,omitempty"`   // parent menu ID for submenus
	Order    int            `json:"order,omitempty"`    // sort order within location
	Children []MenuItemSpec `json:"children,omitempty"` // nested menu items
}

MenuItemSpec defines a navigation menu entry.

type Plugin

type Plugin interface {
	// GKRegister returns plugin metadata. Called once at load time.
	// This is how plugins self-describe their capabilities per the GoatKit spec.
	GKRegister() GKRegistration

	// Init is called after loading, before the plugin serves requests.
	// The HostAPI provides access to host services (db, cache, http, etc).
	Init(ctx context.Context, host HostAPI) error

	// Call invokes a plugin function by name with JSON-encoded arguments.
	// Returns JSON-encoded response or error.
	// This is the primary communication channel between host and plugin.
	Call(ctx context.Context, fn string, args json.RawMessage) (json.RawMessage, error)

	// Shutdown is called before unloading the plugin.
	// Plugins should clean up resources and finish pending work.
	Shutdown(ctx context.Context) error
}

Plugin is the unified interface for WASM and gRPC plugins. Both runtime implementations must satisfy this interface.

type PluginCaller

type PluginCaller struct {
	Manager    *Manager
	PluginName string
	Ctx        context.Context // Optional context for i18n
}

PluginCaller wraps plugin function calls for templates. Exported so it can be used directly in template contexts.

func (*PluginCaller) Call

func (pc *PluginCaller) Call(fn string, args ...interface{}) interface{}

Call invokes a plugin function. Used via {{ plugin.Call("fn", args) }}

func (*PluginCaller) Translate

func (pc *PluginCaller) Translate(key string, args ...interface{}) string

Translate calls the plugin's translation with the current context language.

func (*PluginCaller) Widget

func (pc *PluginCaller) Widget(widgetID string) string

Widget renders a plugin widget by ID. Returns HTML string.

type PluginDisabledError

type PluginDisabledError struct {
	PluginName   string
	CallerPlugin string
}

PluginDisabledError is returned when trying to call a disabled plugin.

func (*PluginDisabledError) Error

func (e *PluginDisabledError) Error() string

type PluginJob

type PluginJob struct {
	PluginName string
	JobSpec
}

PluginJob pairs a job spec with its plugin name.

type PluginMenuItem

type PluginMenuItem struct {
	PluginName string
	MenuItemSpec
}

PluginMenuItem pairs a menu item spec with its plugin name.

type PluginNotFoundError

type PluginNotFoundError struct {
	PluginName   string // The missing plugin
	CallerPlugin string // The plugin that tried to call it (if known)
	Function     string // The function that was called
}

PluginNotFoundError is returned when a plugin dependency is missing.

func (*PluginNotFoundError) Error

func (e *PluginNotFoundError) Error() string

type PluginRoute

type PluginRoute struct {
	PluginName string
	RouteSpec  RouteSpec
}

PluginRoute pairs a route spec with its plugin name.

type PluginWidget

type PluginWidget struct {
	PluginName string
	WidgetSpec
}

PluginWidget pairs a widget spec with its plugin name.

type ProdHostAPI

type ProdHostAPI struct {
	PluginManager *Manager // For plugin-to-plugin calls
	// contains filtered or unexported fields
}

ProdHostAPI is the production implementation of HostAPI. It wires plugins to real database, cache, email, and other services.

func NewProdHostAPI

func NewProdHostAPI(opts ...ProdHostAPIOption) *ProdHostAPI

NewProdHostAPI creates a production host API with the given options.

func (*ProdHostAPI) CacheDelete

func (h *ProdHostAPI) CacheDelete(ctx context.Context, key string) error

CacheDelete removes a value from cache.

func (*ProdHostAPI) CacheGet

func (h *ProdHostAPI) CacheGet(ctx context.Context, key string) ([]byte, bool, error)

CacheGet retrieves a value from cache.

func (*ProdHostAPI) CacheSet

func (h *ProdHostAPI) CacheSet(ctx context.Context, key string, value []byte, ttlSeconds int) error

CacheSet stores a value in cache.

func (*ProdHostAPI) CallPlugin

func (h *ProdHostAPI) CallPlugin(ctx context.Context, pluginName, fn string, args json.RawMessage) (json.RawMessage, error)

CallPlugin calls a function in another plugin. This enables plugin-to-plugin communication via the host. If PluginCallerKey is set in the context, provides better error messages.

func (*ProdHostAPI) ConfigGet

func (h *ProdHostAPI) ConfigGet(ctx context.Context, key string) (string, error)

ConfigGet retrieves a configuration value by key path. Supports dot notation for nested values (e.g., "app.name").

func (*ProdHostAPI) DBExec

func (h *ProdHostAPI) DBExec(ctx context.Context, query string, args ...any) (int64, error)

DBExec executes an INSERT/UPDATE/DELETE and returns affected rows. Uses the default database. For named databases, prefix query with "@dbname:" (e.g., "@analytics:INSERT...").

func (*ProdHostAPI) DBQuery

func (h *ProdHostAPI) DBQuery(ctx context.Context, query string, args ...any) ([]map[string]any, error)

DBQuery executes a SELECT query and returns rows as maps. Uses the default database. For named databases, prefix query with "@dbname:" (e.g., "@analytics:SELECT...").

func (*ProdHostAPI) HTTPRequest

func (h *ProdHostAPI) HTTPRequest(ctx context.Context, method, url string, headers map[string]string, body []byte) (int, []byte, error)

HTTPRequest makes an outbound HTTP request.

func (*ProdHostAPI) Log

func (h *ProdHostAPI) Log(ctx context.Context, level, message string, fields map[string]any)

Log writes a structured log entry.

func (*ProdHostAPI) SendEmail

func (h *ProdHostAPI) SendEmail(ctx context.Context, to, subject, body string, html bool) error

SendEmail sends an email using the configured provider.

func (*ProdHostAPI) Translate

func (h *ProdHostAPI) Translate(ctx context.Context, key string, args ...any) string

Translate translates a key to the current locale. The language is determined from the context (set via PluginLanguageKey). If no language is set, falls back to the default language.

type ProdHostAPIOption

type ProdHostAPIOption func(*ProdHostAPI)

ProdHostAPIOption is a functional option for ProdHostAPI.

func WithCache

func WithCache(c *cache.RedisCache) ProdHostAPIOption

WithCache sets the cache client.

func WithDB

func WithDB(name string, db *sql.DB) ProdHostAPIOption

WithDB adds a named database connection. Use "default" for the primary database.

func WithDefaultDB

func WithDefaultDB(name string) ProdHostAPIOption

WithDefaultDB sets which named database is the default.

func WithLogger

func WithLogger(logger *slog.Logger) ProdHostAPIOption

WithLogger sets the logger.

func WithPluginManager

func WithPluginManager(mgr *Manager) ProdHostAPIOption

WithPluginManager sets the plugin manager for plugin-to-plugin calls.

type RouteSpec

type RouteSpec struct {
	Method      string   `json:"method"`                // GET, POST, PUT, DELETE, etc.
	Path        string   `json:"path"`                  // URL path, e.g. "/admin/stats"
	Handler     string   `json:"handler"`               // plugin function to call
	Middleware  []string `json:"middleware,omitempty"`  // middleware chain, e.g. ["auth", "admin"]
	Description string   `json:"description,omitempty"` // for documentation
}

RouteSpec defines an HTTP route the plugin wants to handle.

type TemplateOverride

type TemplateOverride struct {
	PluginName   string // Plugin providing the override
	TemplateName string // Original template name being overridden
	Handler      string // Plugin function to call for template content
}

TemplateOverride stores information about a template override.

type TemplateOverrideRegistry

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

TemplateOverrideRegistry manages template overrides from plugins.

func GetTemplateOverrides

func GetTemplateOverrides() *TemplateOverrideRegistry

GetTemplateOverrides returns the global template override registry.

func NewTemplateOverrideRegistry

func NewTemplateOverrideRegistry(mgr *Manager) *TemplateOverrideRegistry

NewTemplateOverrideRegistry creates a new template override registry.

func (*TemplateOverrideRegistry) GetOverride

func (r *TemplateOverrideRegistry) GetOverride(templateName string) *TemplateOverride

GetOverride returns the override for a template, if any.

func (*TemplateOverrideRegistry) HasOverride

func (r *TemplateOverrideRegistry) HasOverride(templateName string) bool

HasOverride checks if a template has a plugin override.

func (*TemplateOverrideRegistry) List

List returns all registered overrides.

func (*TemplateOverrideRegistry) Register

func (r *TemplateOverrideRegistry) Register(pluginName string, templates []TemplateSpec)

Register registers template overrides from a plugin.

func (*TemplateOverrideRegistry) RenderOverride

func (r *TemplateOverrideRegistry) RenderOverride(ctx context.Context, templateName string, data map[string]any) (string, bool)

RenderOverride renders a template override by calling the plugin. Returns the rendered HTML and true if an override exists, empty string and false otherwise.

func (*TemplateOverrideRegistry) Unregister

func (r *TemplateOverrideRegistry) Unregister(pluginName string)

Unregister removes template overrides for a plugin.

type TemplateSpec

type TemplateSpec struct {
	Name     string `json:"name"`               // template name, e.g. "stats/dashboard.html"
	Path     string `json:"path"`               // path within plugin package
	Override bool   `json:"override,omitempty"` // if true, overrides host template of same name
}

TemplateSpec defines a template the plugin provides.

type WidgetSpec

type WidgetSpec struct {
	ID          string `json:"id"`                    // unique identifier
	Title       string `json:"title"`                 // display title (can be i18n key)
	Description string `json:"description,omitempty"` // widget description
	Handler     string `json:"handler"`               // plugin function that returns widget HTML
	Location    string `json:"location"`              // dashboard location: "agent_home", "admin_home"
	Size        string `json:"size,omitempty"`        // "small", "medium", "large", "full"
	Order       int    `json:"order,omitempty"`       // sort order within location
	Refreshable bool   `json:"refreshable,omitempty"` // can be refreshed via AJAX
	RefreshSec  int    `json:"refresh_sec,omitempty"` // auto-refresh interval
}

WidgetSpec defines a dashboard widget.

Directories

Path Synopsis
Package core provides core built-in plugins for GoatFlow.
Package core provides core built-in plugins for GoatFlow.
Package example provides example plugin implementations for testing.
Package example provides example plugin implementations for testing.
Package grpc provides gRPC-based plugin runtime using HashiCorp go-plugin.
Package grpc provides gRPC-based plugin runtime using HashiCorp go-plugin.
example command
Example gRPC plugin for GoatKit.
Example gRPC plugin for GoatKit.
Package packaging provides ZIP-based plugin packaging and extraction.
Package packaging provides ZIP-based plugin packaging and extraction.
Package wasm provides a WASM-based plugin runtime using wazero.
Package wasm provides a WASM-based plugin runtime using wazero.

Jump to

Keyboard shortcuts

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