Extension Development
RuleGo-Server uses a modular architecture. It can run independently or be embedded as middleware into existing applications (such as Gin, Echo). It provides a service container (DI), replaceable storage, lifecycle hooks, and other extension capabilities.
# Project Structure
server/
├── cmd/server/ # Entry point + build tag registration
├── app/ # Public: App lifecycle, Container, Module interface
├── config/ # Public: Configuration structures and loader
├── services/ # Public: Service interface definitions
├── model/ # Public: Data models
├── store/ # Public: Storage interfaces
├── bridge/ # Public: HTTP bridging (embedding into other frameworks)
├── bootstrap/ # Public: Default module assembly
├── internal/
│ ├── endpoint/ # REST + WebSocket routing
│ ├── modules/ # Business module implementations
│ │ ├── rule/ # Rule chain module
│ │ ├── user/ # User authentication module
│ │ ├── node/ # Node pool module
│ │ ├── runlog/ # Execution log module
│ │ ├── locale/ # Internationalization module
│ │ ├── skill/ # Skill module
│ │ ├── system/ # System configuration module
│ │ ├── marketplace/ # Marketplace module
│ │ └── mcp/ # MCP module
│ ├── store/ # Storage implementations
│ │ ├── filestore/ # File storage (default)
│ │ ├── bboltstore/ # BBolt storage
│ │ ├── jsonlstore/ # JSONL storage
│ │ └── nopstore/ # No-op storage
│ └── registry/ # Built-in component registration
├── data/ # Runtime data
└── editor/ # Frontend static assets
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# Two Usage Modes
# Mode 1: Standalone Operation
Running RuleGo-Server as standalone middleware is the simplest usage:
package main
import (
"github.com/rulego/rulego/server/bootstrap"
// Import components as needed
_ "github.com/rulego/rulego-components-ai/all"
)
func main() {
application := bootstrap.DefaultApp("./config.conf")
bootstrap.Run(application)
}
2
3
4
5
6
7
8
9
10
11
12
bootstrap.DefaultApp is equivalent to:
app.New(
app.WithConfigFile("./config.conf"),
app.WithModules(bootstrap.DefaultModules()...),
)
2
3
4
# Mode 2: Embedding into Existing Applications
Use bridge.Bridge to bridge the complete RuleGo REST API into a host framework without manually registering routes.
# Gin Example
package main
import (
"github.com/gin-gonic/gin"
"github.com/rulego/rulego/server/app"
"github.com/rulego/rulego/server/bootstrap"
"github.com/rulego/rulego/server/bridge"
)
func main() {
application := bootstrap.DefaultApp("./config.conf")
b, err := bridge.NewBridge(application)
if err != nil {
panic(err)
}
defer b.Stop()
r := gin.Default()
// User's own routes (matched first)
r.GET("/api/users", userListHandler)
// RuleGo complete API; all unmatched routes are handled by Bridge
r.Any("/*path", gin.WrapH(b.Handler()))
_ = r.Run(":8080")
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# Shortcut
If you don't need custom routing, you can use the more concise NewBridgeWithDefaults:
b, _ := bridge.NewBridgeWithDefaults("./config.conf")
defer b.Stop()
handler := b.Handler() // Standard http.Handler, can be integrated with any http framework
2
3
# Manual Lifecycle Control
In embedded mode, you can finely control the App's initialization and startup:
application := app.New(
app.WithConfigFile("./config.conf"),
app.WithModules(bootstrap.DefaultModules()...),
)
// Initialize (load configuration, register modules)
if err := application.Init(); err != nil {
panic(err)
}
// Start
if err := application.Start(); err != nil {
panic(err)
}
// Stop
defer application.Stop()
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# Module System
All features are implemented as Module interfaces with an Init -> Start -> Stop lifecycle:
type Module interface {
Name() string
Priority() int // Lower numbers are initialized first
Init(ctx *ModuleContext) error
Start(ctx context.Context) error
Stop(ctx context.Context) error
}
type ModuleContext struct {
Container *Container
Config *config.Config
Logger types.Logger
DataDir string
}
2
3
4
5
6
7
8
9
10
11
12
13
14
Built-in modules:
| Module | Priority | Description |
|---|---|---|
| user | 10 | Authentication and authorization |
| mcp | 25 | MCP service |
| rule | 30 | Rule chain management |
| node | 35 | Node pool management |
| runlog | 45 | Run logs |
| locale | 50 | Internationalization |
| skill | 55 | Skill management |
| system | 65 | System configuration |
| marketplace | 70 | Component marketplace |
| debug | 75 | Debug service |
# Custom Modules
Implement the Module interface and inject via WithModules():
type MyModule struct{}
func (m *MyModule) Name() string { return "my_module" }
func (m *MyModule) Priority() int { return 50 }
func (m *MyModule) Init(ctx *app.ModuleContext) error {
// Register service to container
ctx.Container.Register("module.my_module.service", &MyService{})
return nil
}
func (m *MyModule) Start(ctx context.Context) error { return nil }
func (m *MyModule) Stop(ctx context.Context) error { return nil }
// Usage
application := app.New(
app.WithConfigFile("./config.conf"),
app.WithModules(append(bootstrap.DefaultModules(), &MyModule{})...),
)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Use WithModuleOverride() to replace a built-in module (e.g., replacing the default rule module):
application := app.New(
app.WithConfigFile("./config.conf"),
app.WithModules(bootstrap.DefaultModules()...),
app.WithModuleOverride(&MyRuleModule{}), // Replaces the module where Name() == "rule"
)
2
3
4
5
# Service Container (DI)
A lightweight dependency injection container where modules register and retrieve services by name:
// Register service (does not allow overwriting services with the same name)
container.Register("module.rule.executor", executor)
// Replace existing service (forced overwrite)
container.Replace(services.KeyAuthenticator, &OAuth2Authenticator{})
// Get service
executor, err := app.GetAs[services.ChainExecutor](container, services.KeyRuleExecutor)
// Get service (panics on failure)
executor := app.MustGetAs[services.ChainExecutor](container, services.KeyRuleExecutor)
2
3
4
5
6
7
8
9
10
11
# Core Service Keys
| Constant | Value | Interface | Description |
|---|---|---|---|
KeyRuleCatalog | module.rule.catalog | ChainCatalog | Rule chain catalog (read-only) |
KeyRuleExecutor | module.rule.executor | ChainExecutor | Execute rule chains |
KeyRuleManager | module.rule.manager | RuleAdminService | Rule chain management (CRUD and deployment) |
KeyEngineManager | module.rule.engine_manager | EngineManager | Multi-tenant engine pool |
KeyNodeService | module.node.service | NodeService | Component + node pool operations |
KeyRunLogService | module.runlog.service | RunLogService | Run logs |
KeyLocaleService | module.locale.service | LocaleService | Internationalization |
KeyMarketplaceService | module.marketplace.service | MarketplaceService | Marketplace |
KeyMcpService | module.mcp.service | McpService | MCP protocol service |
KeyConfigService | module.system.settings | ConfigService | System configuration |
KeyAuthService | module.user.auth | AuthService | Password/API Key authentication |
KeyUserProfile | module.user.profile | UserReader | User information reading |
KeyAuthenticator | module.user.authenticator | Authenticator | Identity authentication (replaceable) |
KeyAuthorizer | module.user.authorizer | Authorizer | Permission verification (replaceable) |
KeySkillService | module.skill.service | SkillService | AI skill management |
KeyDebugService | module.debug.service | DebugService | Debug service |
# Custom Authenticator
Replace the default authenticator to implement OAuth2, LDAP, etc.:
type Authenticator interface {
Authenticate(r *http.Request) (*model.UserContext, error)
}
2
3
Replace via the container in the module's Init:
func (m *MyModule) Init(ctx *app.ModuleContext) error {
ctx.Container.Replace(services.KeyAuthenticator, &OAuth2Authenticator{})
return nil
}
2
3
4
# Custom Authorizer
Implement RBAC, ABAC, or other permission controls:
type Authorizer interface {
Authorize(user *model.UserContext, resource, action string) bool
}
2
3
Similarly, replace in the module's Init:
func (m *MyModule) Init(ctx *app.ModuleContext) error {
ctx.Container.Replace(services.KeyAuthorizer, &RBACAuthorizer{})
return nil
}
2
3
4
# Custom Storage
Implement the StoreProvider interface and inject database storage via WithStoreProvider():
type StoreProvider interface {
GetRuleStore(username string) (RuleStore, error)
GetSettingStore(username string) (SettingStore, error)
GetComponentStore(username string) (ComponentStore, error)
GetNodePoolStore(username string) (NodePoolStore, error)
GetUserStore() (UserStore, error)
GetRunLogStore() (RunLogStore, error)
}
2
3
4
5
6
7
8
application := app.New(
app.WithConfigFile("./config.conf"),
app.WithStoreProvider(&MyDbStoreProvider{db: myDb}),
app.WithModules(bootstrap.DefaultModules()...),
)
2
3
4
5
File storage (filestore) is used by default and can be replaced with MySQL, PostgreSQL, etc.
# Lifecycle Hooks
Insert logic at 5 stages of the App lifecycle via WithHooks():
application := app.New(
app.WithConfigFile("./config.conf"),
app.WithModules(bootstrap.DefaultModules()...),
app.WithHooks(
app.NewFuncHook("my_hook", app.AfterStart, 0,
func(ctx context.Context, appCtx *app.ModuleContext) error {
// Initialization logic after application starts
return nil
},
),
),
)
2
3
4
5
6
7
8
9
10
11
12
Stage order: BeforeInit -> AfterInit -> BeforeStart -> AfterStart -> OnStop
# Application Options
app.New() supports the following functional options:
| Option | Description |
|---|---|
WithConfigFile(path) | Configuration file path |
WithModules(m...) | Add modules |
WithModuleOverride(m) | Replace a registered module by name |
WithStoreProvider(p) | Inject custom storage provider (replaces default file storage) |
WithHooks(h...) | Add lifecycle hooks |
WithGlobal(props) | Inject global configuration, merged with [global] from config file (injected values override file values) |
WithTypesLogger(l) | Inject custom logger (Zap, Logrus, etc.) |
WithTransportDisabled() | Disable default transport layer (embedded mode) |
WithoutAutoMkdir() | Disable automatic data directory creation during Init |
# Hot Reload
Supports runtime configuration reloading without restarting the process:
application.Reload() // Stop all modules -> Reload configuration -> Reinitialize -> Start
# Build Tags
Include components on demand via Go build tags:
// cmd/server/with_ai.go
//go:build with_ai
import _ "github.com/rulego/rulego-components-ai/all"
2
3
4
# Standard build
go build -o server ./cmd/server/
# With AI components
go build -tags "with_ai" -o server ./cmd/server/
# With all optional components
go build -tags "with_all" -o server ./cmd/server/
# Or specify individually
go build -tags "with_ai,with_iot,with_etl,with_ci,with_extend" -o server ./cmd/server/
2
3
4
5
6
7
8
9
10
11
| Tag | Description |
|---|---|
with_all | All optional components (equivalent to enabling all tags below simultaneously) |
with_ai | AI components (ai/agent, LLM tools) |
with_iot | IoT components (OPC UA, Modbus, Serial) |
with_etl | ETL data processing components (MySQL CDC) |
with_ci | CI/CD components |
with_extend | Extension components (Kafka, NATS, Redis, RabbitMQ, MongoDB, gRPC, etc.) |
# Multi-Tenancy
EngineManager maintains an independent rule engine pool for each user:
- Rule chains are isolated per user
- Component configurations are isolated per user
- Shared nodes are isolated per user
- System configurations are isolated per user
User identity is injected into the execution context via ContextWithMCPRequestingUser.