Skip to content

Claude Agent SDK plugin

The DNSid plugin for the Claude Agent SDK is a standard Agent SDK plugin. Load it and the agent gets a complete DNSid identity story without any change to the model’s instructions:

  1. It verifies peers. Before any call to another agent, a hook verifies the peer’s DNSid identity and denies the call unless the peer is ACTIVE. The model cannot skip it.
  2. It is verifiable. A fetch tool signs outbound requests with the agent’s own key using the DNSid HTTP Message Signatures profile (RFC 9421), so the peer can verify who called.
  3. It brings itself online. At session start, the agent submits its own transparency-log ISSUANCE, authenticating with its own key.

Source: dnsid-ai/agent-sdk-plugin (Apache-2.0). The repo’s guide walks through all three behaviors on DNSid Local, and its demo example renders every protocol step as a card in a chat with an agent named Alice.

import { query } from '@anthropic-ai/claude-agent-sdk';
query({
prompt,
options: { plugins: [{ type: 'local', path: '/path/to/agent-sdk-plugin' }] },
});

The agent’s identity is the config.json and private.jwk in DNSID_CONFIG_DIR (default ~/.dnsid), exactly as the dnsid CLI writes them. Set DNSID_AWS_KMS_KEY_ID to sign with an AWS KMS key instead of a local file.

On DNSid Local, run the agent under the CLI so its identity and the local network’s DNS and CA are in the environment:

Terminal window
dnsid local run alice --port 3001 -- node alice.ts '<prompt>'

A PreToolUse hook runs on WebFetch and every mcp__* tool:

ToolPeer verified
WebFetchhostname of url
mcp__plugin_dnsid_dnsid__fetchhostname of url
mcp__plugin_dnsid_dnsid__verifynot verified: it calls no one
mcp__<server>__*the domain you mapped for that server in DNSID_MCP_SERVER_DOMAINS
anything else, including Bashnot verified

ACTIVE allows the call, and your own permission rules still run. Anything else denies with a reason starting DNSid:, even in bypassPermissions mode. A result of “cannot verify” (an untrusted log, an fl=mtls record) denies by default or asks, never allows. A hook that crashes denies. Verdicts are cached in a file until they expire; failures are cached for 30 seconds.

The plugin’s .mcp.json starts a stdio MCP server with two tools:

  • verify(domain) returns the verdict on a peer without calling it, from the same verifier and cache the hook uses.
  • fetch(url, method, headers, body, tag) signs the request with the agent’s operational key (keyid = <domain>#<kid>), sends it, and returns status, headers, and body. The signature covers method, authority, target URI, the body digest, and every header passed. Redirects are returned, not followed. Only https: URLs.

WebFetch stays available and unsigned. Pass disallowedTools: ['WebFetch'] host-side to force the signed path.

A peer verifies those requests with the same verifier the hook uses, exported as createVerifier from @dnsid-ai/agent-sdk-plugin/verify.

A SessionStart hook reads the agent’s registration. If it is READY, nothing happens beyond telling the model the identity is online. If it is VERIFIED, the hook countersigns the prepared ISSUANCE and submits it. Anything else is reported to the model as context, since a SessionStart hook cannot block a session. Requests carry a five-minute JWT signed by the agent’s own key; no organization credential is present at runtime.

Everything the plugin reads is an environment variable starting with DNSID_. A bad value fails the hook, and the hook denies. The ones you are most likely to set:

VariableDefaultMeaning
DNSID_CONFIG_DIR~/.dnsidThe agent’s identity: config.json and private.jwk.
DNSID_MODEenforceobserve logs each verdict to stderr and denies nothing.
DNSID_ON_UNVERIFIABLEdenyask turns a “cannot verify” result into a permission prompt.
DNSID_MCP_SERVER_DOMAINS{}JSON map of MCP server name to agent domain. Every tool of a mapped server is verified against that domain.
DNSID_DNS_SERVER, DNSID_CA_BUNDLEsystemDNS server and extra CA certificates; dnsid local env sets both for DNSid Local.
DNSID_LOG_POLICY_URL / DNSID_LOG_POLICY_FILEDNSid’s managed catalogWhich transparency logs to trust. Set one, not both.
DNSID_AWS_KMS_KEY_IDnoneSign with this AWS KMS key instead of private.jwk.

The full table, including key-rotation and DNSSEC settings, is in the repo README.