Skip to content
← Notes

Billy signs his own requests

What Billy is, what he fetches, and the Ed25519 key you can check it against. The operator policy for a signed agent.

An automated request from a datacenter address is anonymous by default, and every defense built against that fact is a guess. The User-Agent is a string anyone can type. The IP range belongs to a cloud provider and says nothing about who rented it. So origins guess, and the guess lands on everything that is not a laptop - which is how a person's assistant ends up looking exactly like a scraper.

Billy stops guessing at us. Every request he makes to the open web carries an Ed25519 signature over the request itself, and the public half of that key is published on this domain. Anyone can fetch it, check the signature, and know the request came from Billy rather than from something claiming to be him. That is the whole of the arrangement, and it is worth being precise about what it buys: it makes us identifiable. It does not make us allowed. Those remain two different decisions and the second one is yours.

Who runs this

Billy is a personal AI assistant built by Billy Company. One Billy belongs to one person, and everything below happens because that person asked for something.

Contact: griffin@billylives.com.

What Billy fetches, and why

A person asks Billy a question. Sometimes the answer is on a page, so Billy reads the page. That is the entire pattern.

In practice it is one of two shapes. A read - someone hands Billy a link, or Billy finds one, and he fetches that URL to answer from it. Or a research pass - a question turns into a search, and the handful of results worth reading get read. Both are bounded by a live human request, which is why there is no crawl schedule to publish. Billy does not enumerate a site, does not follow every link he finds, and does not come back to see whether the page changed unless someone asks him to.

He also drives a real browser for the pages that need one, because a page rendered by JavaScript is not readable any other way. Same rule: a person asked, so Billy looks.

What Billy does not do: train models on what he reads, resell it, cache it for anyone but the person who asked, or fetch anything nobody asked for.

How to verify a request is Billy

The signature is the identifier. Three headers travel with every open-web request:

Signature-Agent: "https://proof.billylives.com"
Signature-Input: sig1=("@authority" "@method" "signature-agent");created=...;expires=...;keyid="...";alg="ed25519";nonce="...";tag="web-bot-auth"
Signature: sig1=:...:

Signature-Agent names this origin. Append /.well-known/http-message-signatures-directory to it and you get a JWK set of the Ed25519 keys we sign with, served as application/http-message-signatures-directory+json. Match the keyid against an RFC 7638 thumbprint of a key in that set, rebuild the signature base per RFC 9421 §2.5, and verify. The wire format is draft-meunier-webbotauth-httpsig-protocol; Cloudflare's web-bot-auth library reads it, and so does anything else that implements the draft.

The covered components are deliberate. @authority binds the signature to one host, so a signature you receive cannot be replayed against someone else. @method binds it to one verb. signature-agent binds it to this directory, so nobody can strip the header and attribute our signature to their own key set. The window between created and expires is sixty seconds.

The directory response is signed too, over the authority it was served from, so a mirror of our key set at somebody else's URL fails on its own terms.

Keys rotate additively. The directory publishes a set, a retiring key stays in it until nothing it signed can still be in flight, and the successor is published before it ever signs. A verifier that caches the set for an hour never sees a gap.

How to block Billy

If you would rather not serve an AI assistant, the signature is the cleanest handle you will ever get for that: it is unforgeable, it does not change when we change infrastructure, and blocking on it will never catch a real browser by accident. Deny on Signature-Agent: "https://proof.billylives.com", or on the keyid, and we will stay out. No hard feelings and no workaround - the point of signing is that you get a real choice, and a real choice includes no.

If something we did looks wrong, mail the address above. A signed request is a request you can hold someone to, which is the reason to sign it.

What this does not prove

  • A verified signature says a request came from us. It does not say anyone has to serve it, and we are not asking anyone to.
  • Identity is carried by the signature, not by the User-Agent string. Billy reads pages through a real browser engine and sends that engine's own User-Agent, so filtering us by name will not work and was never meant to.
  • Billy is not a crawler and does not enumerate sites, so there is nothing here about crawl budgets. If that ever changes, robots.txt gets honored first and this page gets rewritten before it ships.