Files
NetAlertX/docs/PLUGINS_DEV_DATA_CONTRACT.md
jokob-sk f2d5e3254f DOCS: cleanup
Signed-off-by: jokob-sk <jokob.sk@gmail.com>
2026-01-15 11:00:28 +11:00

254 lines
9.2 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Plugin Data Contract
This document specifies the exact interface between plugins and the NetAlertX core.
> [!IMPORTANT]
> Every plugin must output data in this exact format to be recognized and processed correctly.
## Overview
Plugins communicate with NetAlertX by writing results to a **pipe-delimited log file**. The core reads this file, parses the data, and processes it for notifications, device discovery, and data integration.
**File Location:** `/tmp/log/plugins/last_result.<PREFIX>.log`
**Format:** Pipe-delimited (`|`), one record per line
**Required Columns:** 9 (mandatory) + up to 4 optional helper columns = 13 total
## Column Specification
> [!NOTE]
> The order of columns is **FIXED** and cannot be changed. All 9 mandatory columns must be provided. If you use any optional column (`HelpVal1`), you must supply all optional columns (`HelpVal1` through `HelpVal4`).
### Mandatory Columns (08)
| Order | Column Name | Type | Required | Description |
|-------|-------------|------|----------|-------------|
| 0 | `Object_PrimaryID` | string | **YES** | The primary identifier for grouping. Examples: device MAC, hostname, service name, or any unique ID |
| 1 | `Object_SecondaryID` | string | no | Secondary identifier for relationships (e.g., IP address, port, sub-ID). Use `null` if not needed |
| 2 | `DateTime` | string | **YES** | Timestamp when the event/data was collected. Format: `YYYY-MM-DD HH:MM:SS` |
| 3 | `Watched_Value1` | string | **YES** | Primary watched value. Changes trigger notifications. Examples: IP address, status, version |
| 4 | `Watched_Value2` | string | no | Secondary watched value. Use `null` if not needed |
| 5 | `Watched_Value3` | string | no | Tertiary watched value. Use `null` if not needed |
| 6 | `Watched_Value4` | string | no | Quaternary watched value. Use `null` if not needed |
| 7 | `Extra` | string | no | Any additional metadata to display in UI and notifications. Use `null` if not needed |
| 8 | `ForeignKey` | string | no | Foreign key linking to parent object (usually MAC address for device relationship). Use `null` if not needed |
### Optional Columns (912)
| Order | Column Name | Type | Required | Description |
|-------|-------------|------|----------|-------------|
| 9 | `HelpVal1` | string | *conditional* | Helper value 1. If used, all help values must be supplied |
| 10 | `HelpVal2` | string | *conditional* | Helper value 2. If used, all help values must be supplied |
| 11 | `HelpVal3` | string | *conditional* | Helper value 3. If used, all help values must be supplied |
| 12 | `HelpVal4` | string | *conditional* | Helper value 4. If used, all help values must be supplied |
## Usage Guide
### Empty/Null Values
- Represent empty values as the literal string `null` (not Python `None`, SQL `NULL`, or empty string)
- Example: `device_id|null|2023-01-02 15:56:30|status|null|null|null|null|null`
### Watched Values
**What are Watched Values?**
Watched values are fields that the NetAlertX core monitors for **changes between scans**. When a watched value differs from the previous scan, it can trigger notifications.
**How to use them:**
- `Watched_Value1`: Always required; primary indicator of status/state
- `Watched_Value24`: Optional; use for secondary/tertiary state information
- Leave unused ones as `null`
**Example:**
- Device scanner: `Watched_Value1 = "online"` or `"offline"`
- Port scanner: `Watched_Value1 = "80"` (port number), `Watched_Value2 = "open"` (state)
- Service monitor: `Watched_Value1 = "200"` (HTTP status), `Watched_Value2 = "0.45"` (response time)
### Foreign Key
Use the `ForeignKey` column to link objects to a parent device by MAC address:
```
device_name|192.168.1.100|2023-01-02 15:56:30|online|null|null|null|Found on network|aa:bb:cc:dd:ee:ff
ForeignKey (MAC)
```
This allows NetAlertX to:
- Display the object on the device details page
- Send notifications when the parent device is involved
- Link events across plugins
## Examples
### Valid Data (9 columns, minimal)
```csv
https://example.com|null|2023-01-02 15:56:30|200|null|null|null|null|null
printer-hp-1|192.168.1.50|2023-01-02 15:56:30|online|50%|null|null|Last seen in office|aa:11:22:33:44:55
gateway.local|null|2023-01-02 15:56:30|active|v2.1.5|null|null|Firmware version|null
```
### Valid Data (13 columns, with helpers)
```csv
service-api|192.168.1.100:8080|2023-01-02 15:56:30|200|45ms|true|null|Responding normally|aa:bb:cc:dd:ee:ff|extra1|extra2|extra3|extra4
host-web-1|10.0.0.20|2023-01-02 15:56:30|active|256GB|online|ok|Production server|null|cpu:80|mem:92|disk:45|alerts:0
```
### Invalid Data (Common Errors)
**Missing required column** (only 8 separators instead of 8):
```csv
https://google.com|null|2023-01-02 15:56:30|200|0.7898||null|null
Missing pipe
```
**Missing mandatory Watched_Value1** (column 3):
```csv
https://duckduckgo.com|192.168.1.1|2023-01-02 15:56:30|null|0.9898|null|null|Best|null
Must not be null
```
**Incomplete optional columns** (has HelpVal1 but missing HelpVal24):
```csv
device|null|2023-01-02 15:56:30|status|null|null|null|null|null|helper1
Has helper but incomplete
```
**Complete with helpers** (all 4 helpers provided):
```csv
device|null|2023-01-02 15:56:30|status|null|null|null|null|null|h1|h2|h3|h4
```
**Complete without helpers** (9 columns exactly):
```csv
device|null|2023-01-02 15:56:30|status|null|null|null|null|null
```
## Using `plugin_helper.py`
The easiest way to ensure correct output is to use the [`plugin_helper.py`](../front/plugins/plugin_helper.py) library:
```python
from plugin_helper import Plugin_Objects
# Initialize with your plugin's prefix
plugin_objects = Plugin_Objects("YOURPREFIX")
# Add objects
plugin_objects.add_object(
Object_PrimaryID="device_id",
Object_SecondaryID="192.168.1.1",
DateTime="2023-01-02 15:56:30",
Watched_Value1="online",
Watched_Value2=None,
Watched_Value3=None,
Watched_Value4=None,
Extra="Additional data",
ForeignKey="aa:bb:cc:dd:ee:ff",
HelpVal1=None,
HelpVal2=None,
HelpVal3=None,
HelpVal4=None
)
# Write results (handles formatting, sanitization, and file creation)
plugin_objects.write_result_file()
```
The library automatically:
- Validates data types
- Sanitizes string values
- Normalizes MAC addresses
- Writes to the correct file location
- Creates the file in `/tmp/log/plugins/last_result.<PREFIX>.log`
## De-duplication
The core runs **de-duplication once per hour** on the `Plugins_Objects` table:
- **Duplicate Detection Key:** Combination of `Object_PrimaryID`, `Object_SecondaryID`, `Plugin` (auto-filled from `unique_prefix`), and `UserData`
- **Resolution:** Oldest duplicate entries are removed, newest are kept
- **Use Case:** Prevents duplicate notifications when the same object is detected multiple times
## DateTime Format
**Required Format:** `YYYY-MM-DD HH:MM:SS`
**Examples:**
- `2023-01-02 15:56:30`
- `2023-1-2 15:56:30` ❌ (missing leading zeros)
- `2023-01-02T15:56:30` ❌ (wrong separator)
- `15:56:30 2023-01-02` ❌ (wrong order)
**Python Helper:**
```python
from datetime import datetime
# Current time in correct format
now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
# Output: "2023-01-02 15:56:30"
```
**Bash Helper:**
```bash
# Current time in correct format
date '+%Y-%m-%d %H:%M:%S'
# Output: 2023-01-02 15:56:30
```
## Validation Checklist
Before writing your plugin's `script.py`, ensure:
- [ ] **9 or 13 columns** in each output line (8 or 12 pipe separators)
- [ ] **Mandatory columns filled:**
- Column 0: `Object_PrimaryID` (not null)
- Column 2: `DateTime` in `YYYY-MM-DD HH:MM:SS` format
- Column 3: `Watched_Value1` (not null)
- [ ] **Null values as literal string** `null` (not empty string or special chars)
- [ ] **No extra pipes or misaligned columns**
- [ ] **If using optional helpers** (columns 912), all 4 must be present
- [ ] **File written to** `/tmp/log/plugins/last_result.<PREFIX>.log`
- [ ] **One record per line** (newline-delimited)
- [ ] **No header row** (data only)
## Debugging
**View raw plugin output:**
```bash
cat /tmp/log/plugins/last_result.YOURPREFIX.log
```
**Check line count:**
```bash
wc -l /tmp/log/plugins/last_result.YOURPREFIX.log
```
**Validate column count (should be 8 or 12 pipes per line):**
```bash
cat /tmp/log/plugins/last_result.YOURPREFIX.log | awk -F'|' '{print NF}' | sort | uniq
# Output: 9 (for minimal) or 13 (for with helpers)
```
**Check core processing in logs:**
```bash
tail -f /tmp/log/stdout.log | grep -i "YOURPREFIX\|Plugins_Objects"
```
## See Also
- [Plugin Settings System](PLUGINS_DEV_SETTINGS.md) - How to accept user input
- [Data Sources](PLUGINS_DEV_DATASOURCES.md) - Different data source types
- [Debugging Plugins](DEBUG_PLUGINS.md) - Troubleshooting plugin issues