Skip to content

IP History

IP History automatically tracks every IP address assignment on every managed device, recording when addresses appear, change, and disappear. Each heartbeat, the Breeze agent scans all active network interfaces, detects new or changed IP assignments, and reports the delta to the API. The API maintains a complete timeline of IP assignments per device with first-seen/last-seen timestamps, MAC address correlation, subnet mask, gateway, DNS servers, and whether the address was assigned via DHCP, statically configured, or acquired through a VPN tunnel.

This feature is essential for incident response (answering “which device had IP 10.0.5.42 at 2 PM on Tuesday?”), compliance auditing, and network troubleshooting. When a device moves between subnets, switches from DHCP to static, or connects to a VPN, the full history is preserved with interface-level granularity.


TypeDescription
dhcpAddress assigned by a DHCP server (detected via lease file inspection or OS commands)
staticManually configured address (inferred when no DHCP lease evidence is found)
vpnAddress assigned by a VPN tunnel (detected by interface name heuristics)
link-localAuto-configured link-local address (169.254.x.x for IPv4, fe80:: for IPv6)
unknownAssignment method could not be determined
TypeDescription
ipv4IPv4 address
ipv6IPv6 address
FieldTypeDescription
idUUIDUnique identifier for the IP history record
deviceIdUUIDDevice the IP was observed on
orgIdUUIDOrganization the device belongs to
interfaceNamestring (max 100)Network interface name (e.g., eth0, Wi-Fi, Ethernet)
ipAddressstring (max 45)The IP address
ipTypestringipv4 or ipv6
assignmentTypeenumOne of dhcp, static, vpn, link-local, unknown
macAddressstring (max 17)MAC address of the interface (e.g., 00:1A:2B:3C:4D:5E)
subnetMaskstring (max 45)Subnet mask or prefix length
gatewaystring (max 45)Default gateway address
dnsServersstring[]DNS server addresses (up to 8)
firstSeendatetimeWhen this IP was first observed on this interface
lastSeendatetimeWhen this IP was most recently confirmed active
isActivebooleanWhether the IP is currently assigned to the interface
deactivatedAtdatetimeWhen the IP was removed from the interface (null if still active)
createdAtdatetimeWhen the record was created
updatedAtdatetimeWhen the record was last updated

  1. Agent collects current network state

    During each heartbeat, the agent’s IP tracking subsystem calls the inventory collector to enumerate all active network interfaces and their IP addresses. Loopback and down interfaces are skipped. For each interface with an IP address, the agent records the interface name, IP address, IP type, MAC address, and inferred assignment type.

  2. Assignment type is determined heuristically

    The agent uses platform-specific heuristics to classify how each IP was assigned:

    The interface name is checked against known VPN interface prefixes and keywords: tun, tap, wg, ppp, utun, vpn, tailscale, zerotier, wireguard, openvpn, anyconnect, globalprotect, fortinet, l2tp, pptp, ipsec, protonvpn, nordlynx, hamachi.

    Anything that does not match VPN, link-local, or DHCP criteria is classified as static.

  3. Delta is computed against previous state

    The agent maintains a local state file (ip_state.json) in its data directory. On each heartbeat, it compares the current IP snapshot against the saved state:

    • Changed IPs: Addresses that are new (not present in the previous state) or whose metadata has changed (assignment type, MAC address, subnet mask, gateway, or DNS servers).
    • Removed IPs: Addresses that were in the previous state but are no longer present.
    • Current IPs: The full set of currently active IP assignments (used for lastSeen timestamp updates).

    The current state is then saved to disk for the next comparison.

  4. Update is sent in the heartbeat payload

    The IP history update is included as the ipHistoryUpdate field in the heartbeat request body:

    {
    "ipHistoryUpdate": {
    "currentIPs": [...],
    "changedIPs": [...],
    "removedIPs": [...],
    "detectedAt": "2026-03-02T14:30:00Z"
    }
    }
  5. API processes the delta transactionally

    The API’s processDeviceIPHistoryUpdate service handles the update inside a database transaction:

    • Removed IPs: Matching active records are marked isActive = false with deactivatedAt set to the detection timestamp.
    • Changed IPs (existing): If an active record already exists for the interface/IP/type combination, its metadata (assignment type, MAC, subnet, gateway, DNS) and lastSeen timestamp are updated.
    • Changed IPs (new): New records are inserted with firstSeen and lastSeen set to the detection timestamp.
    • Current IPs: All matching active records have their lastSeen timestamp refreshed.
    • Bootstrap: If the device has no active IP history records but the agent reports currentIPs, all current IPs are treated as new records. This seeds the initial history for newly enrolled devices.

Use the IP history API endpoint to retrieve historical IP assignments for a device.

Returns IP history records for a device, ordered by firstSeen descending (most recent first).

Query parameters:

ParameterTypeDefaultDescription
limitinteger100Maximum number of records to return (1-500)
offsetinteger0Number of records to skip for pagination
active_onlybooleanfalseIf true, only return currently active IP assignments

Response (200):

{
"deviceId": "a1b2c3d4-...",
"count": 3,
"data": [
{
"id": "f5e6d7c8-...",
"deviceId": "a1b2c3d4-...",
"orgId": "11223344-...",
"interfaceName": "Ethernet",
"ipAddress": "10.0.5.42",
"ipType": "ipv4",
"assignmentType": "dhcp",
"macAddress": "00:1A:2B:3C:4D:5E",
"subnetMask": "255.255.255.0",
"gateway": "10.0.5.1",
"dnsServers": ["10.0.5.1", "8.8.8.8"],
"firstSeen": "2026-02-15T08:00:00.000Z",
"lastSeen": "2026-03-02T14:30:00.000Z",
"isActive": true,
"deactivatedAt": null,
"createdAt": "2026-02-15T08:00:00.000Z",
"updatedAt": "2026-03-02T14:30:00.000Z"
},
{
"id": "a9b8c7d6-...",
"deviceId": "a1b2c3d4-...",
"orgId": "11223344-...",
"interfaceName": "Ethernet",
"ipAddress": "192.168.1.100",
"ipType": "ipv4",
"assignmentType": "dhcp",
"macAddress": "00:1A:2B:3C:4D:5E",
"subnetMask": "255.255.255.0",
"gateway": "192.168.1.1",
"dnsServers": ["192.168.1.1"],
"firstSeen": "2026-01-10T12:00:00.000Z",
"lastSeen": "2026-02-14T23:59:00.000Z",
"isActive": false,
"deactivatedAt": "2026-02-15T08:00:00.000Z",
"createdAt": "2026-01-10T12:00:00.000Z",
"updatedAt": "2026-02-15T08:00:00.000Z"
}
]
}

MethodPathDescription
GET/devices/:id/ip-historyRetrieve IP history records for a device (requires organization, partner, or system scope)
POST/agents/:id/heartbeatAgent heartbeat containing ipHistoryUpdate field (agent auth required)

The ipHistoryUpdate object within the heartbeat payload uses this structure for each IP entry:

FieldTypeRequiredDescription
interfaceNamestringYesNetwork interface name (1-100 characters)
ipAddressstringYesValid IPv4 or IPv6 address (max 45 characters)
ipTypeenumNoipv4 or ipv6 (auto-detected from address if omitted)
assignmentTypeenumNodhcp, static, vpn, link-local, or unknown
macAddressstringNoMAC address (max 17 characters)
subnetMaskstringNoSubnet mask (max 45 characters)
gatewaystringNoDefault gateway (max 45 characters)
dnsServersstring[]NoDNS server addresses (max 8 entries, each max 45 characters)

The ipHistoryUpdate wrapper object contains:

FieldTypeDescription
currentIPsarrayAll IP addresses currently assigned (used to refresh lastSeen)
changedIPsarrayNew or modified IP assignments
removedIPsarrayIP assignments that are no longer present
detectedAtISO 8601 datetimeWhen the agent detected the changes

Each array can contain up to 100 entries.


The device_ip_history table has the following indexes for efficient querying:

IndexColumnsUse Case
device_ip_history_device_id_idxdevice_idQuery all IP history for a specific device
device_ip_history_org_id_idxorg_idQuery all IP history across an organization
device_ip_history_ip_address_idxip_addressFind which device had a specific IP address
device_ip_history_first_seen_idxfirst_seenFilter by when IPs were first observed
device_ip_history_last_seen_idxlast_seenFilter by when IPs were last confirmed
device_ip_history_is_active_idxis_activeFilter active vs. deactivated records
device_ip_history_ip_time_idxip_address, first_seen, last_seenFind which device had a specific IP at a specific time

  • Verify the agent is online and sending heartbeats. IP history data is included in the heartbeat payload.
  • Check that the ipHistoryUpdate field is present in the heartbeat body. If the agent detects no network interfaces with IP addresses, the field is omitted.
  • On the first heartbeat after enrollment, the API bootstraps initial records from the currentIPs array. If currentIPs is empty, no records are created.

All IPs show as “static” when they should be “dhcp”

Section titled “All IPs show as “static” when they should be “dhcp””
  • The agent uses heuristics to detect DHCP assignments. On Linux, it checks lease files in standard locations (/run/systemd/netif/leases/, /var/lib/NetworkManager/, /var/lib/dhcp/). If your DHCP client stores leases in a non-standard location, the detection may fail.
  • On macOS, the agent runs ipconfig getpacket <interface>. If the command is not available or returns an error, the fallback heuristic (private IP on common interface prefix) is used.
  • On Windows, the agent parses ipconfig /all output. If the command times out (1500ms limit), detection falls back to the interface name heuristic.
  • Each IP assignment is uniquely identified by the combination of interfaceName, ipAddress, and ipType. If you see what appear to be duplicates, they likely differ in one of these fields (e.g., the same IP on different interfaces, or the same interface name with IPv4 and IPv6 variants).
  • Deactivated records (where isActive = false) are preserved for historical reference. The same IP can appear multiple times with different firstSeen/lastSeen ranges if the address was removed and later reassigned.
  • IP history is collected during each heartbeat cycle (default: every 60 seconds). Changes are detected by comparing the current network state against the locally saved state file.
  • If the agent is restarted, the state file is reloaded from disk. If the state file was lost or corrupted, the next heartbeat will treat all current IPs as new, which may trigger a bootstrap of the full current state.

”Invalid IP address format” validation errors

Section titled “”Invalid IP address format” validation errors”
  • IPv6 addresses with zone IDs (e.g., fe80::1%eth0) have the zone ID stripped before validation. If validation still fails, the IP address format is not recognized by Node.js isIP().
  • Ensure IP addresses do not contain whitespace or other unexpected characters.