Composable Plugins & Context
This document outlines the design for composability, dependency contracts, and building custom editions of Octarq.
1. Principles of Composable Plugins
To allow building custom editions, three structural principles are enforced:
- Dependency Contract — Plugins declare what they require; the app refuses to start on an unsatisfiable composition.
- Build-Time Exclusion — An edition can compile out a core feature (e.g., a links-only binary without mail) so that the excluded feature’s code is absent from the binary, rather than just unmounted.
- Frontend Self-Containment — Each feature’s UI (pages, components, API calls, i18n) lives inside its plugin directory and is composed through the same manifest pipeline Pro plugins use.
2. Dependency Declaration & Validation
To enforce dependency safety at startup, the plugin.Info struct has a Requires field:
type Info struct { Title string Version string Requires []string // Slice of plugin names this plugin depends on}For example:
linksplugin declares:Requires: []string{"dns"}mailplugin declares:Requires: []string{"dns", "links"}
During application startup, a preflight validation checks that every mounted plugin’s dependencies are satisfied by the set of registered plugins. If a dependency is missing, the application halts with a clear error.
3. Frontend Feature Self-Containment
To make features truly modular, all frontend code for a feature is located inside its plugin directory (e.g., web/src/plugins/<feature>/):
index.ts— Contains theUIPlugindefinition (including routes, widgets, areas, i18n).pages/&components/— All page and component files.api.ts— The plugin-specific API client methods.i18n— Localization keys merged dynamically under the plugin’s namespace.
This allows completely dropping a feature from the UI bundle by removing its entry in the default manifest (web/octarq.plugins.json).
4. Custom Edition Build Recipes
Core feature plugins (dns, mail, links) can be selectively excluded from both the backend Go binary and the frontend web UI to produce lightweight, custom-tailored editions of Octarq.
Backend Composition
Trimmed editions are defined at the compilation root (typically in main.go). Instead of auto-mounting all plugins, the composition root explicitly selects the plugins to use:
// Example of a links-only composition rootpackage main
import ( "github.com/octarq-org/octarq/app" "github.com/octarq-org/octarq/plugins/dns" "github.com/octarq-org/octarq/plugins/links")
func main() { a, _ := app.New() a.Use(dns.New()) // Required by links a.Use(links.New()) a.Run()}Because of Go’s dead-code elimination, any unreferenced packages (like plugins/mail) are automatically excluded from the final compiled binary by the linker.
Frontend Manifest Composition
Control which UI plugins are bundled by configuring web/octarq.plugins.json or setting OCTARQ_PLUGINS_MANIFEST environment variables during build:
Example (octarq.nomail.json):
{ "plugins": [ "./src/plugins/dns", "./src/plugins/links" ]}Build command:
OCTARQ_PLUGINS_MANIFEST=octarq.nomail.json npm run build