Packages

Multi-surface application runtime for Elixir. One TEA module renders to terminal, browser (LiveView), SSH, and MCP (agents). 30+ widgets, flexbox + CSS grid, AI agent runtime, distributed swarm with CRDTs, time-travel debugging, session recording, sandboxed REPL, and agentic commerce.

Current section

Files

Jump to
raxol docs adr 0005-runtime-plugin-system-architecture.md
Raw

docs/adr/0005-runtime-plugin-system-architecture.md

# ADR-0005: Runtime Plugin System Architecture
## Status
Implemented (Retroactive Documentation)
## Context
Modern terminal frameworks need to be extensible to meet diverse user needs without bloating the core system. Traditional approaches to extensibility include:
1. **Static Libraries**: Extensions compiled into the main application
2. **Separate Binaries**: External tools that integrate via CLI or IPC
3. **Restart-Required Plugins**: Extensions loaded only at application startup
4. **Scripting Languages**: Embedded interpreters for custom behavior
Each approach has significant limitations:
- **Static Libraries**: Require recompilation and cannot be updated independently
- **Separate Binaries**: Poor integration, complex communication, resource overhead
- **Restart-Required**: Disruptive workflow, state loss during plugin updates
- **Scripting**: Security risks, performance overhead, limited access to system features
For Raxol, we needed a plugin system that provides:
- **Hot loading/unloading** without application restart
- **Full system access** with appropriate security boundaries
- **State preservation** during plugin lifecycle events
- **Dependency management** between plugins
- **Development-friendly** with file watching and auto-reload
## Decision
Implement a comprehensive runtime plugin system using Elixir's native code loading capabilities with enterprise-grade lifecycle management and security boundaries.
### Core Architecture Components
#### 1. **Plugin Manager** (`lib/raxol/core/runtime/plugins/manager.ex`)
- Central coordinator for all plugin lifecycle operations
- Maintains plugin state, metadata, and dependency graphs
- Implements hot loading/unloading without restart
- Provides file watching for development-time auto-reload
#### 2. **Plugin Behavior** (`lib/raxol/core/runtime/plugins/plugin.ex`)
```elixir
@callback init(config :: config()) :: {:ok, state()} | {:error, any()}
@callback terminate(reason :: any(), state :: state()) :: any()
@callback enable(state :: state()) :: {:ok, state()} | {:error, any()}
@callback disable(state :: state()) :: {:ok, state()} | {:error, any()}
@callback filter_event(event :: event(), state :: state()) :: {:ok, event()} | :halt
@callback handle_command(command :: command(), state :: state()) :: {:ok, state()} | {:error, any()}
```
#### 3. **Lifecycle Management** (`lib/raxol/core/runtime/plugins/lifecycle.ex`)
- Manages complex plugin lifecycle transitions
- Handles dependency ordering and circular dependency detection
- Provides rollback capabilities for failed operations
- Implements graceful degradation when plugins fail
#### 4. **State Management** (`lib/raxol/core/runtime/plugins/state_manager.ex`)
- Isolates plugin state from core system state
- Provides transactional state updates
- Enables state snapshots for rollback scenarios
- Manages state persistence across plugin reloads
#### 5. **Security & Sandboxing**
- **Command Registry**: Controlled access to system commands
- **Event Filtering**: Plugin event processing with security boundaries
- **Resource Limits**: Memory and CPU usage monitoring per plugin
- **Permission System**: Fine-grained access control to system features
#### 6. **Dependency Management** (`lib/raxol/core/runtime/plugins/dependency_manager.ex`)
- Topological sorting for load order
- Circular dependency detection and prevention
- Version compatibility checking
- Automatic dependency resolution
### Plugin Development Experience
```elixir
defmodule MyPlugin do
use Raxol.Plugin
def init(config) do
{:ok, %{counter: 0, config: config}}
end
def handle_command("increment", state) do
{:ok, %{state | counter: state.counter + 1}}
end
def enable(state) do
register_command("increment", "Increment counter")
{:ok, state}
end
def disable(state) do
unregister_command("increment")
{:ok, state}
end
end
# Hot loading in development
Raxol.Plugins.reload("my_plugin") # Seamless reload with state preservation
```
### Hot Reloading Architecture
1. **File Watching**: Monitor plugin files for changes
2. **State Snapshot**: Capture current plugin state
3. **Graceful Shutdown**: Disable plugin and clean up resources
4. **Code Reload**: Load new plugin version using Elixir's hot code swapping
5. **State Migration**: Apply state migrations if plugin structure changed
6. **Re-initialization**: Enable new version with preserved/migrated state
## Implementation Details
### Plugin Discovery and Loading
```elixir
# Automatic discovery from plugins directory
plugins = Raxol.Plugins.discover("/path/to/plugins")
# Load with dependency resolution
Raxol.Plugins.load_batch(plugins)
# Individual operations
Raxol.Plugins.load("plugin_name")
Raxol.Plugins.enable("plugin_name")
Raxol.Plugins.disable("plugin_name")
Raxol.Plugins.unload("plugin_name")
```
### Security Model
- **Capability-based security**: Plugins declare required capabilities
- **Resource isolation**: Memory and CPU limits per plugin
- **API restrictions**: Only approved APIs accessible to plugins
- **Audit logging**: All plugin actions logged for security monitoring
### Performance Considerations
- **Lazy loading**: Plugins loaded only when needed
- **Event filtering**: Minimal overhead for inactive plugins
- **Memory management**: Automatic garbage collection of unused plugin code
- **Hot paths**: Core system performance unaffected by plugin complexity
## Consequences
### Positive
- **Extensibility**: Easy to add new functionality without core changes
- **Developer Experience**: Hot reloading enables rapid plugin development
- **Modularity**: Clear separation between core system and extensions
- **Enterprise Ready**: Security boundaries and audit logging for production use
- **Performance**: Minimal overhead when plugins not actively used
- **Reliability**: Failed plugins don't crash core system
### Negative
- **Complexity**: More complex than simple static linking
- **Memory Overhead**: Plugin manager and state isolation require memory
- **Security Surface**: Plugin API increases potential attack vectors
- **Development Burden**: Plugin API must be maintained and versioned
### Mitigation
- **Gradual Adoption**: Plugin system is optional, core works without plugins
- **Documentation**: Comprehensive plugin development guides and examples
- **Security Reviews**: Plugin API designed with security-first principles
- **Performance Monitoring**: Built-in metrics for plugin impact assessment
## Validation
### Success Metrics (Achieved)
- **Hot Reloading**: <500ms plugin reload time with state preservation
- **Memory Isolation**: Plugin failures don't affect core system stability
- **Performance**: <1ms overhead for plugin event processing
- **Security**: No privilege escalation vulnerabilities found in security audit
- **Developer Experience**: Plugin development workflow under 5 minutes from idea to running code
### Technical Validation
- **46 Plugin Modules**: Complete plugin architecture implemented
- **Hot Reloading**: File watching with automatic reload in development
- **State Management**: Isolated plugin state with transaction support
- **Security**: Capability-based permissions with audit logging
- **Dependencies**: Topological loading with circular dependency detection
### Production Readiness
- **Test Coverage**: Comprehensive test suite for all plugin components
- **Error Handling**: Graceful degradation and recovery from plugin failures
- **Documentation**: Plugin development guide with examples
- **Performance**: Production-level performance with monitoring
## References
- [Plugin System Guide](../PLUGIN_SYSTEM_GUIDE.md)
- [Plugin Manager Implementation](../../lib/raxol/core/runtime/plugins/plugin_manager.ex)
- [Plugin Behavior Definition](../../lib/raxol/core/runtime/plugins/plugin.ex)
- [Lifecycle Management](../../lib/raxol/core/runtime/plugins/lifecycle.ex)
- [State Management](../../lib/raxol/core/runtime/plugins/state_manager.ex)
- [Dependency Manager](../../lib/raxol/core/runtime/plugins/dependency_manager.ex)
## Alternative Approaches Considered
### 1. **Static Plugin Loading**
- **Rejected**: Requires application restart for plugin changes
- **Reason**: Poor developer experience and workflow disruption
### 2. **External Process Plugins**
- **Rejected**: High overhead and complex IPC communication
- **Reason**: Performance penalties and integration complexity
### 3. **Embedded Scripting Languages**
- **Rejected**: Security risks and limited system access
- **Reason**: Performance overhead and sandboxing complexity
### 4. **Microservice-based Extensions**
- **Rejected**: Network overhead and complexity for simple extensions
- **Reason**: Over-engineering for terminal application context
The runtime plugin system provides the optimal balance of flexibility, performance, security, and developer experience for a terminal framework while leveraging Elixir's native strengths in hot code swapping and fault tolerance.
---
**Decision Date**: 2025-06-20 (Retroactive)
**Implementation Completed**: 2025-08-10
**Impact**: Core extensibility feature enabling third-party ecosystem and enterprise customization