Sentinel Agent
The Sentinel Agent monitors the host it runs on, discovers services, collects system and process metrics, and generates threshold-based alerts.
discovery_agent.json), REST API, and internal KV bucket names are unchanged and still read discovery_agent / Discovery Agent — the rename is UI-facing only.
Overview
| Property | Value |
|---|---|
| Service name | MugnsoftDiscoveryAgent |
| Default port | 8070 |
| Configuration file | discovery_agent.json |
| Version | v4.0.0 |
| Storage | embedded key-value store (discovery.db, metrics.db, process.db, system.db) |
Service Management
CLI Commands
# Install as service and register with the Webserver
discovery_agent install <webserver_ip>:<port>
# Re-register with the Webserver
discovery_agent register <webserver_ip>:<port>
# Migrate to a new Webserver
discovery_agent migrate <webserver_ip>:<port> <jwt_token>
# Start / Stop / Restart
discovery_agent start
discovery_agent stop
discovery_agent restart
# Run in foreground
discovery_agent run
# Remove the service
discovery_agent uninstall
Configuration
Core Settings
{
"name": "agent1",
"port": "8070",
"webserver": "192.168.1.100:8050",
"integratorEndPoint": "192.168.1.100:8052",
"location": "Datacenter-1",
"sched": "5MIN",
"processes": "nginx,mysql,java",
"ports": "80,443,3306",
"logLevel": "info"
}
| Field | Description | Default |
|---|---|---|
name |
Unique agent identifier | required |
port |
API listening port | "8070" |
webserver |
Webserver IP:port for registration | required |
integratorEndPoint |
Integrator IP:port for data forwarding | (empty) |
probeEndPoint |
Probe name(s) that run delegated URL/API/port checks (required when urls, apis, or ports are set) |
(empty) |
appName |
Application name this agent’s data is grouped under | (empty) |
location |
Geographic/logical location label | (empty) |
tags |
Comma-separated tags for categorization | (empty) |
sched |
Discovery interval | "5MIN" |
schedMonitors |
Interval for delegated monitor checks | same as sched |
processes |
Comma-separated process names to monitor | (empty) |
ports |
Comma-separated ports to monitor | (empty) |
Scheduling Options
| Label | Interval |
|---|---|
1MIN |
Every minute |
2MIN |
Every 2 minutes |
5MIN |
Every 5 minutes |
10MIN |
Every 10 minutes |
30MIN |
Every 30 minutes |
1H |
Every hour |
4H |
Every 4 hours |
8H |
Every 8 hours |
1D |
Daily |
Directory Structure
<install_dir>/
├── discovery_agent(.exe) # Executable
├── discovery_agent.json # Service configuration
├── config/
│ ├── sec/ # RSA keys
│ └── ssl/ # TLS certificates
├── data/
│ ├── process_list.txt # Monitored process names
│ ├── port_list.txt # Monitored network ports
│ ├── process_thresholds.json # Per-process threshold config
│ ├── system_thresholds.json # System threshold config
│ ├── discover_metrics.json # Latest collected metrics
│ └── alerts.json # Generated alerts
├── dbs/ # embedded key-value databases
│ ├── discovery.db # Main discovery data
│ ├── metrics.db # Historical metrics
│ ├── process.db # Process data
│ ├── system.db # System data
│ └── backup/ # Automated backups
└── log/ # Rotated log files
Discovery Mechanisms
Process Discovery
The Sentinel Agent monitors processes by name. Configure which processes to watch:
Method 1: Configuration file
{
"processes": "nginx,mysqld,java,python3"
}
Method 2: Process list file (data/process_list.txt)
nginx:web,production
mysqld:database,production
java:application
python3.12:scripts
Format: processname[.exe]:tag1,tag2 (colon-separated, with optional tags)
Port Discovery
Monitor which processes are listening on specific ports:
Configuration file:
{
"ports": "80,443,3306,5432,8080"
}
Port list file (data/port_list.txt):
80
443
3306
5432
The agent maps port listeners to process names via TCP connection enumeration.
Network Traffic Discovery
Uses packet capture (pcap) via the gopacket library for live traffic analysis:
- Captures packets on all network interfaces
- Duration configurable via
discoCapInterval - Aggregates traffic by process ID (PID)
- Calculates bandwidth in kbits/s (in/out)
- Generates network topology graph in Cytoscape format for visualization
The Web UI renders that graph with link colours driven by traffic severity and label colours driven by protocol family — see Reading the discovery graph.
Packet capture is a runtime dependency
Bandwidth figures come from captured packets, so the host needs a pcap runtime and the privilege to open it. This is a runtime requirement only — nothing needs to be installed to build or upgrade the agent.
| Platform | Needs | Privileges |
|---|---|---|
| Windows | The Npcap runtime installer (npcap.com). No SDK, no headers — the agent loads wpcap.dll at startup. |
Administrator / LocalSystem when Npcap was installed with Restrict driver access to Administrators only (the modern default). |
| Linux | Runtime libpcap (libpcap0.8 on Debian/Ubuntu, libpcap on RHEL). The -dev package is build-time only. |
CAP_NET_RAW, either by running as root or with setcap cap_net_raw+ep <agent binary>. |
On RHEL, the shipped binary is linked against libpcap.so.0.8 while the distro provides libpcap.so.1. Create the link once:
ln -s /usr/lib64/libpcap.so.1 /usr/lib64/libpcap.so.0.8
What you lose when capture is unavailable
The failure is silent and the agent stays up. It logs that it is proceeding without capturing packets, and keeps collecting everything else:
- Kept — the full connection inventory (local/remote IP and port, PID, process name, direction, interface). The topology map is intact.
- Kept — protocol labels, but inferred from port number and process name only, with no wire evidence. Everything lands in the guessed confidence tier, which is why the Unidentified bucket in the Proto filter grows on a host with no capture.
- Lost — every kbits/s value. Per-process Traffic In / Traffic Out stay at
0, map link usage stays at0, and traffic thresholds can never fire.
Metrics that do not depend on packet capture are unaffected: the per-interface netBytesRate and netTxDrops come from OS counters, as do CPU, memory, disk and filesystem metrics.
Capture status is reported, so zero is never ambiguous
A bandwidth of 0 looks the same whether a link is genuinely idle or whether no driver was there to measure it. Every collection run therefore reports a capture status alongside the metrics, and the Web UI surfaces it:
- an amber No packet capture badge in the discovery-graph toolbar, whose tooltip repeats the agent’s own reason (no pcap runtime, permission denied, no usable interface);
- a line at the top of the Proto filter dropdown, explaining the Unidentified bucket where you meet it;
- edge tooltips read “not measured — no packet capture” instead of
0.000 kbits/s, and their bandwidth sparkline is suppressed rather than drawn flat from a history of zeros; - in the process report, the Traffic In and Traffic Out threshold pills and their two chart panels carry a warning icon. No other metric in that report is affected.
When several agents are merged into one map (level 2), the badge means at least one contributing agent could not capture, and the wording says so.
A genuine 0.000 is still displayed when capture is working — that one is a measurement.
Capture behaviour settings
| Key | Default | Effect |
|---|---|---|
discoCapInterval |
"10" |
Seconds of traffic sniffed per collection cycle. |
discoProtocolDetectionEnabled |
"true" |
Application-protocol identification from packet payloads. Set to "false" on hosts where payload inspection is unwanted — the capture loop then never touches packet contents, and protocols fall back to port/process-name inference. |
discoCaptureLoopbackEnabled |
"false" |
Capture the loopback interface, where host-local IPC lives. Expect bandwidth figures to rise when this is switched on, since host-local traffic starts being counted — which is why it defaults to off. |
Both flags are read at the start of every collection run, so a change takes effect on the next cycle — no restart. An absent or unreadable key falls back to the default above, so configurations written before these keys existed keep their previous behaviour.
lo); Windows pcap devices are named \Device\NPF_…, so the flag has no effect there. If the Npcap Loopback Adapter is installed on a Windows host, loopback traffic is captured regardless of the setting.
Delegated Monitors (URL / API / Port checks)
Beyond host metrics, a Sentinel Agent can define synthetic checks — urls, apis, pings, nslookups, tcps, dbs, and syss — directly in its configuration. The agent does not run these checks itself; it delegates them to one or more Monitor probes named in probeEndPoint.
{
"appName": "Shop",
"probeEndPoint": "probe1,probe-win2",
"ports": "80,443,3306",
"urls": [{ "name": "shop-home", "url": "https://shop.example.com/" }],
"apis": [
{
"name": "checkout-api",
"url": "https://api.example.com/health",
"typeApi": "GET"
}
]
}
probeEndPoint is required whenever urls, apis, or ports are defined. Without it the agent logs an error and the checks are not registered. The interval for these delegated checks is controlled by schedMonitors.
This lets you describe a host’s full service surface (system health and its endpoints) in one place, while the actual URL/API execution runs on a probe that has network reach to the targets.
Collected Metrics
System Metrics
| Metric | Description | Threshold Config |
|---|---|---|
| CPU % | Overall CPU usage percentage | discoCPUGFixedThreshCri/Maj/Min |
| Memory % | Memory usage percentage | discoMEMGFixedThreshCri/Maj/Min |
| Load Average (Linux) | 5-minute load average | discoLoadAvg5minGFixedThreshCri/Maj/Min |
| Filesystem % | Disk usage per mount point | discoFSGFixedThreshCri/Maj/Min |
| TCP Sockets | Open TCP socket count | discoTCPSocketsGFixedThreshCri/Maj/Min |
| CPU I/O Wait (Linux) | CPU time waiting for I/O | discoCpuIOWaitGFixedThreshCri/Maj/Min |
| Uptime | System uptime | (informational) |
| Boot time | Last boot timestamp | (informational) |
Per-Process Metrics
| Metric | Description | Threshold Config |
|---|---|---|
| CPU % | Process CPU usage | discoCPUPFixedThresh* or discoCPUPAutoThreshold |
| Memory | RSS memory usage | discoMEMPFixedThresh* or discoMEMPAutoThreshold |
| Swap | Swap usage | discoSWAPPFixedThresh* or discoSWAPPAutoThreshold |
| I/O Read | Disk read bytes/s | discoIostatReadPFixedThresh* or auto |
| I/O Write | Disk write bytes/s | discoIostatWritePFixedThresh* or auto |
| CPU User % | User-mode CPU time | discoCPUUserPFixedThresh* |
| Threads | Thread count | discoNumThreadsPFixedThresh* |
| File Descriptors (Linux) | Open FD count | discoNumFDsPFixedThresh* |
| Network Traffic In | Inbound bandwidth (kbits/s) | via trafficThresholds |
| Network Traffic Out | Outbound bandwidth (kbits/s) | via trafficThresholds |
Metric Enable/Disable
Each metric can be individually enabled or disabled:
{
"discoCPUGMonEnabled": "true",
"discoMEMGMonEnabled": "true",
"discoLoadAvg5minGMonEnabled": "true",
"discoCPUPMonEnabled": "true",
"discoMEMPMonEnabled": "true",
"discoSWAPPMonEnabled": "false"
}
Threshold Configuration
Fixed Thresholds
Set explicit threshold values per metric:
{
"discoCPUGFixedThreshCri": "95",
"discoCPUGFixedThreshMaj": "85",
"discoCPUGFixedThreshMin": "75"
}
Auto Thresholds
Let the agent calculate thresholds based on historical data:
{
"discoCPUPAutoThreshold": "true",
"discoMEMPAutoThreshold": "true"
}
Auto-thresholds use statistical analysis (standard deviation) of historical metrics to dynamically set threshold values.
When auto-threshold is enabled for a metric, leave the corresponding *FixedThresh* fields empty (""). The web UI displays these fields as ∞: while the baseline is being computed (warm-up period), no alert fires for that metric.
999999999999999.00 for this purpose — the agent now automatically normalizes that legacy value back to "" when loading and saving its configuration.
Traffic Thresholds
Per-process network traffic thresholds with directional operators:
{
"trafficThresholds": {
"nginx": {
"operator": ">",
"critical": 10000,
"major": 5000,
"minor": 1000
}
}
}
Operators: > (alert when above) or < (alert when below).
Alerting
Alert Status Levels
| Level | Integer | Description |
|---|---|---|
| OK | 0 | Within normal range |
| MINOR | 1 | Minor threshold breached |
| MAJOR | 2 | Major threshold breached |
| CRITICAL | 3 | Critical threshold breached |
Notification Channels
Identical to other components:
| Channel | Configuration |
|---|---|
smtpEnabled, smtpServerName, smtpPort, smtpUsername, smtpPwd, smtpTLS |
|
| Slack | slackChannel, slackToken |
| Teams | teamsWebhook |
| PagerDuty | pagerDutyAPIKey |
| Custom Script | scriptAction, scriptActionT |
Alert Timing
| Setting | Description |
|---|---|
notifyAfter |
Wait for N consecutive threshold breaches before first alert |
notifyFor |
Continue alerting for N cycles after breach |
notifyStatus |
Minimum status level to trigger (e.g., “CRITICAL”) |
Alert Routing
Alerts can be routed in two ways:
- Via Integrator (recommended) – Sentinel Agent pushes metrics to the Integrator, which handles alerting centrally
- Local alerting – Sentinel Agent sends alerts directly via configured channels (when no Integrator is configured)
Concurrency is controlled by semaphores: 20 concurrent goroutines for Integrator communication, 10 for local alerting.
Execution Flow
Cron trigger (every N minutes)
|
v
execDiscovery()
|
v
runDiscoveryMetrics()
├── CollectSystemMetrics() --> CPU, memory, load, FS, sockets
├── CollectProcessMetrics() --> per-process CPU, memory, I/O, traffic
├── CollectNetworkMetrics() --> pcap traffic capture, connections
└── GenerateCytoscapeData() --> network topology graph
|
v
Write data/discover_metrics.json + data/alerts.json
|
v
discoveryDataFile()
├── processMetricsKV() --> evaluate per-process thresholds
├── systemMetricsKV() --> evaluate system thresholds
└── Route alerts:
├── goDataSent2Integrator() (if Integrator configured)
└── goHandleAlerting() (local fallback)
Background Cron Jobs
| Job | Schedule | Purpose |
|---|---|---|
| Discovery | User-configured (1MIN-1D) | Main metrics collection |
| System metrics | Every 1 minute | Lightweight CPU/memory sampling |
| Auto-backup | Configurable interval | embedded key-value store backups |
| Metrics purge | Daily at 01:00 | Delete old metrics |
| Monitor purge | Daily at 03:00 | Clean up historical data |
Per-item metrics and devices that go away
Disk and network metrics are stored per item — one series per drive, mount point, or interface. The agent creates a series the first time it sees an item, so a USB drive mounted once, or a network adapter the OS has since renamed, leaves a series behind that no longer receives samples.
Two mechanisms keep those out of the way:
- Reports drop them. For auto-discovered items (disks, network interfaces), a series that produced no sample inside the requested time window is left out of the report entirely, so the UI no longer draws an empty “no data” panel for a drive that does not exist. An item that is merely idle still writes a sample every cycle and is unaffected.
- Retention removes them. The nightly purge deletes any item whose newest sample predates the retention window, together with its change and status entries.
Retention covers every metric
The metrics purge walks the whole metrics store rather than a fixed list of metric names, because the metric set differs by platform (no load average or I/O wait on Windows) and grows with each release.
After upgrading, the first nightly purge may have months of accumulated samples to remove for metrics that were never purged before (TCP sockets, swap rates, and all the per-device disk and network series). It runs one transaction per metric so collection is not blocked for the whole job.
The database file does not shrink: freed pages are reused for new samples instead of being returned to the filesystem. What changes is that it stops growing.
REST API Endpoints
Authentication
| Method | Path | Description |
|---|---|---|
| POST | /api/auth |
Login (JWT) |
| POST | /loginComponent |
Component token (15min) |
| POST | /loginComponent1Year |
1-year token |
| GET | /refresh_token |
Refresh JWT |
Discovery
| Method | Path | Description |
|---|---|---|
| GET | /runDiscovery |
Trigger immediate metrics collection |
| GET | /getProcessByPort/:port |
Get process using a specific port |
| POST | /computeThreshold |
Calculate auto-baseline thresholds |
Data Access
| Method | Path | Description |
|---|---|---|
| GET | /api/db/:dbname/bucket/:bucket/key/:key |
Get specific metric |
| GET | /v1/db/:dbname/bucket/:bucket/all |
All keys/values |
| GET | /v1/db/:dbname/bucket/:bucket/allJSON |
All data as JSON |
| GET | /api/reportSysMetricsGraph/:start/:end |
System metrics graph |
| GET | /:type/reportGraph/:key/:start/:end |
Process metrics graph |
Settings
| Method | Path | Description |
|---|---|---|
| GET | /api/setting |
Get current config |
| POST | /updateSetting |
Update config |
| GET | /setting |
Get settings (encrypted) |
Health
| Method | Path | Description |
|---|---|---|
| GET | /ping |
Health check |
| GET | /uptime |
Service uptime |
| GET | /api/metrics |
Current system metrics |
| GET | /docs |
Swagger documentation |
Backup & Recovery
| Method | Path | Description |
|---|---|---|
| POST | /backupDatabase |
Manual backup |
| POST | /reinitDatabase |
Reset databases |
| GET | /api/backupKV |
Backup via API |
| GET | /api/reinitKV |
Reinitialize via API |
Certificates
| Method | Path | Description |
|---|---|---|
| GET | /reloadCertificates |
Reload TLS certificates |
| GET | /getCert |
Get TLS certificate |
| POST | /getCertFile/:filename |
Receive certificate file |
Logs
| Method | Path | Description |
|---|---|---|
| GET | /discovery/logfile/:key/:filename |
Download log file |
| GET | /partServerlogfile/:nbbytes |
Stream recent log entries |
| GET | /serverlogfiles/:filename |
Download log archive |