Skip to contentSkip navigation

whisper.identify() — Identity & Assessment

Answer whose infrastructure a host is, whether it is dangerous, and — when there's no direct match — what sits around it. The identity family: identify, assess, walk.

Procedures
On this page (4)

whisper.identify() — Identity & Assessment Documentation

Three procedures answer the questions you ask about a host before you decide what to do with it: whose infrastructure is this, is it dangerous, and — when neither has a clean answer — what sits around it. They are built to run over a batch of hosts and return decision-ready columns, so they slot straight into triage and enrichment pipelines.

whisper.identify(hosts) — whose infrastructure is this

whisper.identify() resolves a host to the vendor or service that operates it. Pass one host or a batch (1 to 256 per call — larger batches are rejected, not truncated) and it returns the canonical identity plus the evidence behind it.

cypher · runnablegraph.whisper.securitySign in to run
CALL whisper.identify(["api.stripe.com", "cdn.shopify.com"])
YIELD host, vendor_id, canonical_name, category, roles, host_class, band
RETURN host, vendor_id, canonical_name, category, host_class, band

ColumnMeaning
hostThe input host.
vendor_idStable identifier of the resolved vendor (github, stripe, …).
canonical_nameThe resolved vendor or service identity.
categoryWhat kind of service it is (saas, cloud, cdn, …).
rolesThe roles the host plays (DNS_OPERATOR, MAIL_RECEIVER, …).
host_classA coarse classification of the host — multi_tenant_user_content flags platforms where anyone can publish.
bandAttribution band. A host with no match comes back as UNKNOWN with confidence 0.0 — it never fails the batch.
confidenceHow strongly the evidence supports the identity.
evidenceThe signals that produced the match.

Use it to label infrastructure at scale — attributing a list of hostnames from a log or an alert to the services behind them, without a hop-by-hop traversal per host.

whisper.assess(hosts) — is it dangerous

whisper.assess() answers the safety question, and — critically — tells you what it actually looked at. It accepts a single host as a bare string or a batch as a list; both return the same seven columns: CALL whisper.assess("github.com") returns band: INFO, coverage: known-clean.

cypher · runnablegraph.whisper.securitySign in to run
CALL whisper.assess(["example.com", "185.220.101.1"])
YIELD host, label, band, coverage, signals, evidence
RETURN host, label, band, coverage

ColumnMeaning
labelThe assessment (clean, suspicious, …).
bandSeverity band (NONECRITICAL).
sub_labelsFiner-grained labels behind the top-level label.
coverageWhat we looked at, on the malice question — four values on production. This is the column to gate on, and it is not a strength scale. See Coverage.
signalsThe threat signals that fired.
evidenceThe underlying evidence.

Every Whisper verdict answers two independent questions. band tells you how bad. coverage tells you what we actually looked at. Read both. They are a grid, not a ladder.

Only known-clean — coverage: known-clean. In coverage, no malicious evidence. licenses the word "clean". Every other value is not-clean — and no-data — coverage: no-data. Not in coverage. This is not a verdict — nothing was looked at. and deadline-hit mean unknown, which is a different thing again.

whisper.assess and whisper.assessUrl return coverage. whisper.explain does not.

coverageWhat it meansWhat to do
known-clean — coverage: known-clean. In coverage, no malicious evidence.We hold data at this granularity and nothing malicious is in it.Treat as clean. This is the only value that licenses closing a ticket on "clean."
malicious-evidenced — coverage: malicious-evidenced. In coverage, with positive evidence of malice.Some positive evidence of malice exists. It may be a single feed at weight 0.5. It does not mean the band is high.Read evidence[] for feed-source-count, then run explain() for the per-feed provenance, weights and timestamps. A count of 1 on a low-weight aggregate list is a lead, not a finding.
ambiguous — coverage: ambiguous. In coverage, and the evidence points both ways.The evidence points both ways — for example an anonymising-egress signal alongside generic abuse listings.Escalate to a human. Do not automate a decision on this value.
no-data — coverage: no-data. Not in coverage. This is not a verdict — nothing was looked at.We have never observed this host.Unknown. Never benign. Ask a different question — the container, the operator, the age — and escalate with "we have no observation of this host", never with "it came back clean."

Every one of these arrives as a populated row. no-data — coverage: no-data. Not in coverage. This is not a verdict — nothing was looked at. is a row that says no-data — coverage: no-data. Not in coverage. This is not a verdict — nothing was looked at.; it is never an empty result set. If a query returns zero rows, the first hypothesis is that the query is wrong, not that the host is clean.

Which procedure carries coverage —:

ProcedureReturns coverage?What its coverage is about
whisper.assessYesThreat coverage. The four values above.
whisper.assessUrlYesA path axis, not a host axis — read the contract before gating on it.
whisper.walkYes, but not a verdictAtlas and vendor adjacency — whether the host is reachable in the graph's structure. Emits presence-axis values only.
whisper.explainNoReturns score, level, explanation, factors and sources. There is no coverage column, so a NONE level from explain() is not a clean verdict.

structural-onlywalk — a whisper.walk value describing atlas adjacency. Not an assess coverage value. Do not gate on it. is a whisper.walk value describing atlas adjacency. It is not a whisper.assess value, and a branch keyed on it in an assess result is unreachable — see the full contract.

whisper.walk(host [, depth] [, budget_ms]) — the structural neighborhood

When whisper.identify() has no direct match, whisper.walk() returns a bounded structural neighborhood — the host's siblings and the nearest known vendors — so you still get context to reason about an unknown host. It is depth- and budget-bounded so it stays fast on large networks.

cypher · runnablegraph.whisper.securitySign in to run
CALL whisper.walk("unknown-host.example", 2, 800)
YIELD host, no_atlas_match, nearest_known_vendors, siblings, coverage
RETURN host, no_atlas_match, nearest_known_vendors, coverage

ColumnMeaning
no_atlas_matchtrue when the host resolved to no direct vendor identity.
nearest_known_vendorsThe closest identified vendors in the surrounding infrastructure.
siblingsHosts that share infrastructure with the input.
coverageHow much of the neighborhood the walk reached — atlas and vendor adjacency. A presence value, not a verdict, and it uses a different vocabulary from assess. Read arms before you read it.
armsPer-arm traversal detail, including deadline_hit when a budget expired.

The optional second argument is the traversal depth (1 to 6, default 2) and the third is a per-arm budget in milliseconds (default 3000). When the budget runs out, the row comes back with partial data and arms.deadline_hit: true — a partial walk is a result, not an error. Keep both modest — this is a fallback for the case where a direct identity lookup came back empty, not a general graph crawler.

The same word, two meanings — and on one indicator they flatly disagree.

  • CALL whisper.assess(["185.220.101.1"])coverage: "ambiguous" — there is evidence, pointing both ways.
  • CALL whisper.walk("185.220.101.1")coverage: "no-data" — no atlas match, and arms_completed: 7 with nothing truncated.

Both rows are correct. They answer different questions: assess reports threat coverage, walk reports structural adjacency. Never gate a verdict on a walk row. A branch written against walk's vocabulary in an assess result is unreachable, and the branch it leaves you missing is malicious-evidenced — coverage: malicious-evidenced. In coverage, with positive evidence of malice..

Where these fit

  • Start with identify to attribute known infrastructure.
  • Use assess for the safety verdict on a batch, and always read coverage before you act on label.
  • Fall back to walk only when identify returns no match and you need surrounding context.

For a single scored threat verdict with per-feed evidence, use explain(). To band a full URL rather than a host, use whisper.assessUrl() — and read its page first, because its coverage column behaves differently from the one above. For the full procedure catalog, see the Procedures overview.