# Dify

**Your Dify agent can already browse the web and call APIs — it just can't prove to anyone, including you, which agent did it, or stop a compromised one from doing it again.**

Dify apps run behind a shared NAT egress IP indistinguishable from every other tenant, so "which of my 40 agents hit this API" is a grep through logs you have to trust, and "kill that agent's network access" means editing the app instead of revoking an identity. The **Whisper Agent Identity** plugin fixes this at the network layer: a Dify tool-call gets real, routable IPv6 identity — verify any address, provision a new one with its own `/128`, govern it, and (Dify being self-hostable) route the agent's traffic through that address.

It ships as a standard Dify tool plugin, installable straight from GitHub today, with a Marketplace listing in review. Like every Whisper integration it is **two-tier**: the verification tools work with zero configuration and no account, and pasting in a Whisper API key unlocks the control plane.

## Install it

**GitHub install (live now).** In Dify: **Plugins → Install Plugin → Install from GitHub**, point at `whisper-sec/dify-plugin`, pick the latest release. No build step.

**Marketplace (pending review).** [`langgenius/dify-plugins#2636`](https://github.com/langgenius/dify-plugins/pull/2636) ships the packaged `.difypkg` for one-click install from Dify's in-app marketplace once merged.

**Build it yourself** — the source is plain Python + YAML, no Whisper tooling required:

```sh
# stock tools only
gh release download -R langgenius/dify-plugin-daemon \
  --pattern 'dify-plugin-linux-arm64' -O /tmp/dify-plugin && chmod +x /tmp/dify-plugin
git clone https://github.com/whisper-sec/dify-plugin whisper-dify
/tmp/dify-plugin plugin package ./whisper-dify -o whisper-agent-identity-0.1.0.difypkg
```

Open the plugin's **Authorize** panel and you'll see exactly two optional credentials: `api_key` (a `whisper_live_…` key) and `egress_proxy` (a local proxy URL). Both blank is a fully working install.

## The nine tools

The plugin is one Dify tool *provider* (`provider/whisper.yaml`) exposing nine tools, split cleanly along the auth boundary:

| Tier | Tool | Wire call |
|---|---|---|
| Keyless | Verify Agent Identity | `GET /verify-identity?ip=<addr>` |
| Keyless | Lookup RDAP Record | `GET /ip/<addr>` |
| Keyless | Get Transparency Log | `GET /ip/<addr>/transparency` |
| Keyless | Get Inbound Lookups | `GET /ip/<addr>/lookups` |
| Control (needs key) | Register Agent | `CALL whisper.agents({op:'register'})` |
| Control (needs key) | Set Policy | `op:'policy'` |
| Control (needs key) | Get Logs | `op:'logs'` |
| Control (needs key) | Revoke Agent | `op:'revoke'` |
| Control (needs key) | Get Egress Config | `op:'connect'` |

The keyless tools hit the public gateway `rdap.whisper.online` over plain HTTPS — no header, no cookie. The control tools all funnel through the **one** Whisper control verb, `CALL whisper.agents({op, args})`, `POST`ed as Cypher to `https://graph.whisper.security/api/query` with the caller's key in `X-API-Key`. The plugin never invents a second API surface; it's a thin, typed wrapper over the same verb the [CLI](https://github.com/whisper-sec/whisper-cli) uses, so a tool's behavior is exactly what `whisper` on your terminal would do. The DoH resolver (`/dns-query`) is deliberately **not** exposed as a tool — a DNS-message-in/DNS-message-out endpoint is a broken building block for an LLM tool call.

## Verify an agent

`Verify Agent Identity` answers "is this address a genuine Whisper agent, and who runs it?" by checking that the forward name for the agent's `/128` and the reverse PTR agree — the same forward-confirmed reverse DNS trick operators have used since split-horizon anti-spoofing became common practice, formalized here as one call.

Independently, from a terminal, that's a forward-confirmed-reverse-DNS check plus an RDAP lookup — the full walk is in [Verify an agent](/docs/verify). In Dify it's one tool call:

```
Verify Agent Identity   address = 2a04:2a01:eb5a:ca74:cef2:2a:323d:40d4
```
```json
{ "is_whisper_agent": true,
  "fqdn": "acef2002a323d40d4.t7af9242c44554e0f8e183ac8e6fbf645.agents.whisper.online",
  "dane_ok": true,
  "jws_ok": true,
  "evidence": {
    "ptr": "4.d.0.4.d.3.2.3.a.2.0.0.2.f.e.c.4.7.a.c.a.5.b.e.1.0.a.2.4.0.a.2.ip6.arpa",
    "forward_aaaa": "2a04:2a01:eb5a:ca74:cef2:2a:323d:40d4",
    "agent": "acef2002a323d40d4" } }
```

The PTR resolves back to the exact address it claims (`forward_aaaa`), and the leaf is pinned by a DNSSEC-anchored TLSA record (`dane_ok`) whose transparency entry is signed (`jws_ok`) — three independent confirmations in one call. An address that anchors no agent — or where forward and reverse disagree — returns `is_whisper_agent: false` with the specific reason, never a bare 404. That's the same liberal-input contract as `whisper verify` and every keyless SDK call: never an opaque error, always a verdict plus evidence.

**Get Transparency Log** is the deeper check: it returns the hash-chained, signed record of every issuance event for that `/128` — the append-only ledger analogue of Certificate Transparency (RFC 9162)'s core idea applied to address identity rather than X.509 leaves, so a party who doesn't trust Whisper's word can still verify the chain itself rather than trust a point-in-time API answer.

## Register & govern

**With stock tools**, provisioning a routable, verifiable per-agent identity is not something `curl`/`openssl` do for you — there's no RFC for "mint me an IPv6 /128 with reverse DNS, a TLSA record, and a revocation path" you can hand-roll from a resolver. This is exactly the gap the control plane fills.

**With the Dify tool:**

```
Register Agent   name = scout   contact_email = ops@example.com
```
```json
{ "agent": "scout",
  "address": "2a04:2a01:…",
  "fqdn": "a<interface-id>.<your-tenant>.agents.whisper.online",
  "api_key": "whisper_live_…",
  "state": "active" }
```

The `/128` is freshly minted from AS219419 space under **your** tenant (not the demo's), so the reverse name lands in your own `…agents.whisper.online` label — exactly the identity you just learned to verify.

The key is returned exactly once — `register_agent.py` surfaces it in the tool's text summary ("api_key returned once — store it now") precisely so it doesn't silently vanish into workflow logs. From there, `Set Policy` writes the tenant's DNS allow/deny bundle, `Get Logs` reads back recent DNS/connection/allocation activity for auditing inside a Dify workflow, and `Revoke Agent` tears the identity down — withdrawing the `/128`, its PTR, its tokens, and its key in one irreversible call, so a compromised or retired agent can be cut off from a chat message instead of a config edit.

## Egress: making it real, not just observed

Verification and governance are half the story; the other half is that Dify is **self-hostable**, so an agent's *actual* traffic can leave from its `/128` instead of the shared platform IP. `Get Egress Config` reports the binding but — by design — never returns the raw egress bearer or WireGuard key, so nothing secret leaks into workflow data. Bring up real egress with the open-source CLI on the Dify host, then point the deployment (or just this plugin's `egress_proxy` credential) at it:

```sh
curl https://get.whisper.online | sh
whisper connect --port 1080
# → socks5h://127.0.0.1:1080, bound to your /128
```

```yaml
# egress.compose.yaml — applied to plugin_daemon, api, worker
environment:
  ALL_PROXY: socks5h://host.docker.internal:1080
  NO_PROXY:  localhost,127.0.0.1,db,redis,weaviate,sandbox,plugin_daemon,api,ssrf_proxy
```

```sh
docker compose -f docker-compose.yaml -f egress.compose.yaml up -d
curl --proxy socks5h://127.0.0.1:1080 https://api64.ipify.org
# → your agent's own /128; confirm the identity at /docs/verify
```

Full recipe, including the whole-deployment vs. single-plugin tradeoff, is in the plugin's shipped `EGRESS.md`.

## Source

Plugin source: [`github.com/whisper-sec/dify-plugin`](https://github.com/whisper-sec/dify-plugin) (MIT). Control-verb reference client: [`github.com/whisper-sec/whisper-cli`](https://github.com/whisper-sec/whisper-cli).

---

**Next:** [Integrations overview](/docs/integrations) · [MCP server](/docs/mcp)
