Developing Plugins
You can extend Web.Casa by developing custom plugins. A plugin is a Go package that implements the Plugin interface and is compiled into the Web.Casa binary.
Prerequisites
Section titled “Prerequisites”- Go (version matching Web.Casa’s go.mod)
- Familiarity with the Gin web framework (used for routing)
- React (for frontend components)
- Understanding of the Plugin System architecture
Project Structure
Section titled “Project Structure”Create a directory under plugins/ (at the project root) with your plugin name:
plugins/myplugin/ myplugin.go # Plugin struct and lifecycle methods handler.go # HTTP handlers service.go # Business logic model.go # Database modelsImplementing the Plugin Interface
Section titled “Implementing the Plugin Interface”Your plugin struct must implement these lifecycle methods:
type MyPlugin struct { ctx *plugin.Context db *gorm.DB}
func (p *MyPlugin) Metadata() plugin.Metadata { return plugin.Metadata{ Name: "myplugin", Version: "1.0.0", Description: "My custom plugin", Dependencies: []string{"docker"}, // optional }}
func (p *MyPlugin) Init(ctx *plugin.Context) error { p.ctx = ctx p.db = ctx.DB // Auto-migrate database tables // Register event subscriptions return nil}
func (p *MyPlugin) Start() error { // Start background processes return nil}
func (p *MyPlugin) Stop() error { // Clean shutdown return nil}Registering Routes
Section titled “Registering Routes”Routes are registered using the Gin router group provided through the CoreAPI. Routes are mounted under /api/plugins/<plugin-name>/:
func (p *MyPlugin) RegisterRoutes(group *gin.RouterGroup) { group.GET("/items", p.handleListItems) group.POST("/items", p.handleCreateItem) group.GET("/items/:id", p.handleGetItem) group.PUT("/items/:id", p.handleUpdateItem) group.DELETE("/items/:id", p.handleDeleteItem)}Authentication and authorization middleware are automatically applied to plugin routes.
Event Bus
Section titled “Event Bus”Publishing Events
Section titled “Publishing Events”Notify other plugins when something happens:
p.ctx.EventBus.Publish("myplugin.item.created", map[string]interface{}{ "id": item.ID, "name": item.Name,})Subscribing to Events
Section titled “Subscribing to Events”React to events from other plugins or the core system:
p.ctx.EventBus.Subscribe("container.started", func(e plugin.Event) { // Handle container start event})Common events to subscribe to:
| Event | Source | Payload |
|---|---|---|
container.started | Docker plugin | Container ID, name |
container.stopped | Docker plugin | Container ID, name |
host.created | Reverse Proxy | Host ID, domain |
backup.completed | Backup plugin | Backup ID, size |
project.deployed | Deploy plugin | Project ID, status |
Scoped Config Store
Section titled “Scoped Config Store”Use the ConfigStore to persist plugin settings without direct database access:
// Write a settingp.ctx.ConfigStore.Set("myplugin", "api_url", "https://example.com")
// Read a settingvalue := p.ctx.ConfigStore.Get("myplugin", "api_url")Settings stored in the ConfigStore are automatically exposed in the Web.Casa settings UI for user configuration.
Plugin Context Reference
Section titled “Plugin Context Reference”The plugin Context provides access to shared system services:
| Service | Access Method | Purpose |
|---|---|---|
| Database | ctx.DB | GORM database connection |
| Event Bus | ctx.EventBus | Publish and subscribe to events |
| Config Store | ctx.ConfigStore | Scoped key-value settings |
| Logger | ctx.Logger | Structured logging |
Registration
Section titled “Registration”After developing your plugin, register it in the plugin loader so Web.Casa discovers it at startup:
// In the plugin registration filepluginManager.Register(&myplugin.MyPlugin{})Testing
Section titled “Testing”- Write unit tests for your service logic
- Test HTTP handlers using Go’s
httptestpackage - Test event subscriptions by publishing mock events
- Use existing built-in plugins as reference implementations
Tip: See the Architecture Guide for detailed code structure conventions, naming patterns, and best practices.