Sign & encrypt as your agent
One key, already in DNS. Sign a document so anyone can verify it, or encrypt a file so only one agent can open it - no CA, no key exchange, no shared secret to leak.
Your agent already carries a per-agent EC P-256 key, minted for its own /128 and pinned in DNSSEC-signed DNS - the same key its DANE TLSA record and per-agent certificate resolve identity against. Two everyday jobs fall straight out of that one key, one command each, and both anchor in DNSSEC instead of a certificate authority you have to install and trust:
| You want to | Command | The other side | Anchor |
|---|---|---|---|
| Sign a file, PDF or email as your agent | whisper sign file | Anyone verifies - no key | SMIMEA (RFC 8162) |
| Encrypt a file for an agent | whisper encrypt --to | Only that agent decrypts | OPENPGPKEY (RFC 7929) |
The shape mirrors identity verification: the privileged half needs your key, the public half needs nothing. You sign with your key; a stranger verifies with none. Anyone encrypts to your agent with no credentials at all; only your agent, holding its identity, can open it. Trust is the DNSSEC chain from the IANA root, re-derived in-process - not a CA bundle, not a vendor "trust us" endpoint.
Sign a document or email
whisper sign file issues your per-agent emailProtection certificate from the control plane (this half needs your API key) and produces a detached S/MIME (CMS / PKCS#7) signature over the bytes. Hand the .p7s to anyone:
whisper sign file contract.pdf
# -> signed contract.pdf as agent@a1b2c3.agents.whisper.online -> contract.pdf.p7s
# verify (no key): whisper sign verify contract.pdf --sig contract.pdf.p7s
The same certificate signs email: it carries an emailProtection EKU and an rfc822Name SAN, so a standard S/MIME client (or openssl smime -sign) produces a signed message any mail client can check.
Verify with no key
Verification is keyless and needs no Whisper software of the signer's - just the ability to validate DNSSEC, which the command does itself from the IANA root:
whisper sign verify contract.pdf --sig contract.pdf.p7s
# -> VERIFIED - signature is valid and the signer key is DANE-anchored by the
# DNSSEC SMIMEA for agent@a1b2c3.agents.whisper.online (spki sha256 54f0afc7...)
# Trust: DNSSEC/SMIMEA, not a public S/MIME CA.
The trust is DANE, end to end. The SMIMEA record (RFC 8162, DANE-EE 3 1 1) pins the signer's exact key under the signer's mailbox, DNSSEC-signed; the verifier matches the CMS signer's key to that pin and needs no public S/MIME CA and no pre-installed trust anchor. Tamper with a single byte of the document and the check fails closed:
whisper sign verify tampered.pdf --sig contract.pdf.p7s
# -> CMS signature did not validate: Message digest mismatch
Encrypt a file for an agent
Encryption is the mirror image - the public half needs no key. Point whisper encrypt at any agent's name and it seals the data so only that agent can open it:
whisper encrypt --to a1b2c3.agents.whisper.online secret.txt
# -> encrypted for a1b2c3.agents.whisper.online -> secret.txt.wenc
No API key changes hands. The recipient's public key comes straight from its DNSSEC-signed OPENPGPKEY record (RFC 7929) - the same per-agent key the TLSA and SMIMEA pin - validated from the IANA root before anything is sealed, so you can only ever encrypt to a key the DNSSEC chain vouches for. The encryption itself is HPKE (RFC 9180) hybrid public-key encryption: DHKEM(P-256, HKDF-SHA256) + HKDF-SHA256 + AES-256-GCM. The output is a portable, copy-pasteable WHISPER ENCRYPTED MESSAGE block; the recipient and ciphersuite are bound into the message so it cannot be re-targeted or downgraded.
Decrypt as the agent
The agent opens it with its own identity (this half needs the agent's key, fetched from the control plane, or supplied with --key for a sole-control identity):
whisper decrypt secret.txt.wenc
# -> decrypted secret.txt.wenc -> secret.txt (encrypted for a1b2c3.agents.whisper.online)
A message sealed to a different agent simply won't open, and a corrupted message fails closed rather than returning garbage - the same Postel discipline as everywhere else: strict and unambiguous about what it produces, clear (never a stack trace) about what it refuses.
With stock tools - no Whisper software at all
None of this is Whisper plumbing you have to trust on faith. The agent's keys live in standard DNS records (SMIMEA RFC 8162, OPENPGPKEY RFC 7929), the signatures are CMS (RFC 5652), and the encryption is HPKE (RFC 9180). Everything the whisper CLI does you can do, or independently re-check, with dig, openssl and gpg:
# The agent's signing pin and public key are just DNS records, DNSSEC-signed (AD=YES):
dig +dnssec SMIMEA d4f0bc5a29de06b510f9aa428f1eedba926012b591fef7a518e776a7._smimecert.a1b2c3.agents.whisper.online
# -> 3 1 1 54F0AFC79E208F13906DF248B5AF3F215FD661591584A748595B053107CF7863
# That OPENPGPKEY is a real OpenPGP key - gpg reads it straight out of DNS:
dig +short OPENPGPKEY _openpgpkey.a1b2c3.agents.whisper.online | base64 -d | gpg --list-packets
# -> public key packet: version 4, algo 19 (ECDSA), nistp256
Verify a signature and check the DANE pin by hand, with nothing but openssl and dig:
# 1. the CMS signature is cryptographically valid over the document:
openssl smime -verify -in signed.eml -CAfile whisper-ca.pem
# -> Verification successful
# 2. the signer's key is the exact one DNS pins (DANE-EE 3 1 1 = SHA-256 of the SPKI):
openssl x509 -in signer.crt -pubkey -noout | openssl pkey -pubin -outform der | openssl dgst -sha256
# -> 54f0afc7...07cf7863 (equals the SMIMEA pin above -> trust proven against DNSSEC, not a CA)
Encryption is the IETF standard HPKE (RFC 9180) over that same DNS-published P-256 key, so it is not tied to the whisper binary either: any HPKE implementation - OpenSSL 3.2+ (the OSSL_HPKE_* API), or a library such as circl / pyhpke - interoperates with the .wenc container, which is a thin, documented wrapper (magic, ciphersuite ids, recipient, KEM output, ciphertext) around the standard HPKE result. The point throughout: your trust rests on open standards you can check yourself, and the CLI is a convenience on top, never a dependency.
What the trust is - and isn't
Two honest caveats, because a security tool that overclaims is worse than none:
- Hosted key vs. sole control. By default the per-agent key is hosted, so a signature is authenticated (it came from that identity) but not non-repudiable, and an encrypted message is confidential to the agent and to Whisper. For hard non-repudiation and true end-to-end confidentiality where only the agent can act, the agent holds its own key: a sole-control signing certificate, or
whisper decrypt --key. The certificates say which of the two you have - a hosted signing cert carriesdigitalSignatureonly, never a false non-repudiation bit. - Encryption is confidential, not sender-authenticated. Anyone can encrypt to a published key - that is the point of public-key encryption. A clean decrypt proves the message was sealed to your key, not who sent it. When the sender's identity matters, pair the two: sign the content, then encrypt it (sign-then-encrypt).
- DANE-native today, CA-bridge optional. Anything that speaks DANE (
SMIMEA/OPENPGPKEY) trusts these day one with zero configuration -whisper sign verifyis DANE-native and needs nothing. Mainstream clients that only trust a CA can add the optional Whisper root, but the trustless path never depends on it.
Built on open standards
| Piece | Standard |
|---|---|
| S/MIME key in DNS (signing) | SMIMEA, RFC 8162 |
| OpenPGP key in DNS (encryption) | OPENPGPKEY, RFC 7929 |
| Hybrid public-key encryption | HPKE, RFC 9180 |
| The DNS key pin & its trust anchor | DANE / TLSA, RFC 6698 + DNSSEC, RFC 4033-4035 |
| The signature container | CMS / PKCS#7, RFC 5652 |
Next
See /docs/sign-outputs for the broader "prove what an agent produced" pattern, /docs/dane for how the DANE/DNSSEC anchor is built, and /docs/per-agent-ca for why every identity gets its own key.