Web Bot Auth

Verify cryptographically signed AI agents using RFC 9421 HTTP Message Signatures.

What it does

The bot_auth provider verifies cryptographically signed AI agents per the IETF Web Bot Auth pattern. AI crawlers sign each request with an Ed25519 key under RFC 9421 HTTP Message Signatures and advertise their keyid in the Signature-Input header. The gateway looks up the matching public key in its directory and verifies the signature. Agents that pass come through. Everything else gets 401.

Wire shape

GET /article HTTP/1.1
Host: blog.example.com
User-Agent: GPTBot/1.0
Signature-Input: sig1=("@method" "@target-uri" "@authority");created=1700000000;keyid="openai-2026-01";alg="ed25519"
Signature: sig1=:Tcle5Bn3...:

Configuration

authentication:
  type: bot_auth
  clock_skew_seconds: 30
  agents:
    - name: openai-gptbot
      key_id: openai-2026-01
      algorithm: ed25519
      public_key: ${OPENAI_BOT_PUBKEY}
      required_components:
        - "@method"
        - "@target-uri"
        - "@authority"
    - name: anthropic-claudebot
      key_id: anthropic-2026-01
      algorithm: ed25519
      public_key: ${ANTHROPIC_BOT_PUBKEY}

Field reference

FieldTypeDefaultDescription
agentslistrequiredDirectory of known agents. Each key_id must be unique.
clock_skew_secondsint30Tolerance for the created and expires parameters.
agents[].namestringrequiredHuman-readable agent name. Surfaced in logs.
agents[].key_idstringrequiredkeyid parameter the agent advertises in Signature-Input.
agents[].algorithmstringrequireded25519 or hmac_sha256.
agents[].public_keystringrequiredHex- or base64-encoded raw key bytes.
agents[].required_componentslist["@method", "@target-uri"]Signature components every accepted request must cover.

Verdicts

VerdictActionCause
VerifiedAllowSignature valid against an agent in the directory.
Missing401No Signature-Input header.
UnknownAgent401keyid claimed is not in the directory.
Failed401Header parse failure, signature mismatch, expired, or required component missing.

The denial body is intentionally generic. Detailed reasons land in the structured log under the sbproxy::auth target.

Required components

By default a verifier accepts a signature that covers ("@method" "@target-uri"). That alone prevents replay across different routes. Tighten this when the upstream relies on a specific header:

required_components:
  - "@method"
  - "@target-uri"
  - "@authority"
  - "content-digest"   # bind the body
  - "x-replay-id"      # caller-supplied nonce

Limitations

  • The OSS directory is inline in YAML. Dynamic directory refresh from a hosted JWKS-shaped document is on the roadmap.
  • Verification runs in the synchronous auth phase, before the request body is buffered. Signatures that cover content-digest fail when the body is non-empty.

See also