Skip to main content

whisper-id

Three things for any Python agent, in one dependency-free package: query the Whisper security graph (7.48B nodes and 39.5B edges joining the physical, network, naming, ownership and threat layers; live totals at nic.whisper.online/stats, 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 15 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 keyless rate limit. Every verb is sent either way; the graph decides what to answer:

g = graph("whisper_live_...")                # with a key: the keyless rate limit is lifted

# 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 answer keyless; the 15 multi-step flows and the submit write channel are proxied up the same way and the graph decides. 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. Every verb is proxied to the graph with a key or without; the graph decides what to answer. Read verbs (assess, identify, explain, variants, walk, origins, history, ...) answer keyless (rate-limited), and a key lifts the limit. 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 -fsSL https://get.whisper.online | sh

Set WHISPER_API_KEY in the environment (or run whisper login) for the CLI-backed calls and the control plane (whisper.agents), which always needs it. Graph calls never require a key from this SDK: they are proxied to the graph with one or without, and the graph decides what to answer. A key lifts the keyless rate limit. verify and rdap are keyless too.

Links

MIT licensed.

Metadata

Release files for whisper-id 0.7.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for whisper-id 0.7.0
File Size Uploaded
whisper_id-0.7.0.tar.gz 37.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for whisper-id 0.7.0
File Interpreter ABI Platform
whisper_id-0.7.0-py3-none-any.whl Python 3 none any Details

Total release size: 60.5 kB

Release files / whisper_id-0.7.0.tar.gz

Download URL whisper_id-0.7.0.tar.gz
Size 37.2 kB
Tags Source
SHA-256 checksum
How to use checksums
845fe5851021a04f1f7802785c47f6082e7e2af4d21e067c2f5cb4c550efe12e
BLAKE2b-256 checksum
How to use checksums
808d8cabefe121eefa847ca5decc98a49fa9685eb903dea519091b9263ededa8
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 2, 2026.

Transparency log

Release files / whisper_id-0.7.0-py3-none-any.whl

Download URL whisper_id-0.7.0-py3-none-any.whl
Size 23.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
5acc3b052efe6436d4278697fa5152a1c47be0021de66f638511960433ad890d
BLAKE2b-256 checksum
How to use checksums
d8cad504d874d7710631e666290c10ed7086f845bad59c5ec1f1a13c67a47480
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 2, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.7.0 This release

2 release files

0.6.1

2 release files

0.6.0

2 release files

0.5.1

2 release files

0.5.0

2 release files

0.4.0

2 release files

0.3.0

2 release files

0.2.0

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page