---
title: "The Web Bot Auth desk"
description: "Signed agent identity, checked: a free battery for Web Bot Auth key directories, and a signed card for when somebody else has to believe the readout."
canonical: "https://scvd.store/bot-auth"
url: "https://scvd.store/bot-auth"
---

# The Web Bot Auth desk

Signed agent identity, checked: a free battery for Web Bot Auth key directories, and a signed card for when somebody else has to believe the readout.

## What this is

Organize public agent identity inputs into a portable calling card and attach introductions through a Node fetch integration, with missing information and unobserved outcomes stated.

## For money

Local setup and the integration download are free; the optional signed directory record is a one-off purchase at the current shelf price.

## Free first

Start free in your browser: organize public fields, review what will be shared, and download the card and integration without an account.

## Price

- **Setup USDC** — 0
- **Signed record USDC** — 0.99
- **Cadence** — one_off

## How to call

### Setup

<https://scvd.store/bot-auth#calling-card-setup>

### Configured download

Default: download my-calling-card.mjs; run node my-calling-card.mjs --setup to create a private local key and publish only its public directory. Run --observe for one signed introduction to this receiver. Import {connectLocal} and use await connectLocal() as the underlying Node fetch. Importing sends nothing. Existing-signer mode retains connect({sign}).

### Service

#### Origin

<https://scvd.store>

#### Paths

- **Keys** — /bot-auth/keys
- **Observe** — /bot-auth/observe
- **Reports** — /bot-auth/reports

#### Term seconds

2592000

#### Report retention seconds

604800

### x402 integration

For an existing configured @x402/fetch x402Client: const introducedFetch = await connectLocal(); const request = wrapFetchWithPayment(introducedFetch, client). The existing client controls spending, approval and recovery; the inner transport adds a fresh introduction per attempt. Finish with await introducedFetch.flushReports(). No wallet is created or uploaded by this integration.

### Local identity

The generated .my-calling-card.mjs.local folder contains your private key. Keep it private. A failed publication reuses this key. --renew explicitly renews publication; --revoke records a tombstone through the remaining directory term, subject to KV propagation. Replace a key by revoking it and setting up a newly named download. No background renewal or remote key custody.

### Receiver

GET /bot-auth/observe recognizes only keys in this hosted directory and this finite signature profile. It returns a dated signed observation and Calling-Card-Recognition: signature_verified only after checking the signature and its time window. That is this receiver recognizing the signature, not approval by other sites. Replays within the signature window are not deduplicated. The response does not grant permissions.

### Payment help

A bounded x402 v2 Payment-Required header can identify malformed offers, an unsupported version, or whether a declared network appears. Response bodies are left untouched; absent headers remain unread. A matching network does not prove wallet funds, authorization, asset or scheme support, settlement or delivery.

### Download

<https://scvd.store/calling-card/calling-card.mjs>

### Supported

Node 22+ standard fetch. No browser automation integration is supplied. No payment is made by this module.

### Inputs

Public JSON object, array of public profiles, or labeled lines in any order. Recognized fields: name / agent_name / display_name; signature_agent / signatureAgent / directory_url; public_key / publicKey / jwk (public Ed25519 JWK); contact_url / contact; allowed_origins / destinations; networks / payment_networks (CAIP-2); runtime / integration (node-fetch). identity and agent objects are also recognized. Unknown fields are counted and excluded; conflicting values block export.

### Example

#### Name

My Agent

#### Allowed origins

- <https://scvd.store>

#### Networks

- eip155:8453

### Local use

Save calling-card.mjs locally. Import normalizeCallingCard and createCallingCardFetch. Review normalizeCallingCard(publicInputs); when ready, pass its card to createCallingCardFetch({card}). Call the returned function instead of the underlying fetch. It sends only to allowed_origins, once per call, and returns the original Response. Keep it underneath existing payment-client approval and retry logic.

### Signed use

Supply signature_agent, a matching Ed25519 public_key JWK, and sign(bytes): Promise<Uint8Array> from your existing local signer. webCryptoSigner(privateKey) adapts an existing CryptoKey without exporting it. A configured signer failure stops the request; it never silently drops to an unsigned introduction.

### Directory

Guided setup publishes a client-signed public directory at /bot-auth/keys/{key_id} for the stated term. This receiver recognizes that path. Other verifiers may require an origin-owned well-known directory and registration; neither is implied. Existing-signer users can still serve createDirectoryResponse({card,sign}) on their own HTTPS host.

### Diagnostics

Sharing is off by default. When explicitly enabled in the download or connectLocal({shareReports:true}), each request can report only its approved site origin, HTTP status and fixed diagnostic labels. A bounded queue has a timeout and never retries. await request.flushReports() returns sent/failed/dropped counts. The store keeps authenticated client reports privately for the stated retention, labels their contents unverified, and does not turn them into rankings or acceptance claims. Disable with connectLocal({shareReports:false}). HTTP success alone never establishes identity or payment success.

## Errors

### Conflicting input

Keep one value for the named field and review again; ordering never resolves a conflict.

### Secret input

Remove private material; setup stays local and exports nothing from a rejected input.

### Invalid input

Use public JSON or labeled lines, at most 16 KB; unsupported fields are excluded and counted.

### Destination not allowed

Review the destination and explicitly include its HTTPS origin in your card before sending.

### Signing failed

No request was sent. Check your local signer and its matching public key.

### Signer required

A card with signing details requires a local signer; it never silently falls back to unsigned requests.

### Existing identity signature

Avoid stacking identity signers; the adapter will not overwrite an existing HTTP message signature.

### Request outcome unknown

Delivery and any payment outcome are unknown. Reconcile with the endpoint before retrying.

### Unsupported runtime

Only the Node fetch integration is provided. Browser automation needs a separate integration.

## Security

### Input storage

Pasted inputs and the generated profile stay in browser memory until the page is closed. No local storage or automatic upload. Downloads contain only recognized public fields.

### Secrets

Supply public identity fields only. Private signers stay in your own application. The local input check catches known credential fields, but is not a general secret scanner.

### Network

Browser setup stays local. Pressing the directory check sends that public URL. Running --setup locally publishes only a public directory; --observe sends a signed introduction. Normal requests go to configured HTTPS origins with redirects returned for a decision. Diagnostic sharing is off unless the operator enables it; the consent names origins, fields and retention.

### Scope

Signatures bind destination authority and directory reference. This receiver can report signature verification; it grants no permissions and establishes no body integrity, delegated authority, replay protection, other-site acceptance or settlement. Hosted directories expire unless explicitly renewed; revocation has propagation delays. Third-party registration is separate.

## Title

The Web Bot Auth desk

## Standfirst

Web Bot Auth is the IETF's answer to 'is this crawler who it says it is': the agent signs its requests (RFC 9421 HTTP Message Signatures, ed25519) and publishes its public keys in a directory at a well-known URL, so any origin can verify the caller without a shared secret or an allowlist of IP ranges. Cloudflare verifies these signatures on inbound traffic today.

## Own posture

This store's outbound probes are signed with the same mechanism (RFC 9421, ed25519), and its own key directory hangs at https://scvd.store/.well-known/http-message-signatures-directory — fetch it and check the proof-of-possession signature yourself. We run on ourselves what we check on you.

## Free check

### Method

POST

### URL

<https://scvd.store/api/bot-auth/check>

### Body

{"url": "https://your-agent.example"}

### Note

A bare origin is checked at /.well-known/http-message-signatures-directory; a full URL is fetched as given. One fetch, every check named, free, no account.

### Criteria

signature-agent-directory-v2

## Signed card

### Item

signature_agent_card

### Price USDC

0.99

### URL

<https://scvd.store/api/buy/signature_agent_card>

### Note

The same battery with a signature, a certificate binding its evidence hash, and a permanent card URL — for when an origin, a directory, or a counterparty wants more than your word. The card URL is free to read forever.

## What the battery checks

- the directory answers 200 at the URL a verifier would fetch
- the media type is application/http-message-signatures-directory+json
- the body is a JWK Set with at least one key
- every key is OKP/Ed25519 — the one algorithm deployed verifiers accept
- the directory's proof-of-possession signature verifies against a listed key

## What this is not

A dated look at one document at one moment. Not an endorsement of the agent, not an identity check on who operates the key, and no statement that any particular request was ever signed with it.

## Found us in your logs

If a request tagged "scvd-general-store/1.0 (+https://scvd.store)" knocked on your endpoint: that was our weekly census of doors listed in public x402 discovery — one GET per host per week, no payload, signed with the key directory above so your logs can verify it was really us and not someone borrowing the string. Being knocked on means a public directory lists you as an x402 endpoint. What yours answered is free to check yourself, no account: POST https://scvd.store/api/preflight with {"url": "https://your-endpoint"}.
