Plugin System Overview
Introduction
Section titled “Introduction”Aether uses an extension-based architecture where plugins register functionality at predefined extension points throughout the platform. This allows plugins to extend the system without modifying core code.
Key Concepts
Section titled “Key Concepts”Extension Points
Section titled “Extension Points”Extension points are specific locations in the UI where plugins can add functionality:
'work-unit.detail.tabs' // Add tabs to work unit details'work-unit.header.actions' // Add action buttons to work unit headers'customer.detail.tabs' // Add tabs to customer details'dashboard.widgets' // Add dashboard widgets'sidebar.widgets' // Add sidebar components// ... many morePlugin Manifest
Section titled “Plugin Manifest”Every plugin has a manifest that declares:
- Plugin metadata (name, version, author)
- What extensions it provides
- Where those extensions appear
- What components to render
- Lifecycle hooks
export const myPlugin: PluginManifest = { slug: 'my-plugin', name: 'My Plugin', version: '1.0.0', description: 'Plugin description',
extensions: [ { point: 'work-unit.detail.tabs', plugin: 'my-plugin', priority: 50, extension: { type: ExtensionType.TAB, id: 'my-tab', label: 'My Tab', component: MyTabComponent, } } ],};Extension Types
Section titled “Extension Types”Aether supports five extension types:
- TAB - Add custom tabs to detail views
- ACTION - Add buttons and actions
- WIDGET - Add dashboard/sidebar components
- COLUMN - Add custom table columns
- INDICATOR - Add badges and status markers
How Plugins Work
Section titled “How Plugins Work”1. Discovery
Section titled “1. Discovery”When the application starts:
export const ALL_PLUGIN_MANIFESTS: PluginManifest[] = [ timerPlugin, tasksPlugin, discussionsPlugin, // ... all discovered plugins];2. Registration
Section titled “2. Registration”The platform loads all manifests and builds an extension registry:
const registry = new ExtensionRegistry();
ALL_PLUGIN_MANIFESTS.forEach(manifest => { manifest.extensions.forEach(ext => { registry.register(ext.point, ext); });});3. Rendering
Section titled “3. Rendering”When the UI needs to render an extension point:
function WorkUnitDetailTabs({ workUnit }) { // Get all extensions for this point const extensions = registry.getExtensions('work-unit.detail.tabs', { businessType: workUnit.businessType, // ... context });
return ( <Tabs> {extensions.map(ext => ( <Tab key={ext.id} label={ext.label} icon={ext.icon}> <ext.component context={{ workUnit, ... }} /> </Tab> ))} </Tabs> );}Constitutional Isolation
Section titled “Constitutional Isolation”Plugins follow strict isolation principles:
✅ Allowed
Section titled “✅ Allowed”- Import from
@aether/plugins-core(plugin framework) - Import from
@aether/ui(shared UI components) - Import from
@aether/database(Prisma client for DB access) - Import from own plugin package
- Use tRPC for server communication
❌ Forbidden
Section titled “❌ Forbidden”- Import from
apps/web/src(web app internals) - Import from other plugin packages
- Direct database access without Prisma
- Modify global state
- Import React/Next.js directly (use @aether/ui exports)
Example:
// ✅ CORRECTimport { Card, Text, Stack } from '@aether/ui';import { IconClock } from '@aether/ui/icons';import { trpc } from '@/lib/trpc/client';
// ❌ WRONGimport { Card } from '@mantine/core'; // Use @aether/ui insteadimport { SomeComponent } from 'apps/web/src/components'; // Never import from web appimport { otherPlugin } from '@aether/plugin-other'; // No cross-plugin importsPlugin Lifecycle
Section titled “Plugin Lifecycle”Installation
Section titled “Installation”- Plugin package added to
packages/plugins/ npm installinstalls dependenciesnpm run generate:manifestsdiscovers plugin- Manifest added to registry
Enabling
Section titled “Enabling”- User enables plugin in Settings > Plugins
onEnablelifecycle hook called- Plugin extensions registered
- UI updates with new extensions
- User navigates to a view with plugin extensions
- Platform checks if plugin is enabled
- Platform evaluates match conditions
- Extension component rendered with context
Disabling
Section titled “Disabling”- User disables plugin in Settings > Plugins
onDisablelifecycle hook called- Extensions unregistered
- UI updates to remove extensions