Module System

Module System #

The INFINI Framework uses a modular architecture where functionality is organized into self-contained modules. Each module follows a standard lifecycle and can be registered, configured, enabled, or disabled independently. The module system manages startup ordering through priorities and provides a clean separation between core system modules and user-provided plugins.

Module Interface #

Every module must implement the Module interface defined in core/module/interface.go:

type Module interface {
    Setup()
    Start() error
    Stop() error
    Name() string
}

Methods #

MethodDescription
Name() stringReturns a unique identifier for the module, used in configuration and logging.
Setup()Called during the initialization phase. Use this to parse configuration, register API routes, register with subsystems (e.g., KV stores, filters), and perform any pre-start wiring.
Start() errorCalled after all modules have been set up. Use this to start background goroutines, open connections, and begin processing. Return an error to signal a startup failure.
Stop() errorCalled during shutdown. Use this to close connections, flush buffers, and release resources. Return an error to signal a shutdown problem.

Registration Functions #

Modules register themselves using one of the registration functions from core/module/module.go. Registration typically happens inside a Go init() function so that importing the module’s package is sufficient to register it.

// Register a core system module (default priority).
module.RegisterSystemModule(mod Module)

// Register a system module with explicit priority control.
module.RegisterModuleWithPriority(mod Module, priority int)

// Register a user/optional plugin (default priority).
module.RegisterUserPlugin(mod Module)

// Register a user plugin with explicit priority control.
module.RegisterPluginWithPriority(mod Module, priority int)

System Modules vs. User Plugins #

CategoryFunctionTypical Use
System ModuleRegisterSystemModule / RegisterModuleWithPriorityCore framework services such as the API server, task scheduler, or pipeline engine. These are started first.
User PluginRegisterUserPlugin / RegisterPluginWithPriorityOptional or application-specific functionality such as storage backends, metric exporters, or security modules. These are started after all system modules.

Registration Pattern #

The idiomatic way to register a module is via Go’s init() function:

func init() {
    module.RegisterModuleWithPriority(&MyModule{}, 0)
}

Importing the package that contains this init() function is all that is needed to register the module with the framework.

Lifecycle #

The framework manages all registered modules through a three-phase lifecycle:

┌─────────────────────────────────────────────────────┐
│  1. SETUP   (all system modules, then user plugins) │
│     Sorted by priority (ascending). Each module's   │
│     Setup() is called in order.                     │
├─────────────────────────────────────────────────────┤
│  2. START   (all system modules, then user plugins) │
│     Same sorted order. Each module's Start() is     │
│     called. Errors abort the startup.               │
├─────────────────────────────────────────────────────┤
│  3. STOP    (reverse order)                         │
│     On shutdown, Stop() is called on every started  │
│     module in reverse order so that dependencies    │
│     are torn down correctly.                        │
└─────────────────────────────────────────────────────┘

Priority System #

Priority is an integer value that controls the order in which modules are initialized and started. Lower values run first. Negative priorities are commonly used for foundational services that other modules depend on.

PriorityTypical Use
-100Low-level storage backends (e.g., Badger KV store)
0Default — most modules
> 0Modules that depend on other modules being ready

Example — registering a storage backend early:

func init() {
    module.RegisterModuleWithPriority(&Module{}, -100)
}

Configuration #

Modules load their configuration from the application’s YAML config file using env.ParseConfig. The first argument is the YAML section name, and the second is a pointer to a configuration struct. Struct fields are mapped to YAML keys via config tags.

Defining a Config Struct #

type Config struct {
    Enabled       bool   `config:"enabled"`
    ListenAddress string `config:"listen_address"`
    MaxRetries    int    `config:"max_retries"`
}

Parsing Config in Setup #

func (module *MyModule) Setup() {
    module.cfg = &Config{
        Enabled:       true,           // default value
        ListenAddress: "0.0.0.0:8080", // default value
        MaxRetries:    3,              // default value
    }
    env.ParseConfig("my_module", module.cfg)
}

The corresponding YAML section:

my_module:
  enabled: true
  listen_address: "0.0.0.0:9200"
  max_retries: 5

You can also parse configuration directly into the module struct if it carries config tags:

type TaskModule struct {
    TimeZone                string `config:"time_zone"`
    MaxConcurrentNumOfTasks int    `config:"max_concurrent_tasks"`
}

func (module *TaskModule) Setup() {
    module.TimeZone = "UTC"                // default
    module.MaxConcurrentNumOfTasks = 100   // default
    env.ParseConfig("task", module)
}

Enabling and Disabling Modules #

Modules commonly check an Enabled field in their configuration to decide whether to activate. This allows operators to toggle functionality without removing code:

func (module *Module) Start() error {
    if !module.cfg.Enabled {
        return nil
    }
    return module.Open()
}

func (module *Module) Stop() error {
    if !module.cfg.Enabled {
        return nil
    }
    return module.Close()
}

In the YAML configuration file:

my_module:
  enabled: false

Setting enabled: false causes the module to skip its initialization logic while still being registered with the framework.

Complete Example #

Below is a complete, working example of a custom module that exposes an HTTP health-check endpoint and reads its configuration from YAML.

Module Code #

package health

import (
    "net/http"

    httprouter "infini.sh/framework/core/api/router"
    "infini.sh/framework/core/api"
    "infini.sh/framework/core/env"
    "infini.sh/framework/core/module"
)

// Config holds the module's YAML configuration.
type Config struct {
    Enabled bool   `config:"enabled"`
    Path    string `config:"path"`
}

// HealthModule provides a simple health-check endpoint.
type HealthModule struct {
    cfg *Config
    api.Handler
}

func (m *HealthModule) Name() string {
    return "health"
}

func (m *HealthModule) Setup() {
    m.cfg = &Config{
        Enabled: true,
        Path:    "/_health",
    }
    env.ParseConfig("health", m.cfg)

    if m.cfg.Enabled {
        api.HandleAPIMethod(api.GET, m.cfg.Path, m.handleHealth)
    }
}

func (m *HealthModule) Start() error {
    if !m.cfg.Enabled {
        return nil
    }
    // Additional start logic (background tasks, connections, etc.)
    return nil
}

func (m *HealthModule) Stop() error {
    // Cleanup resources if needed.
    return nil
}

func (m *HealthModule) handleHealth(w http.ResponseWriter, req *http.Request, ps httprouter.Params) {
    m.WriteJSON(w, map[string]string{"status": "ok"}, http.StatusOK)
}

// Register the module when this package is imported.
func init() {
    module.RegisterUserPlugin(&HealthModule{})
}

YAML Configuration #

health:
  enabled: true
  path: "/_health"

Importing the Module #

In your application’s main package or plugin registry, import the module so its init() function runs:

import _ "your_app/plugins/health"

Built-in Modules #

The framework ships with a set of built-in system modules and plugins.

System Modules (modules/) #

ModuleDescription
apiHTTP API server and route registration
configsConfiguration management
elasticElasticsearch / OpenSearch / Easysearch integration
keystoreSecure credential and secret storage
metricsMetrics collection and reporting
pipelineData processing pipeline engine
queueMessage queue management
redisRedis client integration
s3S3-compatible object storage
securityAuthentication and authorization (RBAC, OAuth)
statsInternal statistics tracking
taskScheduled and on-demand task execution
webStatic file serving and web UI

Plugins (plugins/) #

PluginDescription
badgerEmbedded key-value store (BadgerDB)
elasticElasticsearch-specific plugin extensions
filter_bloomBloom filter implementation
filter_cuckooCuckoo filter implementation
httpHTTP pipeline processor
queueQueue backend implementations
replayEvent replay functionality
simple_kvSimple in-memory key-value store
smtpSMTP email sending
stats_statsdStatsD metrics exporter
Edit Edit this page