whisper-id
Three things for any Python agent, in one dependency-free package: query the Whisper security graph (3.6B nodes / 30B relationships, Cypher), give the agent a routable IPv6 identity with safe egress, and drive the full control plane.
pip install whisper-id # add [socks] for requests+SOCKS: pip install "whisper-id[socks]"
The security graph (keyless, zero setup)
The Whisper graph knows who operates a host, its threat posture, its look-alikes, the real origins behind a CDN, WHOIS history, and 20+ named investigations. The direct read verbs run with no key at all (rate-limited taste, ~100/window). One import, real answers:
from whisper_id import graph
g = graph() # no key needed for the read verbs
g.assess("8.8.8.8") # -> [{'host': '8.8.8.8', 'label': 'benign-allowlisted', 'band': 'INFO', ...}]
g.identify("api.openai.com") # who operates this host -> vendor + operator roles
g.origins("cloudflare.com") # the real origin IPs behind a CDN
g.explain("paypal.com") # threat-feed score + why
Set WHISPER_API_KEY (or graph("whisper_live_...")) to lift the rate limit and unlock raw Cypher and the multi-step flows:
g = graph("whisper_live_...") # keyed: unlimited + flows + raw Cypher
# raw Cypher, your own query, parameters bound as $-params (never string-built):
g.query("MATCH (h:HOSTNAME {name:$n})-[:RESOLVES_TO]->(ip) RETURN ip.name AS ip LIMIT 5", {"n": "github.com"})
# a named catalog recipe (a multi-step investigation, streamed over SSE):
g.typosquat("paypal.com") # look-alike sweep -> registered variants + verdict
g.run_flow("attack-surface", {"domain": "github.com"}) # any flow by its catalog slug
# discover the whole catalog (29 queries + flows) with no key, no network:
for r in g.recipes():
print(r["method"], "keyless" if r["keyless"] else "keyed", r["docs_url"])
Every verb maps to a catalog entry with its own docs page under whisper.security/docs (e.g. assess, identify); recipes() carries the exact docs_url for each. The 13 direct reads are keyless; the 15 multi-step flows and the submit write channel are keyed. Full query reference: whisper-catalog.
Identity + egress
from whisper_id import register, egress
import requests
agent = register("my-bot") # a routable Whisper /128 identity
with egress(): # route this block through your /128
requests.get("https://api64.ipify.org").text # leaves from your Whisper IPv6
Inside the with block the standard proxy env vars point at your local Whisper proxy, so requests, httpx, urllib, and most libraries just work; on exit they're restored. Pass the proxy explicitly if you prefer:
with egress(set_env=False) as e:
requests.get(url, proxies=e.proxies)
API
| Call | Does |
|---|---|
graph(key=None) |
The security-graph namespace. Read verbs (assess, identify, explain, variants, walk, origins, history, ...) are keyless (rate-limited); query (raw Cypher), the flows, and submit need a key. recipes() lists the whole catalog. |
register(name, *, new_key=False) |
Create a named agent: a routable /128. new_key=True mints a new agent and its own API key. -> Agent(address, id, name) |
egress(agent=None, *, tier="socks5", set_env=True) |
Context manager: bring up egress bound to your /128. Yields Egress (.port, .proxy_url, .socks_url, .proxies). tier="wireguard" for a routed /128. |
verify(address) |
Keyless: is address a real Whisper agent? (DANE + DNSSEC + reverse-DNS + JWS) -> bool |
verify_details(address) |
Keyless: the full verdict (is_whisper_agent, fqdn, operator, tenant, dane_ok, jws_ok, ...) or None |
rdap(address) |
Keyless: the public RDAP record for a /128, or None |
egress_ip() / ip() |
Keyless / CLI: the IP this process leaves from -> str |
Control plane: pure-HTTP (no CLI), one HTTPS call each; needs your key (arg key=... or WHISPER_API_KEY):
| Call | Does |
|---|---|
list_agents(kind="agents") |
Your fleet (kind = agents | identities | records). -> list[dict] |
policy(default=..., allow=..., block=...) |
Set your DNS resolver policy, or read it with no args. -> dict |
logs(agent=..., kind=..., from_=..., to=..., limit=...) |
Recent DNS/conn/alloc activity (kind = dns | conn | alloc). -> list[dict] |
identity(label, contact_email=...) |
Allocate your own /128; release with identity(release=True, address=...). -> dict |
agent(agent_or_address) |
One agent's detail + counters (id, or a /128, anything with : is an address). -> dict |
revoke(agent) |
Fully revoke an agent (irreversible). -> dict |
Keyless / serverless (no key, no CLI)
The graph read verbs plus verify, verify_details, rdap, and egress_ip are keyless: pure HTTPS, no whisper CLI, no key, so they run anywhere including serverless/edge functions:
from whisper_id import graph, verify, rdap
if verify(addr): # one HTTPS call; works in AWS Lambda, Cloudflare, etc.
print(rdap(addr)["name"])
print(graph().assess(addr)) # keyless threat posture, same anywhere
register, egress, and ip need the CLI (egress needs the local proxy; register needs your key).
Control plane: govern your fleet (no CLI)
import os
from whisper_id import identity, list_agents, policy, logs, agent, revoke
os.environ["WHISPER_API_KEY"] = "whisper_live_..." # or pass key=... to any call
a = identity("scout") # allocate a routable /128
policy(default="deny", allow=["api.github.com"]) # deny-by-default DNS policy
logs(agent=a["address"], kind="dns", from_="-1h") # recent activity
revoke(a["address"]) # irreversible
Every control call is one HTTPS POST of CALL whisper.agents({op, args}); it raises WhisperError with the server's message on failure. Set WHISPER_CONTROL_URL / WHISPER_RDAP_URL / WHISPER_FLOW_RUN_URL to point at a self-hosted deployment.
Requirements
For register / egress / ip, the whisper CLI on your PATH (this package is a thin, dependency-free wrapper over it). The graph read verbs and the keyless calls above need nothing.
curl get.whisper.online | sh
Set WHISPER_API_KEY in the environment (or run whisper login) for the CLI-backed calls, the keyed graph (raw Cypher + flows + submit), and the control plane. The graph read verbs, verify, and rdap are keyless.
Links
- Site: https://whisper.online
- Docs: https://www.whisper.security/docs
- Query catalog: https://github.com/whisper-sec/whisper-catalog
- CLI: https://github.com/whisper-sec/whisper-cli
MIT licensed.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file whisper_id-0.6.0.tar.gz.
File metadata
- Download URL: whisper_id-0.6.0.tar.gz
- Upload date:
- Size: 34.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
91ae7aca9008e63b6eab4b0659e73de6601e1f8af282505f4be7948bce3d4134
|
|
| MD5 |
17c587738759ff51c9efb87a9d4255f0
|
|
| BLAKE2b-256 |
4793525dce6c017d9dd271cce36930bea621036ca8b9ef012ce63d50f78f51fb
|
Provenance
The following attestation bundles were made for whisper_id-0.6.0.tar.gz:
Publisher:
publish.yml on whisper-sec/whisper-py
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
whisper_id-0.6.0.tar.gz -
Subject digest:
91ae7aca9008e63b6eab4b0659e73de6601e1f8af282505f4be7948bce3d4134 - Sigstore transparency entry: 2172006327
- Sigstore integration time:
-
Permalink:
whisper-sec/whisper-py@f5a170570bc625949eed3c7e8acb91e64db4aba0 -
Branch / Tag:
refs/tags/v0.6.0 - Owner: https://github.com/whisper-sec
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@f5a170570bc625949eed3c7e8acb91e64db4aba0 -
Trigger Event:
release
-
Statement type:
File details
Details for the file whisper_id-0.6.0-py3-none-any.whl.
File metadata
- Download URL: whisper_id-0.6.0-py3-none-any.whl
- Upload date:
- Size: 22.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
46e67fc6d476c83b169384a252a861cea3f13463a2f2c6dfccf5a0e229c3f0c0
|
|
| MD5 |
a45c0abab3599bc27c2d7b2dea73b77e
|
|
| BLAKE2b-256 |
b98a69b651506e13934406db93f16ab024c3c0c9579c6242dede6f24ab848880
|
Provenance
The following attestation bundles were made for whisper_id-0.6.0-py3-none-any.whl:
Publisher:
publish.yml on whisper-sec/whisper-py
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
whisper_id-0.6.0-py3-none-any.whl -
Subject digest:
46e67fc6d476c83b169384a252a861cea3f13463a2f2c6dfccf5a0e229c3f0c0 - Sigstore transparency entry: 2172006329
- Sigstore integration time:
-
Permalink:
whisper-sec/whisper-py@f5a170570bc625949eed3c7e8acb91e64db4aba0 -
Branch / Tag:
refs/tags/v0.6.0 - Owner: https://github.com/whisper-sec
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@f5a170570bc625949eed3c7e8acb91e64db4aba0 -
Trigger Event:
release
-
Statement type: