# Entities

> Every WhisperGraph node label, from DNS, addressing and routing to WHOIS, geo, threat and physical infrastructure, with counts and queryable properties.

*Source: https://www.whisper.security/docs/whisper-graph/schema/entities*
*Published: 2026-07-06*
*Last updated: 2026-10-01*

---
Every entity in WhisperGraph is a node with a label and, for almost every label, a `name`. This page lists **every node label the engine returns** — what each represents, how many exist, whether it is joinable at all, and the properties you can filter and return. For the edges that join them, see [Connection Types](/docs/whisper-graph/schema/connections); for the chains that cross layers, see [Pivoting Examples](/docs/whisper-graph/schema/pivoting).

There is no `Domain` or `FQDN` label — every name is a `HOSTNAME`. There is no `Certificate` label either — certificate observations are `CT_OBSERVATION`. **The table below is generated**: its rows are whatever `CALL db.labels()` returned when the census last ran, and its counts resolve at render from that same census. A label the engine adds appears here without anyone typing it, and a label the engine drops leaves.

The **Edge types** column is the number of edge types that name the label as a source or a target in `CALL db.relationshipTypes()`. Read it as *declared*, not as *reachable*: `CATEGORY` shows zero and yet `(f:FEED_SOURCE)-[:BELONGS_TO]->(c:CATEGORY)` returns rows, because `BELONGS_TO` carries that hop without declaring it, and `URL` shows zero although `(u:URL {name: "…"})-[:LINKS_TO]->(h:HOSTNAME)` traverses. A zero is a reason to check. Three labels really are node-only — `DNS_ROOT_INSTANCE`, `DWI_DOMAIN` and `RIR` hold no edge of any type. They list; the traversal cannot be written.

## Node labels

### Core DNS & addressing

| Label | Rows | What it is |
|-------|-----:|-------------|
| `HOSTNAME` | 2,795,356,675 | A fully qualified domain name (`google.com`, `mail.google.com`). |
| `IPV4` | 622,296,841 | An IPv4 address. |
| `IPV6` | 9,164,108 | An IPv6 address. |
| `PREFIX` | 2,493,411 | A CIDR block an IP belongs to. |
| `ANNOUNCED_PREFIX` | 1,446,384 | A prefix actually announced in BGP. |
| `REGISTERED_PREFIX` | 333,174 | A prefix allocated by a regional registry. |
| `TLD` | 2,185 | A top-level domain (`com`, `nissan`). |

### Routing & organization

| Label | Rows | What it is |
|-------|-----:|-------------|
| `ASN` | 116,028 | An autonomous system, named `AS` + number (`AS13335`). |
| `ASN_NAME` | 107,720 | The registered name of an autonomous system. |
| `ORGANIZATION` | 119,189,847 | A registrant or network organization, as the registry or WHOIS record wrote it. Fold the spellings of one company together over `SAME_ORG_AS`. |
| `RIR` | 5 | A regional internet registry. |
| `TLD_OPERATOR` | 737 | A registry that operates one or more TLDs. |

### WHOIS & registration

| Label | Rows | What it is |
|-------|-----:|-------------|
| `REGISTRAR` | 50,660 | A domain registrar. |
| `EMAIL` | 237,065,663 | A WHOIS contact email. |
| `PHONE` | 60,194,142 | A WHOIS contact phone. |
| `RDAP_ENTITY` | 375,227 | An RDAP registration entity — a registrant handle. Reached from a prefix or an ASN over `REGISTERED_TO_ENTITY`. |

### Geo & DNSSEC

| Label | Rows | What it is |
|-------|-----:|-------------|
| `CITY` | 54,233 | A GeoIP city (`New York, US`). |
| `COUNTRY` | 424 | A country. |
| `DNSSEC_ALGORITHM` | 8 | A DNSSEC signing algorithm. |

### Threat intelligence

| Label | Rows | What it is |
|-------|-----:|-------------|
| `FEED_SOURCE` | 134 | A threat-intelligence feed. Anchor on the wire `name` (`abuse-ch-feodo-tracker`), not the display label. |
| `CATEGORY` | 33 | A threat or reference category. The `name` is a lowercase slug (`c2`, `phishing`, `blacklists`). Reached from a feed over `BELONGS_TO`, which the introspection call does not declare — see the note below. |
| `THREAT_TAG` | 13,522 | A MISP-galaxy threat tag, reached over `TAGGED_AS`. |
| `THREAT_SIGNAL_TYPE` | 14 | A threat-signal taxonomy entry (`bulletproof-hosting`, `c2-hosting`). |
| `ACTOR` | 1,987 | A named threat actor (`APT28`). Case-sensitive. |
| `ATTACK_PATTERN` | 721 | A MITRE ATT&CK technique or tactic. |
| `DWI_DOMAIN` | 20 | A dark-web domain under watch (`.onion`), carrying its own `dwi_*` properties and a verdict. No edge type reaches it. |

### RPKI & routing observations

| Label | Rows | What it is |
|-------|-----:|-------------|
| `ROA` | 3,017,600 | An RPKI Route Origin Authorization. It has no `name` — identify it by `prefix` and `asn`. |
| `BGP_PATH_OBSERVATION` | 3,328,347 | An observed AS-path, reached over `BGP_PATH`. |

### Physical infrastructure

| Label | Rows | What it is |
|-------|-----:|-------------|
| `FACILITY` | 5,884 | A datacenter or carrier-hotel building (`Equinix DA1 - Dallas`). |
| `INTERNET_EXCHANGE` | 1,330 | An internet exchange point (`LINX LON1`). |
| `SUBMARINE_CABLE` | 712 | A subsea cable (`2Africa`). |
| `CABLE_LANDING` | 1,926 | A landing point for a subsea cable. |
| `CDN_POP` | 1,667 | A CDN point of presence (operator-prefixed id). |
| `DNS_ROOT_INSTANCE` | 1,559 | A root-server instance. The nodes list; nothing joins to them. |
| `CLOUD_REGION` | 91 | A cloud-provider region (`aws:eu-west-1`). |

### Egress, fingerprint & transparency

| Label | Rows | What it is |
|-------|-----:|-------------|
| `VENDOR` | 53 | A cloud or SaaS vendor that operates address space (`cloudflare`). |
| `TOR_RELAY` | 2,583 | A Tor relay, keyed by fingerprint. |
| `TLS_FINGERPRINT` | 765 | A JA3 or JARM TLS fingerprint. |
| `CT_OBSERVATION` | 6,117,080 | A Certificate Transparency observation. |
| `DMARC_RECIPIENT` | 35,747 | An address a domain sends DMARC reports to. |

### Web & technology

| Label | Rows | What it is |
|-------|-----:|-------------|
| `URL` | 2,165 | A URL — most often a phishing-kit path, reached from the host that serves it over `LINKS_TO`. |
| `TECHNOLOGY` | 73,177 | A technology or software product detected running on a host or IP (a CMS, a web server, a framework), reached over `RUNS_TECHNOLOGY`. |

*Rows measured live from `CALL db.labels()`, 2026-10-21. The counts render as tokens resolved from the same live figures, so a replica disagreeing by a few million moves the number without moving the table.*

> Most node labels carry an indexed `name`, and `{name: "value"}` lookups hit that index. **`ROA` does not** — identify a ROA by the prefix and ASN it authorizes. `FEED_SOURCE`, `CATEGORY`, `ANNOUNCED_PREFIX`, and `REGISTERED_PREFIX` are virtual labels synthesized at query time. The small ones (`FEED_SOURCE`, `CATEGORY`) can be listed directly; reach the prefix labels through an edge (`ANNOUNCED_BY`, `BELONGS_TO`) rather than scanning them.

> **`ORGANIZATION` names are raw registrant strings, so reach an organization through an edge and fold it.** The same company appears under several strings — `github hostmaster`, `github,` and `GitHub, Inc.` are all `ORGANIZATION` nodes — and casing and punctuation follow whatever the WHOIS or RIR record carried. Anchor on a hostname, ASN or registered prefix, traverse `REGISTERED_BY` to its organization, then follow `SAME_ORG_AS` to the canonical company record and use that `name` for further pivots. A guessed display string may match a different variant, or none at all.

> **Three labels are node-only**, and the difference matters before you write the query. `DNS_ROOT_INSTANCE` (root-server anycast instances, with a `rootLetter`), `DWI_DOMAIN` (dark-web domains under watch) and `RIR` hold **no edge of any type**. They can be counted and read property by property; the traversal into or out of them cannot be written. Read an ASN's registry off `ASN.autNumSourceRir` rather than joining `RIR`. `RDAP_ENTITY` is **not** in this group: `REGISTERED_TO_ENTITY` reaches it from a `PREFIX` or an `ASN`. Nor is `BGP_PATH_OBSERVATION`, which is reached over `BGP_PATH`.

## Node properties

Most labels carry only `name`. Threat-listed indicators, announced prefixes, ASNs, and ROAs carry richer, queryable properties.

> **Read `coverage` before `band`.** Only `known-clean` licenses the word "clean"; `no-data` means
> *unknown*, which is a different thing again; `malicious-evidenced` and `ambiguous` mean there is
> evidence, whatever the band says.
> Full contract: [Coverage — what we looked at](/docs/whisper-graph/procedures/coverage).

### Threat & verdict (on threat-listed `IPV4` / `IPV6` / `HOSTNAME`)

| Property | Type | Notes |
|----------|------|-------|
| `verdictScore` | Double | **Reconciled triage score — prefer this over `threatScore`.** |
| `verdictLevel` | String | Reconciled level: `NONE` … `CRITICAL`. |
| `verdictBlocking` | Boolean | Whether the reconciled verdict recommends blocking. |
| `verdictAdvisory` | String | `IPV4` / `IPV6` only. A short reason string when one applies (`allowlist-vouched`, `url-scoped-listing`). Frequently `null`, including on listed indicators, so treat a null as *no advisory*, not as an error. |
| `verdictCoverage` | String | A node-level coverage marker. For a decision, read `coverage` from `whisper.assess`, which is the contract the [Coverage](/docs/whisper-graph/procedures/coverage) page documents. |
| `threatScore` | Double | Raw feed-weighted score. Never clamped, so it keeps the full feed evidence. |
| `threatLevel` | String | `NONE` / `INFO` / `LOW` / `MEDIUM` / `HIGH` / `CRITICAL`. |
| `threatSources` | Integer | The **number** of feeds that flagged the indicator — not their names. `WHERE "greensnow" IN ip.threatSources` matches nothing. The names come from the `LISTED_IN` traversal, or from `explain()`'s `sources[]`. |
| `threatFirstSeen` / `threatLastSeen` | epoch ms | First/last time the indicator was seen on a feed. |

Project the verdict properties by name (`RETURN ip.verdictLevel, ip.verdictScore`). A whole-node projection (`RETURN ip`) can leave them out, and the response says so in its `advisories`.

**Boolean flags**, derived from the categories of the feeds that list the node: `isThreat`, `isAnonymizer`, `isC2`, `isMalware`, `isPhishing`, `isSpam`, `isBruteforce`, `isScanner`, `isBlacklist`, `isTor`, `isProxy`, `isVpn`, `isWhitelist`, `isReputation`, `isBotnet`, `isDga`, `isStateActor`, `isExfilDestination`, `isOfacSanctioned`, `isScam` and `isEgressRisk`, plus `egressClasses`, a list naming the kind of egress (`tor-exit`). A node can carry a threat listing and a whitelist marker at the same time: read `verdictLevel` as the verdict and treat the whitelist marker as context, not as an override.

`HOSTNAME` nodes also carry `rank`, a popularity rank where lower means more prevalent (`google.com` ranks first). It is how you tell a listed name apart from a listed name that half the internet visits.

Curated well-known infrastructure (public resolvers like `8.8.8.8`) carries `allowlisted: true`, and its verdict surfaces (`verdictLevel`, `threatLevel`, `isThreat`) are clamped to benign. The raw `threatScore` is not clamped, so it keeps the feed evidence.

```cypher expect=rows>0 seed=185.220.101.1 verified=2026-09-02
// Triage an IP on the reconciled verdict (preferred over raw threatScore)
MATCH (ip:IPV4 {name: "185.220.101.1"})
RETURN ip.name, ip.verdictScore, ip.verdictLevel, ip.verdictBlocking, ip.isTor, ip.isAnonymizer
LIMIT 1
```

### BGP enrichment (on `ANNOUNCED_PREFIX`)

| Property | Type | Notes |
|----------|------|-------|
| `isMoas` | Boolean | Currently announced by more than one origin AS (MOAS). |
| `isAnycast` | Boolean | Announced from multiple locations. |
| `isWithdrawn` | Boolean | Currently withdrawn from the routing table. |
| `rpkiStatus` | String | RPKI validation state of the announcement (`valid`, for example). `roaAsn` and `roaMaxLength` give the covering ROA's origin AS and maximum length. |
| `spamhausDrop` | Boolean | Whether the prefix sits on the Spamhaus DROP list. |
| `dominantCity` | String | Where most of the prefix's addresses geolocate (`Mountain View, US`). |
| `prefixLength` / `ipv4Count` | Integer | The announced mask length and the number of IPv4 addresses it covers. |
| `threatScore` / `threatLevel` / `threatSources` | Double / String / Integer | Aggregate threat across the prefix. `threatSources` is a count of feeds here too, not a list of names. |
| `rir` | String | The allocating regional registry. |
| `registrationDate` / `lastChangedDate` | date | Allocation and last-change dates. |

### Reputation, hijack posture and aggregate threat (on `ASN`)

| Property | Type | Notes |
|----------|------|-------|
| `reputationScore` / `reputationCategory` | Double / String | The network's reputation score and its band (`NEUTRAL`, for example). |
| `hijackPostureScore` / `hijackOriginMismatchCount` | Double / Integer | How exposed the network's announcements are to origin hijack, and how many origin mismatches have been observed. |
| `degreeTrend` / `declineRatio` | Double | Whether the network's BGP adjacency is growing or shrinking. |
| `overallThreatLevel` | String | Rolled-up threat level for the AS. |
| `maxThreatScore` / `avgThreatScore` | Double | Max / average threat across the AS's routed prefixes. |
| `hasThreateningPrefixes` | Boolean | Whether any routed prefix is threat-listed. |
| `autNumSourceRir` / `rir` | String | The registry the AS is registered with. Read it here rather than joining the `RIR` label. |
| `registrationDate` / `lastChangedDate` | date | Allocation and last-change dates. |

### RPKI (on `ROA`)

A `ROA` has no `name`. Its identity is the pair `prefix` + `asn`, and you reach it over `ROA_AUTHORIZES_ORIGIN` from an ASN or `ROA_AUTHORIZES_PREFIX` from a prefix rather than by lookup.

| Property | Type | Notes |
|----------|------|-------|
| `prefix` | String | The prefix the ROA authorizes (`1.1.1.0/24`). Half of the ROA's identity. |
| `asn` | Int | The authorized origin AS as a bare number (`13335`), **not** the `AS13335` string form used to anchor an `ASN` node. |
| `maxLength` | Int | Maximum prefix length the ROA authorizes. |
| `authSource` | String | `rpki-roa` for an RPKI ROA, or `irr-route` for a route object from an internet routing registry. An `irr-route` record carries `mnt_by` and `source_rir` instead of `maxLength`, `trustAnchor` and `validUntil`, and it is not an RPKI authorization, so filter on `authSource` for RPKI questions. |
| `trustAnchor` | String | The RPKI trust anchor (`apnic`, `ripe`, `afrinic`). |
| `validUntil` | timestamp | End of the ROA's validity window. Key expiry checks on this field; a ROA has no `validFrom`. |

### Other enriched labels

| Label | Notable properties |
|-------|--------------------|
| `URL` | `path` (also the `name`, e.g. `/godaddy`), `kind` (`url-kit-fingerprint`), `segmentRarity`, `hostnameCount`, `apexCount`. Phishing-kit URL paths, joined to the hosts that serve them by `LINKS_TO`. Anchor by `{name: …}`, which holds the path and is indexed; `{path: …}` and `{id: …}` scan the whole label, and an `id` can change and name a different kit. |
| `RDAP_ENTITY` | `name` (the registry handle, e.g. `CLOUD14`), `displayName`, `kind` (`org`), `rir`, `abuseEmail`, `address`, `registrationDate`, `lastChangedDate`, plus per-entity threat-density counters. Reached over `REGISTERED_TO_ENTITY`. |
| `ACTOR` | `name` (canonical, case-sensitive: `APT28`) and `aliases`, the vendor names for the same group. |
| `ATTACK_PATTERN` | `name`, `id` (`T1003`) and `kind` (`technique` or `tactic`). Anchor a technique by `{name: "T1003"}`: the name index knows it by its T-number as well as its readable name, while `{id: "T1003"}` returns the same node by scanning the label. Filter `{kind: "technique"}` for the whole set. |
| `FEED_SOURCE` / `CATEGORY` | `id` and `name` both carry the stable slug (`abuse-ch-feodo-tracker`, `c2`); `displayName` is the human label. `FEED_SOURCE` adds `weight`, `isThreat`, `isPopularity` and `category`. |
| `TOR_RELAY` | `name` (fingerprint), `fingerprint`, `exitAddressCount`, `source`. |
| `CT_OBSERVATION` | `name` (the observed cert name), `certCount`, `firstSeen`, `lastSeen`, `wildcard`. |
| `VENDOR` | `name` (lowercase slug), `displayName`, `category` (`cdn` on `cloudflare`), `confidence`, `aliases`. |
| `TECHNOLOGY` | `name` (e.g. `Cloudflare DNS`), `category` and `categories` (`content_delivery_network`), `parentCategories`, `vendorDomain` / `vendorApex` (the technology vendor's own apex, e.g. `cloudflare.com`), `adopterCount`, `description`, `url`, `provenance`, `securityClass`. Reached from a hostname over `RUNS_TECHNOLOGY`, available to accounts with company-data access. |
| `TLS_FINGERPRINT` | `name` (`jarm:`/`ja3:`-prefixed hash), `hash`, `kind`, `family`. |
| `THREAT_SIGNAL_TYPE` | `name`, one of 14 signal kinds. Each belongs to one label, for example: `PREFIX` carries `prefix-age-anomaly` and `toxic-neighborhood`; `ASN` carries `bulletproof-hosting`, `critical-infrastructure`, `ddos-mitigation` and `satellite-network`; `HOSTNAME` carries `hidden-primary-soa`, `subdomain-takeover`, `wildcard-dns` and `infrastructure-staging`. Not every kind is populated at once; `MATCH (n)-[:HAS_SIGNAL]->(s:THREAT_SIGNAL_TYPE) RETURN s.name, labels(n)[0], count(*)` shows which are. |
| `BGP_PATH_OBSERVATION` | `name` is the hyphen-joined AS path, origin last (`132825-174-1299-13335`), with one `BGP_PATH` edge per AS on it. |
| `DNS_ROOT_INSTANCE` | `name` (the instance id) and `rootLetter` (`I`, `M`). Node-only. |

> `LISTED_IN` edges carry no queryable properties. The per-feed evidence behind a verdict (each feed's weight and first/last-seen timestamps) comes from [`explain()`](/docs/whisper-graph/procedures/explain), which returns it as an inspectable `sources` array.

## Confirm a label before you anchor

```cypher expect=rows>0 verified=2026-09-02
CALL db.labels() YIELD label RETURN label ORDER BY label
```

`db.labels()` lists every node label with its count, and it answers immediately. Checking it first is the fastest cure for the most common mistake — anchoring on a label that doesn't exist and getting empty results with no error.

`CALL db.propertyKeys()` and `CALL db.schema.nodeTypeProperties()` show the live property set, but **use them to discover, not to enumerate**: `nodeTypeProperties()` lists a subset of what a node actually carries. When you need the full set for one node, return the node itself and read the keys off the response, or project the properties you need by name.
