Agent Verifier reference

Everything on this page is generated from the code that answers your request, so it cannot drift from what you actually receive. The same contract in machine-readable form is at /openapi.json.

How to call it

GET https://fp.packet.guru/api/v1/connection with no body, no parameters and no credential. Send the ordinary request, signed the way you sign requests to anyone else. A signed request carries three standard headers:

  • Signature-Agent: the origin publishing your keys, quoted per RFC 9651, as "https://example.com"
  • Signature-Input: the RFC 9421 parameters. The tag must be "web-bot-auth", or the signature is not read as an agent signature at all.
  • Signature: the signature itself.

Without them you still get an answer: plain facts about the address you came from are read for every caller, signed or not.

What comes back

A block marked varies is described rather than enumerated: its members depend on what you sent. Fields that do not apply to a request are absent rather than null. An absent key reads the same as a field that does not apply, and a null only invites guessing.

FieldValuesMeaning
geovariesCountry, region, city and timezone of your address.
geo.citystringnullCity the address is placed in.
geo.countrystringnullISO 3166-1 alpha-2 country code of the address.
geo.regionstringnullFirst-level administrative division.
geo.timezonestringnullIANA timezone name for that place.
idstringIdentifier for this verdict, safe to quote in a support request.
identityvariesWho you were established to be, and on what evidence. Empty of judgement: the outcome of checking a signature you sent against a directory you published.
identity.botstringnullThe specific bot within that operator, when the evidence distinguishes one.
identity.methodstringHow the name was established: `signature` when your own signature verified, `range` when your address sat in a list its operator publishes.
identity.operatorstringnullThe name established for you, when one could be. Null when nothing named you.
identity.trustedbooleanWhether the established name is one this verifier holds a trust anchor for. A verified signature from a domain nobody knows is a verified signature from a domain nobody knows.
ipvariesPlain facts about your own address: network, operator, reputation of the address itself.
ip.asnintegernullAutonomous system number of the network.
ip.blacklistedbooleanWhether the address appears on the public abuse feeds this service tracks.
ip.hostnamestringnullReverse DNS name of the address, when it has one.
ip.orgstringnullThe organisation that network is registered to.
ip.originstringWhat kind of network the address belongs to: residential, datacenter, mobile, and so on.
ip.riskLevelstringThe same figure as a word: low, medium or high.
ip.riskScoreintegerReputation of the ADDRESS, 0 to 100, higher meaning worse. A datacenter address scores in the middle by nature: it is a statement about where you are, never about who you are or what you did.
metavariesEnvelope: API version, how long the verdict took, which axes contributed.
meta.apiVersionstringVersion of this API. `v1` today.
meta.axesarrayWhich sources contributed to this answer. An axis absent here contributed nothing, which is why a field can be missing without anything being wrong.
meta.queryTimeMsintegernullHow long the answer took to build, in milliseconds.
modedirectYou called this endpoint yourself, so the answer describes your own request.
signals.agent.cardvariesYour own published card read back to you, plus the one promise a single live request can settle: the User-Agent you said you would send.
signals.agent.keyDirectoryBindingbinding-proven · binding-key-proven · binding-not-provided · binding-failed · binding-not-checkedWhether your directory proved it published the key set that was read. binding-key-proven means the response signature verified but did not cover the body, so possession of the key is proven and the key set is not.
signals.agent.knownBotmatched · noneThe second, independent check, and the one that has nothing to do with your signature: some operators publish the address ranges their crawlers use, and those lists are read here nightly. matched means the address this request came from is inside such a published range. none is the ordinary answer and is not a fault: it means no operator publishes a list containing this address, which is true of every address that is not a large crawler, and true of most agents.
signals.agent.notesarrayObservations about your setup that are not faults.
signals.agent.refutedSignaturesarrayOther signatures on the same request whose named directory does not list the key they used. You can confirm each against the published file yourself.
signals.agent.signaturevariesWhether a Web Bot Auth signature was present and how it came out.
signals.agent.webBotAuthDetailvariesThe specific fault, and the sentence saying what to change. This is the heart of the free answer. Besides reason and fix it may carry three more members. clockDriftSec is how far the signing clock sits from this server, in seconds. directoryStatus is the HTTP status the named key directory answered with, where 403 or 429 is almost always a firewall in front of it refusing this verifier rather than anything about the keys. fromCachedDirectory appears only when true and means the answer rests on a stored copy that could not be refreshed, so a key reported as absent may simply have been published after that copy was taken.
signals.automation.clientHintsstringState of the Client Hints headers you sent.
signals.automation.uaClientstringWhat your User-Agent string alone says you are. A string, not a judgement.
signals.transport.provenancemeasured · unavailablemeasured = your request arrived directly and was read first-hand. unavailable = it did not, and no finding in this answer rests on it.
successbooleanAlways true on a 200. Errors carry success:false and an error object.

All 36 faults, and how to fix each

When a signature does not hold, the answer names one of these in signals.agent.webBotAuthDetail.reason and carries the remedy beside it. The whole list is published on purpose. These are the standard's own refusals, and a report that cannot be acted on without reading somebody else's documentation is not much of a report.

Two things the table cannot show. The sentence in an answer is sometimes more specific than the row: where the situation allows it the remedy names the case rather than the family, so a directory answering a redirect is told about redirects, and a signature base assembled in a known-wrong shape is told which line to change. The row is the general answer for that fault. And bad-tag never arrives as a reason at all. A signature tagged for another profile was not addressed to this check, so the answer reports signature.present: false and carries the same advice as a note.

The outcome column is the standard's own verdict for that fault. invalid means a proof was presented and does not hold.unverified means there was not enough to decide on, which is a statement about reach rather than a finding against the caller, and never a deduction.

The signature itself

The signature was read, and something about it does not hold: the bytes, the parameters, or what it covers.

ReasonOutcomeWhat to change
bad-signatureinvalidThe signature does not verify against the published key. The base is a likelier cause than the key: covering "signature-agent" puts the whole field into it, covering "signature-agent";key="sig1" puts that member value alone. Rebuild the base, then check the key is the one your directory publishes.
bad-taginvalidSet the Signature-Input tag parameter to "web-bot-auth". Without it the signature is not read as a bot-auth signature.
canonicalization-mismatchinvalidThe signature is sound, the base it was built over is not the one rebuilt here: a value formatted differently, identifiers written without quotes, a whole field where the covered member belongs, or a trailing newline. The fix beside this reason names which.
clock-aheadinvalidYour server's clock is ahead: the signature is dated in the future. Enable time synchronisation (NTP).
component-unsupportedunverifiedThe signature covers a component this verifier does not rebuild. Supported: @method, @authority, @path, @target-uri, signature-agent, content-digest. Nothing is wrong with your signature. It simply was not checked here.
component-unsupported-parameterunverifiedA covered component carries a parameter this verifier does not support. Only the key parameter on signature-agent is understood.
content-digest-mismatchinvalidThe Content-Digest you signed does not match the body that arrived. Recompute the digest over the exact bytes you send.
expiredinvalidThe signature expired before the request arrived. Set expires with more headroom. A minute is usually enough.
missing-componentinvalidThe signature covers a component that this request does not carry, so the signature base cannot be rebuilt. Send every component you sign.
missing-fieldsinvalidSignature-Input is missing required parameters. Include created, expires, keyid and tag.
signature-alg-unsupportedunverifiedThe signature names an algorithm this verifier does not implement. Ed25519 is the only one checked here, so nothing was decided about yours. Sign with Ed25519 to be verifiable at this endpoint.
signature-input-unreadableinvalidSignature-Input could not be parsed. It must be a dictionary member such as sig1=("@authority");created=1;expires=2;keyid="…";tag="web-bot-auth".
signature-value-unreadableinvalidThe Signature header has no member matching the Signature-Input label, or its value is not base64 between colons.
target-not-coveredinvalidThe signature covers no request target, so it is valid against any endpoint and replayable elsewhere as your identity. Cover "@authority" or "@target-uri". The draft requires one of them.

The Signature-Agent header

The header that says where your keys are published could not be read, or does not point at one usable place.

ReasonOutcomeWhat to change
signature-agent-absentunverifiedNo Signature-Agent header. The current draft requires one on a signed request, and without it nothing says where your public key is published.
signature-agent-ambiguous-memberinvalidSignature-Agent carries several members and none can be matched to this signature. Add a key parameter to the covered signature-agent component, or use one member named after the signature label.
signature-agent-member-missinginvalidThe signature covers a Signature-Agent member that the header does not contain. The key parameter must name a member that is actually sent.
signature-agent-mixed-formsinvalidSignature-Agent mixes the bare-string and dictionary forms in one header. Send one form only.
signature-agent-not-httpsinvalidSignature-Agent must point at an https address. Key directories are not read over plain http.
signature-agent-not-origininvalidFor the directory type the value must be a bare origin such as "https://example.com", with no path, query or fragment. Use type=jwks_uri to point at a file.
signature-agent-port-unsupportedunverifiedPublish the key directory on the default HTTPS port and name it without one, as "https://example.com". A Signature-Agent carrying another port is not fetched.
signature-agent-unparsableinvalidSignature-Agent is not readable. Send a dictionary member such as sig1="https://example.com".
signature-agent-unsupported-typeunverifiedThe Signature-Agent type parameter names a discovery mechanism this verifier does not support. Use directory, jwks_uri or cimd.

Your key directory

The address you named was fetched, and no usable key directory came back from it.

ReasonOutcomeWhat to change
directory-bad-statusunverifiedYour key directory answered a status other than 200, in directoryStatus here. 403 or 429 is a bot rule refusing this verifier: exempt /.well-known/http-message-signatures-directory itself, which serves public keys to a plain GET, rather than a User-Agent anyone can copy. 5xx is your origin failing. 3xx means the keys are elsewhere than your Signature-Agent says.
directory-host-unknownunverifiedThe host in Signature-Agent does not exist in DNS, so no key directory can be published there. Check the address you send.
directory-no-keysunverifiedYour key directory holds no usable Ed25519 key. Check the kty, crv and x fields.
directory-not-publishedunverifiedThe host exists but publishes no key directory at the well-known path. Publish it at /.well-known/http-message-signatures-directory.
directory-too-largeunverifiedYour key directory is larger than this verifier will read. Publish only the keys currently in use.
directory-too-many-keysunverifiedYour key directory holds more keys than this verifier will search. Remove keys that are no longer in use.
directory-unreachableunverifiedNothing answered at your key directory address: a name that does not resolve, a TLS failure or a timeout. Check the well-known path from outside its own network. It has to answer 200 directly over HTTPS on the default port, since a redirect is not followed.
directory-unreadableunverifiedThe directory body does not parse. It must be JSON containing a keys array.
directory-wrong-media-typeunverifiedYour key directory is served with the wrong Content-Type. Use application/http-message-signatures-directory+json.

The key you signed with

The directory was read, and the specific key your signature names is not usable from it.

ReasonOutcomeWhat to change
key-before-activationinvalidThis key nbf is in the future, so the key is not active yet.
key-not-foundunverifiedThe public key for this signature could not be obtained from the address you published it at.
key-not-in-directoryunverifiedNo key with this keyid is in your directory. Make sure keyid matches either the key thumbprint or its kid.
key-past-expiryinvalidThis key exp is already in the past. Publish a replacement key.

What this does not claim

  • A verified signature is identity, not trust. It proves control of the domain whose keys were published. Anyone can publish keys at their own domain, so a valid signature from a name nobody knows stays a valid signature from a name nobody knows.
  • A directory that could not be reached costs nothing. An unreachable directory is a gap on this side, not a fault on yours, and the answer says so rather than counting it against the signature.
  • Nothing unseen is reported as seen. When the request did not arrive directly, provenance says unavailable, and no finding in that answer rests on something that was never read.

The standards behind this

  • RFC 9421 HTTP Message Signatures. The mechanism everything here is built on.
  • RFC 7638 JWK Thumbprint. How a key identifier is derived from the key itself.
  • RFC 9651 Structured Field Values. The grammar the Signature-Agent header follows.

For machines

  • API catalog Every API here, each with its description and its documentation. Standard address, RFC 9727.
  • openapi.json The response contract, machine-readable. The same source the reference page is built from.
  • MCP server card What an MCP registry reads about this server: address, transport, and the tool it exposes.
  • packet.guru key directory The public keys this site signs its own outbound requests with. A working example of the file you publish.
  • packet.guru agent card What the fetcher on this site promises about itself, including the exact User-Agent it sends.

Read next

MCP server Endpoint, client config, and the one tool it exposes.