Skip to content

Device identity and naming

A device in Nyxtrace is not a MAC address and not an IP address. It is an identity cluster: a set of weighted anchors observed over time, with the evidence for each one kept.

That sounds abstract until you try the alternatives. Identify by IP and a DHCP lease change becomes a new device. Identify by MAC and every modern phone becomes a new device every few days. Either way the inventory fills with ghosts and every device is permanently inside its learning phase.

An anchor is one observation that says something about who a device is.

Class From Role
mac DHCP, controller, layer 2 Establishes
mac_random A MAC with the locally administered bit set Confirms only
mac_oui The vendor prefix of an administered MAC Confirms only
dhcp_hostname The host name in a DHCP request Establishes
dhcp_fingerprint The DHCP parameter and class fingerprint Confirms only
mdns A .local name Establishes
authoritative_map A name a person assigned in a controller Establishes
dot1x A RADIUS identity Establishes
duid, agent A DHCPv6 identifier, an endpoint agent Establishes
ja4 A TLS client fingerprint Confirms only
behavior Behavioural similarity Confirms only
recurrence The same anchors agreeing again on a separate day Confirms only

The split in that last column is the important one. A confirming class can raise confidence but can never establish an identity on its own. Vendor prefix, DHCP fingerprint and TLS fingerprint are shared by thousands of identical devices; treating one as an identity would merge every phone of the same model into one.

An address is a location, not a name. An hourly job resolves the name to show from every source that knows one, in a fixed priority, and the interface says which source it came from (name_source):

Priority Source name_source Confidence Where it comes from
1 Operator manual 1.00 A rename in the interface — PATCH /api/v1/devices/{id}
2 Authoritative inventory authoritative_map 0.90 The client name of a UniFi controller
3 DHCP host name dhcp 0.75 DHCP option 12, from the retained IDS events
4 mDNS name mdns 0.70 A .local name from the DNS events
5 Reverse DNS reverse_dns 0.55 A PTR record from the appliance’s own resolver
6 Address address 0.00 Nothing knew a name

The order deliberately puts the DHCP host name above the mDNS name, unlike the anchor weights above: identity asks which name survives a MAC rotation, naming asks which name an operator recognises, and the name a device asks to be called by is the one that was configured on the device itself.

Every name goes through one normalisation, so a manual name and a DHCP host name cannot differ in what they are allowed to be: a host-name source yields a DNS label (lower case, no spaces, .local removed, so the same name from two sources is one name), while an operator-assigned name keeps its case and its spaces, because “Jannik’s iPhone” is exactly the shape those sources produce. An address, a generic placeholder (localhost, unknown, android, …) and anything over 200 characters are never a name.

A device still named after its address gets upgraded the moment a proven host name appears. A name a human confirmed is never downgraded — it is an overlay that no later resolution overwrites, and renaming a device says nothing about what kind of device it is.

A DHCP host name expires with its lease. The job takes the last host name an address was announced with and the announced lease time as its validity, or 48 hours — two renewal cycles — when none was logged. A name from a finished lease stops being shown and stays listed as a former name, instead of quietly describing whoever holds that address now.

It is the only naming source that talks to the network, so it is limited in every direction: only addresses inside the tenant’s own VLAN ranges are asked about at all, a device a higher-priority source already named is not asked about, the timeout is 300 ms against the appliance’s own resolver over loopback, a token bucket allows 20 queries per second with a burst of 40 and at most 2,000 addresses per run, and answers are cached for an hour (absences for 15 minutes). A query the rate limit stopped is not cached as “no name”: a query that was never sent proves nothing. NXDOMAIN, an empty answer, a name that is itself an address and a generic name all mean the same thing — no name. The first label of the answer becomes the name (camera-01 from camera-01.home.example).

The job runs hourly, is read-only against everything that already exists, and writes only its own name overlay. It is on by default and NDR_NAMING_ENABLED=false switches it off.

A controller also reports which access point a client is on, which SSID, which switch port, which VLAN, the signal strength and whether it is wired. Those are facts about the device, recorded with their source and their age — not claims about its identity.

Keeping that line clean is what stops “two devices were on the same access point” from becoming “two devices are the same device”.

Every fact carries its source and its recency, and a contradiction is shown as a contradiction rather than resolved silently by whichever source wrote last. If DHCP says one host name and the controller says another, you see both, with who said what and when.

An unavailable source leaves its facts stale, never deleted. A controller outage does not erase what it told you yesterday, and no identity decision is made on stale data alone.

The device detail lists every anchor with its class, value, source, weight, first and last seen, the number of separate days it was observed, and two markers: randomized for a privacy address, and confirming for a class that cannot establish identity. Below that: every merge event with its rule, confidence and anchors, and any split that reverted one.

A confidence below 0.90 means the identity is derived, and the interface says so instead of presenting it as fact.