Skip to content
WhisperGraph
Skip navigation

Connection Types

Every edge type in WhisperGraph — direction, source and target labels, and the physical-vs-virtual rules that decide how you traverse it.

Published Last updated

On this page (16)

Connection Types Documentation

Connections are what make a pre-joined graph worth querying. This page covers every edge type the engine exposes — each one's direction, the labels it runs between, and what it means. For the nodes on either end, see Entities; for the chains that string edges across layers, see Pivoting Examples.

The table below is generated from CALL db.relationshipTypes(), so its row set is whatever the census last saw. Several edge types are computed at query time rather than stored, and a computed type can be absent from one live listing while its edges still traverse. If an edge you expect is missing from a listing, anchor a node by name and follow the edge directly before you conclude it is gone.

The From → To column is the source and target labels the call reports, not the ones a writer remembered. Where they name a label CALL db.labels() does not return, the row says so.

Direction matters. Several edges point the opposite way to how you would read them in English — NAMESERVER_FOR and MAIL_FOR run server → domain, so you traverse them backwards to answer "what serves this domain". The table's From → To column is the engine's own answer to which way each one points.

Edge types

DNS resolution & hierarchy

EdgeRowsFrom → ToWhat it means
RESOLVES_TO3,125,689,316HOSTNAME → IPV4, IPV6A host resolves to an IP (forward only — reverse-DNS via (ip)<-[:RESOLVES_TO]-(h)).
ALIAS_OF455,347,689HOSTNAME → HOSTNAMEA CNAME alias.
CHILD_OF2,451,196,569EMAIL, HOSTNAME, IPV4, PREFIX → HOSTNAME, ORGANIZATION, PREFIX, TLDA subdomain points up to its parent (and to the TLD).
NAMESERVER_FOR9,173,662,411HOSTNAME → HOSTNAMEA nameserver serves a domain (edge points server → domain).
MAIL_FOR591,149,876HOSTNAME → HOSTNAMEA mail server handles mail for a domain (edge points server → domain).
LINKS_TO15,147HOSTNAME, URL → HOSTNAMEA phishing-kit URL path served by a host (URL → HOSTNAME), plus a small sample of host-to-host hyperlinks.

BGP & routing

EdgeRowsFrom → ToWhat it means
BELONGS_TO619,734,764IPV4, IPV6 → PREFIXAn IP falls inside an allocated prefix. It also carries the FEED_SOURCE → CATEGORY taxonomy hop.
ANNOUNCED_BY4,327,447,095IPV4, IPV6 → ANNOUNCED_PREFIXAn IP is covered by a BGP-announced prefix.
ROUTES1,441,678ASN → ANNOUNCED_PREFIX, PREFIXAn ASN announces a prefix (matches in either direction).
HAS_NAME31,460ASN → ASN_NAMEAn ASN's registered network name (asn.name is the AS number; the network name lives on ASN_NAME).
BGP_NEIGHBOR322,832ASN → ASNThe canonical ASN↔ASN BGP adjacency edge.
PEERS_WITH322,832ASN → ASNAlias of BGP_NEIGHBOR. The same adjacency under its older name. Both spellings traverse.
BGP_PATH2,904,788BGP_PATH_OBSERVATION → ASNOne hop of an observed AS path — the only route to the BGP_PATH_OBSERVATION label.
CONFLICTS_WITH11,370ANNOUNCED_PREFIX → ASNA multi-origin (MOAS) conflict over a prefix. Synthesized at expand time.
ROA_AUTHORIZES_ORIGIN3,020,064ROA → ASNAn RPKI ROA authorizes an AS as origin.
ROA_AUTHORIZES_PREFIX882,693ROA → ANNOUNCED_PREFIX, PREFIX, REGISTERED_PREFIXAn RPKI ROA authorizes a prefix.
OPERATES1,449TLD_OPERATOR → TLDA registry operates a TLD.

WHOIS & registration

EdgeRowsFrom → ToWhat it means
HAS_REGISTRAR649,320,946HOSTNAME → REGISTRARThe domain's current registrar.
PREV_REGISTRAR618,353,159HOSTNAME → REGISTRARA prior registrar.
HAS_EMAIL546,846,860HOSTNAME → EMAILA WHOIS contact email.
HAS_PHONE550,324,627HOSTNAME → PHONEA WHOIS contact phone.
REGISTERED_BY916,255,242ASN, HOSTNAME, REGISTERED_PREFIX → ORGANIZATIONThe registrant or owning organization.
SAME_ORG_AS7,678ORGANIZATION → ORGANIZATIONTwo organizations resolved to the same entity.
PARENT_OF4,687,568CATEGORY, HOSTNAME → CATEGORY, HOSTNAMEA parent-child relationship between two entities of the same kind — a parent organization and a subsidiary, for instance.
REGISTERED_TO_ENTITY375,236ASN, PREFIX → RDAP_ENTITYA prefix or an ASN registered to an RDAP entity — the edge that reaches RDAP_ENTITY.

Geo

EdgeRowsFrom → ToWhat it means
LOCATED_IN118,196,249IPV4, IPV6 → CITYAn IP's GeoIP city (chain HAS_COUNTRY for the country).
HAS_COUNTRY194,925,670ANNOUNCED_PREFIX, ASN, CITY, HOSTNAME, IPV4, IPV6, ORGANIZATION, PHONE, PREFIX, REGISTERED_PREFIX → COUNTRYThe associated country.

Threat intelligence & attribution

EdgeRowsFrom → ToWhat it means
LISTED_IN14,315,745HOSTNAME, IPV4, IPV6 → FEED_SOURCEAn indicator appears on a threat feed.
HAS_SIGNAL90,822ASN, HOSTNAME, PREFIX → THREAT_SIGNAL_TYPEA node carries a threat signal.
OPERATES_EXIT_NODE1,272IPV4, PREFIX → TOR_RELAYAn IP operates a Tor exit relay.
DELEGATED_TO377,648IPV4, PREFIX, VENDOR → VENDORAddress space operated by a vendor (distinct from the WHOIS owner).
TAGGED_AS132,135HOSTNAME, IPV4, PREFIX → THREAT_TAGA MISP-style classification tag on an indicator — the only route to the THREAT_TAG label.
USES_TECHNIQUE9,630ACTOR → ATTACK_PATTERNA threat actor uses a MITRE ATT&CK technique.
USES_TACTIC881ATTACK_PATTERN → ATTACK_PATTERNA technique grouped under its ATT&CK tactic.
ATTRIBUTED_TO345HOSTNAME, IPV4, PREFIX → ACTORAn indicator attributed to a named actor.

Email security & TLS

EdgeRowsFrom → ToWhat it means
SPF_INCLUDE248,657,397HOSTNAME → HOSTNAMEAn SPF include: mechanism.
SPF_IP184,041,835HOSTNAME → IPV4, IPV6, PREFIXAn SPF ip4:/ip6: authorization.
SPF_A93,812,710HOSTNAME → HOSTNAMEThe SPF a mechanism.
SPF_MX84,451,018HOSTNAME → HOSTNAMEThe SPF mx mechanism.
SPF_EXISTS327,825HOSTNAME → HOSTNAMEThe SPF exists: mechanism.
SPF_REDIRECT2,751,803HOSTNAME → HOSTNAMEThe SPF redirect= modifier.
DMARC_REPORTS_TO53,153HOSTNAME → DMARC_RECIPIENTWhere a domain sends DMARC aggregate reports.
DKIM_SIGNED_BY30,545HOSTNAME → VENDORThe mail vendor whose DKIM key signs a domain's outbound mail.
EMITS_TLS_FINGERPRINT271IPV4 → TLS_FINGERPRINTAn IP presents a JA3/JARM TLS fingerprint.

Physical infrastructure

EdgeRowsFrom → ToWhat it means
AS_PRESENT_AT519,951ASN → FACILITYA network is present in a datacenter.
IX_MEMBER57,701ASN → INTERNET_EXCHANGEA network is a member of an IXP.
IX_HOSTED_AT4,545INTERNET_EXCHANGE → FACILITYAn IXP is hosted in a facility.
CABLE_LANDS_AT3,217SUBMARINE_CABLE → CABLE_LANDINGA subsea cable lands at a landing point.
LANDING_NEAR5,251CABLE_LANDING → FACILITYA landing point is near a facility.
CDN_POP_AT1,668CDN_POP → FACILITYA CDN PoP is in a facility.
FIBER_SEGMENT9,998FACILITY → FACILITYA fiber link between facilities (symmetric).
PREFIX_IN_REGION3,281PREFIX → CLOUD_REGIONA prefix sits in a cloud region.

Certificate Transparency

EdgeRowsFrom → ToWhat it means
SEEN_IN_CT396,679HOSTNAME → CT_OBSERVATIONA host appears in a Certificate Transparency observation.

Web, technology & similarity

EdgeRowsFrom → ToWhat it means
RUNS_TECHNOLOGY1,584,990,259HOSTNAME → TECHNOLOGYA host or IP runs a detected technology — a CMS, a web server, a framework.
SIMILAR_TO94,253,913HOSTNAME → HOSTNAMETwo entities the engine judged similar — near-duplicate organization names or lookalike domains, for instance.

Declared but unpopulated

None. Every edge type in this reading reported rows, and none came back declaredButEmpty.

Rows measured live from CALL db.relationshipTypes(), 2026-10-22. 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.

Direction conventions worth memorising

A handful of edges account for most wrong-direction queries.

  • CHILD_OF points upward: www.google.com → google.com → com. Its sources are HOSTNAME and EMAIL (an address points at its host domain); its targets are HOSTNAME and TLD.
  • MAIL_FOR and NAMESERVER_FOR point from the server to the domain it serves. A domain's mail hosts are (d:HOSTNAME {name: "…"})<-[:MAIL_FOR]-(mx).
  • RESOLVES_TO is forward only (HOSTNAME → IPV4 / IPV6). Reverse DNS is the same edge read backwards: (ip)<-[:RESOLVES_TO]-(h).
  • ANNOUNCED_BY goes IP → ANNOUNCED_PREFIX, not to the ASN. ROUTES goes ASN → PREFIX or ANNOUNCED_PREFIX and matches in either arrow direction. Walk from an IP to the network that routes it with ANNOUNCED_BY then ROUTES; do not join ROUTES and BELONGS_TO in one pattern to do the same job.
  • BELONGS_TO is overloaded: IPV4 / IPV6 → PREFIX in the addressing plane, FEED_SOURCE → CATEGORY in the feed taxonomy. Label both endpoints so the planner knows which you mean.
  • BGP_NEIGHBOR is the ASN-to-ASN adjacency edge. PEERS_WITH is its deprecated alias; write BGP_NEIGHBOR.
  • CONFLICTS_WITH runs ANNOUNCED_PREFIX → ASN: the prefix is the source, the competing origin AS the target.
  • BGP_PATH runs BGP_PATH_OBSERVATION → ASN, one edge per AS on the path. The observation's name is the hyphen-joined AS path, origin last.
  • LOCATED_IN is IPV4 / IPV6 → CITY only. Chain HAS_COUNTRY from the city for the country. HAS_COUNTRY itself has many sources: ASNs, prefixes, organizations, phones and hostnames carry one too.
  • HAS_NAME is ASN → ASN_NAME. asn.name is the AS number; the network name lives on the ASN_NAME node.
  • REGISTERED_TO_ENTITY runs PREFIX / ASN → RDAP_ENTITY and is the way in to RDAP_ENTITY. Hostnames do not carry it; a domain's registration data is on EMAIL, PHONE, REGISTRAR and ORGANIZATION.
  • LINKS_TO's live use is URL → HOSTNAME: this phishing-kit path has been served by this host. The HOSTNAME → HOSTNAME form the listing reports is a small sample of hyperlinks, not a web-scale link graph, so do not plan a link-graph question on it. Anchor the URL node by {name: …}, which holds the path and is indexed, or bound it with WITH u LIMIT n, before following the edge.
  • SIGNED_WITH runs HOSTNAME → DNSSEC_ALGORITHM, presence-only. It confirms a domain is DNSSEC-signed and names the algorithm; it does not say whether the signature validates. It is also invisible to CALL db.relationshipTypes() — the census will not list it — so anchor a hostname and follow the edge directly rather than concluding from the table above that it does not exist.

Technology and company layer

WhisperGraph also joins a hostname to the technology stack it runs and to the wider company it belongs to, available to accounts with company-data access. A worked example: Which technologies does a vendor run, and who else does it own?

  • RUNS_TECHNOLOGY runs HOSTNAME → TECHNOLOGY, 1,584,990,259 edges. It names a specific product (Cloudflare DNS, Atlassian Statuspage) with a category and the technology vendor's own apex domain, not just a vendor name.
  • PARENT_OF runs HOSTNAME → HOSTNAME, 4,687,568 edges, and covers two distinct relationships: the same brand under another ccTLD or gTLD (google.com → google.af) and a parent company to a differently branded subsidiary or product domain (meta.com → rewind.ai). CALL db.relationshipTypes() also lists a TLD → HOSTNAME source for this edge; that source returns no edges live, so anchor HOSTNAME on both ends rather than building a query off a TLD node.
  • SIMILAR_TO runs HOSTNAME → HOSTNAME, 94,253,913 edges, and is directional, not symmetric: google.com names bing.com as similar, but the reverse edge does not exist. Read it as one company's own outbound comparison set, and query both directions if you need the full pairing.

Where the planes are thin

Some of these planes are small enough that a zero-row result says more about Whisper's coverage than about the indicator, and reading one as evidence of absence is the most expensive mistake on this page. The row count in the table above is what tells them apart.

The ATT&CK layer is a curated reference plane, not Whisper's own attribution: 9,630 USES_TECHNIQUE and 881 USES_TACTIC edges reflect what public reporting has mapped. The edge that joins it to live infrastructure is sparse: ATTRIBUTED_TO, a published attribution from an indicator to a named group, holds 345 edges. It answers no attribution question at scale, and an empty result from it is no-data — coverage: no-data. Not in coverage. This is not a verdict — nothing was looked at., never evidence of absence.

The email and TLS planes are not the same size, and lumping them together hides which results you can trust: DKIM_SIGNED_BY holds 30,545 edges and DMARC_REPORTS_TO 53,153 — populated planes where an empty result usually means the domain publishes no such record. EMITS_TLS_FINGERPRINT holds 271, a small fraction of either.

TLS-fingerprint coverage is partial. Expect no match on almost any indicator. A zero-row result here means Whisper holds no observation — not that the host shares no infrastructure.

Certificate Transparency coverage is partial. Many well-known hosts have no SEEN_IN_CT edge at all. A zero-row result here means Whisper holds no CT observation for that host. It never means the host has a clean certificate history. If certificate history is load-bearing for your decision, query a CT log directly — crt.sh or the Google CT API — and come back with the hostnames you find.

Physical vs. virtual edges

Edge types come in two kinds, and the distinction changes how you write the query. Read the split from the engine rather than from a list here: CALL db.schema('json') marks every edge type virtual: true or false.

Physical edges are materialized on disk — the DNS, geo and SPF sets, and most of the WHOIS set. An unanchored MATCH ()-[r:RESOLVES_TO]->(h) returns rows.

Virtual edges are computed at query time and are not in the physical store: the routing set, the whole threat and attribution set, the DMARC/DKIM/TLS-fingerprint edges, SEEN_IN_CT, REGISTERED_TO_ENTITY, and the physical-infrastructure set. BELONGS_TO is mixed — the IP → PREFIX side is stored, the FEED_SOURCE → CATEGORY taxonomy hop is computed — and the engine treats it as virtual when it plans a traversal. Five rules follow:

  • Label or anchor at least one endpoint. A pattern with both ends bare is refused, not answered: MATCH (a)-[:LISTED_IN]->(b) RETURN a.name, b.name returns HTTP 400 query-unservable, naming the edge type. Label one end — MATCH ()-[:LISTED_IN]->(f:FEED_SOURCE) — and it answers.
  • Variable-length patterns work. Computed edges expand inside [*1..N] and shortestPath(), and length(p) reports correctly, as long as one endpoint is labelled or anchored. Bound the range, and on a peering walk filter WHERE n <> a — an undirected mesh routinely returns to the origin AS.
  • Type the edge when you expand outward from an announced prefix. Write -[:ROUTES]->, -[:CONFLICTS_WITH]-> or -[:HAS_COUNTRY]-> rather than a bare -[r]->, or anchor on the PREFIX-labelled node for the same CIDR.
  • De-duplicate on the client, or aggregate. Over a routing chain that projects an ANNOUNCED_PREFIX, use count(DISTINCT …) or collect(DISTINCT …), or de-duplicate the rows yourself, rather than RETURN DISTINCT.
  • Read edge counts from the census calls. CALL db.relationshipTypes() YIELD type, count and GET /api/query/stats give the per-type totals. An unanchored count(r) over a computed edge is not the way to get one.

ROUTES and HAS_NAME do appear in db.relationshipTypes() with real counts — read them off the table above. Anchor them and they work.

CALL db.schema() in its default form attaches a traversalHints list to many relationship rows — copy-pasteable starter patterns anchored on the labels that edge actually accepts. The db.schema('json') form carries no hints. See Best Practices for the full set of query rules.

Confirm an edge before you traverse

cypher · runnablegraph.whisper.securitySign in to run
CALL db.relationshipTypes() YIELD type, sourceLabels, targetLabels
RETURN type, sourceLabels, targetLabels ORDER BY type

db.relationshipTypes() lists every edge type with the source and target labels observed on it, and it is cheap — it answers immediately. Checking it first — alongside db.labels() — is the fastest cure for empty, error-free results from a mistyped edge name.