Altdown Specification
Version 1.0 (Draft) · October 31, 2026
What is Altdown?
Altdown is a format for publishing a Markdown copy of a web page next to the page itself.
Its name is short for Alternate Markdown.
An Altdown copy is a plain static file. A site publishes one copy per page and announces it with one <link> element in the page's <head>. A site may also publish one index that lists every copy. No server-side logic, proxy, or custom HTTP header is needed. A folder of files on any web host can conform.
This copy exists for software that reads pages as text. Most of an HTML page is not content- it has navigation, scripts, banners, and layout. A program that reads the page pays for all of it and keeps little. The Altdown copy contains the content and nothing else.
Altdown borrows ideas from RSS. The Altdown copy sits beside the page, as a feed does. The page declares the copy, as a page declares its feed. The copy carries the content itself, not a description of it. Where RSS gave feed readers the words without the chrome, Altdown gives the same deal to agents.
Altdown does not grant or deny access to a site. robots.txt does that. Altdown does not let an agent act on a site. Other protocols do that. Altdown is the reading half only.
This document is version 1.0 of the specification, in draft. The format is small on purpose. Later versions should clarify this document, not add to the format. New capabilities belong in extensions, as described under Extending Altdown.
About this document
This document defines three things: the Markdown copy, the way a page announces it, and the optional site index. It then says what an agent must do with them. The last sections answer common questions, describe how to extend the format, and give a complete example.
The words must, must not, should, should not, and may carry their usual meanings from RFC 2119. A rule marked must is required for conformance. A rule marked should is the right default, and a party that departs from it should have a reason. A rule marked may is optional.
Two kinds of party conform to this document.
A publisher is a site that produces Altdown copies. A publisher conforms when every copy it announces meets the rules in The Markdown copy, Front matter, and Discovery. A publisher that offers an index must also meet the rules in The site index.
An agent is any program that reads Altdown copies. A browser extension, a crawler, a fetch library, and a language model tool are all agents. An agent conforms when it meets the rules in Agent behavior.
The word page means an HTML document at a URL. The word copy means the Altdown file for that page. The word canonical URL means the URL of the page that the copy stands for.
A few terms come from other specifications. Front matter is a block of metadata at the top of a Markdown file, set off by lines of three hyphens. JSON Feed is the syndication format at jsonfeed.org. Origin has the meaning given in RFC 6454: the scheme, host, and port of a URL.
Overview
A site adopts Altdown with three parts. The first two are required. The third is optional.
| Part | What it is | Where it lives | Required |
|---|---|---|---|
| Markdown copy | One .md file per page, with front matter and the page's main content |
Next to the page, on the same origin | Yes |
| Link element | One <link rel="alternate" type="text/markdown"> per page |
In the page's <head> |
Yes |
| Site index | One JSON Feed 1.1 document that lists every copy | Anywhere on the origin, named in robots.txt |
No |
The parts fit together like this. An agent fetches a page. It finds the link element and follows it to the copy. It reads the front matter and checks that the canonical field points back to the page it came from. It then reads the body instead of the HTML. An agent that wants a whole site reads robots.txt, finds the index, and fetches each copy the index lists. It need not touch the HTML at all.
The page remains the source of truth. The copy is derived from the page and may be checked against it at any time. Nothing in this document asks a publisher to change the page itself, beyond adding one line to its <head>.
A typical build runs after the site generator and writes the copies into the output folder. The reference build script does this in about 40 lines of Python. A site generator plugin can do better, because it holds the Markdown source before it is rendered.
The Markdown copy
A copy is a text file. It has two parts: a front matter block and a body. The front matter comes first. The body is everything after it.
File rules
A copy must be encoded as UTF-8 without a byte order mark.
A copy must use \n as its line ending. A publisher should not emit \r\n. An agent must accept both.
A copy should be served with the media type text/markdown; charset=utf-8. Many static hosts cannot set this, so an agent must not reject a copy on media type alone. See Hosting notes.
A copy must live on the same origin as its canonical page. A copy may live at any path on that origin. The convention is index.md beside index.html, or page.md beside page.html, but this is not required. The link element tells the agent where the copy is, so the agent never has to guess.
A publisher should keep each copy under 1 MB. Copies above that size are unlikely to be read whole by any agent.
Body rules
The body is CommonMark. A publisher may use GitHub Flavored Markdown tables, strikethrough, and task lists, because most agents accept them. A publisher should not use any other extension.
The body must hold the main content of the page in reading order. It must not hold the site navigation, the footer, cookie notices, advertisements, comment forms, or related-post lists. The test is simple: if a reader would skip it, leave it out.
The body should begin with a level-one heading that matches the title field.
The body must not contain raw HTML, except for elements that have no Markdown form. Line breaks, images with captions, and tables with merged cells are the usual cases. An agent may strip any raw HTML it finds.
The body must not contain scripts, style blocks, or HTML comments.
Links should be absolute URLs. A relative link must resolve against the canonical URL, not against the URL of the copy. The two are usually the same directory, but a publisher must not depend on that.
Images should be included as Markdown image syntax with alt text. A publisher may omit decorative images.
The faithful copy rule
This is the rule that makes Altdown trustworthy. The rest of this section depends on it.
The body may contain only text that a person can see on the canonical page. The exceptions are alt text, link destinations, image sources, and the heading markers, emphasis markers, and other Markdown syntax needed to structure the text.
In practice this means:
- A publisher must not add text to the copy that is not on the page.
- A publisher must not add instructions, prompts, or notes addressed to software.
- A publisher must not add keywords, summaries, or descriptions of the page.
- A publisher must not leave out text on the page that is part of the main content.
- A publisher may leave out text that is not part of the main content, as described above.
- A publisher may change whitespace, punctuation, and quotation marks where Markdown requires it.
Text that is on the page but hidden from a person does not count as visible. Text in a display: none block, text in a collapsed section that has no control to open it, and text the same color as its background are all hidden. A publisher must not include them.
For a page that shows different content to different visitors, the copy must hold what an anonymous visitor sees. A metered article shows its free portion. A page behind a login shows nothing, and should have no copy at all.
An agent is entitled to check this rule and to stop reading a site's copies if the rule is broken. Agent behavior describes the check.
Front matter
The front matter holds the copy's metadata. It replaces the HTTP headers that a dynamic server would send, because a static host cannot send them.
The front matter must be the first thing in the file. It must begin with a line that holds only --- and end with a line that holds only ---. Between those lines, each line must hold one field in the form key: value. A key is lowercase ASCII letters and hyphens. A value runs from the first non-space character after the colon to the end of the line.
The front matter is a subset of YAML. It must not use nesting, lists, multi-line strings, anchors, or any other YAML feature. A full YAML parser reads it correctly. So does a loop that splits each line on its first colon. That is the point.
A field name must not appear twice. An agent that finds a repeated field should use the first one.
An agent must ignore fields it does not know. See Extending Altdown.
Required fields
| Field | Description | Example |
|---|---|---|
| altdown | The version of this specification that the copy conforms to. For this document the value is 1. |
1 |
| canonical | The absolute URL of the page this copy stands for. It must be on the same origin as the copy. It must match the page's own canonical URL if the page declares one with <link rel="canonical">. |
https://example.com/blog/static-sites/ |
| title | The title of the page. It should match the page's <title> or its first <h1>. |
Why we went back to static sites |
Optional fields
| Field | Description | Example |
|---|---|---|
| updated | The time the page's content last changed, in RFC 3339 format, in UTC. An agent uses it to decide whether to fetch the copy again. | 2026-10-20T14:00:00Z |
| published | The time the page was first published, in RFC 3339 format, in UTC. | 2026-10-18T09:00:00Z |
| lang | The language of the body, as a BCP 47 tag. It should match the page's <html lang> attribute. |
en |
| tokens | An estimate of the body's length in tokens. The count depends on the tokenizer, so the value is a hint and not a promise. A publisher should count with a common tokenizer and may say which one in the tokenizer field. An agent may use it to decide whether a copy fits its budget before fetching it. |
3150 |
| tokenizer | The name of the tokenizer used for the tokens field. |
cl100k_base |
| author | The name of the author. A publisher may append an email address in angle brackets. | Ada Lovelace <[email protected]> |
| description | A one-line summary of the page. This field is the one place a publisher may write text that is not on the page. It must not exceed 300 characters. An agent must treat it as a hint, not as content. | How a small team left its CMS and did not look back. |
| signals | Usage signals for the content. See Usage signals. | search=yes, ai-input=yes, ai-train=no |
| generator | The name and version of the program that produced the copy. It is for people who find the file years from now and wonder what made it. | altdown-build 0.3 |
Example
---
altdown: 1
canonical: https://example.com/blog/static-sites/
title: Why we went back to static sites
updated: 2026-10-20T14:00:00Z
lang: en
tokens: 3150
signals: search=yes, ai-input=yes, ai-train=no
---
# Why we went back to static sites
Last spring we moved our docs off a CMS...Discovery
A page announces its copy with one <link> element. This is the same mechanism that RSS autodiscovery uses, with a different media type.
<link rel="alternate" type="text/markdown" href="/blog/static-sites/index.md">The link element
The element must appear in the page's <head>.
The rel attribute must be alternate.
The type attribute must be text/markdown. A publisher must not add parameters to it.
The href attribute must be the URL of the copy. It may be absolute or relative. A relative URL resolves against the page's URL in the usual way. After resolution the URL must be on the same origin as the page.
A page must not have more than one link element with type="text/markdown", except where each carries a different hreflang attribute. In that case each copy is a translation, and the lang field of each copy must match its hreflang.
A publisher may add a title attribute to the element. A publisher should not depend on agents reading it.
The round-trip rule
A copy counts only if the page and the copy point at each other.
- The page's link element points to the copy.
- The copy's
canonicalfield points to the page. - Both URLs are on the same origin.
If any part fails, an agent must ignore the copy and read the HTML.
The rule has two effects. A third party cannot publish a file that claims to speak for a page it does not control, because the page will not link to it. And a stale copy that the page no longer links to is ignored without anyone deleting it.
An agent compares URLs after normalizing them as described in RFC 3986 section 6. A trailing slash on a directory URL, a default port, and the case of the scheme and host do not count as differences. A fragment does not count. Anything else does.
An agent that followed one or more redirects to reach the page should compare against the final URL. A publisher should put the final URL in canonical, not an alias.
A visible link
A publisher may also place an ordinary link to the copy in the page's body, where a person can see it. "View as Markdown" in the footer is enough. This lets a person hand the copy to an agent that does not know about Altdown. It is not required and it does not replace the link element.
A publisher must not hide text in the page that tells software to read the copy. See Security considerations.
The site index
The index lists every copy on a site. It is for agents that want to read a whole site, or that want to know what changed since their last visit. A site may have one index or none.
An index is a JSON Feed 1.1 document. Altdown does not define a format of its own, because JSON Feed already has what is needed: items with dates, attachments with media types and sizes, pagination, and a convention for extensions. An agent syncs an index the way a feed reader syncs a feed. It remembers the newest date_modified it has seen and reads until it reaches it.
Announcing the index
A site announces its index with one line in robots.txt:
Altdown: https://example.com/altdown.jsonThe directive follows the pattern of the existing Sitemap: directive. It may appear anywhere in the file. It is not tied to a User-agent group. The URL must be absolute and must be on the same origin as the robots.txt file. A robots.txt file may hold more than one Altdown: line, for example one per language or one per section. An agent should read all of them.
A publisher may also announce the index from any page with a link element:
<link rel="alternate" type="application/feed+json" href="/altdown.json" title="Altdown index">This is the standard JSON Feed autodiscovery link. An agent tells an Altdown index from an ordinary JSON Feed by the _altdown extension at the top level, described below.
Required top-level members
| Member | Description | Example |
|---|---|---|
| version | The JSON Feed version URL. | https://jsonfeed.org/version/1.1 |
| title | The name of the site. It should match the site's title. | Example Blog |
| items | The list of pages, as described under Items. | |
| _altdown | An object with one required member, version, whose value is the Altdown version as a number. Its presence marks the document as an Altdown index. |
{"version": 1} |
Optional top-level members
| Member | Description | Example |
|---|---|---|
| home_page_url | The URL of the site's home page. | https://example.com/ |
| feed_url | The URL of this index. An agent uses it to detect a moved index. | https://example.com/altdown.json |
| description | One sentence about the site. | Notes on building for the web. |
| language | The language of most items, as a BCP 47 tag. | en |
| next_url | The URL of the next page of the index, for sites with many pages. Items on later pages should be older than items on earlier pages. | https://example.com/altdown-2.json |
| expired | true if the site will not update the index again. |
false |
A publisher may include any other JSON Feed 1.1 member. An agent may ignore it.
Items
Each item in items is one page. The following members apply.
| Member | Required | Description |
|---|---|---|
| id | Yes | A string that uniquely identifies the page. It should be the canonical URL. |
| url | Yes | The canonical URL of the page. It must match the canonical field in the copy. |
| title | Yes | The title of the page. It should match the title field in the copy. |
| date_modified | Yes | The time the content last changed, in RFC 3339 format. It should match the updated field in the copy. |
| attachments | Yes | A list with exactly one attachment, the copy, as described under The copy attachment. |
| date_published | No | The time the page was first published. |
| language | No | The language of this page, if it differs from the index's language. |
| summary | No | One sentence about the page. The same limits apply as to the description field in front matter. |
An item must not carry content_html or content_text. The content lives in the copy, not in the index. An index that carried content would be a syndication feed, and the site already has one of those.
The copy attachment
| Member | Required | Description | Example |
|---|---|---|---|
| url | Yes | The URL of the copy. It must match the page's link element. | https://example.com/blog/static-sites/index.md |
| mime_type | Yes | Must be text/markdown. |
text/markdown |
| size_in_bytes | No | The size of the copy in bytes. An agent may use it to plan fetches. | 18204 |
| _altdown | No | An object that may carry tokens and signals, with the same meanings as in front matter. It lets an agent filter before it fetches. |
{"tokens": 3150, "signals": "ai-train=no"} |
Example
{
"version": "https://jsonfeed.org/version/1.1",
"title": "Example Blog",
"home_page_url": "https://example.com/",
"feed_url": "https://example.com/altdown.json",
"language": "en",
"_altdown": { "version": 1 },
"items": [
{
"id": "https://example.com/blog/static-sites/",
"url": "https://example.com/blog/static-sites/",
"title": "Why we went back to static sites",
"date_published": "2026-10-18T09:00:00Z",
"date_modified": "2026-10-20T14:00:00Z",
"attachments": [
{
"url": "https://example.com/blog/static-sites/index.md",
"mime_type": "text/markdown",
"size_in_bytes": 18204,
"_altdown": { "tokens": 3150, "signals": "search=yes, ai-input=yes, ai-train=no" }
}
]
}
]
}Ordering and size
Items should be sorted by date_modified, newest first. This lets an agent stop reading when it reaches a date it has seen.
An index page should hold no more than 1,000 items. A publisher with more pages should use next_url.
A publisher should regenerate the index on every build. An index that lists a copy the site no longer serves is a defect, and an agent may treat a site with many such entries as it treats a site that fails the spot check.
Agent behavior
This section says what an agent must do with the files a publisher provides. The rules are few. Most of them are about when not to trust a copy.
Reading one page
An agent that has fetched a page and wants its content should proceed as follows.
- Parse the
<head>and look for a<link rel="alternate" type="text/markdown">element. If there is none, read the HTML as usual. - Resolve the
hrefand check that it is on the same origin as the page. If not, read the HTML. - Fetch the copy. If the response is not
200 OK, read the HTML. - Parse the front matter. If it is missing, or
altdownorcanonicalis missing, read the HTML. - Check the round-trip rule:
canonicalmust match the page's URL. If not, read the HTML. - Use the body as the page's content.
An agent should send Accept: text/markdown, text/html;q=0.9 when it fetches a page. A host that supports content negotiation may then return the copy directly, and steps 1 through 5 are skipped. An agent must still check the round-trip rule when it can, because the response may have come from a proxy and not from the publisher.
An agent must not probe for a copy at a guessed path. If the page does not announce a copy, there is no copy.
An agent must not follow a copy's link element, if it has one. A copy is a leaf.
Reading a whole site
An agent that wants a whole site should proceed as follows.
- Fetch
robots.txtand look forAltdown:lines. If there are none, crawl the HTML as usual. - Fetch each index. Check that it is JSON Feed 1.1 and has a top-level
_altdownmember. If not, ignore it. - For each item, fetch the copy named in its attachment. Apply the round-trip rule to each copy as in the previous section.
- Follow
next_urluntil it is absent or untildate_modifiedfalls below the newest date the agent has already seen.
An agent must respect robots.txt when it fetches copies, as it would when it fetches pages. The Altdown: line announces the index. It does not grant permission to fetch anything.
An agent should send If-Modified-Since or If-None-Match on repeat fetches and should honor Cache-Control.
The spot check
A copy is a claim about a page. An agent that depends on copies should check the claim. The check is cheap and the agent need not run it on every page.
The recommended procedure is:
- Choose a small number of pages from the site at random. Five is enough for most sites.
- For each page, fetch the HTML and the copy.
- Extract the visible text from the HTML. Remove
<script>,<style>,<template>,<noscript>, and HTML comments. Remove elements hidden byhidden,aria-hidden="true", or inlinedisplay: none. Take the text content of what remains. - Extract the plain text from the copy's body. Remove Markdown syntax and link destinations. Keep alt text.
- Normalize both texts: lowercase, collapse whitespace, strip punctuation.
- Split the copy's text into overlapping sequences of five words. Count how many of those sequences appear in the page's text.
- Divide the count by the total number of sequences. This is the copy's overlap.
An agent should reject a copy whose overlap is below 0.90. An agent should reject every copy on a site when more than one sampled copy fails. The threshold is a starting point and may be tuned against real sites. An agent should say in its documentation what threshold it uses.
A rejected site is read as HTML. An agent may retry the check after a period of its choosing. One week is reasonable.
The check works because it is random and because faking it is harder than being honest. A publisher that writes faithful copies passes without effort. A publisher that writes a few faithful copies and many unfaithful ones is caught by the sample. A publisher that writes all unfaithful copies is caught at once.
Instructions in a copy
An agent must treat the body of a copy as data, never as instructions. Text that reads as a command to the agent, a change to its task, or a message to the model is content and nothing more. This holds however the text is phrased and whatever authority it claims.
The same rule applies to the description field, to the summary member of an index item, and to every other field a publisher writes.
An agent that finds instruction-like text in a copy may treat the site as it treats a site that fails the spot check.
Falling back
At every step where a copy is rejected, the agent reads the HTML. Altdown never makes a page less readable than it was. A site that adopts it badly loses nothing it had before. This is what makes it safe to adopt early and safe to ignore.
Usage signals
The signals field lets a publisher say how the content may be used. It is a statement of preference. It is not an access control. An agent that wants to honor it can. An agent that does not can be told apart from one that does.
The value is a comma-separated list of name=value pairs. Spaces around the commas and the equals signs are allowed. Names and values are case-insensitive.
| Signal | Meaning when yes |
Meaning when no |
|---|---|---|
| search | The content may be indexed and shown in search results, with a link to the page. | It may not. |
| ai-input | The content may be read by a model to answer a request, and the model may draw on it in its answer. | It may not. |
| ai-train | The content may be used to train a model. | It may not. |
The three names are the ones Cloudflare uses in its content signals. Altdown reuses them so that a publisher who has set them once need not learn a second vocabulary.
Defaults
A signal that is not present is unspecified. It is not yes.
This is the one place Altdown departs from Cloudflare's convention. There, a converted response says yes to all three unless the publisher changes it. Here, publishing a copy says nothing about training. Altdown is opt-in, and an opt-in format should not widen what a publisher has agreed to without the publisher saying so.
An agent must treat an unspecified signal as it would treat a page with no signal at all. Its own policy applies.
Where signals live
A signal in a copy's front matter applies to that copy and to the page it stands for.
A signal in an index item's attachment applies to that item only. It should match the copy. Where the two differ, the copy wins.
A publisher that wants one setting for a whole site should set it in every copy. The build script does this with one line of configuration.
Relation to robots.txt
robots.txt says what may be fetched. signals says what may be done with it after fetching. The two are independent. A publisher who wants to block a crawler entirely should use robots.txt. A publisher who welcomes the crawler but not the training should set ai-train=no.
Hosting notes
Altdown asks nothing of a host beyond serving files. Still, a few hosts have habits that get in the way. This section lists the known ones. It is not part of the format, and it will change as hosts change.
Media type
Some hosts serve .md files as application/octet-stream or text/plain. A browser then offers a download instead of showing the text. This does not break an agent, which reads the bytes either way. It does make the visible link less useful to a person.
Where a host allows it, a publisher should map .md to text/markdown; charset=utf-8. On Netlify, a _headers file does this. On Apache, one AddType line does it. On Amazon S3, the type is set per object at upload. On GitHub Pages, it cannot be changed.
GitHub Pages and Jekyll
GitHub Pages runs every site through Jekyll by default. Jekyll treats a file with front matter as a source file and renders it to HTML. A copy would arrive as a web page, which defeats the purpose.
A publisher on GitHub Pages must add an empty file named .nojekyll to the root of the site. Jekyll then leaves every file alone.
Clean URLs
Some hosts rewrite /blog/post/ to /blog/post/index.html and /about to /about.html. The copy's URL should be whatever the host actually serves. A publisher should fetch one copy from the live site after the first deploy and confirm that it arrives.
Content negotiation
A host that routes on request headers can serve the copy to any client that asks for it, with no change to the client. Vercel rewrites, Cloudflare rules, and Apache mod_rewrite can all match Accept: text/markdown and return the .md file in place of the .html file. This is optional. A publisher that does it gets most of what a conversion proxy offers, from files it already has. A publisher that does it should add Vary: Accept to the response so that caches keep the two versions apart.
Caching
A copy changes only when its page changes. A publisher may set a long Cache-Control on copies and rely on updated in the index to signal change. A publisher should set a short Cache-Control on the index itself, because it is what an agent checks first.
Build output
The reference build script walks the output folder, finds each .html file, extracts the main content, and writes the copy beside it. It looks for <main>, then <article>, then the largest block of text. A site generator plugin should not do this. It should write the copy from the Markdown source and skip the extraction entirely.
Security considerations
A file written for machines invites three abuses. Altdown is designed so that each one fails or costs more than it earns.
Cloaking
A publisher could put one thing on the page and another in the copy. The faithful copy rule forbids this, and the spot check detects it. The check is random, so a publisher cannot know which pages will be sampled. A publisher who cloaks one page in a hundred is likely to be caught, and the penalty is that the whole site falls back to HTML.
Prompt injection
A publisher could write text in the copy that tries to steer the model reading it. The faithful copy rule forbids text that is not on the page. The agent rules require that body text is treated as data. The two rules together mean that an injection must appear on the page, where a person can see it, and that a well-built agent ignores it anyway.
The same applies to the HTML page itself. A publisher must not hide instructions in the page that tell agents to read the copy, or to do anything else. White text on a white background is prompt injection whatever it says. A visible link informs. A hidden instruction tries to steer. Only the first is honest, and only the first conforms.
Impersonation
A third party could publish a copy that claims to stand for someone else's page. The round-trip rule stops this. The copy must be on the page's origin, and the page must link to it. A party who cannot edit the page cannot make it link anywhere.
Resource use
An index could list millions of entries, or a copy could be very large. An agent should cap the number of items it reads per index page and the number of bytes it reads per copy. The size_in_bytes and tokens hints exist so that an agent can decide before it fetches. An agent should not trust them absolutely. It should stop reading when its own limit is reached, whatever the hint said.
Stale copies
A publisher could forget to rebuild, leaving copies that no longer match their pages. The spot check catches this when the drift is large. The updated field and HTTP caching headers help an agent notice smaller drift. A publisher should run the build on every deploy, so that a copy can never be older than its page.
Private content
A build script that extracts content from rendered HTML will extract whatever is there. A publisher who renders pages with a logged-in session, or with draft content, could leak that content into a copy. A publisher should build copies from the same output that anonymous visitors receive, and from nothing else.
Extending Altdown
Altdown is small and should stay small. A publisher who needs more may add to it under one rule.
A front matter field not defined in this document must begin with x-. An agent must ignore any x- field it does not understand. A field without the prefix that is not defined here is an error, and an agent may ignore the whole copy.
x-reading-time: 7
x-section: engineeringIn the index, extensions follow the JSON Feed convention. A member not defined by JSON Feed or by this document must begin with an underscore. The _altdown object is reserved for this document. A publisher who needs a new member should put it in an object of its own, named for the publisher or the tool.
A later version of this document may promote a common x- field to a defined one. When it does, it will keep the old x- name valid for at least one version.
Comments
This section answers questions that came up while the format was being drafted.
Why not llms.txt? llms.txt is a description of a site, written by the site, found at a guessed path. Nothing checks it against the site, and few agents read it. Altdown is a copy of each page, announced by the page, and checkable against it. The two can coexist. They answer different questions.
Why not content negotiation? It works well where a server exists to do it. Most small sites have no server. A bucket cannot read an Accept header. Altdown works on a bucket, and a site with a server can add negotiation on top as described under Hosting notes.
Why JSON Feed and not a sitemap? A sitemap lists URLs and little else. JSON Feed carries per-item dates, attachments with sizes and media types, and pagination. Every field the index needs already exists there. Inventing a format to hold the same fields would help nobody.
Why five-word sequences? Shorter sequences match by accident. Longer ones fail on small edits. Five is where the two errors are both low. It is a starting point, not a law.
Can the copy hold more than the page? No. That is the point. A copy that said more than the page would be a claim about the page, and nobody could check it. See The faithful copy rule.
Does Altdown replace RSS? No. RSS tells a reader what is new. Altdown tells a reader what a page says. A site should have both.
What about pages that are mostly not text? A photo gallery, a map, or an interactive chart has little that Markdown can carry. A publisher may omit the copy for such pages, or include one that holds the captions and surrounding text. The copy should not pretend to be more than it is.
Who decides what the main content is? The publisher does. That is the second reason to use Altdown after cost. A heuristic that guesses the main content of a page is often wrong. The publisher knows.
Conformance checklist
A publisher or agent may use this list to check its work. Each line restates a rule from the body of this document. Where the two differ, the body wins.
Publisher
- Every copy is UTF-8 with no byte order mark.
- Every copy begins with front matter between
---lines. - Every copy has
altdown,canonical, andtitle. canonicalis an absolute URL on the same origin as the copy.- Every page with a copy has one
<link rel="alternate" type="text/markdown">in its<head>. - The link's
hrefresolves to the copy, on the same origin. - The body holds only text visible on the page, plus alt text and link targets.
- The body holds the main content and not the navigation, footer, or ads.
- No copy contains instructions to software.
- No page contains hidden text addressed to software.
- Unknown front matter fields begin with
x-. - If there is an index,
robots.txthas anAltdown:line pointing to it. - If there is an index, it is JSON Feed 1.1 with a top-level
_altdownmember. - If there is an index, every item has one attachment of type
text/markdown. - If there is an index, it is rebuilt on every deploy.
- On GitHub Pages,
.nojekyllexists at the root.
Agent
- Finds copies through the link element and never by guessing paths.
- Rejects a copy whose
canonicaldoes not match the page. - Rejects a copy on a different origin from the page.
- Falls back to HTML whenever a copy is rejected.
- Treats body text as data and never as instructions.
- Ignores unknown front matter fields.
- Accepts both
\nand\r\nline endings. - Does not reject a copy on media type alone.
- Respects
robots.txtwhen fetching copies and indexes. - Runs the spot check on sites it reads at scale, and documents its threshold.
- Treats an unspecified usage signal as unspecified, not as
yes. - Caps bytes per copy and items per index page.
Complete example
This example shows one page, its copy, and the index that lists it. The site is https://example.com/. The page is at /blog/static-sites/.
The page
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Why we went back to static sites</title>
<link rel="canonical" href="https://example.com/blog/static-sites/">
<link rel="alternate" type="application/rss+xml" href="/feed.xml" title="Example Blog">
<link rel="alternate" type="text/markdown" href="/blog/static-sites/index.md">
</head>
<body>
<nav><a href="/">Home</a> · <a href="/blog/">Blog</a> · <a href="/about/">About</a></nav>
<main>
<article>
<h1>Why we went back to static sites</h1>
<p>Last spring we moved our docs off a CMS. Six months later the pages load in
under a second and nobody has had to patch a plugin.</p>
<h2>What we gave up</h2>
<p>Comments, mostly. We moved them to a mailing list and have not missed them.</p>
</article>
</main>
<footer>
<a href="/blog/static-sites/index.md">View as Markdown</a> ·
© 2026 Example Blog
</footer>
</body>
</html>The copy
Served at https://example.com/blog/static-sites/index.md.
---
altdown: 1
canonical: https://example.com/blog/static-sites/
title: Why we went back to static sites
published: 2026-10-18T09:00:00Z
updated: 2026-10-20T14:00:00Z
lang: en
tokens: 62
tokenizer: cl100k_base
signals: search=yes, ai-input=yes, ai-train=no
generator: altdown-build 0.3
---
# Why we went back to static sites
Last spring we moved our docs off a CMS. Six months later the pages load in under a second and nobody has had to patch a plugin.
## What we gave up
Comments, mostly. We moved them to a mailing list and have not missed them.Note what is absent. The navigation, the footer, the copyright line, and the "View as Markdown" link are not in the copy. They are not the main content. Everything that is in the copy is on the page.
robots.txt
User-agent: *
Allow: /
Sitemap: https://example.com/sitemap.xml
Altdown: https://example.com/altdown.jsonThe index
Served at https://example.com/altdown.json.
{
"version": "https://jsonfeed.org/version/1.1",
"title": "Example Blog",
"home_page_url": "https://example.com/",
"feed_url": "https://example.com/altdown.json",
"description": "Notes on building for the web.",
"language": "en",
"_altdown": { "version": 1 },
"items": [
{
"id": "https://example.com/blog/static-sites/",
"url": "https://example.com/blog/static-sites/",
"title": "Why we went back to static sites",
"date_published": "2026-10-18T09:00:00Z",
"date_modified": "2026-10-20T14:00:00Z",
"attachments": [
{
"url": "https://example.com/blog/static-sites/index.md",
"mime_type": "text/markdown",
"size_in_bytes": 611,
"_altdown": { "tokens": 62, "signals": "search=yes, ai-input=yes, ai-train=no" }
}
]
},
{
"id": "https://example.com/about/",
"url": "https://example.com/about/",
"title": "About",
"date_modified": "2026-09-02T11:30:00Z",
"attachments": [
{
"url": "https://example.com/about/index.md",
"mime_type": "text/markdown",
"size_in_bytes": 402
}
]
}
]
}What an agent does with it
An agent given the page URL fetches the page, sees the link element, fetches the copy, confirms that canonical matches, and reads 62 tokens instead of the whole page.
An agent given the site fetches robots.txt, finds the index, reads two items, and fetches two copies. On its next visit it reads the index, sees that nothing is newer than 2026-10-20T14:00:00Z, and stops.
Roadmap
This document is a draft. It will change as people try it. Once it reaches 1.0 the format should be treated as frozen, in the way the RSS 2.0 specification was. Later revisions should clarify the text and should not add fields, directives, or rules. New work belongs in x- fields and _ members, or in a new format with a new name.
Three questions are open and may change the text before 1.0.
- The spot-check threshold of 0.90 has not been tested against real sites.
- The rule for metered pages (copy what an anonymous visitor sees) has not been tested against a real publisher.
- Where the specification should live is undecided. A plain repository with a spec page, as JSON Feed did, is the current plan.
References
- RSS 2.0 Specification, RSS Advisory Board. The model for this document.
- RSS Autodiscovery, RSS Advisory Board. The source of the link element pattern.
- JSON Feed Version 1.1. The format of the site index.
- CommonMark Spec. The Markdown dialect of the body.
- GitHub Flavored Markdown Spec. The permitted extensions.
- RFC 2119, Key words for use in RFCs to Indicate Requirement Levels.
- RFC 3339, Date and Time on the Internet: Timestamps.
- RFC 3986, Uniform Resource Identifier: Generic Syntax.
- RFC 6454, The Web Origin Concept.
- RFC 9309, Robots Exclusion Protocol.
- BCP 47, Tags for Identifying Languages.
License and authorship
This document was drafted from the post Altdown: RSS for Agents (October 28, 2026). It borrows its structure from the RSS 2.0 specification, which is published under the Creative Commons Attribution-ShareAlike license. This document should be published under the same license, so that anyone may copy it, build on it, and keep it open.