ai-security
Verifying OpenAI's signed agent traffic from Python
Promtime
ai-securityRegent Protocol has published regent-httpsig, a Python library that verifies the RFC 9421 message signatures OpenAI's agents attach to every HTTP request, and signs outgoing agent traffic in the same format. The code, test matrix and documentation are on Github under Apache-2.0, and the package installs with pip install regent-httpsig.
At a glance
- Key discovery follows the Web Bot Auth draft: the verifier fetches the directory named by the request's Signature-Agent header, and both the current sf-dictionary form and the legacy sf-string form OpenAI sends are accepted.
- Verification is enrichment by default: a missing Signature header costs nothing, a bad signature yields None, and untrusted input never raises, while a separate FastAPI dependency returns 401 with signing instructions.
- Because directories are fetched from origins the caller names, SSRF guards ship on: https only, resolved IPs must be public, redirects are never followed, and responses are size-capped.
Signed agent traffic has so far been mostly an edge-side story: according to the project's documentation, OpenAI signs, and Cloudflare, AWS WAF and Google verify. Moving both halves into an application dependency appears to shift the decision down to the API owner, who can read the calling agent's identity per route instead of inheriting a verdict made at the perimeter. The trust question stays open, since a valid signature proves key possession and nothing more.
Five lines of FastAPI code surface the calling agent's URL and key thumbprint
The FastAPI integration attaches a verifier to the application and exposes the result as a dependency: when a signature is present, sig.agent carries the signing agent's URL, such as https://chatgpt.com, and sig.keyid carries an RFC 7638 key thumbprint. The core verifier has no framework dependency, takes a method, a URL and headers, and returns a VerifiedSignature or None.
Deployments behind a reverse proxy need care, because the agent signs the public URL while the ASGI server sees an internal one. The FastAPI dependency rebuilds the signed URL from X-Forwarded-Proto and Host, and the project flags an unforwarded scheme as the first thing to check when production verification fails.
EgressSigner and a keygen command publish the agent's own key directory
On the signing side, EgressSigner takes a key seed and a signature_agent URL and returns headers for an outgoing request, so a Python agent can present itself the way OpenAI's agents do. A keygen command generates the key and writes the ready-to-publish files for /.well-known/http-message-signatures-directory.
Web Bot Auth discovery reads that directory at the origin named in Signature-Agent. AAuth identity mode works differently: the agent carries a JWT agent_token in Signature-Key, the issuer's JWKS verifies the token, and the token's cnf.jwk verifies the request signature. It installs as an optional extra.
The AAuth support tracks the -11 editor's copy, with RFC 9864 fully-specified Ed25519 and a transition flag for the -10 ecosystem's EdDSA, plus opt-in person tokens. Regent Protocol points to christian-posta/aauth-python-library for a full-protocol implementation covering both roles.
CI pins the RFC 9421 Appendix B.2.6 Ed25519 vector byte-exact
The test matrix pins that vector, both Web Bot Auth wire forms from draft -05, a sign-to-verify roundtrip on fresh keys, an AAuth identity-mode roundtrip with cnf.jwk proof of possession, and rejection of tampered requests, expired signatures and wrong directory keys.
Cross-library interop with aauth-signing's jwt scheme verifies at the token layer and the canonicalization layer. Its signers omit the optional keyid parameter, which exposed an unconditional keyid read in the underlying RFC 9421 library; that library also cannot resolve ;key= dictionary members and looks up header names case-sensitively, both handled by wrappers.
HttpsigConfig takes a trusted_agents allow-list, in the documented example https://chatgpt.com and https://operator.openai.com, a max_age_hours of 25 and a cache_ttl of 600 seconds. Directories are cached per instance with eviction and negative caching, and VerifiedSignature.trusted reflects only that allow-list.
Two reports sitting upstream
The project reports two defects upstream: the signature bytes in the Web Bot Auth draft's own A.2.2 example do not verify over the draft's own signature base, and aauth-signing emits the Signature byte sequence as base64url where RFC 8941 requires standard base64.
Web Bot Auth and AAuth remain IETF drafts while RFC 9421 is final, and the library states that breaking draft changes will land as minor releases for as long as it stays on 0.x. Ed25519 is the only algorithm supported, and no timeline for a 1.0 is given.
Comments
No comments yet. Be the first.
Join the conversation
Sign in with Google to leave a comment. Your name and avatar come from your Google profile, and the comment appears after moderation.
We only use your name and avatar from Google. We never store your email address.
