# Plugins Implementation Details Plugins provide data to the NetAlertX core, which processes it to detect changes, discover new devices, raise alerts, and apply heuristics. --- ## Overview: Plugin Data Flow 1. Each plugin runs on a defined schedule. 2. Aligning all plugin schedules is recommended so they execute in the same loop. 3. During execution, all plugins write their collected data into the **`CurrentScan`** table. 4. After all plugins complete, the `CurrentScan` table is evaluated to detect **new devices**, **changes**, and **triggers**. Although plugins run independently, they contribute to the shared `CurrentScan` table. To inspect its contents, set `LOG_LEVEL=trace` and check for the log section: ``` ================ CurrentScan table content ================ ``` --- ## `config.json` Lifecycle This section outlines how each plugin’s `config.json` manifest is read, validated, and used by the core and plugins. It also describes plugin output expectations and the main plugin categories. > [!TIP] > For detailed schema and examples, see the [Plugin Development Guide](PLUGINS_DEV.md). --- ### 1. Loading * On startup, the core loads `config.json` for each plugin. * The file acts as a **plugin manifest**, defining metadata, runtime configuration, and database mappings. --- ### 2. Validation * The core validates required keys (for example, `RUN`). * Missing or invalid entries may be replaced with defaults or cause the plugin to be disabled. --- ### 3. Preparation * Plugin parameters (paths, commands, and options) are prepared for execution. * Database mappings (`mapped_to_table`, `database_column_definitions`) are parsed to define how data integrates with the main app. --- ### 4. Execution * Plugins may run: * On a fixed schedule. * Once at startup. * After a notification or other trigger. * The scheduler executes plugins according to their `interval`. --- ### 5. Parsing * Plugin output must be **pipe-delimited (`|`)**. * The core parses each output line following the **Plugin Interface Contract**, splitting and mapping fields accordingly. --- ### 6. Mapping * Parsed fields are inserted into the plugin’s `Plugins_*` table. * Data can be mapped into other tables (e.g., `Devices`, `CurrentScan`) as defined by: * `database_column_definitions` * `mapped_to_table` **Example:** `Object_PrimaryID → devMAC` --- ### 6a. Plugin Output Contract All plugins must follow the **Plugin Interface Contract** defined in `PLUGINS_DEV.md`. Output values are pipe-delimited in a fixed order. #### Identifiers * `Object_PrimaryID` and `Object_SecondaryID` uniquely identify records (for example, `MAC|IP`). #### Watched Values (`Watched_Value1–4`) * Used by the core to detect changes between runs. * Changes in these fields can trigger notifications. #### Extra Field (`Extra`) * Optional additional value. * Stored in the database but not used for alerts. #### Helper Values (`Helper_Value1–3`) * Optional auxiliary data (for display or plugin logic). * Stored but not alert-triggering. #### Mapping * While the output format is flexible, the plugin’s manifest determines the destination and type of each field. --- ### 7. Persistence * Parsed data is **upserted** into the database. * Conflicts are resolved using the combined key: `Object_PrimaryID + Object_SecondaryID`. --- ## Plugin Categories Plugins fall into several functional categories depending on their purpose and expected outputs. ### 1. Device Discovery Plugins * **Inputs:** None, subnet, or discovery API. * **Outputs:** `MAC` and `IP` for new or updated device records in `Devices`. * **Mapping:** Required – usually into `CurrentScan`. * **Examples:** `ARPSCAN`, `NMAPDEV`. --- ### 2. Device Data Enrichment Plugins * **Inputs:** Device identifiers (`MAC`, `IP`). * **Outputs:** Additional metadata (for example, open ports or sensors). * **Mapping:** Controlled via manifest definitions. * **Examples:** `NMAP`, `MQTT`. --- ### 3. Name Resolver Plugins * **Inputs:** Device identifiers (`MAC`, `IP`, hostname`). * **Outputs:** Updated `devName` and `devFQDN`. * **Mapping:** Typically none. * **Note:** Adding new resolvers currently requires a core change. * **Examples:** `NBTSCAN`, `NSLOOKUP`. --- ### 4. Generic Plugins * **Inputs:** Custom, based on the plugin logic or script. * **Outputs:** Data displayed under **Integrations → Plugins** only. * **Mapping:** Not required. * **Examples:** `INTRSPD`, custom monitoring scripts. --- ### 5. Configuration-Only Plugins * **Inputs/Outputs:** None at runtime. * **Purpose:** Used for configuration or maintenance tasks. * **Examples:** `MAINT`, `CSVBCKP`. --- ## Post-Processing After persistence: * The core generates notifications for any watched value changes. * The UI updates with new or modified data. * Plugins with UI-enabled data display under **Integrations → Plugins**. --- ## Field Update Authorization (SET_ALWAYS / SET_EMPTY) For tracked fields (devMac, devName, devLastIP, devVendor, devFQDN, devSSID, devParentMAC, devParentPort, devParentRelType, devVlan), plugins can configure how they interact with the authoritative field update system. ### SET_ALWAYS **Mandatory when field is tracked.** Controls whether a plugin field is enabled: - `"1"` - Plugin can always overwrite this field when authorized (subject to source-based permissions) - `"0"` - Plugin doesn't use this field **Authorization logic:** Even with `SET_ALWAYS: "1"`, the plugin respects source-based permissions: - Cannot overwrite `USER` source (user manually edited) - Cannot overwrite `LOCKED` source (user locked field) - Can overwrite `NEWDEV` or plugin-owned sources (if plugin has SET_ALWAYS enabled) **Example in config.json:** ```json { "setKey": "NEWDEV_devName", "displayName": "Device Name", "SET_ALWAYS": "1" } ``` ### SET_EMPTY **Optional field override.** Restricts when a plugin can update a field: - `"1"` - Overwrite only if current value is empty OR source is NEWDEV (conservative mode) - `"0"` - No extra restriction; respect authorization logic (default) **Use case:** Some plugins discover optional enrichment data (like vendor/hostname) that shouldn't override user-set or existing values. Use `SET_EMPTY: "1"` to be less aggressive. **Example in config.json:** ```json { "setKey": "NEWDEV_devVendor", "displayName": "Device Vendor", "SET_ALWAYS": "1", "SET_EMPTY": "1" } ``` ### Authorization Decision Flow 1. **Source check:** Is field LOCKED or USER? → REJECT (protected) 2. **SET_ALWAYS check:** Is SET_ALWAYS enabled for this plugin+field? → YES: ALLOW (can overwrite empty values, NEWDEV, plugin sources, etc.) | NO: Continue to step 3 3. **SET_EMPTY check:** Is SET_EMPTY enabled AND field non-empty+non-NEWDEV? → REJECT 4. **Default behavior:** Allow overwrite if field empty or NEWDEV source ### Plugin Field Mappings Reference This table shows all device discovery and enrichment plugins and their tracked field configuration: | Plugin | Tracked Fields | Behavior | |--------|---|---| | ARPSCAN | devMac, devLastIP | SET_ALWAYS for both | | IPNEIGH | devMac, devLastIP | SET_ALWAYS for both | | DHCPLSS | devMac, devLastIP | SET_ALWAYS for both | | ASUSWRT | devMac, devLastIP | SET_ALWAYS for both | | LUCIRPC | devMac, devLastIP | SET_ALWAYS for both | | PIHOLE | devMac, devLastIP, devName, devVendor | SET_ALWAYS for MAC/IP | | PIHOLEAPI | devMac, devLastIP, devName, devVendor | SET_ALWAYS for MAC/IP, SET_EMPTY for name/vendor | | NBTSCAN | devName | SET_ALWAYS | | DIGSCAN | devName, devFQDN | SET_ALWAYS | | NSLOOKUP | devName, devFQDN | SET_ALWAYS | | AVAHISCAN | devName | SET_ALWAYS | | VNDRPDT | devMac, devVendor | SET_ALWAYS for both | | SNMPDSC | devMac, devLastIP | SET_ALWAYS for both | | UNIFIMP | devMac, devLastIP, devName, devVendor, devSSID, devParentMAC, devParentPort | SET_ALWAYS for MAC/IP | | UNIFIAPI | devMac, devLastIP, devName, devParentMAC | SET_ALWAYS for MAC/IP | **Note:** Check each plugin's `config.json` manifest for its specific SET_ALWAYS/SET_EMPTY configuration. --- ## Summary The lifecycle of a plugin configuration is: **Load → Validate → Prepare → Execute → Parse → Map → Persist → Post-process** Each plugin must: * Follow the **output contract**. * Declare its type and expected output structure. * Define mappings and watched values clearly in `config.json`.