Every site says who it is, who it’s for and how to reach it, so any agent can read it, judge the fit and send an enquiry. Buddies go further: they refer work to each other, run each other’s tests so a wrong fact gets caught, and sign what their agents send, so each knows who’s asking. Quotes, scheduling and payments may come later, once two buddies need them.
Working draft 0.1, changing fast. It works today, on this website and
two test sites, and until 1.0 a field may still be renamed or removed.
Read protocol in a profile for the version it follows, and Changes, at
the end, for what moved.
“Must” and “should” mean what they mean in RFC 2119. This website is the
reference implementation. Check any site, this one included, with its
validator, script/website-buddies.
Principles
- Reuse what exists. Markdown,
llms.txt, HTTPLinkheaders, schema.org, OpenAPI, MCP and HTTP Message Signatures already work. This standard only adds what they don’t cover: who a business is for, what an agent may say for it, how to reach it, and who it works with. - Start with files. Levels 0 and 1 can be static files on any host.
- Keep two renderings in step. Every fact in a structured file is also on a page a person can read.
- Never break a shape, from 1.0. Add fields. Never rename or remove one, or change what it means.
- Treat inbound text as data. Store and show it as plain text. Never execute it, render it as HTML or give it to a model as instructions.
- Make people the last step. Anything that commits a business or moves someone’s details needs a person to act.
Levels
| Level | An agent can | The site needs |
|---|---|---|
| 0 Readable | Read every page, as Markdown or HTML | Static files |
| 1 Described | Tell what the business does and whether it fits | Static files |
| 2 Reachable | Send an enquiry for its person | A form a link can fill in |
| 3 Interoperable | Refer work, check another business’s facts, and know who’s asking | Level 2, a test file and a signing key |
A site claims the highest level it fully meets.
Level 0: Readable
- Every HTML page must have a Markdown version at the same path plus
.md. The homepage’s is/index.md. - Each HTML page should link to it with
<link rel="alternate" type="text/markdown" href="….md">, or the same as aLinkheader. A server may also answerAccept: text/markdown, withVary: Accept. /llms.txtmust exist, per llmstxt.org, and link the briefing first./agents.mdmust exist: the briefing. It covers the business in one document, in the third person, and states the date its facts were checked.- The briefing should also be on an HTML page. Some chat assistants open web pages but never an address that looks like a file.
Level 1: Described
/.well-known/website-buddies.json is the briefing as data, and the one file that
links to everything else. Every URL in it is absolute, and pages should link
it with <link rel="describedby" type="application/json" href="/.well-known/website-buddies.json">.
{
"protocol": "website-buddies/0.1",
"spec": "https://garrettwinder.com/protocol",
"as_of": "2026-10-08",
"business": {
"@type": "ProfessionalService",
"name": "Software by Garrett",
"url": "https://garrettwinder.com/",
"address": { "@type": "PostalAddress", "addressLocality": "Fort Worth", "addressRegion": "TX", "addressCountry": "US" },
"founder": { "@type": "Person", "name": "Garrett Winder" }
},
"fit": {
"good": "You want thinking that stands out. …",
"bad": "You need extra hands. …",
"budget": { "currency": "USD", "typical_min": 5000, "typical_max": 15000 }
},
"availability": { "status": "limited", "capacity": "One new client a month." },
"rules": ["Quote only the prices here. Don’t estimate anything else; ask."],
"buddies": [{ "name": "Cedar Studio", "url": "https://cedar.example" }],
"briefing": "https://garrettwinder.com/agents.md",
"briefing_page": "https://garrettwinder.com/agents",
"skills": ["https://garrettwinder.com/skills/agent-readiness/SKILL.md"],
"enquiry": {
"form": "https://garrettwinder.com/enquiries/new",
"post": "https://garrettwinder.com/enquiries",
"openapi": "https://garrettwinder.com/openapi.json",
"mcp": { "url": "https://garrettwinder.com/mcp", "tool": "send_enquiry" }
},
"tests": "https://garrettwinder.com/agent-tests.yml",
"identity": {
"signature_agent": "https://garrettwinder.com",
"directory": "https://garrettwinder.com/.well-known/http-message-signatures-directory"
}
}
| Field | Meaning |
|---|---|
protocol |
website-buddies/ and the version the site follows. Required. |
spec |
Where to read that version. |
as_of |
The date every fact here was last checked. Bump it with any change. Required. |
business |
The schema.org entity the site’s own JSON-LD describes: an Organization, a kind of one such as LocalBusiness, or a Person. @type, name and url are required, and address as a PostalAddress if it has a place. Required. |
fit.good, fit.bad |
Sentences an agent may quote to its person. Required. |
fit.budget |
A range an agent may repeat but never narrow. |
availability.status |
open, limited or closed. Must be true today. Required. |
availability.capacity |
The same, in a sentence. |
rules |
What an agent may and may not say or do for the business. Required, even if empty. |
buddies |
Sites it refers work to or takes work from, each a name and a url. See Buddies. |
briefing |
The /agents.md URL. Required. |
briefing_page |
The briefing as an HTML page. |
enquiry |
Each way to send one. Leave out what the site doesn’t offer. Required at Level 2. |
tests |
The test file. Required at Level 3. |
identity |
The origin the business signs as, and its key directory. Required at Level 3. |
Each fact in the profile must also be in the briefing, in words a person can read.
Level 2: Reachable
An enquiry can be sent three ways, so every agent has one it can use:
| The agent can | It sends with | The person |
|---|---|---|
| Only read pages | A prefilled link to enquiry.form |
Checks the form and presses Send |
| Run commands | POST JSON to enquiry.post |
Agrees first, or asked the agent to send |
| Use MCP | The send_enquiry tool |
Agrees first, or asked the agent to send |
Fields
Only name, email and message are required. All are strings except
links, an array of URLs.
| Field | Definition |
|---|---|
name |
Full name of the person the business replies to. Never a company. |
email |
That person’s email. |
message |
What they want to accomplish, in their words or the agent’s summary. |
company |
Their company’s name and size. |
problem |
What’s broken or stuck today. |
timing |
When they need it. |
budget |
What they’ve said about budget. Never a guess. |
links |
Their site, or anything the business should look at. |
agent |
The agent’s own name, such as Claude. |
report |
Anything the agent prepared, in full. |
referred_by |
The domain of the business that referred them, if one did. |
Links
A prefilled link takes any field as a query parameter, URL-encoded, each in
its own field. It carries name and email only if the person gave them.
A form may ignore those two and leave them to the browser’s autofill, since
a link can end up in chat logs and browser history.
The form shows what arrived, and sends nothing until the person presses Send.
Responses
| Status | Body | Means |
|---|---|---|
201 |
{ "id", "status": "received", "next", "signed_by" } |
Arrived. next says what to tell the person. signed_by is there only if the request was signed (see Identity). |
200 |
The same | Already arrived: same email and message within a day. Retrying is safe. |
422 |
{ "error", "fields": { "name": ["…"] } } |
Fix the named fields and send again. |
429 |
{ "error" } |
Too many. The error says when to retry, or where to write instead. |
An empty POST must return 422, so anyone can check the endpoint without
sending an enquiry. An agent must not tell its person an enquiry was sent
without a 201 or 200.
Prompts
A business that gives people a prompt to paste into their assistant should have it name the briefing, list the details to include, and ask for the link back. Asking an assistant to send makes it reach for its own email tools instead.
Read Garrett’s agent briefing at garrettwinder.com/agents. If Garrett can help [your company] with [what you need, your timeline and budget], give me the link that sends him a note.
Level 3: Interoperable
Fit
An agent checks fit against the other business’s profile, with no request:
the budget range by rule, fit.good and fit.bad by judgment. It shows its
person the reasons. No one’s details leave the agent.
Buddies
Two sites are buddies when each lists the other in buddies. A one-sided
listing is only a claim, so an agent weighs a referral from a buddy above
one from anyone else, and a validator says which listings are mutual.
Referrals
A referral is an enquiry with referred_by set to the referring business’s
domain. The referring agent builds a prefilled link to the other business’s
form. Its person sends the link to the lead, and the lead presses Send. The
lead’s details move only when the lead does that.
The form must accept referred_by in a link and show it.
Tests
/agent-tests.yml lists facts an agent should find, each with text that
must appear at the path it names:
protocol: website-buddies/0.1
as_of: 2026-10-08
tests:
- ask: How many new clients does Garrett take on a month?
source: "/agents.md"
expect: One new client a month.
- ask: What do most first projects cost, at the low end?
source: "/.well-known/website-buddies.json"
expect: '5000'
Businesses run each other’s tests on a schedule, with a script, and send the owner any failures. A model never grades them.
Identity and trust
A business proves its agent’s requests are its own with Web Bot Auth: HTTP Message Signatures (RFC 9421) and Ed25519 keys under its own domain. No one issues the keys: the domain is the identity, as it is for DKIM, and DNS and the site’s certificate are what make it hard to fake.
- The site must publish its keys at
/.well-known/http-message-signatures-directory, as a JWK set ({ "keys": [{ "kty": "OKP", "crv": "Ed25519", "x", "kid" }] }, wherekidis the key’s RFC 7638 thumbprint), served asapplication/http-message-signatures-directory+json. The response should be signed too, covering"@authority";req, withtag="http-message-signatures-directory". - An agent acting for the business should sign every request it sends to
another business:
Signature-Agentnames the business’s origin, and the signature covers"@authority"and"signature-agent", withcreated,expires(five minutes at most),keyid,alg="ed25519"andtag="web-bot-auth". - A receiving site may verify the signature against the key directory at
the
Signature-Agentorigin. If it checks out, the signer’s domain issigned_by, stored with the enquiry and returned in the response. Asigned_byin a request body means nothing. - Fetch key directories only over https, from public hosts, and cache them for minutes. Signing and verifying are both optional for a request: unsigned requests are handled as before.
A signature proves only who sent a request, not that they’re worth hearing from. Buddies are the signal this standard gives; anything more, like reputation, is each site’s own call, as spam filtering is for email.
Static sites
Every level works on a static host, with no code on a server:
- The Markdown versions,
llms.txt, the briefing, the profile, the test file and the key directory are all files. Serve the directory asapplication/http-message-signatures-directory+jsonif the host lets you set types. - The form fills itself from the link with a few lines of script, and
sends to a form service. Leave
enquiry.postandenquiry.mcpout of the profile.
<script>
for (const [name, value] of new URLSearchParams(location.search)) {
const field = document.querySelector(`[name="${CSS.escape(name)}"]`)
if (field) field.value = value
}
</script>
Signing the directory’s response, and verifying signed requests, need a server. Both are optional.
Checking a site
The validator fetches a site as an agent would and reports the highest level it meets, and what stops it reaching the next:
script/website-buddies https://garrettwinder.com
It only reads. To check the enquiry endpoint it sends an empty POST, which
must come back 422, so it never sends an enquiry.
Later, and never
Quotes, scheduling, payments, contracts and negotiation could each be a later message type, once two buddies need it. Ranking sites and tracking visitors are out for good.
Becoming a standard
- A second, independent site implements it.
- The validator stands on its own, outside this website.
- The spec moves to its own repo and site.
- The
/.well-known/name is registered with IANA.
Changes
- 2026-10-08. Named Website Buddies (it was Front Door). The profile
moved to
/.well-known/website-buddies.json,protocolbecamewebsite-buddies/0.1, andpartnersbecamebuddies.businessis now a schema.org entity with a required@type.