Plugin System
How the NaaP plugin system works: types, lifecycle, manifest format, and loading mechanism.
Plugin Types#
NaaP supports three types of plugins:
Full-Stack Plugins#
Complete features with frontend UI, backend API, and database:
- Frontend: React app compiled to UMD
- Backend: Express server with API routes
- Database: PostgreSQL via Prisma ORM
Examples: Dashboard, Gateway Manager, Community Forum
Frontend-Only Plugins#
UI components without a backend:
- Frontend: React app compiled to UMD
- Uses shell API proxy for data (e.g., external APIs)
Examples: Network Analytics (reads from existing APIs), Wallet (Web3 integration)
Backend-Only Plugins#
API services without a UI:
- Backend: Express server providing API endpoints
- Other plugins can consume these APIs
Examples: Data aggregation services, integration adapters
Plugin Lifecycle#
1. Discovery#
The shell queries the plugin registry to find available plugins:
TypeScript
// Shell fetches installed plugins for the current user/team
GET /api/v1/plugins?installed=true2. Registration#
Plugins are registered in the database with their manifest:
JSON
| 1 | { |
| 2 | "name": "my-plugin", |
| 3 | "displayName": "My Plugin", |
| 4 | "version": "1.0.0", |
| 5 | "enabled": true, |
| 6 | "routes": ["/my-plugin", "/my-plugin/*"], |
| 7 | "order": 50 |
| 8 | } |
3. Loading#
When a user navigates to a plugin route:
- The Plugin Loader component mounts
- It fetches the plugin manifest and UMD entry point URL
- The UMD bundle is loaded via a script tag
- The global
mount()function is called with a container element and shell context
4. Mounting#
TypeScript
| 1 | // Plugin's mount.tsx |
| 2 | export function mount(container: HTMLElement, context: ShellContext) { |
| 3 | const root = createRoot(container); |
| 4 | root.render( |
| 5 | <ShellProvider value={context}> |
| 6 | <App /> |
| 7 | </ShellProvider> |
| 8 | ); |
| 9 | |
| 10 | // Return cleanup function |
| 11 | return () => root.unmount(); |
| 12 | } |
5. Running#
While mounted, the plugin:
- Receives updates through the shell context (theme changes, team switches)
- Can emit and listen for events on the event bus
- Makes API calls through the shell proxy
6. Unmounting#
When the user navigates away:
- The cleanup function returned by
mount()is called - React root is unmounted
- Event listeners are cleaned up
Plugin Manifest (plugin.json)#
The manifest file is the source of truth for a plugin's configuration:
JSON
| 1 | { |
| 2 | "name": "my-plugin", |
| 3 | "displayName": "My Plugin", |
| 4 | "version": "1.0.0", |
| 5 | "description": "A brief description", |
| 6 | "category": "monitoring", |
| 7 | |
| 8 | "frontend": { |
| 9 | "entry": "./frontend/dist/production/my-plugin.js", |
| 10 | "devPort": 3010, |
| 11 | "routes": ["/my-plugin", "/my-plugin/*"], |
| 12 | "navigation": { |
| 13 | "label": "My Plugin", |
| 14 | "icon": "Activity", |
| 15 | "section": "main", |
| 16 | "order": 50 |
| 17 | } |
| 18 | }, |
| 19 | |
| 20 | "backend": { |
| 21 | "entry": "./backend/dist/server.js", |
| 22 | "port": 4010, |
| 23 | "apiPrefix": "/api/v1/my-plugin", |
| 24 | "healthCheck": "/healthz" |
| 25 | }, |
| 26 | |
| 27 | "database": { |
| 28 | "type": "postgresql", |
| 29 | "schema": "./backend/prisma/schema.prisma" |
| 30 | }, |
| 31 | |
| 32 | "permissions": { |
| 33 | "required": ["read:data"], |
| 34 | "optional": ["write:data", "admin:settings"] |
| 35 | } |
| 36 | } |
Plugin Categories#
| Category | Description |
|---|---|
analytics | Data visualization and reporting |
communication | Messaging, forums, notifications |
developer-tools | Development utilities and APIs |
finance | Tokens, wallets, treasury |
infrastructure | Node management, scaling |
integration | External service connectors |
monitoring | Health checks, dashboards |
networking | Network protocols, routing |
security | Auth, encryption, auditing |
storage | File and data storage |
other | Uncategorized plugins |