The specification
Three documents, one standard: a hardened floor, standardized hooks, and a layer where services compete. Rendered from the repository — this page is never hand-edited.
- 01 · Core FormatNormative
- 02 · Trust & IntegrityNormative
- 03 · Trust Services & ReceiversNon-normative
How a Layer-2 signature is verified
The strongest trust level in one flow, from 02-TRUST §2.1 — no account, no registry, just the domain:
A document is L2-verified only when ALL of the following hold:
- It carries a signature block that verifies (§3).
- The verifier derives the registrable domain (eTLD+1 per the Mozilla Public Suffix List, as currently published) from the document's
issuer-url, and fetches the key itself athttps://<registrable-domain>/.well-known/intents-md/key.asc. The verifier MUST NOT fetch the rawkey:URL from the signature block; that URL is advisory display metadata only. - The fetched key's fingerprint matches the document's
keyid. - Fetch basis. The verifier's key fetch uses exactly
https://<registrable-domain>/.well-known/intents-md/key.asc— schemehttps, default port, fixed path. Any port, path, query, or fragment present inissuer-urlMUST be ignored for this fetch; anissuer-urlcannot steer the fetch to an attacker-chosen endpoint. - No issuer-url, no L2. A document without an
issuer-urlcannot attempt L2 verification; display falls back to the signature state (L0/L1). Soft-fail, never a rejection.
Binding is to the domain, never to the name. L2 binds a document to a registrable domain. It does NOT bind the human-readable issuer name, and MUST NOT be presented as doing so. Renderers displaying L2 state MUST display the registrable domain (e.g. "Signed · alex.example") — never a bare checkmark and never the self-declared issuer name alone.
Free-host cap. If the registrable domain is itself on the Public Suffix List or a suffix of a PSL entry (e.g. github.io, pages.dev), the hostname is a platform tenancy, not an owned identity: verifiers MUST treat keys served there as L1-equivalent for all ranking and badge purposes. The domain-cost sybil defense applies only to domains an issuer actually had to register.
Tenancy test (defines the operative test where the cap sentence above is ambiguous). A hostname is platform tenancy when the prevailing public suffix for it comes from the PRIVATE section of the Public Suffix List (e.g. github.io, pages.dev, vercel.app, netlify.app, neocities.org). ICANN-section suffixes (ch, co.uk, …) are registry TLDs: registrable domains on them cost registry money and earn full L2. This reading covers the dominant user.github.io form — where the eTLD+1 (user.github.io) is itself free tenancy — and not only bare path-style publishers on a platform apex.
PSL timing — L2 is a verifier-local judgment. L2 status is computed at fetch time against the PSL snapshot the verifier currently holds; two verifiers MAY disagree, and a hostname's status MAY change when the PSL changes. Verifiers SHOULD pin a PSL snapshot and refresh on a documented cadence. This is consistent with receiver sovereignty ([SERVICES] §4): L2 is a badge a receiver grants, not a property a document carries.
Honest cost of the cap (owned trade). Free-host publishers who could have displayed "L2" under the old self-declared rule are demoted to L1-equivalent. That is deliberate: the domain-cost sybil rung must price identities at registry cost, and free tenancy prices them at zero. The path to L2 is registering a domain — a low-single-digit-CHF-per-year action — and everything else about zero-config static publishing in this spec is unchanged.
PSL freshness gap. The PSL lags reality: a new free-host platform is full-L2 until its suffix reaches the PSL PRIVATE section, and verifiers on different snapshot cadences MAY disagree in that window. Verifiers SHOULD therefore maintain a local denylist of known free-tenancy suffixes alongside the PSL and apply the same L1-equivalent cap to them; the PSL remains the normative source, the denylist only closes the lag. Known per-path multi-tenant hosts — unrelated publishers sharing one registrable domain by URL path — belong on the same denylist and carry the same cap. (Directory-grade verifiers SHOULD publish their denylist + refresh cadence — disagreement is honest when it is legible.)
Lookalike honesty (non-normative note). L2 still cannot prevent alex-shop.ch from impersonating alex.ch visually. The domain-display requirement above is the mitigation: the reader always sees which domain vouches, so lookalikes compete on legibility, not on crypto badges. Receivers MAY add an edit-distance UX nudge — visually highlighting when the issuer display name and the registrable domain diverge sharply (issuer alex over alex-shop.ch) — keeping L2 honest without overselling it as identity proof. In one line: L2 proves a domain vouches; it does not prove human identity — alex-shop.ch's vouching is worth exactly what alex-shop.ch is worth, and lookalike domains remain a reader-literacy problem no cryptography in this spec solves. Launch copy MUST NOT oversell L2 as identity proof.
01 · Core Format
NormativeLayer: 1 (hardened floor) · Status: Normative
1. Status of this document
This document specifies the intents.md core format. The key words MAY, MUST, MUST NOT, OPTIONAL, RECOMMENDED, REQUIRED, SHALL, SHOULD, and SHOULD NOT are to be interpreted as described in RFC 2119.
Everything in this document is normative except where explicitly marked otherwise. Layers 2 and 3 are defined in companion documents ([TRUST], [SERVICES]). This split supersedes the earlier single-document layout.
2. Overview
An Intent Document is a single UTF-8 plain-text file served at an ordinary HTTPS URL, expressing what its issuer wants to offer, say, or ask. It rides on existing infrastructure — HTTPS transport, text/markdown; charset=utf-8 media type — and requires no new ports, DNS records, registries, or accounts.
Recommended file naming: <slug>.intents.md for individual documents (an ordinary .md file; plain .md and any other filename are also valid). A bare intents.md at a site root is the conventional index — the site's or publisher's own intents, linking to its other documents. Naming is a discovery convenience only and carries no trust: a document named intents.md is not thereby trustworthy, and a valid document at any other filename is not thereby invalid.
Personal documents (non-normative). The same format works privately: an intents.md in a workspace or vault, read by its owner's own agents, expressing standing goals and current priorities. Trust is by possession — an agent that can read your filesystem is already trusted — so signatures are unnecessary there. Nothing in this specification changes for public documents.
A document has three parts, in order:
- Header block —
name: valuelines, terminated by the first blank line. - Body — free-form plain text.
- Signature block — OPTIONAL, defined in [TRUST].
The core invariant: there is nothing in a conforming document that any renderer could execute, even by accident. Publishers ship data. Readers render it with components they trust. The reader — not the publisher — is sovereign over rendering.
3. Header block
Format: one name: value per line. Names are lowercase; a single space follows the colon. Values are single-line; continuation lines (beginning with a space) are permitted for long values. The header block ends at the first blank line.
3.1 Core headers
| Header | Status | Rule |
|---|---|---|
intent |
REQUIRED | Version integer. This specification: 2. |
type |
REQUIRED | A content type from §5, or any custom string. Unknown types MUST be rendered as post. |
title |
REQUIRED | One line of plain text. |
published |
RECOMMENDED | ISO 8601 date or datetime, UTC. |
updated |
RECOMMENDED | ISO 8601 date/datetime of the last material change (status, price, terms). |
expires |
RECOMMENDED | ISO 8601. After expiry, renderers SHOULD display "expired" but MUST NOT hide content. |
status |
OPTIONAL | active (default) | sold | withdrawn. Renderers SHOULD display non-active status prominently; content MUST remain visible. Issuers SHOULD re-sign on status change so receivers can corroborate it ([SERVICES] §4.2). |
lang |
OPTIONAL | ISO 639-1 code of the body's language. Multilingual issuers publish one document per language. |
issuer |
OPTIONAL | Display name of the publisher, free text. |
issuer-url |
OPTIONAL | HTTPS URL of the publisher's site; used for key binding ([TRUST] §3). |
id |
OPTIONAL | Stable identifier: UUID, slug, or URL. |
prev |
OPTIONAL | sha256:… fingerprint of the previous version's canonical form — hash chaining, defined in [TRUST] §5. MUST remain optional; standalone documents are always valid. |
3.1a Subject binding headers
These headers bind reviews, offers, and any machine-readable reference to another document. They apply to type: review, negotiation citations, and any document or message that references another intent document. All OPTIONAL on the document; the aggregation rule in §5 governs what receivers do without them.
| Header | Status | Rule |
|---|---|---|
subject-keyid |
OPTIONAL | The keyid of the reviewed issuer's signing key. Required for aggregation when the subject is signed (signed-subject branch, §5); MUST NOT be required when the subject is unsigned. |
subject-url |
OPTIONAL | HTTPS URL of the subject document (the listing, rfp, or profile being reviewed). |
subject-hash |
OPTIONAL | sha256:… of the subject document's canonical form at review time — binds the review to the exact version seen, and is the only subject binding available when the subject is unsigned. |
Receivers MAY require any combination; publishers SHOULD provide subject-url and subject-hash always, plus subject-keyid when the subject is signed. A review of an unsigned subject binds by subject-hash only — keyid binding is impossible there by design.
Negotiation citation. Negotiation stays out-of-band (email or contact:). Any machine-readable offer, counter-offer, or negotiation message referencing a listing — whether or not it is published as an intent document — MUST cite the listing's document URL and canonical-form hash (subject-url + subject-hash). For plain-text threads (agent-drafted replies, ordinary email) the minimum citation block is two lines at the top of the message:
subject-url: https://example.org/listing.md
subject-hash: sha256:…
Offers published as intent documents use the §3.1a headers directly; the future public offer document type is reserved as offer (§5, alongside deal). An unsigned offer email carrying the citation block is a conformant citation, not a conforming document — the block travels; the envelope is optional.
Citation mismatch handling. If the fetched canonical hash differs from the cited subject-hash, the receiver MUST treat the offer as answering a stale version and MUST surface both hashes to the human before any reply is sent. If the URL fetch fails, the thread soft-fails with a visible "could not verify cited version" state. Agents MUST NOT auto-accept or auto-send on a mismatched or unverified citation. subject-hash in any citation — document headers or email blocks — MUST be the sha256 of the subject document's canonical form bytes ([TRUST] §4.1), computed exactly as signature verification computes it; hashing raw file content or non-canonical bytes produces a hash no conforming receiver can match. Agents and conforming negotiation tools MUST fetch and verify the cited URL before sending any reply; plain mail clients SHOULD display the cited hash as unverified until the reader's agent or the human verifies it out of band.
3.2 Commerce headers
Apply to type: listing and type: rfp. All OPTIONAL — a listing without ask is valid.
| Header | Status | Rule |
|---|---|---|
ask |
OPTIONAL | Number plus ISO currency code (e.g. 450 CHF). Renderers MUST display verbatim; silent currency conversion is forbidden. A renderer MAY show a conversion if clearly labelled as its own calculation. |
negotiable |
OPTIONAL | yes | no. Default: no. |
payment |
OPTIONAL | Comma-separated accepted rails, preference order, free text (e.g. bank-transfer, escrow, x402-usdc). Declares accepted rails only; payment itself happens out-of-band (§7). |
quantity |
OPTIONAL | Integer. Default: 1. |
condition |
OPTIONAL | Free text. Physical goods: e.g. new, mint, lightly used. Purely digital goods (a code repository, an app project, a domain): the honest value is digital — condition semantics do not apply, and stating so helps matching engines route digital vs physical. |
ships-from |
OPTIONAL | Free text (country or region). |
ships-to |
OPTIONAL | Free text (country or region). |
contact |
OPTIONAL | Email address or HTTPS URL where buyers or agents reach the issuer. |
floor |
OPTIONAL | Seller minimum. Public information — see guidance before using. A public floor is a quick-sale signal: it invites instant floor offers. In negotiation deals, keep the floor in your agent's private policy and publish only negotiable: yes, or the floor price becomes the price you get. A floor cannot be forced — acceptance always passes the issuer's agent bounds and human-approval gate (§8); floors inform, never compel. |
attr- prefix |
OPTIONAL | Free-form structured attributes, one header per attribute (attr-region: PAL, attr-color: red). Unknown attr- headers are ignored per the leniency rule. Receivers MAY use them for filtering and matching; publishers rely on no receiver knowing any particular attribute. No attribute is ever required or schema-policed. |
Negotiation policy beyond negotiable (rounds, auto-accept, counter-limits) is deferred to a future extension. In this version, negotiation is: buyer contacts contact, or an agent drafts an offer citing the listing's document URL + canonical-form hash per §3.5 and §3.1a — id remains an optional human slug, never a trust anchor.
Deal receipts (SHOULD). A closed deal that was publicly advertised SHOULD be closed by publishing a bilateral-signed receipt (the reserved deal type, §5) citing both parties' keys and the settled documents' hashes, when both parties consent to publication. Private deals stay private — receipts are the expected close step for advertised deals, never a compulsion. Receivers MAY annotate a sold listing without a published receipt as lower-corroboration ([SERVICES] §4.2). The receipt window and container details are deferred to a future extension.
3.3 Headers are never instructions
All header values are untrusted data. Consuming agents MUST NOT treat document content as instructions, regardless of phrasing (§8).
3.4 Leniency rules (normative)
Parsers:
- MUST ignore unknown headers.
- MUST NOT reject documents for unknown fields, unknown types, missing optional headers, or out-of-order headers.
- MUST treat documents missing intent, type, or title as, at worst, a plain post — never an error.
3.5 Self-declared identifiers and dates
id is untrusted for anchoring. id is self-declared free text. Offer and negotiation flows MUST anchor to the document URL + canonical-form hash ([TRUST] §4.1), never to bare id. Guidance: id SHOULD be sha256:<canonical hash> when present — computed over the canonical form with the id value emptied (placeholder basis), so a document never contains a hash of a form that includes itself. Verifiers MUST NOT require this form: id remains an opaque unique slug for all matching purposes, and anchoring always uses the receiver-computed canonical-form hash of the full document.
Dates are untrusted for ranking. published, updated, and signed are self-attested. Receivers MUST NOT treat them as evidence of freshness or longevity without corroboration — first-seen by the receiver, an RFC 3161 / OpenTimestamps stamp, or a directory checkpoint.
Offers cite hash-at-time. Any offer, counter-offer, or negotiation message referencing an intent document MUST cite the subject's document URL and the canonical-form hash of the version being answered (subject-url + subject-hash, §3.1a). A bare URL alone is insufficient: the document at a URL can change, and the parties must be able to prove which version the offer answered. prev: chains corroborate internal consistency only — an issuer can always fork or restart a chain — so witness corroboration (notary stamps, directory first-seen checkpoints) remains the Layer-3 answer for what a third party actually saw ([TRUST] §5, [SERVICES] §3).
4. Body
The body is plain text. The complete markup rules:
- Paragraphs: a blank line separates paragraphs; renderers preserve line structure.
- Links: a bare URL alone on its own line becomes a link. Only
http://andhttps://schemes are linkified; all other schemes remain inert text. - Images: a bare URL alone on its own line whose path ends in
.jpg,.jpeg,.png,.webp, or.gifrenders as an image, fetched by the renderer. - Everything else is literal text. Angle-bracket content (
<script>,<b>) MUST be escaped and displayed literally, never interpreted. There is no raw HTML passthrough, no stylesheets, no iframes, no scripts, no fonts, no remote includes of any kind. - No executable content can exist. Renderers that add interpretation beyond rules 1–3 are non-conformant.
Optional sugar (non-normative): renderers MAY style a first-line # prefix or *emphasis*, but publishers SHOULD NOT rely on it; the plain-text reading must always be complete and natural. Blessing markup subsets happens only by community consensus after demonstrated demand.
Limits (SHOULD): documents SHOULD stay under 1 MB total; headers under 64 KB. Media is referenced by URL, never embedded. Renderers MAY truncate larger documents with a visible notice; agents MAY decline to fetch unbounded documents.
Media fetch policy. Renderers fetching images per rule 3 MUST conform to the media-fetch policy in [TRUST] §9 item 3 — magic-byte validation, blocked destinations, and size/decode caps. SVG is not an image type in this version.
5. Content types
| Type | Meaning | Notes |
|---|---|---|
listing |
Something offered for sale | Uses the §3.2 commerce headers |
post |
Article, update, statement | Body is the whole point; also the fallback for unknown types |
rfp |
Request for something (buy/hire/rent) | Reverse listing; demand-side twin of listing |
profile |
Identity and pointers to other documents | Reputation anchor |
review |
Attestation bound to another document via the §5 two-branch rule: signed subjects — signed review + subject-keyid + subject-hash; unsigned subjects — signed review with subject-hash only (§3.1a; id is display-only) |
Reputation as documents — the protocol's answer to "signatures don't make people honest" |
feed |
Ordered pointers to other intent documents | The RSS-again moment |
All types share the envelope, body rules, signature model, and rendering tiers. Templates per type are guidelines (SHOULD), never validation gates. The type registry is open: new types are added by community consensus after demonstrated usage.
Review binding. Two explicit branches govern whether a review document counts as reputation input. Signed subject: the review is itself signed, carries subject-keyid: matching the reviewed issuer's keyid, and satisfies the aggregation tightening below (subject-hash match when the receiver can fetch the subject). A subject-keyid that mismatches the fetched subject's actual signing keyid MUST NOT aggregate — even if subject-hash matches. Unsigned subject: the review MUST itself be signed — binding is subject-hash only (§3.1a), because the subject document has no signing key; subject-keyid MUST NOT be required, and any subject-keyid header present is ignored for aggregation — branch selection governs which rules apply. Reviewer identity cost still applies ([SERVICES] §4 reviewer-cost guidance). Unsigned or unbound reviews render fine — they simply do not aggregate.
Branch selection (normative). The receiver classifies the subject by fetching subject-url when available. Signed subject = the fetched subject carries a signature block that verifies ([TRUST] §2 soft-fail rule). Unsigned subject = no verifiable signature on the fetched subject. On fetch failure, the review does not aggregate until the subject class is known; receivers SHOULD surface a visible "could not verify subject" state (same posture as the §3.1a fetch-failure rule, stated here for reviews rather than by cross-reference).
Aggregation tightening. For aggregation, a review MUST satisfy the signed-subject or unsigned-subject branch above. Shared tail, both branches: a review missing subject-hash where the receiver could verify it renders fine but MUST NOT aggregate into reputation scores. Receivers SHOULD NOT aggregate reviews from reviewer keys younger than the receiver's first-seen threshold or lacking L2-bound reviewer identity ([SERVICES] §4 reviewer-cost guidance) — a fresh-key army praising each other's keyid is the naive attack; reviewer age and domain cost are the counter.
Type templates (SHOULD). Minimal body shapes per hollow type; guidelines, never validation gates:
feed— exactly two meaningful line classes: absolute HTTPS URL lines (pointers) and#-prefixed lines (comments and titles — titles SHOULD be# My Feed Title). Any other line is ignored prose, never a pointer. (Pointer format only; ordering semantics are the consumer's.)profile— body is a short self-description in plain text; structured facts travel asattr-headers (attr-location: Zürich). A profile is the natural place to publishissuer-urlfor L2 binding.review— body is free-text assessment; SHOULD carrysubject-urlandsubject-hashalways, plussubject-keyidwhen the subject is signed (§3.1a); a numeric rating, if used, travels asattr-rating: 1-5(receiver-interpreted, never schema-policed).deal(future extension) — reserved: a bilateral-signed receipt container citing both parties' keys and the settled documents' hashes. The negotiation itself stays out-of-band by design; only the receipt standardizes. Recorded here so edge apps build toward the right shape from day one.offer(future extension) — reserved: the public offer/counter document type, carrying the §3.1a subject headers. Until standardized, offers live out-of-band with the §3.1a citation block; the reservation prevents namespace drift.index-request(edge, defined [TRUST] §7.1) — reserved: signed directory submission document;subject-urlpoints at the document to index; proof-of-control via the [TRUST] §7.1 flows. Minimal template:intent/type/titleREQUIRED (core envelope as any document);subject-url= the document URL to index;issuer-urlREQUIRED, its registrable domain MUST equal the registrable domain ofsubject-url; a signature block REQUIRED, verified per [TRUST] §7.1 flow 2 (key fetched at thesubject-urlregistrable domain, fingerprint matchingkeyid). Directories MAY require more; never less than this.
6. Rendering conformance
Three tiers; every tier is strictly safer than a normal web page because no publisher code exists in the channel:
- Tier 0 — Any reader (always works): open the file. It reads top to bottom in any editor, terminal, or
cat. Zero fidelity loss of information; zero risk. - Tier 1 — Browser renderer: a static page fetches the document and renders it as clean HTML/CSS per §4. No publisher code runs, because none exists.
- Tier 2 — Agent-native: an agent parses headers, renders in its own UI, and may act on structured fields — surface an offer, draft a reply, schedule expiry — behind its normal human-approval gates (§8).
Adoption rule (normative): agent-better, not agent-only. Every conforming document MUST remain fully readable by a plain browser (Tiers 0–1) and become better when rendered by an agent (Tier 2).
Growth ladder for richer media (non-normative guidance): as the protocol grows beyond text and images, pre-render server-side and ship pixels, or declarative formats decoded inside sandboxed native players. Capability expands only by adding declarative item types — never by opening script or network gates.
Renderer conformance: implement §4 rules; never execute; never silently convert currency; soft-fail signatures ([TRUST] §2); ignore unknown everything.
Reference renderer guidance (non-normative): - The reference renderer should ship before third-party renderers exist, and should be safe by default: no raw HTML passthrough, hostile-document test vectors passing, embeddable as a library. The path of least resistance for implementers should be the safe path — a no-execution norm holds in practice through tooling, not paper. - Renderer implementations are edge nodes. A renderer that executes document content is non-conformant (§4) and answerable to receiver reputation ([SERVICES] §4) — flagged, down-ranked, routed around. - Tier 0 remains the unconditional floor: opening a document as raw text in any browser is always safe — browsers display plain text; they do not execute markdown. Execution risk exists only in renderers that add interpretation, which §4 binds and the test vectors enforce.
6.1 Render integrity: the interpretation boundary
Principle: the rendered document is the ledger; the agent is the analyst. The analyst may report on the ledger, never edit it.
- Ground truth. The document view is the ground-truth representation of what the publisher said. Conforming renderers MUST render document content deterministically per §4: the same document yields the same content for every reader. Styling MAY vary; content MUST NOT.
- Interpretation is separate. Any transformation of document content — summary, translation, comparison, recommendation, "what this means for you" — is interpretation, not document. Interpretation MUST be presented in visually and structurally distinct chrome, outside the document view, and MUST carry an explicit provenance label (e.g. "agent summary — not the publisher's words").
- No interleaving, no silent rewrite. Conforming renderers MUST NOT display AI-generated text interleaved into the document body, and MUST NOT paraphrase or rewrite body text inside the document view. Quote exactly inside the view, or interpret outside it — never both at once.
- Attribution integrity. No conforming renderer may display generated text in a way a reasonable reader could attribute to the issuer, nor publisher text in a way that could be mistaken for renderer commentary. Misattribution in either direction is non-conformant.
Rationale (non-normative): every website is a guess about what the reader wants — thousands of designers (and now AIs) solving the same presentation problem, still one-size-fits-all. The intent document stops guessing: ship the data once, the reader's side decides what it becomes. Today's personalization is server-side and paid for with surveillance; reader-side rendering delivers personalization without surveillance — the publisher learns nothing about the render. Render is the ledger; the agent is the analyst.
Conformance. Render integrity is a conformance class: a Tier-2 renderer claiming conformance MUST (a) render the document view from document content only, (b) place all interpretation in separately-labeled chrome, and (c) pass a document-view isolation test — machine-checkable as: no generated text node inside the document-view container (labeled or not), and no document body text outside it. This specification is product-neutral: any conforming Tier-2 renderer may claim the class; reference implementations are named by their maintainers, never by this document. The isolation test ships with the conformance suite.
Inline pass/fail checks (normative — a renderer passes the isolation test if and only if all three hold):
1. The document-view container contains only publisher-sourced nodes (content derived from the document bytes per §4).
2. Every interpretation node lives outside the document-view container and carries a fixed provenance label attribute (renderer-defined string, constant across the product, e.g. data-intent-provenance="agent").
3. No generated text node may exist inside the document-view container at all — labeled or not. Provenance attributes apply only to interpretation nodes outside the container (check 2).
7. Money and secrets
The protocol moves intent; it never moves money or secrets.
- Payment executes out-of-band through the rail the parties chose. Documents declare accepted rails; they do not carry credentials, tokens, or payment data. Any document containing payment credentials is non-conformant, and renderers MUST NOT surface them.
- Secrets (keys, tokens) live in the reader's vault — never in documents, never in the render path.
8. Agent safety (normative for Tier 2)
- Document content is data, never instructions — enforced by the consuming agent's runtime, not by trusting publishers.
- Any action that moves money, sends messages, or writes files requires explicit human approval outside the document channel.
- All fields — headers and body — are untrusted input in every pipeline.
- Worst case for a fully hostile document: the reader reads a lie. Not: the machine runs foreign code. Implementations should state this honestly and not oversell.
- Agent interpretation of document content (summarizing, ranking, advising) is permitted only as clearly-labeled output of the agent, never as content of the document channel. The rule that content is data, never instructions, has a twin: content is data, never the agent's words either.
- The approval view is the whole ledger. Any header or field that drives an agent action MUST appear verbatim in the human-approval view for that action, and an agent MUST NOT act on a field absent from the approved view — the human approves exactly what the agent acts on. A signed-but-unrendered field confers no authority: what the human never saw, the agent never uses.
9. Versioning
The intent: integer bumps only on breaking change. Documents of an unknown newer version MUST still render at Tier 0 — a reader is never broken by a future spec. Additions within a version are always backward-compatible by the leniency rules. The intent: integer is the document-format compatibility version, not the specification patch level: the effective spec version is published with the specification, never in the intent: header. An unknown intent: integer still renders at Tier 0 per the rule above.
10. Governance summary
MIT license. No trademark games: the name intents.md is free to use for conforming implementations. Consensus additions only — the core grows on demonstrated demand, blessed after the fact; additions are permanent. The protocol is for everyone; intended stewardship is a neutral, community-governed foundation. Founding authors are first implementers, not owners.
[TRUST] 02-TRUST — Trust & Integrity. [SERVICES] 03-SERVICES — Trust Services & Receivers.
02 · Trust & Integrity
NormativeLayer: 2 (standardized hooks, optional practice) · Status: Normative
1. Status of this document
This document specifies the intents.md trust and integrity mechanisms. RFC 2119 keywords apply as defined therein. The mechanisms are normative; their use is optional. Layer 2 exists so that trust services ([SERVICES]) compose across implementations instead of siloing in per-product cages — the hooks are standardized here, from launch, so they never need retrofitting.
Trust decisions — ranking, filtering, weighing — are always the receiver's ([SERVICES] §4). The core never adjudicates trust.
2. Trust levels
| Level | What | Gives the reader |
|---|---|---|
| L0 — Anonymous | No signature | Content only. Valid for posts and casual listings. |
| L1 — Self-signed | Same key across documents | Persistent pseudonymous identity; reputation over time; tamper-evidence — any edit breaks the signature. |
| L2 — Domain-bound | Key fetched by the verifier from the registrable domain's /.well-known/intents-md/key.asc (§2.1) |
Cryptographic binding to a domain the reader can check. A spoofed "issuer: Example Shop" document without Example Shop's key at its own domain fails verification. |
Soft-fail rule (normative): an invalid or unverifiable signature MUST NOT hide a document. Renderers display a visible "signature invalid / unverified" state; content still renders. Trust is information for the reader — never an execution decision. Any receiver MAY additionally filter by signature state ([SERVICES] §4); hiding at the renderer's display layer is forbidden, filtering at the receiver's policy layer is sovereign.
Downgrade rule (normative). Signature state is tracked per issuer identity — the verifier-computed key fingerprint (§3.1 keyid), and the registrable domain for L2. A later document from an identity previously seen signing that arrives unsigned, or whose signature fails verification, is a downgrade event: renderers MUST display a visible downgrade state (never a silent return to L0), and receivers SHOULD suppress or down-rank the document pending re-verification. Soft-fail still governs display — content renders — but a downgrade is never presented as the same trust level the issuer previously held. Stripping a signature block to shed a bad history is thus visible and counterproductive; rotating to a new identity resets nothing a receiver keys on domain or computed fingerprint.
2.1 L2 binding (normative — governs the L2 row above)
A document is L2-verified only when ALL of the following hold:
- It carries a signature block that verifies (§3).
- The verifier derives the registrable domain (eTLD+1 per the Mozilla Public Suffix List, as currently published) from the document's
issuer-url, and fetches the key itself athttps://<registrable-domain>/.well-known/intents-md/key.asc. The verifier MUST NOT fetch the rawkey:URL from the signature block; that URL is advisory display metadata only. - The fetched key's fingerprint matches the document's
keyid. - Fetch basis. The verifier's key fetch uses exactly
https://<registrable-domain>/.well-known/intents-md/key.asc— schemehttps, default port, fixed path. Any port, path, query, or fragment present inissuer-urlMUST be ignored for this fetch; anissuer-urlcannot steer the fetch to an attacker-chosen endpoint. - No issuer-url, no L2. A document without an
issuer-urlcannot attempt L2 verification; display falls back to the signature state (L0/L1). Soft-fail, never a rejection.
Binding is to the domain, never to the name. L2 binds a document to a registrable domain. It does NOT bind the human-readable issuer name, and MUST NOT be presented as doing so. Renderers displaying L2 state MUST display the registrable domain (e.g. "Signed · alex.example") — never a bare checkmark and never the self-declared issuer name alone.
Free-host cap. If the registrable domain is itself on the Public Suffix List or a suffix of a PSL entry (e.g. github.io, pages.dev), the hostname is a platform tenancy, not an owned identity: verifiers MUST treat keys served there as L1-equivalent for all ranking and badge purposes. The domain-cost sybil defense applies only to domains an issuer actually had to register.
Tenancy test (defines the operative test where the cap sentence above is ambiguous). A hostname is platform tenancy when the prevailing public suffix for it comes from the PRIVATE section of the Public Suffix List (e.g. github.io, pages.dev, vercel.app, netlify.app, neocities.org). ICANN-section suffixes (ch, co.uk, …) are registry TLDs: registrable domains on them cost registry money and earn full L2. This reading covers the dominant user.github.io form — where the eTLD+1 (user.github.io) is itself free tenancy — and not only bare path-style publishers on a platform apex.
PSL timing — L2 is a verifier-local judgment. L2 status is computed at fetch time against the PSL snapshot the verifier currently holds; two verifiers MAY disagree, and a hostname's status MAY change when the PSL changes. Verifiers SHOULD pin a PSL snapshot and refresh on a documented cadence. This is consistent with receiver sovereignty ([SERVICES] §4): L2 is a badge a receiver grants, not a property a document carries.
Honest cost of the cap (owned trade). Free-host publishers who could have displayed "L2" under the old self-declared rule are demoted to L1-equivalent. That is deliberate: the domain-cost sybil rung must price identities at registry cost, and free tenancy prices them at zero. The path to L2 is registering a domain — a low-single-digit-CHF-per-year action — and everything else about zero-config static publishing in this spec is unchanged.
PSL freshness gap. The PSL lags reality: a new free-host platform is full-L2 until its suffix reaches the PSL PRIVATE section, and verifiers on different snapshot cadences MAY disagree in that window. Verifiers SHOULD therefore maintain a local denylist of known free-tenancy suffixes alongside the PSL and apply the same L1-equivalent cap to them; the PSL remains the normative source, the denylist only closes the lag. Known per-path multi-tenant hosts — unrelated publishers sharing one registrable domain by URL path — belong on the same denylist and carry the same cap. (Directory-grade verifiers SHOULD publish their denylist + refresh cadence — disagreement is honest when it is legible.)
Lookalike honesty (non-normative note). L2 still cannot prevent alex-shop.ch from impersonating alex.ch visually. The domain-display requirement above is the mitigation: the reader always sees which domain vouches, so lookalikes compete on legibility, not on crypto badges. Receivers MAY add an edit-distance UX nudge — visually highlighting when the issuer display name and the registrable domain diverge sharply (issuer alex over alex-shop.ch) — keeping L2 honest without overselling it as identity proof. In one line: L2 proves a domain vouches; it does not prove human identity — alex-shop.ch's vouching is worth exactly what alex-shop.ch is worth, and lookalike domains remain a reader-literacy problem no cryptography in this spec solves. Launch copy MUST NOT oversell L2 as identity proof.
3. Signature block
A document MAY end with a detached signature block:
-----BEGIN INTENT SIGNATURE-----
algo: ed25519
key: https://issuer.example/.well-known/intents-md/key.asc
keyid: sha256:9f2a…c41b
signed: 2026-08-21T08:00:00Z
<base64 signature over the canonical form>
-----END INTENT SIGNATURE-----
3.1 Fields
| Field | Status | Rule |
|---|---|---|
algo |
REQUIRED | Algorithm identifier (e.g. ed25519, pgp). The specification never mandates which algorithm — only that it is declared. A block with no algo is treated as absent; the document displays as L0 unsigned. |
key |
RECOMMENDED | URL of the public key — the L2 domain-bound path. |
keyid |
RECOMMENDED | Fingerprint of the signing key. The portable identity: survives key-URL death and key rotation, and lets L1 reputation track an issuer with no domain at all. The asserted string is a hint, never an identity: verifiers MUST compute the fingerprint of the key that actually verifies (or that was fetched at the well-known path) and key all reputation, aggregation, and review-binding on the computed fingerprint — an asserted keyid alone never establishes identity (copying a reputable issuer's keyid onto an unsigned or differently-signed document verifies nothing). |
signed |
REQUIRED | Timestamp of signing (RFC 3339). A signature block without signed is treated as absent; the document displays as L0 unsigned. |
| (others) | OPTIONAL | Unknown fields inside the block are ignored, per the global leniency rule. |
3.2 Algorithm agility (normative)
Freshness (normative). signed bounds staleness, not forgery: it is block metadata, self-attested (§4.1 rule 1 — outside the signed range). Receivers SHOULD apply a max-age freshness window to signed (the window is receiver policy; directory-grade receivers publish theirs), and issuers of time-sensitive documents SHOULD set an expires header so staleness is declarative. A document whose signature verifies but whose signed is older than the receiver's window displays as verified-but-stale — visible state, never silent acceptance of an eternal claim.
Verifiers MUST NOT reject a document because its algo is unknown to them; they display it as unverified ("unknown algorithm") with content still rendering. Hard-coding a single algorithm into a renderer is non-conformant. The specification mandates declared algorithms, never chosen ones.
3.3 Immutability by signature
The cryptography is self-enforcing: any edit to the document (or the block) breaks verification and surfaces as soft-fail. A signed document is therefore immutable-by-signature. To amend, the issuer re-signs and republishes; versioning over time is new documents (or the same URL re-signed), never a mutated signed artifact. Amendment is always possible; silent amendment is impossible.
4. Canonical form (for signing and hashing)
- All header lines normalized to
name: value: lowercase name, single space, template order —intent, type, title, published, updated, expires, status, lang, issuer, issuer-url, id, prev— then extension headers alphabetically. (Exact byte-level rules, including byte-exact values, are §4.1 — §4.1 governs wherever the two diverge.) - One blank line.
- Then the body byte-exact as written. (Value byte-exactness per §4.1 rule 9: the canonical form preserves the value bytes after the
name:prefix —title:Xandtitle: Xare distinct documents; §4.1 governs.) - Line endings normalized to LF (CRLF → LF) across the entire document before signing and before verification — a round-trip through a Windows editor or mail client must never break a signature.
4.1 Exact canonical form (normative — governs over the §4 summary above wherever the two diverge)
The canonical form is a byte sequence built by the following rules, applied in order. Every conforming signer and verifier MUST produce identical bytes.
- Signed range. The canonical form covers the header block and the body. The signature block (§3) is outside the signed range — a detached signature cannot cover itself. Block fields (
signed:,key:, …) are self-attested metadata and MUST be displayed as such. - BOM. A leading U+FEFF, if present, MUST be stripped.
- Line endings. CRLF → LF throughout.
- Trailing whitespace. Trailing spaces/tabs on each line MUST be stripped.
- Duplicates. If a header name appears more than once, the last occurrence wins and earlier duplicates are removed. Renderers MUST display the winning occurrence — the signed value and the displayed value are always the same occurrence (no sign/display divergence).
- Continuation lines. Unfolded before signing: each continuation line (leading space or tab) is joined to its parent with a single space. The canonical form contains no continuation lines.
- Header order. Core headers first, in exactly this order, each at most once after rule 5:
intent, type, title, published, updated, expires, status, lang, issuer, issuer-url, id, prevthen all remaining headers — extension headers (ask,contact, …),attr-headers, and unknown headers alike — in ascending byte-wise ASCII order of the header name (raw byte comparison, no locale, no case folding; all names are already lowercase). - Unknown headers are signed. No header line in the header block is ever dropped from the canonical form. "Ignored" ([CORE] §3.4) means not interpreted, never excluded from signing. The header block ends at the first blank line (the separator) or at the first line that is neither a continuation nor a valid
name:header — whichever comes first; from that line the body begins, and no separator is emitted when the boundary was a non-header line. Everything after the boundary is body bytes, signed byte-exact (rule 10). This is the conventional-parse reading: a reader of the raw text sees body where the canonical form puts body. - Value normalization: none. Values are byte-exact after unfolding. No case folding and no Unicode normalization — NFC and NFD variants are simply different documents (both valid; never conflated, never silently unified). The value is the bytes after the first colon of the unfolded line (
contact: mailto:…is one header, one value — a value may itself contain colons), and no whitespace normalization applies:title: Xandtitle:Xare different documents. - Separator and body. One blank line, then the body byte-exact subject to rules 2–4. Trailing blank lines at end of body are removed; the canonical form ends with exactly one LF.
- Header-name case. Header names are matched case-insensitively and MUST be written lowercase in the canonical form.
Title:andtitle:are the same header; a case-variant duplicate collapses under rule 5 (last occurrence wins). Sorting (rule 7) applies to the lowercased name. This normalizes names only — values remain byte-exact (rule 9, unchanged). Lowercasing is ASCII-only (0x41–0x5A → 0x61–0x7A): non-ASCII bytes in a name are never case-folded, so no Unicode locale can split two implementations' results. Header names SHOULD match^[a-z0-9-]+$. - Execution pipeline (governs ordering). The rules above are the inventory; execution order is: BOM strip (2) → line-ending normalization (3) → unfolding (6) → trailing-whitespace strip (4) → duplicate collapse, last-wins (5) → name lowercasing (11) → header ordering (7–8) → separator + body assembly (10). Where this pipeline and the rule numbering differ, the pipeline governs: unfolding precedes trailing-whitespace stripping, so a whitespace-only continuation line joins to its parent and contributes no residue (any resulting trailing space is stripped in the next stage).
duplicate detection under rule 5 is by lowercased name — rule 11 makes
Title:andtitle:the same header — so case-variants collapse last-wins before the lowercase rewrite. - Empty body. A document with an empty (or absent) body canonicalizes as: header block, the separator blank line, and a single terminating LF. The separator is retained so headers-only documents remain structurally valid: the canonical bytes end with the final header line's LF followed by exactly one blank-line LF (
…last-header\n\n) and nothing more — no body LF, no extra terminator. - Closed core list. The core header sequence in rule 7 is CLOSED as of this version. Headers admitted later by consensus always sort among the extension/unknown set by byte order — never inserted into the core sequence. Canonical forms of existing documents therefore remain byte-stable across future spec versions; a version bump can never silently break an existing chain.
Conformance test cases (normative — a machine-readable suite ships with the reference implementation at the ratification gate): CRLF document verifies · BOM document verifies · duplicate title: displays and signs last-wins · continuation-line document folds identically across tools · NFC vs NFD bodies are distinct valid documents · a 3-document prev: chain links under every implementation · unknown-header document signs with the unknown header included · trailing-whitespace edit does not break signature · document with prev: sorts it after id, before extension headers · case-variant names (Title: / title: mix) canonicalize identically across tools · stacked continuation lines (two or more) fold left-to-right into one line identically across tools · whitespace-only continuation line folds and strips cleanly · empty-body document verifies and displays · future consensus header (e.g. zz-future:) sorts after core bytewise, byte-stable across versions.
Authoring guidance (non-normative): publish NFC. Canonical verification stays byte-exact (rule 9 — decided trade), but authors SHOULD normalize text to NFC at authoring time; publishing NFD invites visually-duplicate search entries at receivers that do not fuzzy-dedup ([SERVICES] §3).
5. Hash chaining (prev:)
A document MAY declare prev: sha256:… — the fingerprint of the previous version's canonical form. This is the Git/Merkle model without any coin.
What it buys:
- Provable revision history — a price change is demonstrably the same listing evolving, not a rewrite. Kills stealth-edit scams.
- Provable longevity — an unbroken chain of signed documents is a dated, ordered history; old keys become worth protecting.
- Feed integrity — a feed document chaining to its prior editions gets integrity for free.
- Works unsigned — hashing is not signing; L0 issuers still get sequence tamper-evidence. Chains are even more universal than signatures.
Honest limit: chains are self-attested. An issuer can always start a fresh history; a chain proves internal consistency, not completeness. Completeness is answered at Layer 3 — timestamping and witness notaries (RFC 3161 / OpenTimestamps style) and directory checkpoints ([SERVICES] §3).
prev: MUST remain optional; standalone documents are always valid.
6. Key custody (SHOULD)
Signing keys SHOULD live in a hardware-backed secrets vault under the issuer's control, never in plaintext on the publishing machine. Signing is one well-scoped operation — signature = sign(canonical form) — which a vault can perform without ever exposing the key itself. A hardware-backed vault is a trusted execution environment (TEE) in the practical sense — Secure Enclave, HSM, secure element — and is the recommended custody anchor for L1/L2 issuers. The strongest posture: the key exists in hardware and is never extractable at all.
7. Revocation (known limitation, deferred)
This version defines no mandatory revocation feed — no key revocation mechanism is required of any issuer. Optional manifest.json at the sibling well-known path provides the recommended revocation and rotation upgrade path below; if a key leaks before that path is adopted, L2 issuers mitigate by rotating the key file at their well-known URL: new documents verify against the new key, and old ones surface a visible key mismatch that readers judge for themselves. A full revocation design remains deferred; the manifest below is the bridge.
Key manifest (RECOMMENDED upgrade path). The well-known URL MAY serve a manifest listing current AND retired keys with validity windows + revocation dates. Verifiers check signed against the window; documents signed by a revoked key display "signed by revoked key". Full design still deferred, but the file format reserves this shape now so rotation isn't an identity reset later.
Path harmonization. The required well-known path remains /.well-known/intents-md/key.asc (§2.1 item 2 — single key, current shape). The manifest is OPTIONAL and lives at a sibling path, /.well-known/intents-md/manifest.json; verifiers MUST accept bare key.asc and MAY consult the manifest when present. This reserves the rotation upgrade without moving the required path — no later adoption can invalidate existing key.asc deployments.
Manifest schema (RECOMMENDED shape). When served, manifest.json SHOULD conform to:
{
"manifest": 1,
"keys": [
{
"keyid": "sha256:…",
"key": "-----BEGIN PGP PUBLIC KEY BLOCK-----…",
"valid-from": "2026-08-01T00:00:00Z",
"valid-until": null,
"revoked": null
}
]
}
Semantics: keyid = fingerprint as used in signature blocks; valid-from/valid-until bound the signing window — verifiers checking signed against the window display out-of-window signatures as "signed outside key validity"; revoked (ISO 8601, null when not revoked) marks the cutoff — documents with signed after revoked display "signed by revoked key". Unknown fields are ignored per the leniency rule. Serving a manifest is OPTIONAL; when absent, bare key.asc semantics apply unchanged.
7.1 Directory proof-of-control challenge
Directory admission ([SERVICES] §4) needs an interoperable proof. Two flows, either satisfies:
- Challenge file. The directory issues an opaque nonce (printable ASCII, ≤ 128 characters, single-use, validity window stated by the directory). The publisher serves it at
/.well-known/intents-md/challenge.txtcontaining exactly one line:intents-challenge: <nonce>. The directory fetches once over HTTPS at the registrable domain of the submitted document URL — the document being indexed, never the directory's own domain and never a sidecar submission's headers — under the §2.1/§9 fetch basis and policy (fixed path, default port, no redirects beyond §9 caps). One verified fetch = control proven. - Signed index request. Alternatively, a signed intent document of reserved type
index-request([CORE] §5) carryingsubject-url(the document to index) andissuer-url(REQUIRED forindex-requestper [CORE] §5); the two registrable domains MUST agree. The signature is verified per §2.1 items 1 and 3–5 against thesubject-urlregistrable domain — the key is fetched at that domain's well-known path and its fingerprint must match the document'skeyid: proof of control of the exact domain the indexed document lives on.
Submission transport (non-normative interop note): the typical submission is the signed index-request document bytes carried in the directory's admission API request body alongside the subject-url; directories verify per this flow before indexing. No specific API schema is mandated.
Both proofs bind control of the registrable domain. Nonce values are directory-issued and never interpreted by the publisher; the challenge file is deleted (or left to expire) after verification.
Honest limit — well-known write access. Both §7.1 flows require write access to the registrable domain's /.well-known/intents-md/ tree. Publishers on path-only hosts (github.io/<repo>), raw CDN URLs, or corporate sites where another team owns the apex cannot pass directory admission from those URLs. Their documents remain fully valid for Tier-0/Tier-1 reading — proof-of-control gates directory admission, never document validity. The paths to admission: host the document at a URL on a domain whose well-known tree the publisher controls (a registrable domain is a low-single-digit-CHF-per-year action), or relocate to a convenience host that itself passes proof-of-control. Cross-domain vouching (a key on domain A admitting documents at domain B) is deliberately not defined here — weaker binding, needs its own threat model; recorded as a possible future extension ([SERVICES] §4).
Consultation & display. A verifier that displays L2 state SHOULD consult manifest.json when present (publishing it remains OPTIONAL). A verifier claiming L2 conformance that does not consult an available manifest MUST NOT display revoked or out-of-window keys as current. Exact display strings: signed by revoked key (documents with signed after the key's revoked date) and signed outside key validity window (signed outside valid-from/valid-until).
Apply-if-obtained is MUST (clarification). The SHOULD above governs fetching the manifest; applying it is stronger — a verifier that has obtained a manifest (fetched it, or holds it from any source) MUST NOT display a key as current L2 while that manifest marks it revoked or out-of-window. Fetching may stay SHOULD so day-one zero-config verifiers are conformant; ignoring a known revocation never is.
Rotation playbook (minimum response to key compromise). (1) Immediately replace key.asc at the well-known URL with the new key; (2) serve a manifest marking the old keyid revoked with the compromise date; (3) re-publish current documents signed by the new key (prev:-chained where a chain exists); (4) old documents signed by the revoked key then display signed by revoked key at conforming verifiers — visible history, not silent invalidation.
8. Phishing resistance
A fake page cannot mimic a login form it never renders. The reader's own renderer draws everything, and credentials live in the reader's vault — never in the document channel. Spoofed identity is handled by signatures (this document), not by mimicry. What no signature can give: honesty. A signed document can still lie about condition; countermeasures live at the deal layer — escrow rails and review documents, which are themselves intent documents.
9. Verifier & renderer fetch policy
Any conforming implementation that fetches keys, documents, or media referenced by intent documents:
- Key fetches. Only from the derived registrable-domain well-known path (§2.1). MUST refuse: non-HTTPS, IP-literal hosts, private/loopback/link-local destinations (RFC 1918, 127/8, 169.254/16, ::1, fc00::/7, fe80::/10), userinfo in URL, more than 1 redirect. MUST cap: 64 KB response, 10 s timeout. SHOULD cache by
keyid≥ 24 h. MUST require fetched-key fingerprint =keyidwhen present. SHOULD rate-limit key fetches per registrable domain (amplification defense: attacker docs cannot order the verifier's network activity). - Document fetches (renderers, directories, agents): same destination rules; MUST cap size (spec limit 1 MB) and time; MUST apply per-domain crawl budgets; redirects capped at 3.
- Media fetches ([CORE] §4 rule 3): renderer MUST validate magic bytes (JPEG/PNG/WebP/GIF raster signatures) and render non-raster bytes — including anything served as SVG — as inert text. SVG is not an image type in this version. MUST block private/loopback/link-local destinations; SHOULD cap size (≤ 10 MB) and decode time (decompression-bomb defense); SHOULD cache.
- Privacy disclosure (SHOULD). Publishers learn the receiver's IP/UA when media renders. Receivers SHOULD document this, and MAY render media only on explicit reader action (privacy mode default is conformant).
- Blocked destinations — completion and notation defenses. In addition to the ranges in items 1–3, fetches MUST refuse: IPv6-mapped IPv4 (
::ffff:0:0/96, e.g.::ffff:10.0.0.1), NAT64 (64:ff9b::/96),0.0.0.0/8,100.64.0.0/10(CGNAT),198.18.0.0/15, and IP literals in any alternate notation (integer, octal, hex, or mixed forms — e.g.http://2130706433/). A DNS name that resolves to ANY blocked address is blocked. - Redirect and rebinding hardening. Every redirect hop MUST independently satisfy all destination rules before being followed, and the connection target MUST be re-validated at connect time. Implementations SHOULD pre-resolve and pin (connect only to a pre-validated address) as DNS-rebinding defense. (Stated once: connect-time revalidation is already MUST above and is the minimum rebinding defense every conforming fetcher owes its users; pre-resolve-and-pin is the recommended implementation strategy for that MUST and is additionally MUST for the reference renderer per the hardening note below. Third-party renderers are not second-class: skipping connect-time revalidation is non-conformant for them too.)
- Fetch on action, not preview (SHOULD). Key, document, and media fetches SHOULD be triggered by an explicit verify/reveal action, not by passive preview rendering. Privacy-mode-default implementations are conformant (extends item 4).
- Scope: all renderer-initiated fetches. This section governs every fetch a conforming renderer/directory initiates because of a document — including
contact:endpoints, URLs linkified in the body,issuer-url, and feed/profile pointers — not only keys, documents, and media. Two follow-ons: (a) navigation away from the document view (a human or agent clicking a body link) SHOULD carry an off-document warning — the destination is outside the rendered, no-execution channel; (b) agents MUST NOT auto-fetchcontact:endpoints — contacting an issuer is a reader-authorized action, never an ambient one (an endpoint that learns your IP/UA on contact is a tracking surface; [CORE] §8 item 1's data-not-instructions rule has a fetch-side twin — see also §6.1 render integrity).
Item 8 handling split: (i) mailto: contact values open the reader's mail client — never fetched, never probed; (ii) HTTPS contact: values follow the full fetch policy if fetched, but the default is no prefetch — they open only on human- or agent-authorized action with the off-document warning (this prevents agents from probing contact URLs as health checks); (iii) body links follow the off-document warning rule (a) above.
9. Media privacy default. The reference renderer ships with privacy mode ON by default: media loads only on explicit reader action (item 7), and unique per-request image URLs (a fingerprinting vector — item 4) are flagged. Receivers MAY offer a proxied/cached image path as a reader option; publishers learn nothing about the render by default.
Reference renderer hardening. For the reference renderer specifically (the /render?url=… endpoint and its embeddable library), pre-resolve-and-pin (item 6) is MUST, not SHOULD: the reference implementation defines the safe path others copy, and an open public render endpoint without pinning is the single highest-risk surface in the ecosystem. Hostile fetch cases (IP-literal notations, mapped ranges, redirect chains to internal targets) MUST pass before public exposure.
Conformance framing. A renderer or directory that fetches without this policy is non-conformant — same weight as the no-execution rule. The reference renderer (/render?url=…) MUST implement this section from first public exposure.
[CORE] 01-CORE — Core Format. [SERVICES] 03-SERVICES — Trust Services & Receivers.
03 · Trust Services & Receivers
Non-normativeLayer: 3 (competing services at the edge) · Status: Non-normative
1. Status and design rule
This document describes patterns for trust services and receiver policies. Nothing here is mandatory; all of it is encouraged. The design rule: hardened floor, extensible above, best layer wins at the edge. The core ([CORE]) defines the format and rejects everything outside it; Layer 2 ([TRUST]) standardizes the hooks; Layer 3 is where services compete. Each receiver — directory, agent, renderer — chooses which layers to apply. The market of receivers, not the spec, decides which trust layer wins.
Layer 3 — Competing trust services (choose freely, per receiver)
reputation graphs · bonded/staked reputation · proof-of-work
stamps · paid ranking · payment rails (x402, escrow, bank)
directory admission policies · review aggregators
matching engines (rfp ↔ listing) · inverted advertising
(offer delivery against declared intent) · longevity
provenance (key age + signed history) · community badges
(signed endorsements) · TEE-backed signing (Secure Enclave,
HSM — hardened key custody) · timestamping & witness notaries
(RFC 3161 / OpenTimestamps) · directory checkpointing
Layer 2 — Interop hooks (in core, from day one) — [TRUST]
signature block · issuer-url + /.well-known key binding
review type · contact-as-endpoint · payment rails list
status lifecycle
Layer 1 — Hard core (never extended by trust logic) — [CORE]
plain text · no execution, ever · leniency rules · size limits
Layer 2 is the load-bearing idea: hooks standardized so Layer 3 services compose across implementations instead of siloing.
2. The anti-abuse ladder (lowest cost to highest)
- Sybil cost via domain binding (L2, free, live now). Domains cost money and registrars kill abuse cycles — an identity that self-replenishes its cost. The default floor every receiver can apply.
- Emergent reputation via signed reviews (free).
reviewdocuments referencing issuer keys aggregate into a web of trust. Nobody builds "the reputation system"; it is what thereviewtype becomes when agents aggregate it. Reputation is earned, never bought. - Proof-of-work stamps (free, costly at scale). Hashcash-style stamps per submission, demanded by directories as admission policy. Free for an honest publisher; ruinous for a million-document spam run. Directory policy, never protocol.
- Bonded reputation (skin in the game). An issuer posts a stake on any rail — crypto escrow, fiat deposit with a bond service — forfeited if reviews prove fraud. The crucial distinction: bonded ≠ purchased. Purchased ranking ("pay more → more reputation") is plutocracy: the rich spammer outbids the honest seller. Bonding inverts it: money buys risk, not rank.
- Payment-gated interaction (x402 and friends). Because
contactis just a URL, an issuer's contact endpoint may itself demand payment (HTTP 402): messaging me costs 0.50 USDC. Bot spam dies economically; serious buyers don't notice. Composition modes, all valid today with zero spec change: (a)payment: x402-usdcas a rail string; (b) 402-required contact endpoints; (c) directories charging admission or bonds via any rail. Best rail on top wins — crypto competes with escrow APIs, bank rails, and everything else, at the edge.
L2 binding note: the L2 rung above is defined normatively in [TRUST] §2.1 — registrable-domain derivation (PSL eTLD+1), verifier-controlled key fetch, free-host tenancy cap demoting PSL PRIVATE-section hostnames to L1-equivalent.
L2 is a floor, not a ceiling. Registrable domains cost money but not much: a determined spam run at ~$1–15/domain is still feasible. L2 kills free-sybil, not paid-sybil. Directory admission policy (stamps rung 3, bonds rung 4, first-seen checkpoints) is the load-bearing defense at scale — directories SHOULD treat L2 as the entry floor and stack admission costs above it from launch, not retrofit them after the first flood.
Paid-sybil scaling: proof-of-control prices identity at one domain; volume floods still scale with domains purchased. Directories SHOULD combine proof-of-control with per-registrable-domain rate limits and first-seen checkpointing (§3) so paid-sybil cost scales with volume, not just identity. Below that, an honest limit stands: L2 prices identity, not intent — accepted, not solved.
3. Matching, advertising, and community trust (patterns)
- Matching engines.
rfp(demand) andlisting(supply) are duals; any party may run a matching service over public documents without permission. Engines MAY rank by any signal, including geography (ships-from/ships-toexpress locality) andattr-attributes. - Inverted advertising. Today's advertising pushes offers at people who never asked. In an intent-first web, offers arrive at the agent because intent was declared — an
rfpsays what is wanted, and sellers pay to deliver a relevant offer against it (e.g. via 402-gated contact). Payment-to-deliver replaces payment-to-interrupt; spam dies economically because the declared-intent channel is paid and the unsolicited channel is filtered. - Longevity provenance (chain-free). Trust accrues to a signing key with a dated, verifiable document history: an issuer whose key has signed honest documents for years is demonstrably not a hit-and-run sybil. Wallet-provenance economics — old keys are worth protecting — without any chain or token.
- Community badges (chain-free). A community, brand, or club issues signed
review/profiledocuments endorsing an issuer. A badge is a signature, verifiable by anyone; a badge issuer is accountable for their endorsements — their own key's reputation is at stake. - TEE-backed signing. The strongest custody posture: the signing key lives in hardware and never exists in extractable form ([TRUST] §6).
- Rfp flood posture. Public
rfps invite solicitation floods by design (acknowledged, not "solved"). Publishers with broad demand SHOULD use payment-gated contact endpoints (402-style); receivers SHOULD apply stricter greylist defaults to unsignedrfpvolume than to unsignedlistingvolume — demand-side spam is cheaper than supply-side spam, so the filter is asymmetric by design. - Directory checkpointing (SHOULD). Directories SHOULD store the first-seen canonical hash per document URL and expose it to receivers — the [CORE] §3.5 dates-untrusted corroboration rule gains a concrete hook, and provenance/staleness display follows. No specific notary is mandated: first-seen is the minimal witness every directory already has. Directories exposing first-seen hashes SHOULD include them in API responses as a corroboration hint for offer validation — the cited-hash vs. first-seen-hash comparison gives commerce flows a concrete witness without mandating a notary. Non-normative field-name example for interop:
"first_seen_hash": "sha256:…"beside the document URL in the response entry; one line, no API schema mandated. - Fuzzy dedup (MAY). Receivers MAY fuzzy-dedup listing search results on normalized title +
ask— NFC/NFD visual duplicates are distinct documents by canonical law ([TRUST] §4.1), and search is receiver territory, so dedup lives here, never in the canonical form.
4. Receiver posture (SHOULD)
Directories, agents, and renderer defaults SHOULD: rank L2-verified above L1 above L0; greylist unsigned flood volumes; weight the review graph; apply cost limits (stamps, admission fees) to unknown issuers. Trust is always displayed — badges, ranking, filters — never executed, and never hidden content removal. A receiver that ignores trust layers entirely is conformant; it is simply less useful, and the market answers that.
Policy vs. soft-fail (clarification): default feed ranking and filtering that omits low-trust documents is receiver policy, not renderer soft-fail hiding ([TRUST] §2) — the two are conformant together as long as the omitted document remains reachable by direct URL and any conforming renderer still displays it when fetched. Omission from a default feed is not hiding at render.
Commerce default: commerce-focused receivers SHOULD default unsigned listings out of default search ranking while still rendering them by direct URL (the policy-vs-soft-fail clarification above applies). The signatures-optional, edge-filtering-is-the-mechanism principle is untouched; this is that mechanism, stated as a sensible default.
4.1 Sponsored-ranking disclosure (SHOULD). A directory, matching engine, or other receiver that accepts payment — any rail — for ranking, placement, or offer delivery SHOULD disclose that fact to the reader (label, badge, or separate lane). Voluntary and unenforced: the norm exists so honest behavior is legible and standard, receivers can filter sponsored results on request ("ignore paid"), and clean services gain a credibility badge; dirty ones answer to Layer-3 reputation. Analogous to ad-labelling norms in search — written on day one, before anyone has incentives to fight it.
4.2 Staleness decay. The older a listing without an updated or status change, the less likely it remains available. Receivers MAY annotate possibly-stale listings and down-rank with age — display, never hide. expires makes the inference precise where present; its absence never blocks it. Issuers who maintain their documents (updated, prev: chains, re-signing on status change) rank visibly fresher — document hygiene becomes a trust signal.
Dates caveat: published/updated are self-attested and MUST NOT be treated as evidence without corroboration (first-seen, notary stamp, checkpoint) — see [CORE] §3.5. Receivers use corroborated dates only.
Corroborated status: a status change corroborated by prev:-chain re-sign or directory first-seen of the changed document SHOULD rank above a bare status header edit — receivers weigh what they can verify. sold without a published receipt MAY be annotated as lower-corroboration ([CORE] §3.2 deal receipts). Default marketplace receivers SHOULD rank sold listings with a published bilateral deal receipt above sold by bare status header, and both above stale-dated active listings — expected hygiene made legible for matching engines and edge feed defaults; receipt publication itself is never mandated.
Reviewer-cost guidance: the review graph is only as strong as reviewer identity cost. Receivers weighting reviews SHOULD discount reviews from keys younger than the receiver's own first-seen history of them, and weight reviews from aged, L2-bound reviewer keys highest — a million fresh L1 keys signing glowing subject-keyid reviews for each other is the naive attack ([CORE] §5 binding stops unbound reviews; age + domain cost stops the army). Bilateral-signed deal receipts (future deal type, [CORE] §5) are the endgame: reviews anchored to completed, mutually-attested deals. Reference default for interoperable receivers (non-normative): do not aggregate reviews from reviewer keyids first seen less than 30 days before the review's signed date, unless the reviewer key is L2-bound — the exact window is receiver policy; publishing one reference default lets independent directories behave similarly.
Directory admission — proof of control: a directory that indexes by submitted URL SHOULD require proof of control before indexing via [TRUST] §7.1 flow 1 (challenge file at the submitted URL's registrable domain) OR flow 2 (signed index-request document). Indexed documents will often also verify as L2-signed, but L2 verification of the target document is not proof of submitter control — a third party can sign nothing and still submit a victim's public listing; the §7.1 flows are the only admission proofs. The challenge-file path, nonce line format, and index-request flow are defined normatively in [TRUST] §7.1. Indexing arbitrary pasted URLs lets third parties index victims' documents or pollute search with unrelated content — proof-of-control closes the hijack without adding a permission layer (anyone who controls the URL passes; nobody who doesn't). The well-known-write-access honest limit also applies: path-only/CDN publishers cannot pass admission from those URLs ([TRUST] §7.1).
[CORE] 01-CORE — Core Format. [TRUST] 02-TRUST — Trust & Integrity.