Inventory and Heartbeat
Learn how external systems send inventory data and heartbeat signals to My platform.
Overview
After system registration, external systems communicate with My through two mechanisms:
- Inventory: Complete system information snapshot (hardware, software, configuration)
- Heartbeat: Periodic "I'm alive" signal to indicate system is active
Both operations use HTTP Basic Authentication with the registered credentials.
Authentication
Credentials
Use the credentials obtained during system lifecycle:
- Username:
system_key(received at registration) - Password:
system_secret(from system creation)
HTTP Basic Auth
Header format:
Authorization: Basic base64(system_key:system_secret)
Example:
system_key: NOC-F64B-A989-C9E7-45B9-A55D-59EC-6545-40EE
system_secret: my_a1b2c3d4e5f6g7h8i9j0.k1l2m3n4o5p6q7r8s9t0u1v2w3x4y5z6a7b8c9d0
Base64 encode: "NOC-F64B-A989-C9E7-45B9-A55D-59EC-6545-40EE:my_a1b2c3d4e5f6g7h8i9j0.k1l2m3n4o5p6q7r8s9t0u1v2w3x4y5z6a7b8c9d0"
Authorization: Basic bXlfc3lzX2FiYzEyM2RlZjQ1NjpteV9hMWIyYzNkNGU1ZjZnN2g4aTlqMC5rMWwybTNuNG81cDZxN3I4czl0MHUxdjJ3M3g0eTV6NmE3YjhjOWQw
Most HTTP libraries handle this automatically:
import requests
requests.post(url,
auth=('NOC-F64B-A989-C9E7-45B9-A55D-59EC-6545-40EE', 'my_a1b2c3d4e5f6g7h8i9j0.k1l2m3n4o5p6q7r8s9t0u1v2w3x4y5z6a7b8c9d0'),
json=data
)
Heartbeat
Heartbeat is a simple signal to indicate the system is alive and reachable.
Purpose
- Detect when systems become inactive
- Trigger alerts for dead systems
- Monitor system reliability
Endpoint
POST https://my.nethesis.it/collect/api/systems/heartbeat
Collect service runs on /collect (different from main backend on /backend)
Request
Headers:
Authorization: Basic <credentials>
Content-Type: application/json
Body:
{}
Empty JSON object - no data needed!
Response
Success (HTTP 200):
{
"code": 200,
"message": "heartbeat acknowledged",
"data": {
"system_key": "NOC-80F8-89A4-40B0-4AE9-A670-7C5F-99B3-F3EA",
"acknowledged": true,
"last_heartbeat": "2025-11-07T10:37:27.360343+01:00"
}
}
Frequency
Recommended: Every 5 minutes
Why 5 minutes?
- Platform considers system "active" if heartbeat < 15 minutes
- 5-minute interval provides 3 missed beats before marking dead
- Balance between network traffic and responsiveness
Example Implementation
Python:
import requests
import time
COLLECT_URL = "https://my.nethesis.it/collect"
SYSTEM_KEY = "NOC-F64B-A989-C9E7-45B9-A55D-59EC-6545-40EE"
SYSTEM_SECRET = "my_a1b2c3d4e5f6g7h8i9j0.k1l2m3n4o5p6q7r8s9t0u1v2w3x4y5z6a7b8c9d0"
def send_heartbeat():
"""Send heartbeat to My platform"""
try:
response = requests.post(
f"{COLLECT_URL}/api/systems/heartbeat",
auth=(SYSTEM_KEY, SYSTEM_SECRET),
json={},
timeout=10
)
response.raise_for_status()
print("Heartbeat sent successfully")
return True
except Exception as e:
print(f"Heartbeat failed: {e}")
return False
# Send heartbeat every 5 minutes
while True:
send_heartbeat()
time.sleep(300) # 5 minutes
Bash (cron):
#!/bin/bash
# /usr/local/bin/my-heartbeat.sh
COLLECT_URL="https://my.nethesis.it/collect"
SYSTEM_KEY="NOC-F64B-A989-C9E7-45B9-A55D-59EC-6545-40EE"
SYSTEM_SECRET="my_a1b2c3d4e5f6g7h8i9j0.k1l2m3n4o5p6q7r8s9t0u1v2w3x4y5z6a7b8c9d0"
curl -s -X POST "$COLLECT_URL/api/systems/heartbeat" \
-u "$SYSTEM_KEY:$SYSTEM_SECRET" \
-H "Content-Type: application/json" \
-d '{}' > /dev/null
Crontab entry (every 5 minutes):
*/5 * * * * /usr/local/bin/my-heartbeat.sh
Heartbeat Status
Systems are classified based on heartbeat:
| Status | Condition | Color | Meaning |
|---|---|---|---|
| Active | < 15 minutes | Green | System is healthy |
| Inactive | >= 15 minutes | Yellow | System is not responding |
| Unknown | Never sent | Gray | Never communicated |
Inventory
Inventory is a complete snapshot of system configuration and installed software.
Purpose
- Track system hardware and software
- Detect configuration changes
- Monitor software versions
- Audit system compliance
- Track inventory history
Endpoint
POST https://my.nethesis.it/collect/api/systems/inventory
Same collect service, port 8081
Request
Headers:
Authorization: Basic <credentials>
Content-Type: application/json
Body Structure:
{
"fqdn": "server.example.com",
"ipv4_address": "192.168.1.100",
"ipv6_address": "2001:db8::1",
"version": "8.0.1",
"os": {
"name": "Rocky Linux",
"version": "9.3",
"kernel": "5.14.0-362.8.1.el9_3.x86_64"
},
"hardware": {
"cpu_model": "Intel Xeon Gold 6248R",
"cpu_cores": 8,
"cpu_threads": 16,
"memory_total_gb": 32,
"disk_total_gb": 500
},
"network": {
"hostname": "server01",
"interfaces": {
"eth0": {
"ip": "192.168.1.100",
"netmask": "255.255.255.0",
"mac": "00:1a:2b:3c:4d:5e"
},
"eth1": {
"ip": "10.0.0.10",
"netmask": "255.255.0.0",
"mac": "00:1a:2b:3c:4d:5f"
}
}
},
"services": {
"nginx": {
"version": "1.24.0",
"status": "running"
},
"postgresql": {
"version": "15.5",
"status": "running"
},
"redis": {
"version": "7.2.3",
"status": "running"
}
},
"features": {
"docker": {
"enabled": true,
"version": "24.0.7"
},
"firewall": {
"enabled": true,
"type": "nftables"
}
},
"custom": {
"environment": "production",
"datacenter": "EU-West-1",
"backup_enabled": true
}
}
Response
Success (HTTP 200):
{
"code": 200,
"message": "Inventory received and queued for processing",
"data": {
"data_size": 16433,
"message": "Your inventory data has been received and will be processed shortly",
"queue_status": "queued",
"system_id": "0a98637c-077b-428a-8e57-c2fbb892051a",
"timestamp": "2025-11-07T10:39:05.897352+01:00"
}
}
Frequency
Recommended: Every 6 hours (4 times per day)
Why 6 hours?
- Balance between freshness and network/storage load
- Captures daily changes
- Reduces database growth
- Sufficient for most monitoring needs
Special cases:
- After system changes: Send immediately
- During updates: Send before and after
- On-demand: Admin can trigger via API
Inventory Schema
Required Fields
Minimum required data:
{
"fqdn": "server.example.com",
"ipv4_address": "192.168.1.100",
"os": {
"name": "NethSec",
"type": "nethsecurity",
"family": "OpenWRT",
"release": {
"full": "8.6.0-dev+43d54cd33.20251020175318",
"major": 7
}
}
}
Optional Sections
All other sections are optional but recommended:
os: Operating system informationhardware: Physical/virtual hardware specsnetwork: Network configurationservices: Installed services and versionsfeatures: Enabled features and capabilitiescustom: Any custom data (free-form)
Auto-Detection
Some fields are auto-detected by platform:
- System Type: Detected from inventory data (ns8, nsec, etc.)
- Status: Auto-updated based on heartbeat
- Last Update: Timestamp of inventory receipt
Example Implementation
Python:
import requests
import platform
import psutil
import json
COLLECT_URL = "https://my.nethesis.it/collect"
SYSTEM_KEY = "NOC-F64B-A989-C9E7-45B9-A55D-59EC-6545-40EE"
SYSTEM_SECRET = "my_a1b2c3d4e5f6g7h8i9j0.k1l2m3n4o5p6q7r8s9t0u1v2w3x4y5z6a7b8c9d0"
def collect_inventory():
"""Collect system inventory"""
return {
"fqdn": platform.node(),
"ipv4_address": get_primary_ip(), # Your implementation
"version": "8.0.1",
"os": {
"name": platform.system(),
"version": platform.release(),
"kernel": platform.version()
},
"hardware": {
"cpu_cores": psutil.cpu_count(logical=False),
"cpu_threads": psutil.cpu_count(logical=True),
"memory_total_gb": round(psutil.virtual_memory().total / (1024**3), 2)
},
"services": collect_services(), # Your implementation
"features": collect_features(), # Your implementation
}
def send_inventory():
"""Send inventory to My platform"""
try:
inventory = collect_inventory()
response = requests.post(
f"{COLLECT_URL}/api/systems/inventory",
auth=(SYSTEM_KEY, SYSTEM_SECRET),
json=inventory,
timeout=30
)
response.raise_for_status()
data = response.json()
print(f"Inventory sent successfully")
print(f"Changes detected: {data['data']['changes_detected']}")
return True
except Exception as e:
print(f"Inventory send failed: {e}")
return False
# Send inventory every 6 hours
import time
while True:
send_inventory()
time.sleep(21600) # 6 hours
Bash:
#!/bin/bash
# /usr/local/bin/my-inventory.sh
COLLECT_URL="https://my.nethesis.it/collect"
SYSTEM_KEY="NOC-F64B-A989-C9E7-45B9-A55D-59EC-6545-40EE"
SYSTEM_SECRET="my_a1b2c3d4e5f6g7h8i9j0.k1l2m3n4o5p6q7r8s9t0u1v2w3x4y5z6a7b8c9d0"
# Collect inventory (example - customize for your system)
INVENTORY=$(cat <<EOF
{
"fqdn": "$(hostname -f)",
"ipv4_address": "$(hostname -I | awk '{print $1}')",
"version": "8.0.1",
"os": {
"name": "$(uname -s)",
"version": "$(uname -r)"
},
"hardware": {
"cpu_cores": $(nproc),
"memory_total_gb": $(free -g | awk '/^Mem:/{print $2}')
}
}
EOF
)
# Send to platform
curl -X POST "$COLLECT_URL/api/systems/inventory" \
-u "$SYSTEM_KEY:$SYSTEM_SECRET" \
-H "Content-Type: application/json" \
-d "$INVENTORY"
Inventory History & Timeline
My retains the full history of inventory changes for each system, allowing you to see how a system evolved over time -- from its first registration to today.
What is Preserved
- All changes (diffs): Every detected change is stored permanently and never deleted. Each diff records the field that changed, the previous value, and the new value.
- Inventory snapshots: Full JSON snapshots are retained with exponential density -- more frequent near the present, progressively sparser for older dates:
| Age | Snapshot frequency |
|---|---|
| Last 7 days | All snapshots |
| 7 days - 1 month | 1 per day |
| 1 month - 3 months | 1 per week |
| 3 months - 1 year | 1 per month |
| Older than 1 year | 1 per quarter |
The first snapshot ever received for a system (the baseline) and the most recent snapshot (current state) are always preserved regardless of age.
The Timeline
The timeline shows the complete evolution of a system grouped by date. For each inventory submission that contained changes, you can see:
- When the change occurred
- Which fields changed (path, previous value, new value)
- Severity and category of each change
Since all diffs are kept permanently, the timeline remains fully navigable even for systems registered years ago. You can filter by date range, severity, category, or change type.
Viewing History
In Admin Panel:
- Navigate to Systems > System Details
- Click the Inventory tab
- Use the Timeline view for date-grouped change history
- Use the History view for paginated raw snapshots
Change Detection
My automatically detects changes between inventory snapshots.
What is Tracked
- Hardware changes (CPU, memory, disk)
- Software version changes
- Service additions/removals
- Network configuration changes
- Feature toggles
- Custom field modifications
Change Categories
Changes are categorized by type:
- OS: Operating system and kernel
- Hardware: Physical/virtual hardware
- Network: Network interfaces and configuration
- Features: Enabled features and capabilities
- Services: Installed services
- System: General system settings
Change Severity
Each change has a severity level:
- Critical: Requires immediate attention (e.g., hardware failure)
- High: Important changes (e.g., OS upgrade)
- Medium: Notable changes (e.g., service update)
- Low: Minor changes (e.g., metrics update)
Viewing Changes
In Admin Panel:
- Navigate to Systems > System Details
- Click Inventory tab
- View Changes section
- See detailed diff between versions
Change types:
- Create: New field added
- Update: Field value changed
- Delete: Field removed
Example change log:
[2025-11-06 10:30] OS Version updated
- Old: Rocky Linux 9.2
- New: Rocky Linux 9.3
- Severity: High
- Category: OS
[2025-11-06 10:30] Nginx version updated
- Old: 1.23.0
- New: 1.24.0
- Severity: Medium
- Category: Services
[2025-11-06 10:30] New feature enabled: Docker
- Value: 24.0.7
- Severity: Medium
- Category: Features
Monitoring in Admin Panel
Real-time Status
Dashboard view:
- Total systems count
- Active / Inactive / Unknown breakdown
- Recent inventory changes
- Systems requiring attention
System list:
- Heartbeat status indicator
- Last heartbeat time
- Last inventory time
- Change notifications
Alerts (if configured)
Automatic alerts for:
- System becomes inactive (no heartbeat for 15+ minutes)
- Critical changes detected in inventory
- New system registered
- System version mismatch
- Security vulnerabilities detected
The built-in LinkFailed alert is raised by Collect after the configured heartbeat timeout (10 minutes by default), separate from the 15+ minute system-status threshold shown above. Collect refreshes it every 5 minutes while the system stays inactive, so it can remain visible for up to 10 minutes after heartbeat resumes.
System Health
Health score based on:
- Heartbeat reliability (% uptime)
- Inventory freshness
- Number of changes
- Critical issues count
Troubleshooting
Authentication Fails (HTTP 401)
Problem: "Invalid system credentials" or "Unauthorized"
Solutions:
- Verify credentials are correct:
echo -n "system_key:system_secret" | base64
- Check for extra spaces in credentials
- Ensure system is registered
- Verify secret wasn't regenerated
- Test with curl:
curl -v -u "system_key:system_secret" \https://my.nethesis.it/collect/api/systems/heartbeat \-H "Content-Type: application/json" \-d '{}'
Connection Timeout
Problem: Request times out, no response
Solutions:
- Check network connectivity:
ping my.nethesis.it
- Verify port 8081 is accessible:
telnet my.nethesis.it 8081
- Check firewall rules (allow outbound to port 8081)
- Verify DNS resolution
- Test from different network
Inventory Not Updating
Problem: Inventory sent successfully but not visible in admin panel
Solutions:
- Wait 60 seconds and refresh (cache propagation)
- Verify you're viewing the correct system
- Check inventory was sent to correct endpoint (port 8081)
- Verify system is not deleted
- Check system logs for errors
Heartbeat Shows as "Dead"
Problem: System shows red/dead status despite sending heartbeat
Solutions:
- Check heartbeat frequency (must be < 15 minutes)
- Verify heartbeat is reaching platform:
curl -v https://my.nethesis.it/collect/api/systems/heartbeat \-u "key:secret" -H "Content-Type: application/json" -d '{}'
- Check system time is synchronized (NTP)
- Verify no clock drift
- Review collect service logs (admin only)
Changes Not Detected
Problem: Inventory sent but no changes shown
Solutions:
- Verify actual data changed between inventories
- Check that changed fields are supported
- Small numerical changes may not trigger detection
- Custom fields are tracked for changes
- Wait for next inventory and compare
Best Practices
Heartbeat
- Send every 5 minutes consistently
- Use scheduled task (cron, systemd timer)
- Log heartbeat failures for debugging
- Implement retry logic (exponential backoff)
- Monitor heartbeat success rate
Inventory
- Send complete inventory every time
- Don't send partial updates
- Include all relevant data
- Use consistent field names
- Validate JSON before sending
- Send immediately after significant changes
Error Handling
- Implement retry logic for network failures
- Log all errors with context
- Don't retry authentication errors (401)
- Use exponential backoff for retries
- Alert on repeated failures
Security
- Store credentials securely
- Never log credentials
- Use HTTPS only
- Verify SSL certificates
- Rotate credentials periodically
- Monitor for authentication failures
Performance
- Compress large inventories
- Batch data collection
- Avoid unnecessary inventory sends
- Use efficient data structures
- Monitor network bandwidth