Skip to main content

wingfoot

CI License: MIT Python

wingfoot doctor: a blocked 403 request becomes a verified 200 OK

wingfoot doctor sends a signed request, reads the response, and tells you why a blocked 403 becomes a verified 200.

Verified-bot identity for AI agents, using Web Bot Auth (a profile of RFC 9421 HTTP Message Signatures).

Sign your agent's outbound HTTP requests with an Ed25519 key, publish the public key at a well-known directory, and check whether a site accepts the signature. Includes a doctor command that sends a signed request and reports why it was accepted or rejected.

Background: Cloudflare, Akamai, and Fastly increasingly block AI agents and crawlers by default. Web Bot Auth lets a legitimate agent prove its identity instead of being treated as a hostile scraper. Setting it up by hand is fiddly, and a failed signature usually just returns 403 with no explanation.

Install

pip install wingfoot
wingfoot demo

Installing it as a standalone CLI tool (e.g. on macOS, where Homebrew blocks global pip3 installs): uv tool install wingfoot or pipx install wingfoot. Or without installing anything: uvx wingfoot demo.

Python 3.9+. The only runtime dependency is cryptography.

Demo

wingfoot demo starts a local reference verifier, sends one unsigned request and one signed request, and prints the result. It runs in a single process, so it works offline with no setup.

$ wingfoot demo

1. Unsigned request
   HTTP 403 Forbidden   missing Signature-Input header

2. Signed request
   HTTP 200 OK          verified (keyid ig0azrI2bZdo...)

Usage

# create a keypair and a key directory
wingfoot init --agent https://your-bot.example

# print the JWKS to publish at /.well-known/http-message-signatures-directory
wingfoot directory

# send a signed request
wingfoot sign https://example.com/

# check why a URL accepts or rejects your signed agent
wingfoot doctor https://example.com/

# get a ready-to-paste registration packet for Cloudflare, DataDome, ...
wingfoot register --email you@example.com

wingfoot doctor sends a signed request, reads the response, and prints a checklist:

$ wingfoot doctor http://localhost:8088/whoami

  ok   Bot identity                       keyid RtsLV3gfcK73Ql4_...
  ok   Signature is well-formed and cryptographically valid
  ok   Key directory reachable and publishes this key
  Probing the target
  -    Unsigned request                   HTTP 403
  ok   Signed request                     HTTP 200

  Verified-bot access is working: signing changed HTTP 403 to 200.

When something is wrong it reports which check failed and how to fix it: expired signature, clock skew, unreachable directory, key not listed, missing tag="web-bot-auth", and so on.

Register with verifiers

A valid signature only helps once a verifier (Cloudflare, DataDome, ...) knows your key. None of them offer automated registration yet — each runs its own web form with a manual review behind it. wingfoot register shrinks that step to copy/paste: it first checks your hosted directory the way a reviewer would, then prints every answer their forms ask for:

$ wingfoot register --email you@example.com

Preflight — what a reviewer will check
  ok   Key directory reachable
  ok   Directory publishes this key
  ok   Directory response is signed

Cloudflare — Verified Bots
  form   https://dash.cloudflare.com/?to=/:account/configurations
    Bot / agent name          your-bot.example
    User-Agent                wingfoot/0.1.2 (+https://github.com/AmirF194/wingfoot)
    Key directory URL         https://your-bot.example/.well-known/http-message-signatures-directory
    ...

Add --open to launch the forms in your browser, or name one provider: wingfoot register datadome.

Commands

Command What it does
wingfoot demo Run the unsigned/signed flow against a local verifier. No setup.
wingfoot init --agent <url> Create a lasting identity (keypair + directory).
wingfoot directory Print the JWKS to host at the well-known path.
wingfoot serve Serve your key directory locally (and verify requests).
wingfoot sign <url> Send a Web-Bot-Auth-signed request.
wingfoot doctor <url> Diagnose why a URL blocks or accepts your signed agent.
wingfoot register [provider] Preflight your setup, then print what to paste into each verifier's registration form.
wingfoot verifier Run a reference verifier for others to test against.

Use it in your code

Signing is one line — hand a wingfoot auth object to the HTTP client you already use. Every outbound request gets a fresh Web Bot Auth signature (re-signed per call, so it never expires mid-session). Run wingfoot init --agent <url> once, then:

import requests, wingfoot
requests.get("https://example.com/", auth=wingfoot.requests_auth())

import httpx, wingfoot
httpx.get("https://example.com/", auth=wingfoot.httpx_auth())

requests and httpx are optional extras (pip install "wingfoot[requests]" / "wingfoot[httpx]"); wingfoot itself needs only cryptography. Driving a different client? wingfoot.signed_headers(url) returns the headers as a plain dict to merge in yourself.

How it works

Web Bot Auth signs at least the @authority derived component and a Signature-Agent header, with the parameter tag="web-bot-auth". Three request headers carry it:

  • Signature-Agent: the origin that hosts your key directory.
  • Signature-Input: the covered components plus created, expires, keyid, tag.
  • Signature: the Ed25519 signature over the RFC 9421 signature base.

A verifier fetches <Signature-Agent>/.well-known/http-message-signatures-directory (a JWKS), looks up the key by its RFC 7638 thumbprint (keyid), and verifies the signature. The implementation of that slice lives in src/wingfoot/rfc9421.py.

Status

Alpha (v0.1). Signing, verification, the directory, and doctor work and are covered by tests against a reference verifier. Interoperability with a specific CDN depends on that CDN having your registered key. Doctor confirms your side is correct so you can register with confidence.

Drop-in signing for requests and httpx ships now (see Use it in your code).

Planned:

  • Doctor checks for Cloudflare and Akamai rejection signals.
  • Middleware for aiohttp, plus an async httpx example.
  • A TypeScript/Node port.

Issues and PRs welcome. See CONTRIBUTING.md.

License

MIT. See LICENSE.

References

Release files for wingfoot 0.1.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for wingfoot 0.1.2
File Size Uploaded
wingfoot-0.1.2.tar.gz 2.8 MB Details

Built distribution (wheel)

Table of built distributions (wheels) for wingfoot 0.1.2
File Interpreter ABI Platform
wingfoot-0.1.2-py3-none-any.whl Python 3 none any Details

Total release size: 2.8 MB

Release files / wingfoot-0.1.2.tar.gz

Download URL wingfoot-0.1.2.tar.gz
Size 2.8 MB
Tags Source
SHA-256 checksum
How to use checksums
842d834de42e91297c913f020887c5f027d6b7bd2386fd34fe0e81d55a5c4fa4
BLAKE2b-256 checksum
How to use checksums
4e2fbaea6ec13c098575c4e1d192a1eea108058a7d404d91927d9cd37175350e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 19, 2026.

Transparency log

Release files / wingfoot-0.1.2-py3-none-any.whl

Download URL wingfoot-0.1.2-py3-none-any.whl
Size 26.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
3af093df9dae9b5d99211adc36f86351e6e2d228cb47c0a513ce7a50e01e06ca
BLAKE2b-256 checksum
How to use checksums
01d48e0d480a67caf656a718516d5d61cfcff355e01a4ae800c4afa3fe933fa4
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 19, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.2 This release

2 release files

0.1.1

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page