plugin

package
v0.23.0 Latest Latest
Warning

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

Go to latest
Published: Feb 12, 2026 License: MIT Imports: 10 Imported by: 0

README

QNTX Plugin System

Infrastructure for extending QNTX with domain-specific functionality through plugins.

Overview

The plugin system allows QNTX to be extended with domain-specific features (code analysis, financial modeling, legal document processing, etc.) without coupling them to the core system.

Architecture

  • interface.go - DomainPlugin interface that all plugins implement
  • registry.go - Plugin registration, initialization, and lifecycle management
  • services.go - ServiceRegistry providing plugins access to core QNTX services (database, logger, config, ATS, Pulse queue)
  • grpc/ - gRPC transport layer for external (out-of-process) plugins

Plugin Types

Built-in Plugins (temporary)

Currently qntx-code/ is bundled at the repository root for convenience during development. This will be extracted to a separate repository (github.com/teranos/qntx-code) and become an external plugin.

External Plugins (future)

Separate repositories that communicate via gRPC. Loaded dynamically at runtime from binaries in ~/.qntx/plugins/.

Examples:

  • github.com/teranos/qntx-code - Code domain (git, GitHub, gopls)
  • github.com/teranos/qntx-finance - Financial analysis domain
  • Community plugins

Design Philosophy

QNTX core is minimal. Domain-specific functionality lives in plugins, keeping the core focused on:

  • Attestation system (ATS)
  • Configuration (am)
  • Database (db)
  • Async job processing (Pulse)
  • Query system (ax)

Plugins handle everything else: code analysis, GitHub integration, language servers, domain-specific visualizations, etc.

Documentation

Documentation

Overview

Package plugin provides the plugin architecture for QNTX domain extensions.

A domain plugin represents a complete functional area (e.g., code, biotech, finance). Each domain provides HTTP endpoints, WebSocket handlers, and lifecycle management.

Architecture:

  • All domains run as separate processes via gRPC
  • All domains implement the same DomainPlugin interface
  • Domains are isolated - interact only via shared database (attestations)

Example domains:

  • code: Software development (git ingestion, GitHub PRs, language servers, code editor)
  • biotech: Bioinformatics (sequence analysis, protein folding, genomics)
  • finance: Financial analysis (market data, risk modeling, portfolio optimization)

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func List

func List() []string

List returns all plugin names from the global registry (Issue #4: Thread-safe)

func Register

func Register(plugin DomainPlugin) error

Register registers a plugin with the global registry (Issue #4: Thread-safe)

func SetDefaultRegistry

func SetDefaultRegistry(registry *Registry)

SetDefaultRegistry sets the global registry (Issue #4: Thread-safe) Panics if called more than once. The mutex ensures thread-safe check-and-set.

Types

type Config

type Config interface {
	// GetString retrieves a string configuration value
	GetString(key string) string

	// GetInt retrieves an integer configuration value
	GetInt(key string) int

	// GetBool retrieves a boolean configuration value
	GetBool(key string) bool

	// GetStringSlice retrieves a string slice configuration value
	GetStringSlice(key string) []string

	// Get retrieves a raw configuration value
	Get(key string) interface{}

	// Set sets a configuration value (for runtime overrides)
	Set(key string, value interface{})

	// GetKeys returns all available configuration keys (sorted)
	GetKeys() []string
}

Config provides access to plugin configuration

type ConfigField

type ConfigField struct {
	Type         string // "string", "number", "boolean", "array"
	Description  string // Human-readable description
	DefaultValue string // Default value as string
	Required     bool   // Whether field is required
	MinValue     string // For numbers: minimum value
	MaxValue     string // For numbers: maximum value
	Pattern      string // For strings: regex validation pattern
	ElementType  string // For arrays: element type
}

ConfigField describes a single configuration field for UI-based configuration. This maps directly to protocol.ConfigFieldSchema for gRPC serialization.

type ConfigProvider

type ConfigProvider interface {
	// GetPluginConfig returns configuration for a specific plugin
	GetPluginConfig(domain string) Config
}

ConfigProvider provides configuration for plugins

type ConfigurablePlugin

type ConfigurablePlugin interface {
	DomainPlugin

	// ConfigSchema returns the configuration schema for this plugin.
	// The returned map keys are configuration field names (e.g., "gopls.workspace_root").
	// Values describe each field's type, description, default, and validation constraints.
	//
	// Field types: "string", "number", "boolean", "array"
	// See protocol.ConfigFieldSchema for the full schema definition.
	ConfigSchema() map[string]ConfigField
}

ConfigurablePlugin is an optional interface for plugins that expose configuration schemas for UI-based configuration. Plugins implementing this interface will have their configuration schema exposed via the gRPC ConfigSchema RPC, enabling the web UI to render configuration forms.

type DefaultServiceRegistry

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

DefaultServiceRegistry is the standard implementation of ServiceRegistry

func (*DefaultServiceRegistry) ATSStore

ATSStore returns the attestation storage interface

func (*DefaultServiceRegistry) Config

func (r *DefaultServiceRegistry) Config(domain string) Config

Config returns plugin-specific configuration

func (*DefaultServiceRegistry) Database

func (r *DefaultServiceRegistry) Database() *sql.DB

Database returns the shared QNTX database connection

func (*DefaultServiceRegistry) Logger

func (r *DefaultServiceRegistry) Logger(domain string) *zap.SugaredLogger

Logger returns a logger for the specified domain with version information

func (*DefaultServiceRegistry) Queue

Queue returns the Pulse async job queue

type DomainPlugin

type DomainPlugin interface {
	// Metadata returns information about this domain plugin
	Metadata() Metadata

	// Initialize is called when the plugin is loaded
	// The plugin receives a service registry to access QNTX core services
	Initialize(ctx context.Context, services ServiceRegistry) error

	// Shutdown is called when QNTX is shutting down
	Shutdown(ctx context.Context) error

	// RegisterHTTP registers HTTP handlers for this domain
	// Handlers will be mounted at: /api/<domain-name>/*
	RegisterHTTP(mux *http.ServeMux) error

	// RegisterWebSocket registers WebSocket handlers for this domain
	// Handlers will be mounted at: /<domain-name>-ws
	RegisterWebSocket() (map[string]WebSocketHandler, error)

	// Health returns the health status of this domain plugin
	Health(ctx context.Context) HealthStatus
}

DomainPlugin defines the interface that all domain plugins must implement. All plugins implement this interface.

func Get

func Get(name string) (DomainPlugin, bool)

Get retrieves a plugin from the global registry (Issue #4: Thread-safe)

type HealthStatus

type HealthStatus struct {
	Healthy bool
	Paused  bool // True if plugin is intentionally paused (not a failure)
	Message string
	Details map[string]interface{}
}

HealthStatus represents the health of a domain plugin

type Metadata

type Metadata struct {
	// Name is the domain identifier (e.g., "code", "biotech")
	Name string

	// Version is the plugin version (semver)
	Version string

	// QNTXVersion is the required QNTX version (semver constraint)
	QNTXVersion string

	// Description is a human-readable description
	Description string

	// Author is the plugin author/maintainer
	Author string

	// License is the plugin license (e.g., "MIT", "Apache-2.0")
	License string
}

Metadata describes a domain plugin

type PausablePlugin

type PausablePlugin interface {
	DomainPlugin

	// Pause temporarily suspends the plugin's operations.
	// The plugin should stop processing new requests but maintain its state.
	// HTTP endpoints may return 503 Service Unavailable while paused.
	Pause(ctx context.Context) error

	// Resume restores the plugin to active operation after a pause.
	Resume(ctx context.Context) error
}

PausablePlugin is an optional interface for plugins that support pause/resume. Plugins that implement this interface can be paused and resumed at runtime without a full shutdown/restart cycle.

type PluginState

type PluginState string

PluginState represents the current state of a plugin

const (
	// StateLoading indicates the plugin is currently loading/connecting
	StateLoading PluginState = "loading"
	// StateRunning indicates the plugin is active and processing requests
	StateRunning PluginState = "running"
	// StatePaused indicates the plugin is temporarily suspended
	StatePaused PluginState = "paused"
	// StateStopped indicates the plugin has been shut down
	StateStopped PluginState = "stopped"
	// StateFailed indicates the plugin failed to initialize or encountered a fatal error
	StateFailed PluginState = "failed"
)

type QueueService

type QueueService interface {
	// Enqueue adds a new job to the queue
	Enqueue(job *async.Job) error

	// GetJob retrieves a job by ID
	GetJob(id string) (*async.Job, error)

	// UpdateJob updates a job's state
	UpdateJob(job *async.Job) error

	// ListJobs lists jobs with optional status filter
	ListJobs(status *async.JobStatus, limit int) ([]*async.Job, error)
}

QueueService defines the job queue operations available to plugins. This interface allows both local and remote queue implementations.

type Registry

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

Registry manages all domain plugins

func GetDefaultRegistry

func GetDefaultRegistry() *Registry

GetDefaultRegistry returns the global registry (Issue #4: Thread-safe read)

func NewRegistry

func NewRegistry(qntxVersion string, logger *zap.SugaredLogger) *Registry

NewRegistry creates a new plugin registry

func (*Registry) Get

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

Get retrieves a domain plugin by name

func (*Registry) GetAll

func (r *Registry) GetAll() map[string]DomainPlugin

GetAll returns all registered plugins

func (*Registry) GetAllStates

func (r *Registry) GetAllStates() map[string]PluginState

GetAllStates returns the states of all plugins

func (*Registry) GetState

func (r *Registry) GetState(name string) (PluginState, bool)

GetState returns the current state of a plugin

func (*Registry) HealthCheckAll

func (r *Registry) HealthCheckAll(ctx context.Context) map[string]HealthStatus

HealthCheckAll checks health of all plugins

func (*Registry) InitializeAll

func (r *Registry) InitializeAll(ctx context.Context, services ServiceRegistry) error

InitializeAll initializes all registered plugins

func (*Registry) IsPausable

func (r *Registry) IsPausable(name string) bool

IsPausable checks if a plugin implements the PausablePlugin interface

func (*Registry) IsReady

func (r *Registry) IsReady(name string) bool

IsReady returns whether a plugin is ready to handle requests

func (*Registry) List

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

List returns all registered domain plugin names in sorted order

func (*Registry) ListEnabled

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

ListEnabled returns all enabled plugin names (including pre-registered ones) in sorted order This includes plugins that are still loading, not just fully loaded ones

func (*Registry) MarkReady

func (r *Registry) MarkReady(name string)

MarkReady marks a plugin as ready (StateRunning) after successful loading Used by async plugin loading to indicate plugin is ready to handle requests

func (*Registry) Pause

func (r *Registry) Pause(ctx context.Context, name string) error

Pause pauses a plugin if it implements PausablePlugin

func (*Registry) PreRegister

func (r *Registry) PreRegister(name string)

PreRegister reserves a plugin slot in loading state before async initialization This allows routes to be registered immediately while plugins load in background

func (*Registry) Register

func (r *Registry) Register(plugin DomainPlugin) error

Register registers a domain plugin Returns error if plugin name conflicts or version incompatible

func (*Registry) Resume

func (r *Registry) Resume(ctx context.Context, name string) error

Resume resumes a paused plugin

func (*Registry) ShutdownAll

func (r *Registry) ShutdownAll(ctx context.Context) error

ShutdownAll shuts down all registered plugins

type ServiceRegistry

type ServiceRegistry interface {
	// Database returns the shared QNTX database connection
	Database() *sql.DB

	// Logger returns a logger for this plugin
	Logger(domain string) *zap.SugaredLogger

	// Config returns plugin-specific configuration
	Config(domain string) Config

	// ATSStore returns the attestation storage interface
	ATSStore() ats.AttestationStore

	// Queue returns the Pulse async job queue
	Queue() QueueService
}

ServiceRegistry provides access to QNTX core services for domain plugins. Plugins use this registry to look up services they need.

func NewServiceRegistry

func NewServiceRegistry(db *sql.DB, logger *zap.SugaredLogger, store ats.AttestationStore, config ConfigProvider, queue QueueService) ServiceRegistry

NewServiceRegistry creates a new service registry

type WebSocketHandler

type WebSocketHandler interface {
	ServeWS(w http.ResponseWriter, r *http.Request)
}

WebSocketHandler handles WebSocket connections

Directories

Path Synopsis
Package grpc provides gRPC transport for external domain plugins.
Package grpc provides gRPC transport for external domain plugins.

Jump to

Keyboard shortcuts

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