Graph & cognition
An agent about to fetch a URL has about a millisecond to decide whether it should — and "run it through a blocklist" isn't an answer, it's a guess with a timestamp.
A static feed is hours behind a fast-flux C2 domain; whois tells you who registered a name, not who's serving traffic behind the CDN in front of it; and none of it comes back with the receipts a human, an auditor, or another agent needs to trust the call. Whisper answers differently: one call against a live graph of the internet — DNS, BGP, WHOIS, TLS, hosting, threat-intel, fused and current — that hands back a verdict and the exact evidence behind it, in under 300ms.
The question every agent asks before it acts
Three shapes of the same question come up constantly, and none of them are answerable from a flat list:
- "Who actually runs this host?" —
api.openai.comand half the internet sit behind Cloudflare or a load balancer. The IP tells you the CDN, not the tenant. - "Is this indicator safe to touch?" — an IP might be a Tor exit, a bulletproof host, or a freshly-registered domain that hasn't earned a reputation yet either way.
- "Why should I believe that?" — a verdict with no reasoning attached is not a decision you can defend later, to a compliance reviewer or to yourself.
Whisper's cognition surface is the graph-first answer to all three, and it's the same graph that powers graph-first resolution — the policy engine that decides whether a query even leaves an agent's /128 runs these exact verbs internally, on your behalf, before every answer.
The surface: named verbs over one graph
Every verb below is a read-only CALL against the shared graph. They group into four jobs:
| Group | Verb | What it answers |
|---|---|---|
| Attribution | identify(target) |
Who really operates a host or IP — operator, category, and the resolution chain that gets you there, even behind a CDN |
asset(id) |
The full graph record for one node — hostname, IPv4/IPv6, ASN, or organization — by identifier | |
psl(domain) |
The registrable domain boundary for a name, per the Public Suffix List — sub.example.co.uk → example.co.uk |
|
| Risk | assess(targets[]) |
A risk verdict for one or more indicators — verdict, severity, category (Tor exit, bulletproof hosting, scanner, …) |
explain(target) |
The reasoning behind an assess verdict — which feeds fired, the score, how fresh the observation is |
|
threatintel(indicator) |
Direct hits against the fused threat-intelligence feeds for one indicator, unscored | |
lookupTorRelay(ip) |
Whether an address is a current Tor relay or exit node | |
lookupTlsFingerprint(ja3\|ja4) |
Known clients/tooling associated with a TLS client fingerprint | |
| Topology | walk(node, depth) |
Traverse outward from a node along graph edges — infrastructure clusters, shared-hosting genealogies |
origins(prefix) |
BGP origin history for a prefix — who has announced it, and when | |
topAsnsByPrefixCount() |
Internet-wide aggregate: ASNs ranked by announced prefix count | |
variants(domain) |
Look-alike / typosquat domain variants for a brand or name | |
| Monitoring | watch(target) |
Register a standing watch on an entity so future graph changes surface as events |
history(target) |
The historical timeline of DNS, WHOIS, and ownership changes for a target | |
submit(indicator) |
Contribute an observation into the shared graph |
Every verb is live today. A caller may also run arbitrary read-only Cypher — MATCH patterns of your own — directly against the shared subgraph; the named verbs are just the common queries pre-packaged as one call.
How to ask: two doors, one graph
Both doors run the identical query engine and return the identical shape. The only difference is how the caller is authenticated.
With your key, from anywhere, over the gateway:
curl -s https://graph.whisper.security/api/query \
-H 'X-API-Key: whisper_live_…' \
--data "CALL whisper.assess(['185.220.101.1'])"
Keyless, from inside a connected agent — the query rides the agent's own routable address, and that /128 is the credential, so no key travels on the wire at all:
curl -s https://[2a04:2a01:eb5a:ca74:cef2:2a:323d:40d4]/api/query \
--data "CALL whisper.identify('api.stripe.com')"
The same keyless pattern is exposed as a plain HTTP endpoint too, purpose-built for the single most common cognition question — "is the thing I'm about to connect to safe?" — asked from inside an agent that has no key on hand at all:
curl -s https://rdap.whisper.online/identify/api.openai.com
# -> {"host":"api.openai.com","operator":"Cloudflare","category":"cdn",
# "evidence":"RESOLVES_TO -> IPV4 -> DELEGATED_TO -> cloudflare"}
# the identical call from outside a connected agent's address:
curl -s https://rdap.whisper.online/identify/api.openai.com
# -> 403 — the same lock that authenticates the agent is what authorizes the answer
That 403 is not a bug to route around — it's the point. identify from /identify/<host> is scoped to callers whose source address is already a Whisper agent; the network layer does the authentication, so the endpoint never has to trust a header a caller could forge.
Under the hood: what "evidence" actually means
The query language is openCypher — the same declarative graph pattern language used across the property-graph ecosystem — sent as plain text in the request body and answered as JSON rows. A verb like identify is a named, parameterized Cypher procedure; running it is equivalent to writing the underlying MATCH pattern yourself, without needing to know the schema.
What matters for trust is what comes back alongside the verdict. Every attribution or risk answer carries an evidence field that is the literal traversal the graph made to reach its conclusion — a chain of typed nodes and relationships, not a black-box score:
identify('api.openai.com')
-> operator=Cloudflare · category=cdn
-> evidence: (HOSTNAME)-[:RESOLVES_TO]->(IPV4)-[:DELEGATED_TO]->(ORGANIZATION:Cloudflare)
assess(['185.220.101.1'])
-> suspicious · MEDIUM · Tor exit
explain('185.220.101.1')
-> 3 feeds · score 5.3 · last seen 3h ago
identify walked RESOLVES_TO (a DNS answer) into DELEGATED_TO (a WHOIS/RDAP organization edge) and stopped at an ORGANIZATION node — that's the whole chain, reproducible, and disprovable if it's wrong. explain unpacks an assess verdict the same way: which of the fused threat-intel feeds contributed, the resulting score, and the age of the freshest observation behind it. Nothing here is a magic number; it's a graph traversal you can read.
Dual example: attribution, the hard way and the easy way
With stock tools — chase the resolution chain yourself and hope the last hop tells you something:
dig +short api.openai.com # CNAME/A chain, ends on a CDN edge IP
whois -h whois.arin.net 104.18.0.0 # org on the IP -- usually the CDN, not the tenant
curl -s https://rdap.whisper.online/ip/104.18.0.0 | jq .entities
# best case: "Cloudflare, Inc." -- correct, but you still don't know who's BEHIND Cloudflare
None of those three commands tells you the tenant behind the edge — CDNs exist specifically to hide that. identify says "Cloudflare, cdn" honestly rather than guess further; for names it fully resolves and owns, the chain terminates at the real organization instead.
With Whisper — the same investigative chain, pre-walked, in one round trip:
curl -s https://graph.whisper.security/api/query -H 'X-API-Key: whisper_live_…' \
--data "CALL whisper.identify('api.openai.com')" | jq .
# {"host":"api.openai.com","operator":"Cloudflare","category":"cdn",
# "evidence":"RESOLVES_TO -> IPV4 -> DELEGATED_TO -> cloudflare"}
Dual example: risk, the hard way and the easy way
With stock tools, checking one IP against Tor and whatever abuse databases you have accounts for means several disjoint tools that don't agree on a scale or a timestamp:
curl -s https://check.torproject.org/torbulkexitlist | grep -qx 185.220.101.1 && echo "tor exit"
whois -h whois.abuseipdb.example 185.220.101.1 2>/dev/null # only if you have an abuse-DB account
With Whisper, one verdict and one explanation, on the same fused graph the resolver itself queries:
curl -s https://graph.whisper.security/api/query -H 'X-API-Key: whisper_live_…' \
--data "CALL whisper.assess(['185.220.101.1'])" # -> suspicious · MEDIUM · Tor exit
curl -s https://graph.whisper.security/api/query -H 'X-API-Key: whisper_live_…' \
--data "CALL whisper.explain('185.220.101.1')" # -> 3 feeds · score 5.3 · seen 3h ago
Latency, and what happens when the graph is slow
Cognition calls target under 300ms end to end, including the graph traversal. That budget matters beyond the API itself: graph-first resolution calls assess on the resolver's hot path before letting a DNS query proceed, under the same strict timeout — and if the graph doesn't answer inside it, the resolver treats that as no opinion and fails open, never a resolution outage. A slow cognition call degrades gracefully everywhere it's used; it never becomes the single point of failure for traffic that has nothing to do with it.
A verdict without evidence is an opinion. Every assess, identify, and explain response here returns the graph rows behind the answer — the same rows you could walk yourself with raw Cypher — so the decision holds up outside the call that made it.
Next
- Graph-first resolution — the same graph, called on the DNS hot path to gate what an agent can resolve
- Control plane —
whisper.agents, the sibling verb that provisions and governs identity rather than answering questions about it