{"slug": "show-hn-verify-openai-s-signed-agent-traffic-in-python-rfc-9421-web-bot-auth", "title": "Show HN: Verify OpenAI's signed agent traffic in Python (RFC 9421/Web Bot Auth)", "summary": "Regent released regent-httpsig, a Python library that verifies and signs AI agent HTTP traffic per RFC 9421 and the Web Bot Auth draft, enabling FastAPI and framework-agnostic verification of OpenAI-signed requests and egress signing for agent identification. The library passes byte-exact test vectors for RFC 9421 Appendix B.2.6 and Web Bot Auth drafts -05 A.2.2 and A.2.3, with sign-verify roundtrips and AAuth identity-mode interop pinned in CI, and reports upstream deviations in the draft's A.2.2 example and aauth-signing's base64 encoding. Verification is enrichment by default, returning None for missing or invalid signatures, and the FastAPI dependency rebuilds signed URLs from X-Forwarded-Proto and Host for reverse proxy deployments.", "body_md": "**Verify and sign AI agent HTTP traffic in Python — the way OpenAI signs and Cloudflare verifies.**\nRFC 9421 · Web Bot Auth · AAuth\n\nOpenAI's agents cryptographically sign every HTTP request they make. Cloudflare, AWS WAF and\nGoogle verify those signatures. This library brings both sides of that handshake to Python:\n**verify** signed agents hitting your API, and **sign** your own agent's traffic so bot walls\nrecognize it.\n\n```\npip install regent-httpsig\npython\nfrom fastapi import FastAPI\nfrom regent_httpsig import HttpsigVerifier\nfrom regent_httpsig.fastapi import attach, SignatureDep, VerifiedSignature\n\napp = FastAPI()\nattach(app, HttpsigVerifier())\n\n@app.post(\"/v1/orders\")\nasync def create_order(sig: VerifiedSignature | None = SignatureDep):\n    if sig:\n        print(sig.agent)    # \"https://chatgpt.com\"\n        print(sig.keyid)    # RFC 7638 key thumbprint\n    ...\n```\n\nNo FastAPI? The core has no framework dependencies:\n\n```\nverifier = HttpsigVerifier()\nsig = await verifier.verify(method, url, headers)   # VerifiedSignature | None\n```\n\nVerification is **enrichment by default**: no `Signature`\n\nheader costs nothing, a bad\nsignature yields `None`\n\n, and nothing ever raises on untrusted input. Use\n`regent_httpsig.fastapi.RequiredSignatureDep`\n\nwhen a signature must be present — the 401\ntells the agent exactly how to sign.\n\nBehind a reverse proxy?The agent signed thepublicURL (`https://api.example/…`\n\n), but your ASGI server sees`http://container/…`\n\n. The FastAPI dependency rebuilds the signed URL from`X-Forwarded-Proto`\n\n+`Host`\n\n, so make sure your proxy forwards the scheme — nginx:`proxy_set_header X-Forwarded-Proto $scheme;`\n\n. If signatures mysteriously fail to verify in production, check this first.\n\n``` python\nfrom regent_httpsig import EgressSigner\n\nsigner = EgressSigner(seed=os.environ[\"AGENT_KEY_SEED\"],\n                      signature_agent=\"https://myagent.example\")\nheaders = signer.sign(\"POST\", url, {\"content-type\": \"application/json\"})\nresp = httpx.post(url, json=body, headers=headers)\n```\n\nGenerate a key and the ready-to-publish `/.well-known/`\n\nfiles in one command:\n\n```\nregent-httpsig keygen --agent https://myagent.example --out ./well-known/\n```\n\nPublish the directory at `https://myagent.example/.well-known/http-message-signatures-directory`\n\nand every Web Bot Auth verifier on the internet can now identify your agent.\n\n| Check | Status |\n|---|---|\n| RFC 9421 Appendix B.2.6 Ed25519 vector (byte-exact) | ✅ in CI |\nWeb Bot Auth draft -05 A.2.2 — sf-dictionary `Signature-Agent` covered with `;key=` |\n✅ in CI¹ |\nWeb Bot Auth A.2.3 — legacy sf-string form (what OpenAI ships in production) |\n✅ in CI |\n| Sign → verify roundtrip (fresh keys, full pipeline) | ✅ in CI |\nAAuth identity-mode roundtrip (`aa-agent+jwt` + `cnf.jwk` proof of possession) |\n✅ in CI |\n| Signed by\n`aauth-signing` |\n\n¹ The signature bytes printed in the draft's own A.2.2 example do **not** verify over the\ndraft's own signature base (the legacy A.2.3 vector and RFC 9421 B.2.6 both do, so the defect\nis in the example, not the canonicalization). Ed25519 is deterministic, so our test pins the\nvector re-signed with the same RFC test key over the same byte-exact base — reported upstream.\n\n² Cross-library interop with `aauth-signing`\n\n's jwt scheme: token layer, `cnf.jwk`\n\nproof of\npossession and canonicalization all verify. Its signers correctly omit the optional `keyid`\n\nparameter — which exposed an unconditional `keyid`\n\nread in the underlying RFC 9421 library\nthat we now handle. One deviation reported upstream to `aauth-signing`\n\n: it emits the\n`Signature`\n\nbyte sequence as base64url, while RFC 8941 requires standard base64. The\nkeyid-less shape is pinned in CI.\n\n**Web Bot Auth**(`draft-meunier-web-bot-auth-architecture`\n\n): key discovery via`{Signature-Agent}/.well-known/http-message-signatures-directory`\n\n. Both wire forms of`Signature-Agent`\n\nare accepted — the current sf-dictionary and the legacy bare sf-string OpenAI actually sends.**AAuth**(`draft-hardt-oauth-aauth-protocol`\n\n, identity-based mode): the agent carries a JWT`agent_token`\n\nin`Signature-Key`\n\n; the issuer's JWKS verifies the token, the token's`cnf.jwk`\n\nverifies the request signature. Install with`pip install 'regent-httpsig[aauth]'`\n\n. Tracks the**-11 editor's copy**: fully-specified algorithms (RFC 9864,`Ed25519`\n\n— with a transition flag for the -10 ecosystem's`EdDSA`\n\n) and**person tokens**(`aa-person+jwt`\n\n, opt-in via`HttpsigConfig.resource_url`\n\n). For a full-protocol AAuth implementation (both roles, all token types) see[christian-posta/aauth-python-library](https://github.com/christian-posta/aauth-python-library)— this library is the thin relying-party verifier that handles both dialects.\n\nThe verifier fetches key directories from **attacker-nameable origins** — whoever signs a\nrequest chooses its `Signature-Agent`\n\n. regent-httpsig ships with the guard rails on:\n\n**SSRF protection by default**: https-only, every resolved IP must be public (catches`169.254.169.254`\n\n, loopback, private ranges, DNS names mapping to internal services), redirects never followed, responses size-capped.**Bounded caching**: per-instance TTL cache with eviction — a keyid-spam attack can't grow memory; failures are negative-cached so a dead origin can't be used to slow you down.**A valid signature proves key possession — not trustworthiness.**`VerifiedSignature.trusted`\n\nreflects only your configured allow-list; deciding*whether to trust*a key is your policy layer's job.\n\nKnown sharp edges of the underlying ecosystem, already handled: the upstream\n`http-message-signatures`\n\nlibrary cannot resolve RFC 9421 `;key=`\n\ndictionary members (we\nprovide the component resolver), it looks up header names case-sensitively while ASGI\nframeworks lowercase them (we wrap), and it forgets to declare `typing_extensions`\n\n(we\ndeclare it).\n\n``` python\nfrom regent_httpsig import HttpsigConfig, HttpsigVerifier\n\nverifier = HttpsigVerifier(HttpsigConfig(\n    trusted_agents=frozenset({\"https://chatgpt.com\", \"https://operator.openai.com\"}),\n    max_age_hours=25,       # reject signatures created earlier than this\n    cache_ttl=600,          # key-directory cache seconds\n))\n```\n\nPass your app's shared client to reuse its pool: `HttpsigVerifier(http_client=my_async_client)`\n\n.\n\n- Web Bot Auth and AAuth are\n**IETF drafts**(RFC 9421 itself is a final standard). We track the drafts; breaking draft changes land as minor releases while we're 0.x. **Ed25519 only** for now — it's what the agent ecosystem ships.- Body coverage (\n`content-digest`\n\n) is verified when covered by the signature, but this library does not require it; decide per-route whether you need it.\n\n[cloudflare/web-bot-auth](https://github.com/cloudflare/web-bot-auth) (TypeScript/Rust) ·\n[christian-posta/aauth-python-library](https://github.com/christian-posta/aauth-python-library)\n(full AAuth protocol) · [pyauth/http-message-signatures](https://github.com/pyauth/http-message-signatures)\n(the RFC 9421 primitive this builds on)\n\nBuilt and battle-tested in production by [Regent Protocol](https://regentprotocol.org) —\nruntime control and identity for AI agents. Apache-2.0.", "url": "https://wpnews.pro/news/show-hn-verify-openai-s-signed-agent-traffic-in-python-rfc-9421-web-bot-auth", "canonical_source": "https://github.com/regent-protocol/regent-httpsig", "published_at": "2026-08-19 14:01:41+00:00", "updated_at": "2026-08-19 14:14:40.895896+00:00", "lang": "en", "topics": ["ai-agents", "developer-tools", "ai-infrastructure", "ai-safety"], "entities": ["OpenAI", "Cloudflare", "AWS WAF", "Google", "Regent", "FastAPI", "RFC 9421", "Web Bot Auth"], "alternates": {"html": "https://wpnews.pro/news/show-hn-verify-openai-s-signed-agent-traffic-in-python-rfc-9421-web-bot-auth", "markdown": "https://wpnews.pro/news/show-hn-verify-openai-s-signed-agent-traffic-in-python-rfc-9421-web-bot-auth.md", "text": "https://wpnews.pro/news/show-hn-verify-openai-s-signed-agent-traffic-in-python-rfc-9421-web-bot-auth.txt", "jsonld": "https://wpnews.pro/news/show-hn-verify-openai-s-signed-agent-traffic-in-python-rfc-9421-web-bot-auth.jsonld"}}