Aleph
Concepts

Extension System

Plugin and skill management with WASM and Node.js runtimes, manifest-driven registration, and unified hook execution.

The extension module provides a unified extension system for Aleph. It supports both plugins (WASM and Node.js runtimes) and skills, with manifest-driven registration, a unified registry, and hook-based integration.

Design Philosophy

  1. Unified registry — Everything a plugin can provide (tools, channels, HTTP routes) is registered in a single PluginRegistry
  2. Capability declarations — Plugins declare what they provide via CapabilityDeclaration (see Capability System)
  3. Sandboxed runtimes — WASM plugins run in a sandbox; Node.js plugins run in separate processes
  4. Hot reload — Extensions can be added, updated, or removed without restarting

Architecture

┌─────────────────────────────────────────────────────────────────────────┐
│                        ExtensionManager                                  │
│  - Orchestrates discovery, loading, registration, integration            │
└────────────────────────────┬───────────────────────────────────────────┘

          ┌───────────────────┼───────────────────┐
          ▼                   ▼                   ▼
     PluginRegistry      PluginLoader        SkillSystem
   (unified registry)  (Node.js, WASM)    (skills, agents)
          │                   │                   │
          └───────────────────┼───────────────────┘


                        HookExecutor
                       (unified hooks)

Core Components

ExtensionManager

Central coordinator for all extensions:

pub struct ExtensionManager {
    registry: PluginRegistry,
    loader: PluginLoader,
    skill_system: SkillSystem,
}

impl ExtensionManager {
    pub async fn discover(&self,
    ) -> Result<Vec<PluginManifest>> { /* ... */ }

    pub async fn load(
        &self,
        manifest: &PluginManifest,
    ) -> Result<LoadedPlugin> { /* ... */ }

    pub async fn register(
        &self,
        plugin: LoadedPlugin,
    ) -> Result<()> { /* ... */ }
}

PluginRegistry

Unified registry for all plugin-provided capabilities:

pub struct PluginRegistry {
    tools: HashMap<String, ToolRegistration>,
    channels: HashMap<String, ChannelRegistration>,
    hooks: HashMap<String, HookRegistration>,
    http_routes: Vec<HttpRouteRegistration>,
    providers: HashMap<String, ProviderRegistration>,
}

Registration types:

  • Tool — Callable functions for the agent
  • Hook — Event interceptors
  • Channel — Messaging interface integrations
  • Provider — AI model backends
  • HttpRoute — REST API endpoints
  • Cli — CLI commands
  • Service — Background services
  • Command — In-chat commands
  • Skill — Prompt-based skills
  • Agent — Agent definitions
  • McpServer — MCP server configurations

PluginLoader

Loads plugins from different sources:

pub struct PluginLoader;

impl PluginLoader {
    pub async fn load_wasm(
        &self,
        path: &Path,
    ) -> Result<WasmPlugin> { /* ... */ }

    pub async fn load_nodejs(
        &self,
        path: &Path,
    ) -> Result<NodeJsPlugin> { /* ... */ }
}

WASM runtime: Uses Extism for sandboxed execution. Limited I/O, fast startup.

Node.js runtime: Spawns Node.js process with IPC communication. Full Node.js API access.

Manifest

Plugins declare their capabilities in a manifest file:

[plugin]
id = "my-plugin"
name = "My Plugin"
version = "1.0.0"

[[capabilities]]
type = "tool"
name = "my_tool"
description = "Does something useful"

[[capabilities]]
type = "hook"
event = "message_received"

Manifest formats:

  • plugin.toml — TOML format
  • plugin.json — JSON format
  • cc-plugin.json — Claude Code compatible
  • .mcp.json — MCP server config

Scope

Plugins operate within a scope that limits their access:

pub struct PluginScope {
    allowed_apis: Vec<String>,
    file_access: FileAccessPolicy,
    network_access: NetworkAccessPolicy,
}

Hook System

Hooks allow plugins to intercept and modify system events:

pub enum HookEvent {
    MessageReceived { message: Message },
    ToolInvoked { name: String, args: Value },
    ResponseGenerated { text: String },
}

pub trait HookHandler: Send + Sync {
    async fn handle(
        &self,
        event: &HookEvent,
    ) -> HookResult;
}

HookResult:

  • Continue — Proceed with the event
  • Intercept { modified } — Replace the event with modified version
  • Block { reason } — Cancel the event

Discovery

Extensions are discovered from multiple locations:

  1. ~/.aleph/plugins/ — User-installed plugins
  2. ./.aleph/plugins/ — Project-level plugins
  3. Built-in plugins bundled with Aleph

The PluginWatcher monitors these directories for changes and triggers hot reload.


Safety Properties

  • Sandboxed WASM — Extism provides memory isolation and capability-based security
  • Scope enforcement — Plugins can only access declared APIs
  • Manifest validation — All manifests are validated before loading
  • No static mut — Uses OnceLock for lazy initialization

Code Location

  • src/extension/mod.rsExtensionManager
  • src/extension/registry.rsPluginRegistry
  • src/extension/loader.rsPluginLoader
  • src/extension/manifest/ — Manifest parsing
  • src/extension/runtime/ — WASM and Node.js runtimes
  • src/extension/hooks/ — Hook system
  • src/extension/watcher.rs — Hot reload watcher
  • src/extension/scope.rs — Permission scopes

See Also

On this page