~/blygger.org/spec/0.3/2026-09-28/

The Blygger Protocol — Version 0.3

Status: DRAFT. This document specifies protocol version 0.3 at conformance Level 2 — the publish side, the subscribe side, and the cross-client constructs that let one blyg quote, answer, and descend from another. Every spec version numbered below 1.0 is a working draft: the reference-implementation development phase is a testing phase for the protocol itself, and the spec changes in response to what testing discovers. Version 1.0 will be the first version its authors stand behind as stable and publish to a larger audience; until then, users of this spec and builders of implementations should assume no promises — including of the wire surface. Breaking changes are permitted pre-1.0 but must bump the manifest version and be recorded in the project devlog.

Each protocol version has its own standalone-complete document, and the highest-numbered document is the living one. This document supersedes version 0.2: it carries the full 0.2 text forward, revised, and adds the cross-client layer — transclusion across origins and thread nesting (§10), stubs (§10.6), lineage (§5.6), the page field (§5.8), and Webmention with structural verification (§15). Version 0.2 stops receiving revisions when this document is published, and stays citable with its snapshots intact.

What this document does not add, deliberately. Every normative construct below was built and exercised between two independent deployments before it was written down here — including cited (§5.9), [[id]] (§10.1) and generator_url (§6.1), which were ruled on 2026-09-28, built the same day, and promoted from §16 into the normative text once both live nodes had exercised them across origins. Constructs that have been decided but not yet built are described in §16 with their ruled shapes and enter the normative text in a later revision, once a client emits them. That sequencing is the project's rule, not an oversight.

Which version to implement. Implement against the highest-numbered document published at https://blygger.org/spec/ — that is the living one, and this is it. The three places a version of this text can be found are not interchangeable:

What is not the spec: the plan documents (docs/v0.3-plan.md and its siblings) and the reference client's source. Both have carried wire shapes before this text did — the 0.3 constructs shipped in the reference client twelve days before this document existed — and several independent clients were built from them in that window. That was a lag in this project's process, not a stable arrangement. From this version on, a construct that has been ruled but not yet built appears in §16 of the living document with its exact shape and an explicit "not yet normative" label, so that the spec is always the first place a shape is visible.

The risk of the living text is that it moves: pre-1.0, every version is a draft and no wire promise is made (§3.1's version key is informative for the same reason). The risk of a superseded text is only that it is incomplete: levels are strict supersets and every 0.2 document is a valid 0.3 document, so a client built to 0.2 remains conformant and simply does not emit or understand the 0.3 constructs. Declare what you implement in the blyg key, and readers will treat the rest under the ignore rules.

The key words MUST, MUST NOT, SHOULD, SHOULD NOT, RECOMMENDED, MAY are to be interpreted as described in RFC 2119.


1. Introduction (non-normative)

Blygger is a decentralized public writing medium built on static files and RSS. A blyg is a directory of files — mounted anywhere on the publisher's own domain, at a path (conventionally /blyg/) or at the domain root — containing versioned items of writing, a manifest, an archive index, and an RSS feed. Anything that can serve files can serve a blyg; anything that can read RSS can follow one.

Four design invariants shape everything below:

  1. The protocol is a static file contract. It governs only the published artifact — the page. How the publisher composes, edits, imports, or generates content (the studio) is entirely out of scope. This includes how an authoring tool writes into a blyg: the protocol specifies no write surface, at this version or any version foreseen (§16).
  2. Every blyg feed is a valid RSS 2.0 feed whose items carry self-contained HTML. A plain RSS reader always sees a sensible microblog.
  3. AI is never in the protocol. Generation, if any, happens in the studio at authoring time; the page publishes output plus provenance (§5.7). Readers need no models or keys. This constrains the wire and the reader side, never the writer: a blyg written, maintained, or entirely operated by a software agent is a blyg like any other, and the protocol cannot and does not tell.
  4. Identity is never in the protocol. The only authenticated entity is the publishing client at its domain (the origin). DNS is the namespace.

The protocol has two planes. The state plane — canonical item files and the archive index — is ground truth and losslessly complete. The notification plane — the RSS feed — is a lossy, windowed signal that something changed. Readers reconstruct state from the state plane; they never treat the feed as authoritative.

Version 0.2 specified the reading path built on that split: a reader handed any URL resolves it (§12) to a blyg origin or a legacy feed, subscribes, and polls; the archive index heals every gap (§13). Subscription is deliberately invisible: no follow object, no follower list, and the only public trace of anyone's reading is the curated blogroll they choose to publish (§11).

Version 0.3 makes threads cross-client. A thread may quote any item a reader has imported, from any origin, by the same ![[id]] directive it uses for its own fragments — and the quote is always the reader's local snapshot, never a live fetch (§10). A thread may declare itself a stub: a response to exactly one target (§10.6). An item may declare lineage from a pinned version of another item (§5.6). And an origin that has been quoted, stubbed, or forked can find out, reliably and verifiably, without subscribing to the quoter: Webmention with structural verification (§15), the notification half of the discovery model whose static half is the blogroll. None of this adds a reply primitive. This remains a network of soapboxes: public speech that cites, and citations that can be checked.

2. Terminology

3. Conformance levels

Level Meaning
L0 Any plain RSS feed, grandfathered via a reader-side wrapper (summary fragment + link, §13.6). No blyg constructs.
L1 The single-origin protocol, as specified at version 0.2 and carried forward here unchanged in substance. Publish side: stable ids, versioned items, canonical item files, manifest, archive index, rollup semantics, withdrawal, pins, threads and same-origin transclusion, generation provenance. Subscribe side: resolution, importer conformance, lossless recovery, the optional blogroll.
L2 This specification. Everything in L1 plus the cross-client constructs: transclusion across origins and thread nesting (§10), stubs (§10.6), lineage (§5.6), the page field (§5.8), and Webmention (§15).
L3 (future) Encrypted/permissioned content.

Levels are strict supersets. A conforming reader at any level MUST ignore constructs it does not understand rather than reject the document containing them (this is what lets levels and versions advance without breaking anyone).

A publisher conforms at L1 by serving the surfaces in §4 with the semantics in §§5–11, and at L2 by additionally following §10's cross-client rules and §5.6, §5.8 and §10.6 for any lineage, page, or stub_of it emits. Every L2 construct is additive and optional to emit: a valid 0.2 document is a valid 0.3 document unchanged, and a publisher that never quotes another origin, never stubs, never forks, and receives no mentions is fully conformant at L2 by doing nothing new. Webmention in particular is optional at every level; a static-only deployment advertises no endpoint and remains conformant (§15.7). A reader conforms at L1 by following the rules in §§12–13, and at L2 by additionally honouring the cross-origin provenance rules of §10.3.

3.1 The protocol version key

Manifests and item documents carry a "blyg" key naming the spec version their publisher implements (a deployment serving the 0.3 surfaces emits "blyg": "0.3"). The key is informative, not a negotiation: readers MUST accept any 0.x value and apply the standing ignore-unknown rules (§13.1) to whatever they find. Vocabulary evolves inside one namespace under those ignore rules; a breaking change, if one ever happens, gets a major version bump and its own migration story — never this mechanism.

3.2 The level, generator and generator_url keys are informative

The manifest's level (§6.1) is the conformance level the publisher claims, generator is the name and version of the software that produced the blyg, and generator_url is where that software's source or home page lives. All three are self-descriptions. Readers MUST NOT gate any behaviour on any of them — not parsing, not feature selection, not trust. A reader decides what a document contains by reading the document, under the ignore-unknown rules; a reader that would render, verify, or import differently because of a generator string is treating an unverified label as a capability, and would break the moment a client renamed itself.

generator nonetheless carries real weight, which is why publishers SHOULD emit it in the form name/version (e.g. blygger-studio/0.4.0): the manifest is a file the protocol requires to be public, so generator is the only census of the ecosystem that exists without anyone registering anything. A client with no other public trace is counted by this key alone. It is the client's name, never the protocol's: a client rename is not a protocol change and MUST NOT be read as one.

4. The publication surface

A blyg is the following file tree under its origin. The origin's location is the publisher's free choice — a domain root (https://example.com/), any path (https://example.com/blyg/, https://example.com/notes/b/), or a subdomain. The surface is strictly origin-relative: no protocol construct may assume any particular path component, and readers MUST NOT infer anything from the mount path. The file names within the surface (blyg.json, feed.xml, items/…) are protocol-fixed and MUST NOT vary per deployment — a known manifest filename at an arbitrary origin is what keeps free mounting discoverable (a reader handed any base URL fetches blyg.json relative to it). /blyg/ is the reference client's default mount and the convention used in examples throughout this document; it carries no protocol meaning.

Publishers MUST serve:

Path (relative to origin) Content Spec
blyg.json Manifest §6
feed.xml RSS 2.0 feed §7
items/index.json Archive index §6.2
items/{id}.json Canonical item document §5
items/{id}/v{n}.json Pinned version document (only for pinned versions) §8
media/… Media objects referenced by items §5.4

Publishers MAY additionally serve:

Path (relative to origin) Content Spec
blogroll.opml Curated blogroll (OPTIONAL) §11
(the URL named by the manifest's webmention key) Webmention endpoint (OPTIONAL; the protocol's only dynamic surface) §15

Publishers SHOULD additionally serve human-readable HTML (a feed page, item permalink pages); their form is presentation, not protocol, except where noted (§5.8, §8.4, §10.5, §15.1). The reference client's permalink paths — f/{id}/ for fragments, t/{id}/ for threads — are a convention, not a requirement: an item's permalink is whatever its document's page field says (§5.8).

Requirements:

5. The item document — items/{id}.json

Ground truth for one item. Example (a fragment):

{
  "blyg": "0.3",
  "id": "7c9wk2mhq0v3xj8tn5rzfd41bg",
  "kind": "fragment",
  "origin": "https://example.com/blyg/",
  "page": "f/7c9wk2mhq0v3xj8tn5rzfd41bg/",
  "author": { "name": "Venkatesh Rao", "url": "https://example.com/blyg/" },
  "created": "2026-07-17T18:00:00Z",
  "updated": "2026-07-18T09:30:00Z",
  "version": 3,
  "content_md": "Markdown source of the *latest* version.",
  "content_html": "<p>Markdown source of the <em>latest</em> version.</p>",
  "content_hash": "sha256:9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
  "media": [
    { "url": "media/x7f2.png", "mime": "image/png", "alt": "a diagram" }
  ],
  "changelog": [
    { "version": 1, "at": "2026-07-17T18:00:00Z", "note": null },
    { "version": 2, "at": "2026-07-17T21:12:00Z", "note": "typo", "pinned": true },
    { "version": 3, "at": "2026-07-18T09:30:00Z", "note": "sharpened the claim" }
  ]
}

Threads additionally carry transclusions (§10.3) and MAY carry stub_of (§10.6). Any item MAY carry forked_from (§5.6) and generated (§5.7).

5.1 Identity

5.2 Versioning and rollup presentation

5.3 Kinds

5.4 Media

5.5 The author field

author is OPTIONAL, per-item, client-asserted, and opaque:

5.6 Lineage — forked_from

(Reserved at 0.1 and 0.2; defined at 0.3.) An item MAY carry a top-level forked_from: a reference (§5.9) to the pinned version of another item from which this item's first draft was copied.

"forked_from": { "origin": "https://blyg.protocol-institute.org/",
                 "id": "1vgtgz0gq5b2c9k7d3m8r4n6xy", "version": 2 }

Rules:

  1. origin is REQUIRED, even when it is the publisher's own — a citation is absolute. (The 0.1 reservation's two-field form {id, version} is amended additively; nothing ever emitted it.)
  2. The referenced version MUST be pinned at its origin, i.e. fetchable as {origin}items/{id}/v{n}.json. A pin is the only version anyone can promise a lineage still points at (§8.1). Publishers SHOULD check this at publish time with one conditional fetch, and SHOULD refuse to publish on evidence that the promise is not kept — a 4xx, or a 200 that is not that pinned version. A network failure, timeout, or 5xx is evidence of nothing; refusing to publish an author's own words because a stranger's host is down would hand third parties a veto, so publishers SHOULD proceed with a warning instead.
  3. Lineage is fixed when the item is created and is immutable: it never changes across versions, and a client MUST NOT offer a way to add, retarget, or clear it after the fact. An item either came from somewhere or it did not. This immutability is what lets pinned documents (§8) and withdrawal endcaps (§9) carry it without any risk of drift.
  4. Lineage survives withdrawal: the endcap keeps forked_from (§9). Where an item came from is not the work being taken back.
  5. Lineage across origins is a remote reference and SHOULD send a mention (§15.2), whose relation is fork.
  6. A fork is a copy, not a transclusion: the forked content becomes the new item's own content_md, with no wrapper and no transclusions entry. The lineage marker is the only trace. Readers MUST NOT infer that a fork still resembles its source.

5.7 Generation provenance — the generated array

Invariant 3 stands: AI is never in the protocol. Generation, if any, is a studio act at authoring time, and the published content_md is ordinary markdown — no markers, no instructions, no authoring grammar of any kind reaches the wire. The hash covers exactly what readers read. What the wire gains is generation's provenance shadow: a publisher whose version contains machine-generated prose SHOULD disclose it via an OPTIONAL top-level "generated" array, parallel to transclusions, one entry per generated span in document order:

"generated": [
  { "sources": [ { "id": "7c9wk2mhq0v3xj8tn5rzfd41bg", "version": 3 } ],
    "model": "claude-opus-5",
    "at": "2026-08-10T18:00:00Z" }
]

Rules:

  1. sources names the exact published versions of this origin's items whose content was drawn on by the generator for that span — the same disclosure whether the generator was handed them by an author or retrieved them itself from the archive. It MAY be empty, meaning no sources declared: pure instructed generation, or generation whose sources the publisher cannot name. model and at are RECOMMENDED. A span MAY be the whole body. At 0.3 a source is always own-origin and carries no origin member; generation from another origin's items is not expressible on the wire at this version and is deferred (§16), where its shape is already decided: when it arrives, sources[] takes the reference shape of §5.9 with origin omitted for own-origin sources, exactly as transclusions[] does.
  2. The array MUST NOT carry instruction text or any other pre-generation authoring state — instructions are studio-private, permanently. Disclosure covers what was produced and from what, never how it was asked for.
  3. A source is not a transclusion. Drawn-upon material is woven into new prose, not quoted: source references never appear in transclusions and never produce a blyg-transclusion blockquote. Verbatim, visible, cited quotation remains exclusively transclusion (§10). The two disclosures are disjoint by construction.
  4. The key is omitted entirely when the version involved no generation, and a withdrawal endcap (§9) never carries it. Readers ignore the unknown key (§13.1) — the construct is fully backward compatible.
  5. Pinned version files (§8) carry the pinned version's generated array, like transclusions — a citation includes its provenance.
  6. Like all provenance in this protocol, generated is self-asserted and unverifiable — the same honesty stance as timestamps and author. The protocol does not pretend to verify what it cannot.
  7. Where the generation ran is not part of the claim. generated[] says this prose is machine-generated, and, when known, by what model, from what, and when. Text generated outside the publishing studio and brought into an item — pasted from another tool, produced by an external agent — is disclosed the same way: an entry with sources empty or naming what is known, model and at if known. There is no marker for "generated elsewhere", because no reader could verify it and none would act on it. A publisher that knows a span is machine-generated SHOULD disclose it regardless of who ran the model; the construct exists so that disclosure is always possible.

Publishers additionally disclose generated spans in the rendered HTML: the renderer MUST wrap each generated span in content_html as <span class="blyg-tk-gen">…</span> (inline output) or <div class="blyg-tk-gen">…</div> (block output). The class name blyg-tk-gen is a permanent wire token — it is baked into published content_html, like blyg-transclusion (§10.2), and cannot be renamed. No data attributes are required: span-level source mapping is deliberately not promised (the JSON provenance is version-level and robust; span-level claims would be brittle across the author's post-generation edits). Styling the class is presentation, any client's free choice — the reference client deliberately leaves it unstyled, because a visible tint would present self-asserted provenance as a verified authorship badge, a claim the protocol refuses to make.

5.8 The page field

(New in 0.3.) An item document MAY carry "page": the origin-relative URL of the item's human-readable permalink, resolved against the origin ("page": "t/7c9wk2mhq0v3xj8tn5rzfd41bg/").

5.9 The reference shape

Three constructs in this version name a version of an item at an origin — forked_from (§5.6), stub_of (§10.6), and transclusions[] (§10.3) — and all three use one shape:

{ "origin": "https://blyg.protocol-institute.org/",
  "id": "1vgtgz0gq5b2c9k7d3m8r4n6xy",
  "version": 1 }

The human half of a citation — cited (new in 0.3). A reference names a version and carries no words, so a reader of a document whose target has since disappeared would hold an identity and nothing to show. A reference MAY therefore carry an OPTIONAL cited object: the label the citing publisher saw when it made the reference.

"stub_of": { "origin": "https://blyg.protocol-institute.org/",
             "id": "1vgtgz0gq5b2c9k7d3m8r4n6xy", "version": 1,
             "cited": { "source": "Protocol Institute Blyg",
                        "author": "Editor",
                        "excerpt": "Stigmergy is what a protocol looks like from inside…",
                        "url": "https://blyg.protocol-institute.org/f/1vgtgz0gq5b2c9k7d3m8r4n6xy/",
                        "retrieved": "2026-09-16T20:11:00Z" } }

Why it is on the wire at all: a transclusion already bakes the target's entire content into the quoter's document, self-asserted, so a label is strictly weaker than what §10 permits; and a citation is as-of-retrieval by nature, so the citing publisher's frozen label is more faithful to what was cited than a reader's later lookup of the live target — not a fallback for when the link dies.

6. Manifest and archive index

6.1 Manifest — blyg.json

{
  "blyg": "0.3",
  "level": 2,
  "generator": "blygger-studio/0.6.0",
  "generator_url": "https://github.com/blygger/blygger-studio",
  "site": "https://example.com/blyg/",
  "title": "Venkat's blyg",
  "author": { "name": "Venkatesh Rao", "bio": "…", "avatar": "media/avatar.png",
              "links": [{ "label": "Home", "url": "https://venkateshrao.com" }] },
  "feed": "feed.xml",
  "items": "items/index.json",
  "blogroll": "blogroll.opml",
  "webmention": "webmention",
  "updated": "2026-07-18T09:30:00Z"
}

The manifest author is the publication identity — a person, a collective, a masthead: the imprint, where per-item author (§5.5) is the byline. When an item carries no author, no assertion is made; readers fall back to this site-level identity for display.

The blogroll key is OPTIONAL: an origin-relative path, canonically "blogroll.opml", present only when the publisher serves a non-empty blogroll (§11). The webmention key (new in 0.3) is OPTIONAL: the URL of the publisher's Webmention endpoint, origin-relative allowed, present only when the publisher receives mentions (§15.1); a static export omits it. The site value is self-asserted and display-advisory only; it never establishes identity (§12.2). level and generator are informative (§3.2), and so is generator_url (new in 0.3): OPTIONAL, one absolute URL to the client software's canonical source repository or home page, baked in by the client's author beside generator (the precedent is Atom's generator uri). Publishers SHOULD emit it; readers MUST NOT gate on it; its absence means only that nothing was stated. It is a SHOULD and will never be a MUST — a required field that readers may not act on would be a conformance rule serving a directory's convenience, and most existing clients would fail it for a reason unrelated to publishing. There is deliberately no maintained/unmaintained declaration: the software that would need to say "I am unmaintained" is exactly the software nobody is updating, so maintenance status is a fact for directories to observe, never for the wire to assert. A registry MAY require a client source as a condition of listing; that is its business, not conformance.

6.2 Archive index — items/index.json

Every item ever published — including withdrawn items — with no window, ordered by updated descending:

{
  "updated": "2026-07-18T09:30:00Z",
  "items": [
    { "id": "7c9wk2…", "kind": "fragment", "created": "…", "updated": "…", "version": 3 }
  ]
}

The index is what makes new and lagging subscribers lossless: any reader can enumerate it and fetch item documents, regardless of how much feed window it missed. On the subscribe side it is the reconciliation surface (§13.2) — complete by construction, so diffing it against local state is total recovery.

7. The feed — feed.xml

RSS 2.0 with the blyg: namespace (https://blygger.org/ns/0.1):

<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:blyg="https://blygger.org/ns/0.1">
  <channel>
    <title>Venkat's blyg</title>
    <link>https://example.com/blyg/</link>
    <description>…</description>
    <lastBuildDate>Sat, 18 Jul 2026 09:30:00 GMT</lastBuildDate>
    <blyg:level>2</blyg:level>
    <blyg:manifest>https://example.com/blyg/blyg.json</blyg:manifest>
    <item>
      <guid isPermaLink="false">blyg:7c9wk2mhq0v3xj8tn5rzfd41bg:v3</guid>
      <link>https://example.com/blyg/f/7c9wk2mhq0v3xj8tn5rzfd41bg/</link>
      <title>Sharpened the claim — Markdown source of the latest…</title>
      <description><![CDATA[<p>rendered HTML of latest version</p>]]></description>
      <pubDate>Sat, 18 Jul 2026 09:30:00 GMT</pubDate>
      <blyg:id>7c9wk2mhq0v3xj8tn5rzfd41bg</blyg:id>
      <blyg:kind>fragment</blyg:kind>
      <blyg:version>3</blyg:version>
      <blyg:created>2026-07-17T18:00:00Z</blyg:created>
      <blyg:item>https://example.com/blyg/items/7c9wk2mhq0v3xj8tn5rzfd41bg.json</blyg:item>
    </item>
  </channel>
</rss>

Rules:

8. Pins — items/{id}/v{n}.json

A pin is the publisher's irrevocable hosting promise for one specific published version: this exact content stays fetchable at this URL forever — surviving later edits and withdrawal. Only pinned versions get per-version files; unpinned history stays withheld. (Analogy: the item id plays the IPNS-name role — a mutable pointer; a pinned version plays the CID role — immutable and citable. Blygger inverts IPFS's default because it is an authoring medium: mutable by default, immutable by explicit act.)

{
  "blyg": "0.3",
  "id": "7c9wk2mhq0v3xj8tn5rzfd41bg",
  "kind": "fragment",
  "version": 2,
  "at": "2026-07-17T21:12:00Z",
  "note": "typo",
  "pinned": true,
  "origin": "https://example.com/blyg/",
  "author": { "name": "Venkatesh Rao", "url": "https://example.com/blyg/" },
  "content_md": "…that version's markdown…",
  "content_html": "<p>…that version's HTML…</p>",
  "content_hash": "sha256:…"
}

Rules:

  1. Irrevocable. Once pinned, the file MUST return 200 forever — including after the item is withdrawn. Unpinned versions and unknown ids: 404.
  2. Any published version with content MAY be pinned, including retroactively (exposing withheld history is the publisher's right). Withdrawal endcaps (§9) MUST NOT be pinned — there is nothing to cite.
  3. Pinning does not freeze the id: the live stream continues under the same identity; the pin guarantees only the citation.
  4. Media referenced by any pinned version MUST be retained forever. Pinned documents carry no media array; their content_html references media directly, relying on media immutability (§5.4).
  5. Threads are pinnable like fragments: a pinned thread version serves its publish-time content_html (baked snapshots included, cross-origin ones too) and its transclusions provenance, with "kind": "thread". A pinned version that involved generation carries its generated array (§5.7). A pin carries its own citations: a pinned stub version carries the stub_of it was published with (§10.6), and a pinned version of a forked item carries forked_from (§5.6) — the frozen artifact says what it answered and where it came from, at the version it said so.
  6. author rides outside the pin promise. content_hash covers content_md only, so the pin's immutability guarantee covers content, never the author bytes. A pinned file SHOULD carry the assertion as published with that version, but both serve-time and publish-time assertion strategies are conformant. Verifiable authorship, if wanted, is a signature inside an authorspace grammar (§5.5), not a protocol feature.
  7. A pin is the only thing lineage may point at (§5.6), and the only thing a reader may retain past withdrawal (§13.4). Pins are therefore the protocol's whole answer to "how do I make sure this stays citable": ask the author to pin it.

8.4 Historical versions: pinned-only, JSON promised, HTML optional

No route ever serves an unpinned older version, in any representation — a general historical route would gut withheld-unless-pinned. The same rule bounds what a public page's version display may offer: every version it exposes — as a link, or presented in place — MUST be one the origin already promises forever, i.e. the live version and pinned versions, nothing else. A version display MUST NOT offer, imply, or hint at access to unpinned history.

Within that bound, presentation is the client's. The floor is discrete pin citations (e.g. "v6 · pinned: v2, v4"); a page MAY additionally present the pinned artifacts themselves in place (the reference client pages among them with each shown version visibly marked frozen). The design intent to honor, whatever the presentation: pins are a sequence of frozen citable artifacts of one identity, not pages of one document. Keep each pinned version discrete, visibly frozen, and individually citable; a presentation cannot reconstruct a continuous edit history, because the bytes for one are never served.

The pin's promise is the JSON file (§8.1). Additionally, a publisher MAY serve a rendered page for each pinned version at the item's permalink path plus a version segment:

GET {origin}{page}v{n}/     e.g. {origin}f/{id}/v{n}/ or {origin}t/{id}/v{n}/

Rules, for publishers that serve these pages:

  1. Gated exactly like the JSON file: 404 unless that exact version is pinned; 200 forever once it is, surviving withdrawal. The route exposes only bytes already promised forever — never unpinned history.
  2. The page's content is that version's publish-time content_html, verbatim (baked transclusion snapshots included for threads). Page chrome (banner, links) is presentation and may evolve; the content is what is promised.
  3. The page SHOULD carry rel="canonical" pointing at the live permalink, SHOULD be visibly marked as a frozen snapshot, and SHOULD link its v{n}.json twin — the page is the human citation, the JSON the machine citation.
  4. Pinned pages are not publish events: they MUST NOT appear in feed.xml, the archive index, or items/index.json, and they add no manifest vocabulary. Discovery is the live page's pin citations.
  5. Readers MUST NOT require these pages — the subscribe side works entirely from the JSON surfaces.

9. Withdrawal

Withdrawing is the only exit for a published item. There is no delete: no permanent-delete state exists in the protocol. (Discarding a never-published draft is a hard delete — nothing was ever public.)

Withdrawing publishes a permanent endcap: a version bump with content_md = "", content_html = "", media = [], an optional note, "kind": "withdrawn", updated set, and the changelog retained plus the endcap entry. For threads, the endcap also empties transclusions to [] (§10.4) and omits stub_of (§10.6); an endcap never carries a generated array (§5.7). The endcap keeps page and forked_from: the endcap empties what the item said, and where an item lives and where it came from are not what it said.

10. Threads and transclusion

A thread is an item with "kind": "thread": long-form markdown that transcludes other items. Threads use the same identity, versioning, changelog, withdrawal, and pin machinery as fragments. At 0.2, transclusion was same-origin and fragments-only. At 0.3 both restrictions are lifted: a thread may transclude fragments or threads, of its own origin or of any origin its publisher has imported — and the rule that makes this safe is that the quote is always the publisher's local snapshot, never a live fetch.

10.1 Grammar (permanent protocol surface)

10.2 Publish-time resolution

Resolution happens in the studio at publish time; readers never resolve anything.

Resolution order. Each directive MUST resolve, in this order, to:

  1. a local, currently-published item with that id — fragment or thread;
  2. otherwise an imported item with that id, from any subscribed origin, whose local state is current — or withdrawn with a pin-retained snapshot (§13.4 retention is exactly what makes such a snapshot still quotable) — and which is a blyg item, not an L0 wrapper (§13.6: an L0 row is a wrapper around someone's RSS summary, not their item);
  3. otherwise a publish error. Drafts, unknown ids, withdrawn items with nothing retained, and L0 rows are publish errors. More than one imported match is a publish error, never a guess — the directive names an identity, and the publisher does not pick an origin on the author's behalf.

The local snapshot is what gets baked — never a live fetch. Resolution snapshots the target's latest version as the publisher holds it: its rendered HTML is baked into the thread's content_html, wrapped as

<blockquote class="blyg-transclusion"
            data-blyg-id="{id}"
            data-blyg-version="{n}">…item html…</blockquote>

for own-origin sources, and for remote sources additionally

<blockquote class="blyg-transclusion"
            data-blyg-id="{id}"
            data-blyg-version="{n}"
            data-blyg-origin="https://blyg.protocol-institute.org/">…item html…</blockquote>

data-blyg-origin appears only for remote sources, so a baked own-origin quote is byte-identical to its 0.2 form. The wrapper is a bare blockquote plus data attributes — no link inside; any provenance link or byline shown on an HTML page is presentation, not part of the published content_html (a byline, if shown, is the source's own author passed through, §5.5). The class name blyg-transclusion is a permanent wire token, baked into published content_html; its styling is presentation.

Consequences of the snapshot rule, all deliberate:

content_md keeps the directives — it is the authoring source of truth. Republishing a thread re-resolves every directive to the then-current local snapshot. A source that has since been withdrawn at its origin, and not pin-retained, is a publish error on republish — exactly as a withdrawn local fragment is — and the author removes the directive or leaves the thread at its current version. The protocol does not offer "freeze this quote at the withdrawn version": that would be the first place withdrawal failed to roll to null, and it is deliberately not offered here.

10.3 Provenance

Thread item documents carry a top-level "transclusions" array, in directive order, of references (§5.9) naming the exact versions baked into this thread version:

"transclusions": [
  { "id": "7c9wk2mhq0v3xj8tn5rzfd41bg", "version": 3 },
  { "id": "1vgtgz0gq5b2c9k7d3m8r4n6xy", "version": 1,
    "origin": "https://blyg.protocol-institute.org/" }
]

10.4 Snapshot independence

The rule that makes network cycles harmless, applied at every origin:

10.5 Feed and presentation

Feed entries for threads carry the full baked self-contained HTML (automatic, given the snapshot rule). Threads have no length cap. How an HTML feed page excerpts threads is presentation, not protocol.

10.6 Stubs — stub_of

(New in 0.3.) A stub is a thread that declares itself a response to exactly one target. Threads only — the frozen v0 concept's own framing ("stubs are threads, so a stack always looks like a link to a local thread"), and what makes stubs stackable: threads can be transcluded, so a stub of a stub is nesting, not a new construct.

A thread document MAY carry a top-level "stub_of", in one of two shapes:

"stub_of": { "origin": "https://blyg.protocol-institute.org/",
             "id": "1vgtgz0gq5b2c9k7d3m8r4n6xy", "version": 1 }

for a blyg target — a reference (§5.9), origin REQUIRED even when it is the publisher's own, because a citation is absolute — or

"stub_of": { "url": "https://simonwillison.net/2026/Sep/10/some-post/" }

for a plain-web target: an L0 subscription's entry, or anything with a URL.

Rules:

  1. Exactly one target. A stub responds to one thing. Other transclusions in the body are quotes, not additional targets. A response to two things is two stubs.
  2. The body is the author's. The protocol never requires the body to transclude the target. stub_of is the machine-readable response marker, and readers rely on it, never on body inspection. A stub whose body contains no quote of its target is a response by link, which is legitimate and verifies as a stub (§15.4). (The reference client's stub action always starts the body with ![[id]] for a blyg target, because a stub without the quote is not a stub in the medium's own aesthetic; an author who deletes the directive has still published a stub.)
  3. Version agreement. At publish, if the body transcludes the stub's target, stub_of.version MUST equal the version actually baked (§10.3); otherwise it keeps the value set when the stub was created — what the author saw. The two can never disagree on a published document.
  4. stub_of lives on versions, like transclusions: a pinned version of a stub carries its own citation (§8), the live document reflects the latest, and a withdrawn stub's endcap omits it (§9) — which is precisely what makes a withdrawn stub stop verifying as a stub.
  5. A blyg-target stub is a remote reference when its origin is not the publisher's own, and SHOULD send a mention (§15.2), whose relation is stub. A {url} stub SHOULD send an ordinary W3C Webmention to that URL if the target advertises an endpoint; most will not.
  6. No feed vocabulary (§7). A stub is a thread and appears as one.
  7. Stubs are not coupled to any studio construct — not to hoppers, not to subscriptions. The marker is the whole of the protocol's knowledge that a response happened.

What a stub is not: a reply. There is no thread of replies, no conversation object, no notification to anyone but the target's origin, and nothing a target can do about being stubbed except read it. The stub is on the stubber's soapbox, under the stubber's identity, in the stubber's feed.

A stub is a response, and a stub emitted without one is a misuse. An aggregator, script, or agent that emits a stub for every item some set of origins publishes — with nothing to say about any of them — is not responding: it is re-emitting other people's content through a construct whose cost is supposed to be editorial work (§13.5), and it fills every target's response signal with non-responses (§15.5). The honest shape for "one place to follow a group" is a blogroll plus curation (§11, §13.5). An author, human or otherwise, that reads each item and answers it under its own byline is stubbing legitimately, however many stubs that is.

11. The blogroll — blogroll.opml

(OPTIONAL at every level.) The blogroll is the protocol's static discovery plane: a curated, published list of subscriptions the publisher chose to show. It is a publishing act, not an export — the outbound half of the public graph. From 0.3 the inbound half exists too, and is dynamic: verified mentions (§15).

<?xml version="1.0" encoding="UTF-8"?>
<opml version="2.0">
  <head><title>Venkat's blogroll</title></head>
  <body>
    <outline type="rss" text="Interconnected"
             xmlUrl="https://interconnected.org/home/feed"
             htmlUrl="https://interconnected.org/home/" />
    <outline type="rss" text="Another blyg"
             xmlUrl="https://example.org/blyg/feed.xml"
             htmlUrl="https://example.org/blyg/" />
  </body>
</opml>

12. Resolution — from URL to subscription

Resolution turns a URL found out-of-band — a link someone shared, a blogroll entry, an address bar — into a subscription target. It is deterministic, ordered, and bounded (at most 6 fetches), so that any two conforming readers handed the same URL arrive at the same subscription identity. Readers that subscribe MUST implement the following procedure in this order. Output: a blyg subscription { origin, manifest }, a legacy feed subscription { feed_url } (L0), or failure with the list of URLs tried.

12.1 The algorithm

  1. Normalize. Parse the URL; strip query and fragment. If the path does not end in /, append /. Call this the candidate origin. HTTPS is expected; readers MAY permit http: (local development) but SHOULD warn.
  2. Direct probe. GET {candidate}blyg.json. If the body parses as a manifest — a JSON object with a "blyg" version key — the URL is resolved as a blyg. Do not trust Content-Type (static hosts misreport); parse-success is the test.
  3. Feed upgrade. If the input URL itself returns XML that parses as RSS or Atom: if the channel carries <blyg:manifest> (§7), fetch that manifest → resolved as a blyg. Otherwise hold the feed as the L0 candidate and continue — a later step may still find a manifest.
  4. rel probe. If the input returned HTML, look for <link rel="blyg" href="…">. The href is the origin base URL — not the manifest file; the manifest filename is protocol-fixed, so readers append blyg.json. Resolve the href against the document URL and probe as in step 2. One hop only: a rel target's own HTML is never scanned — no recursive discovery, no loops. Readers MUST support the HTML <link> element here; supporting an equivalent HTTP Link header is OPTIONAL. (Publishers whose blyg mounts away from their front page SHOULD emit this link on pages they expect to be shared.)
  5. Conventional-mount probes. At the input's scheme+host root, probe /blyg/blyg.json, then /blyg.json, skipping any URL already probed. This is a courtesy fallback only — publishers MUST NOT rely on it. The mount is free (§4); these probes exist because /blyg/ is the reference default and root mounts are first-class.
  6. RSS fallback. If no manifest was found: the L0 candidate from step 3, or standard RSS autodiscovery on the input's HTML (rel="alternate" type="application/rss+xml" or the Atom equivalent), resolves the URL as a legacy L0 feed (§13.6). Otherwise resolution fails, and the reader SHOULD report the trail of URLs tried.

12.2 Subscription identity

12.3 Polling etiquette

13. Reader conformance — the import side

A conforming reader at L1 follows the ground rules in §13.1; a reader that maintains subscriptions additionally follows §§13.2–13.7.

13.1 Ground rules

  1. MUST treat item documents as ground truth and the feed as a lossy signal.
  2. MUST roll up by blyg:id: highest version wins; ties are broken by updated. Timestamps are self-asserted by origins; ordering across origins is the reader's own policy (§13.7).
  3. MUST treat a withdrawal endcap as roll-up-to-null (§13.4). A later version under the same id is the item returning.
  4. MUST ignore unknown kinds, unknown JSON members, unknown blyg:* XML elements, and reserved constructs, without rejecting the containing document.
  5. MUST NOT reject an item over its author contents, and MUST NOT treat equal author values from different origins as the same entity (§5.5).
  6. SHOULD backfill from items/index.json when the feed window has been missed; a reader offline for any duration recovers losslessly.
  7. MUST scope everything it learns to the origin: ids, authors, and trust do not transfer across origins. A reference's origin (§5.9) is the scope of the id it carries.
  8. MUST NOT gate any behaviour on generator or level (§3.2).

13.2 The reconciliation model

The archive index is the reconciliation surface; the feed is a cheap trigger. items/index.json is complete by construction (§6.2), so diffing it against local state is total reconciliation — the feed's only jobs are cheap change detection and low-latency pickup. This one commitment dissolves most failure modes structurally:

A 404 on items/index.json from an otherwise-live blyg is a nonconforming publisher: readers SHOULD fall back to feed-window-only operation and surface the subscription as lossy.

13.3 The watermark, history rewrites, and stealth edits

The highest version ever observed per imported item is a monotonic watermark, and it never silently regresses:

13.4 Withdrawal and retention

On a withdrawal endcap, a conforming reader rolls the item up to null: it MUST stop presenting the withdrawn content as live and SHOULD delete its stored copy — with one principled exception. Retention follows the origin's own serving surface: content corresponding to a version the origin has pinned (a fetchable items/{id}/v{n}.json) MAY be retained and continue to be displayed, with attribution linking the pin — the origin itself still serves those exact bytes forever (§8.1), so local retention never exceeds the origin's own promise. Everything unpinned rolls to null.

A reader that wants a durable citation of someone else's content has exactly one instrument: the origin's pin. Local hoarding past withdrawal is nonconforming; asking the origin's author to pin is the protocol's answer. A pin-retained snapshot remains a valid transclusion source (§10.2); an unretained withdrawn item is not.

13.5 Curation display and re-emission

A client that both subscribes and publishes MUST keep the two planes separate:

13.6 L0 grandfathering — the legacy RSS wrapper

A plain-RSS subscription (resolution step 6) imports each entry as a summary item + link, best-effort by design:

13.7 Ordering across origins

Ordering the merged reading surface is the reader's own policy, never the protocol's — remote timestamps are self-asserted (§5.2) and comparable only advisorily. Readers SHOULD ensure a skewed or future-dated origin cannot permanently dominate the display order. (The reference client sorts by min(claimed updated, first observed locally): an item sorts no newer than when the reader actually saw it.)

14. Security and privacy considerations

15. Webmention — response notification with structural verification

(New in 0.3. OPTIONAL at every level.) The blogroll (§11) is the static half of discovery: who a publisher chose to show they read. Webmention is the dynamic half: how an origin learns that it has been quoted, stubbed, or forked, from a publisher it does not subscribe to. It is the protocol's first and only dynamic surface, and it is optional precisely so that the static file contract (§4) survives intact — a static-only blyg does nothing here and stays fully conformant (§15.7).

The mechanism is the W3C's, reused without invention: a POST of source=…&target=…. What this protocol adds is structural verification — a receiver believes a mention only when the source's item document, fetched from the origin it claims, names the target with a reference (§5.9).

15.1 Advertising an endpoint

15.2 Sending

A publisher SHOULD send a mention for each remote reference in a newly published version: every blyg-target stub_of whose origin is not its own (§10.6), every transclusions[] entry carrying an origin (§10.3), and a forked_from naming another origin (§5.6). A {url} stub SHOULD send an ordinary Webmention to that URL. Same-origin references never generate mentions.

15.3 Receiving

The endpoint contract is the W3C's: POST, body application/x-www-form-urlencoded, source and target.

  1. Syntactic checks → 400. Both MUST be absolute http(s) URLs; source MUST differ from target; target's origin (scheme, host, port) MUST be this blyg's own, and its path MUST name a published item — by page, or as items/{id}.json. An unknown item is 400, not 404: the endpoint exists, the claim is bad. A withdrawn target is accepted (people may respond to a withdrawal).
  2. Rate limit → 429. RECOMMENDED: more than 60 mentions from one source host in an hour. Cheap — but not, on its own, enough to bound the queue: a sender that controls a wildcard DNS zone has unlimited hosts, and every accepted claim costs the receiver up to two fetches at URLs the sender chose (§15.4). Receivers SHOULD therefore also cap by a coarser grouping of the source (the reference client groups by registrable domain, at a looser limit than the per-host one, because the grouping is a heuristic that can over-collect) and cap total accepted claims per hour regardless of source. A claim refused by any cap is 429 and is never queued. The numbers are the receiver's; the shape — per source, per source group, global — is the recommendation.
  3. Accept → 202, record the (source, target) pair as pending, and verify asynchronously. W3C permits synchronous 200 or 201; 202 is the honest answer since verification fetches the network. A re-sent pair re-verifies rather than creating a duplicate.

15.4 Structural verification

Verification is bounded — RECOMMENDED: at most 2 fetches, at most 3 redirects, 5 seconds per fetch, 1 MB per body — and these bounds are the receiver's protection, not a nicety (§14).

  1. GET source. If the response is JSON with a blyg key, it is the item document. If it is HTML, find <link rel="alternate" type="application/json"> (§5.8) and fetch that; it MUST be a blyg item document. Anything else → failed.
  2. The document's origin MUST equal the final source URL's origin (scheme, host, port; trailing slash normalized). A page on one host pointing at a document claiming another origin does not verify. This is §12.2's identity rule applied inbound, and it is what stops a mirror or an impostor from speaking in a real blyg's name.
  3. A document with "kind": "withdrawn" → gone.
  4. Relation, in order: stub_of naming { origin: this blyg, id: target } → stub; else a transclusions[] entry with origin = this blyg and id = target → transclusion; else forked_from naming a pinned version of the target → fork; else failed — the document exists but does not reference us. "This blyg" compares the document's asserted origin string against the receiver's own identity origin, normalized. The receiver is the only party that can say whether a version is pinned, so the fork check consults its own pins and fails a claim naming an unpinned version, at no extra fetch. Every comparison here reads a reference's identity members only; a cited object (§5.9) is ignored.
  5. Success → verified, recording the source item's id, origin, version, kind, the relation, the source author (pass-through, §5.5), and the source page URL. No content is stored — a verified mention is a pointer, and what it points at is fetched from its origin when displayed, or not at all.
  6. A re-sent mention re-verifies and updates what it recorded. A mention that verified before and fails now becomes gone; the receiver SHOULD keep the record with that status rather than deleting it, so that a stubber who withdraws and later republishes is recognized rather than treated as new.

The relation set is exactly stub, transclusion, fork — one per wire construct that names a target — and nothing else. A plain link is not a relation (§10.1).

15.5 What a verified mention is, and is not

Verified mentions are studio signals. They are never auto-published, never enter the feed, the archive index, or any item document, and add no manifest vocabulary. A publisher MAY display verified mentions of an item publicly — a "responses" list on a permalink page — and if it does, that is per-item curation under §13.5: a shown list with attribution and links to the origin, publicity being a property of the list, never of the mention. Nothing in this protocol lets a stubber put words on the target's page.

15.6 Plain mentions

A receiver MAY accept mentions whose source is not a blyg item document, verified the W3C way (the source page contains the target URL), and if it does MUST hold them apart from structurally verified mentions as a lower class — never in the same signal, never displayed as a response — because link verification is exactly the check that trackback spam defeated. The reference client does not accept them at 0.3.

15.7 Withdrawal and static deployments

A withdrawn stub re-sends its mention once (§15.2); the receiver finds a withdrawn document and marks the mention gone. A withdrawn target keeps accepting mentions — its document is 200 forever — and its studio shows them under the withdrawn item. Nothing cascades: a stub of a withdrawn item is the stubber's speech and stays published.

A static-only deployment does nothing: it advertises no endpoint, receives nothing, and remains fully conformant at every level. It may still send — a static export is produced by some dynamic studio, which can send at export time — but is not required to.

16. Ruled, deferred, and reserved constructs (non-normative)

This section records what has been decided but not yet built (and so is not yet normative text — the project's rule is that testing precedes prose), what has been explicitly deferred to a later version, and what is reserved. Implementations should leave room accordingly. Decision records are in blygger-spec's CLAUDE.md and docs/v0.3-plan.md. A subsection that has since been promoted into the normative text keeps its number here as a pointer, so that citations of it made before the promotion still resolve.

16.1 The human half of a citation — cited (promoted to §5.9, 2026-09-28)

Ruled 2026-09-28, emitted by blygger-studio 0.6.0 the same day on all three reference kinds, exercised across both live nodes (a remote transclusion carrying cited was imported byte-for-byte by the other node; a cross-origin stub's mention verified on the bare reference with the citation ignored), and promoted into §5.9 in the first published revision. The normative text is there; this number is kept only so that earlier citations of §16.1 resolve.

Ruled 2026-09-28, rendered by blygger-studio 0.6.0 the same day, exercised across both live nodes (an inline link survived import into the other node's reading view intact, with no import error and no mention sent), and promoted into §10.1 in the first published revision, which replaced the sentence declaring [[id]] undefined. The normative text is there.

16.3 Remote generation sources (deferred to 0.4; shape decided)

At 0.3 a generated[].sources[] entry is own-origin by construction (§5.7). Whether a generator may be fed another origin's words — and whether that is disclosed as a source or is really a quotation — is a 0.4 question, decided alongside the rest of the AI layer. Its wire shape is already fixed so that clients can leave room: sources[] takes the reference shape of §5.9, with origin omitted for own-origin, exactly as transclusions[] does. Four constructs, one reference object.

16.4 Partial quotation and titles (deferred to 0.4)

16.5 Transitive staleness (open; may be nothing)

§10.4 defines staleness as a direct relation and notes that snapshot independence makes the transitive case ill-defined. Whether "staleness over the DAG" should exist as a concept — and if so, what a client is meant to do about it — is open for 0.4. It may resolve to: there is no such thing.

16.6 The write surface (never normative; companion note planned)

Multiple authoring tools already write into blygs, and each does so through whatever interface its client happens to expose. Ruled 2026-09-28: the protocol does not, and will not, specify how a client is written to. A normative write API would make a static-file client with no server non-conformant for a reason unrelated to publishing, and the write side is exactly what invariant 1 puts out of scope. What will exist instead is a non-normative technical note describing the reference client's write surface as a way, in the genre of TN-1, once that surface has been built with per-tool, scoped, revocable credentials and used by at least one third-party tool. Tools discover a write endpoint through an HTML rel link on the studio's page, never through the manifest; the manifest is wire and stays clean.

16.6a Client source discovery — generator_url (promoted to §6.1, 2026-09-28)

Ruled 2026-09-28, emitted by blygger-studio 0.6.0 the same day on both live nodes, and promoted into §6.1 (with §3.2 extended to cover it) in the first published revision. The normative text — SHOULD emit, MUST NOT gate, no maintenance declaration ever — is there.

16.6b Identity, groups, and agents (ruled; technical notes, never protocol)

Three questions raised on 2026-09-28 were ruled to need no construct, and are recorded here so that the absence is legible as a decision:

16.6c Generated changelog notes (ruled; next revision)

Ruled 2026-09-28: a changelog entry MAY carry "generated": true, meaning the publisher's studio wrote the note (from the local diff between versions) rather than the author. Self-asserted and unverifiable like generated[] (§5.7 rule 6); absent means only "not stated"; readers MUST NOT gate on it. It exists because a note is prose readers read and is the source of the feed <title>, so a client reconstructing an item's history from its notes should be able to tell the author's words from a machine's summary. The depth rule of §5.2 binds generated notes exactly as it binds authored ones. Enters §5.2 once a client emits it.

{ "version": 3, "at": "2026-07-18T09:30:00Z",
  "note": "Sharpened the second paragraph's claim; no change to the examples.",
  "generated": true }

Not opened, recorded as a 0.4 candidate: feed entries for pinned publish events carrying that pinned version's content rather than the latest, which §7 currently forbids for all entries. Pinned content is public, so it would leak nothing, and it would make the duplicate entries a plain RSS reader shows truthful to their events; against it, the §7 rule is simple and every reader relies on it today.

16.6d Discovery through references (ruled; no construct; one 0.4 candidate)

Everything a reader needs to discover origins backward from what it holds is already on the wire: nested transclusion layers carry their origins in the baked attributes (§10.2), stub_of and forked_from name origins and items that any reader may fetch (§5.9), and subscriptions may publish blogrolls (§11). A reader MAY walk these — the chain in a snapshot, the stub_of chain to its root, its own verified mentions, its subscriptions' blogrolls — and MAY order what it finds by any local heuristic, provided nothing derived is published (the no-metrics rule).

Forward discovery — who has responded to an item you did not publish — is deliberately not on the wire: verified mentions are studio signals (§15.5), and a publisher's public responses list is presentation that readers MUST NOT parse as protocol. Recorded as a 0.4 candidate, not opened: an OPTIONAL per-item curated responses surface in the blogroll's shape — opt-in per item, structurally verified sources only, no completeness claim, never re-emitting content.

16.7 Reserved

16.8 What will never appear

Reply primitives (this is a network of soapboxes, not a conversation medium); follower graphs or any protocol "follow" object; addressable authors; AI constructs on the wire beyond the passive provenance of §5.7; version-significance markup (the counter stays bare; the pin is the significance primitive); content-addressed identity; metrics of any kind; a fourth mention relation for links; and a normative write API.

17. Revision history (non-normative)

One line per published change to this document, newest first. Snapshots are cut at blygger.org/spec/0.3/{date}/ and each carries a diff link to the one before it.


Published from blygger/blygger-spec@36e068f.