# NexaDash Plugin Developer Documentation ## Table of Contents 1. [What is a NexaDash Plugin?](#what-is-a-nexadash-plugin) 2. [Plugin Structure](#plugin-structure) 3. [Manifest](#manifest) 4. [Settings & Credentials Schema](#settings--credentials-schema) 5. [Widget Schema](#widget-schema) 6. [Backend Connector](#backend-connector) 7. [Frontend Widget](#frontend-widget) 8. [Permissions](#permissions) 9. [Secrets Handling](#secrets-handling) 10. [Background Jobs](#background-jobs) 11. [Error Handling](#error-handling) 12. [Versioning](#versioning) 13. [Testing](#testing) 14. [Security Rules](#security-rules) 15. [Example: Creating a Plugin](#example-creating-a-plugin) 16. [Packaging](#packaging) 17. [Upload](#upload) 18. [Best Practices](#best-practices) ## What is a NexaDash Plugin? A NexaDash plugin is a declarative package that connects an external service to the dashboard. It defines: - A `plugin.manifest.json` describing metadata, schemas and widgets. - A backend connector (declarative or built-in Python class) that fetches data from the service. - Optional frontend widget components that render the data. Plugins never execute arbitrary code in the main API process. Connectors are loaded from a known registry and run in a sandboxed layer. ## Plugin Structure ```text my-plugin/ plugin.manifest.json README.md widgets/ MyWidget.tsx connector/ connector.py (optional, for custom Python connectors) ``` ## Manifest ```json { "id": "my-plugin", "name": "My Plugin", "version": "1.0.0", "nexadashPluginApi": "1.0", "description": "Short description", "author": "Your Name", "category": "Infrastructure", "permissions": ["network:outbound", "secrets:read"], "settingsSchema": {}, "credentialsSchema": {}, "widgets": [], "apiRoutes": [], "hasFrontend": true, "healthcheck": { "method": "GET", "intervalSeconds": 60, "timeoutSeconds": 30 } } ``` ## Settings & Credentials Schema Schemas follow JSON Schema with a few conventions: - `format: "password"` hides the value in the UI. - `enum` renders as a select dropdown. - Credentials are stored encrypted and never returned to the frontend. ## Widget Schema Each widget needs: - `id`: unique widget type identifier. - `name`: human-readable label. - `description`: short help text. - `defaultWidth`, `defaultHeight`: default grid size. - `settingsSchema`: per-widget settings. ## Backend Connector Built-in connectors cover many services. For custom services, you can provide a Python class that extends `PluginConnector`: ```python from apps.api.src.plugins.base import PluginConnector class MyConnector(PluginConnector): id = "my-plugin" name = "My Plugin" async def healthcheck(self): return {"status": "ok"} async def fetch_widget_data(self, widget_type, settings): return {"value": 42} ``` Register it in `apps/api/src/plugins/registry.py`. ## Frontend Widget Frontend widgets are React components that receive `data` and `settings` props. They are rendered by the dashboard editor. ## Permissions Common permissions: - `network:outbound` — plugin may make HTTP requests. - `secrets:read` — plugin may decrypt its own credentials. - `jobs:schedule` — plugin may schedule background jobs. ## Secrets Handling Never log or return secrets. The backend decrypts credentials only when executing a connector and passes them to the connector instance. ## Background Jobs Use the Celery worker for long-running sync tasks. Schedule via the `background_jobs` API or from a connector. ## Error Handling Return `{status: "error", message: "..."}` on failures. The dashboard shows a user-friendly error card. ## Versioning Use semantic versioning. The plugin registry tracks versions in `plugin_versions`. ## Testing - Validate the manifest with `@nexadash/plugin-sdk`. - Test connectors with mocked HTTP clients. - Add API tests for custom endpoints. ## Security Rules - Only `http`/`https` URLs are allowed (SSRF protection). - No credentials in frontend code. - No dynamic code execution (`eval`, `exec`). - Use least-privilege API tokens. - Validate all inputs with schemas. ## Example: Creating a Plugin See `plugins/example-plugin/` for a complete working example. ## Packaging Zip the plugin directory with the manifest at the root: ```bash zip -r my-plugin.zip my-plugin/ ``` ## Upload Upload the ZIP in the UI under **Plugins → Upload Plugin** or via the API: ```bash curl -F "file=@my-plugin.zip" https://nexadash.local/api/v1/plugins/upload ``` ## Best Practices - Keep widgets focused on one metric. - Use pagination for large lists. - Cache expensive requests in the connector. - Provide clear error messages. - Document required credentials.