Fwd: New Version Notification for draft-toomim-httpbis-versions-04.txt

17 views
Skip to first unread message

Michael Toomim

unread,
Mar 5, 2026, 5:43:43 PMMar 5
to HTTP Working Group, Braid
Hi all,

I have updates on HTTP Resource Versioning, last discussed on-list in February 2025, and presented at IETF 120 in Vancouver. I took some time off for my health, but am back at work. I'll be at IETF 125 in Shenzhen.

This is the first of the component specs broken out from Braid-HTTP, as the group requested at IETF 118. I'm also developing the companion specs (Subscriptions, Multiresponse, and Updates), but this versioning spec is where I'd most value review right now. The published -04 is here:


The spec itself is simple: a Version and Parents header for requests and responses, identifying where a resource's state sits in its history. This is useful on its own for everyday things like cache consistency, history archives, optimistic concurrency, and requesting incremental updates since a known version. But the same headers are general enough to also support the most ambitious use-cases; all the way up to fully distributed, eventually-consistent OT/CRDT collaborative editing. These simple headers provide a general power.

I'm looking for critique as we integrate this into HTTP: problems, things to test, and gaps I should be filling. I have some specific questions below.

What's changed since -02:

Julian suggested the "309 Version Unknown Here" status code should be a 4xx-series error rather than a 3xx redirect. We agreed, and it's now 432 Version Not Found. This is analogous to 416 Range Not Satisfiable for range requests— it indicates the client asked for a version that the server did not have.

We added a clearer comparison with existing versioning mechanisms (Last-Modified, ETags, Memento, WebDAV, Link Relations) including a feature table showing what each approach does and doesn't support (Section 1.3.4). We also distinguished Event IDs from Version IDs with a formal definition (Section 2.1.2).

For -05 (to be published when the submission window reopens), I'm reorganizing the document to put the specification before the prior art analysis, adding a section on interaction with conditional request headers (If-Match, If-None-Match), specifying unicode support, and filling in Security Considerations. The core mechanism is unchanged.

Implementation updates:

We have two independent protocol implementations — one in JavaScript and one in Emacs Lisp — that interoperate in production, giving us confidence that the spec is implementable and unambiguous. We also have experimental implementations in Rust and Haskell.

The JavaScript implementation is used for a range of applications: filesystem sync over HTTP [1], realtime-updated webpages, including a live <img> polyfill [2], collaborative text editing, eventually-consistent binary sync [3], and a Chrome extension [4]. The Emacs Lisp implementation [5] is fully standalone and supports collaborative editing of https:// resources, synchronizing with other Emacs and the JavaScript-based clients.

These are used in production on braid.org.

[5] https://github.com/braid-org/braid-emacs

Cache testing:

As reported previously [6], we ran the Version and Parents headers through real-world intermediaries at https://cache-tests.braid.org. This led to the intermediary instructions in Section 4 of the spec. I'd love to expand this testing — what other intermediaries, CDNs, or edge cases should we be running these headers through? This is an area where the group's expertise would really help.

[6] https://lists.w3.org/Archives/Public/ietf-http-wg/2024OctDec/0131.html

Questions for the group:

  1. Status code 432: Is a new code the right approach? The analogy with 416 Range Not Satisfiable feels right to us: a version range is like a byte range, and there's a parallel error when the server can't satisfy it. But we could also see this as 404 with response headers indicating which versions weren't found. What does the group think?
  2. What should we be testing against, or trying to break? We've tested with caches, but I'm not an expert in the full range of HTTP intermediaries, CDNs, and edge cases that exist in the wild. What should we be throwing at this?
  3. For -05, I'm adding conditional header interaction, security considerations, unicode support, and a better document structure. [7] What other gaps do reviewers see in the spec?

Thanks!
Michael

Begin forwarded message:

Subject: New Version Notification for draft-toomim-httpbis-versions-04.txt
Date: March 2, 2026 at 1:57:01 PM HST
To: "Michael Toomim" <too...@gmail.com>

A new version of Internet-Draft draft-toomim-httpbis-versions-04.txt has been
successfully submitted by Michael Toomim and posted to the
IETF repository.

Name:     draft-toomim-httpbis-versions
Revision: 04
Title:    HTTP Resource Versioning
Date:     2026-03-02
Group:    Individual Submission
Pages:    30
URL:      https://www.ietf.org/archive/id/draft-toomim-httpbis-versions-04.txt
Status:   https://datatracker.ietf.org/doc/draft-toomim-httpbis-versions/
HTMLized: https://datatracker.ietf.org/doc/html/draft-toomim-httpbis-versions
Diff:     https://author-tools.ietf.org/iddiff?url2=draft-toomim-httpbis-versions-04

Abstract:

  HTTP resources change over time.  Each change to a resource creates a
  new "version" of its state.  HTTP systems often need a way to
  identify, read, write, navigate, and/or merge these versions, in
  order to implement cache consistency, create history archives, settle
  race conditions, request incremental updates to resources, interpret
  incremental updates to versions, or implement distributed
  collaborative editing algorithms.

  This document analyzes existing methods of versioning in HTTP,
  highlights limitations, and specifies a more general versioning
  approach that can enable new use-cases for HTTP.  An upgrade path for
  legacy intermediaries is provided.



The IETF Secretariat



Michael Toomim

unread,
Mar 9, 2026, 10:01:17 PMMar 9
to HTTP Working Group, Braid
Hey all,

After surveying the different approaches to Versioning in HTTP for draft
-04, I'm realizing there's an important design question for the group here:

=== Q. "What is a Version in HTTP?" ===

Back in October 2024, Julian Reschke said "a version is a HTTP
resource". This definition implied that, for instance, if a version
doesn't exist, we could return 404:

https://lists.w3.org/Archives/Public/ietf-http-wg/2024OctDec/0172.html

This is interesting. Credit to Julian for recognizing the impact of
this question, and suggesting we work to define how these basic concepts
work in HTTP.

When I surveyed the existing versioning approaches in -04, I found two
distinct models:

First, in WebDAV Versioning, Memento, and Link Relations, a version is
itself a resource with its own URI:

- In WebDAV Versioning (RFC 3253), a "version resource" is an
immutable HTTP resource at a server-assigned URL, created when a
"version-controlled resource" is checked in. You GET the version
at its own URL.

- In Memento (RFC 7089), a "Memento" (URI-M) is a resource that
encapsulates a prior state of an "Original Resource" (URI-R). It
has its own URI, and you navigate to it via a TimeGate Resource.

- In Link Relations for Version Navigation (RFC 5829), the link
relation types (predecessor-version, successor-version) assume
you're navigating between version resources, each with its own URI.

In all three, a version is a thing you dereference -- a resource at a
URL. If it's missing, 404 is natural.

Second, we have the Last-Modified and ETag headers, and the Version and
Parents headers proposed in this draft. Instead of version being a
resource with a URI, these simply identify a resource at a *point in
time*. The version is a "coordinate of a resource." Last-Modified and
ETag use coordinate as a condition for a request:

- Last-Modified:

GET /doc
If-Unmodified-Since: Tue, 15 Oct 2024 12:00:00 GMT

- ETag:

GET /doc
If-Match: "abc5"

This draft extends that model by letting the coordinate select a
representation directly:

GET /doc
Version: "abc5"

or:

GET /doc
Parents: "abc4"

This is analogous to how Range requests work. When a client sends
`Range: bytes 500-1000`, it isn't requesting a different resource. The
range is a coordinate within the resource. We do not give ranges their
own URIs, such as `https://example.com/bytes/500-1000` or
`urn:bytes:500-1000`.

Thus, it appears there are two models of "versioning" in HTTP:

1. A version is a HTTP resource
2. A version is a coordinate of a HTTP resource, in time

So the question for the group: What is the right model for HTTP?
What is a version?

My own view is that the coordinate model is the right foundation. It's
simpler, it composes naturally with existing HTTP semantics like
conditional requests and Range, and it doesn't require minting new URIs.
But crucially, the two models are not incompatible -- the coordinate
model can serve as a foundation that the resource model is built on top of:

1. Memento already combines both models. The Memento TimeGate maps a
"coordinate" of time into a "version resource." You give it a
wallclock time, and it gives you back a URI for the resource at
that time.

2. This versions-04 draft can be extended to URIs, by registering
a "Version-Type" with IANA that expresses version coordinates as
URIs, e.g. `Version: "https://example.com/doc^abc123"`.

So if we adopt the coordinate model, applications that need version
resources can still build that on top. The reverse isn't as clean --
if we start from "a version is a resource with a URI", then simple
coordinates like a wallclock time or an opaque version string need to be
encoded into URIs, e.g.
"urn:wallclock:Tue,%2015%20Oct%202024%2012:00:00%20GMT", which works
against the simplicity that HTTP headers already give us.

I'd like to hear the group's thoughts on this framing before draft -05.

Thanks!

Michael

Rahul Gupta

unread,
Mar 9, 2026, 11:06:00 PMMar 9
to Michael Toomim, HTTP Working Group, Braid
Hi Mike,

A version (is a representation that)* reflects the state of a resource at a point in time. This is consistent with the text in RFC9110 'A "representation" is information that is intended to reflect a past, current, or desired state of a given resource, in a format that can be readily communicated via the protocol.', but I think some of descriptive text in the standard is a bit short-sighted for the problem of tracking the evolution of resources.

If you want to create a separate resource out of that, it is fine. Much like a content negotiated document in say a particular language can point to another resource, so can a version upon negotiation. But that's an implementation detail, i.e. demanding the same is wrong. It is very much the same resource, should be identifiable by the same URI, just that it was in a different state in the past (and no different from any other form of content negotiation).


I am sympathetic to the term co-ordinates, another language and media-type could be thought of as co-ordinates in space; *(and all these co-ordinates together identify the representation). Having said that, I would strongly suggest not introducing additional language/concepts where the present language in the standard can easily suffice and would be more recognizable to the standards community.


My quick 2 cents here... I am busy with ICANN 85 this week (and it extremely is rare in my part of the world to be able to ride the subway from home to a global event :) ), so I might fully engage with this discussion later.

BR/Rahul
publickey - cxres@protonmail.com - 0x0CEC7748.asc
signature.asc

Herbert Van de Sompel

unread,
Mar 10, 2026, 8:35:07 AMMar 10
to Michael Toomim, Working Group HTTP, Braid
On Mar 10, 2026, at 03:05, Michael Toomim <too...@gmail.com> wrote:

Hey all,


After surveying the different approaches to Versioning in HTTP for draft -04, I'm realizing there's an important design question for the group here:

         ===   Q. "What is a Version in HTTP?"  ===

Back in October 2024, Julian Reschke said "a version is a HTTP resource".  This definition implied that, for instance, if a version doesn't exist, we could return 404:

https://lists.w3.org/Archives/Public/ietf-http-wg/2024OctDec/0172.html

This is interesting.  Credit to Julian for recognizing the impact of this question, and suggesting we work to define how these basic concepts work in HTTP.

When I surveyed the existing versioning approaches in -04, I found two distinct models:

First, in WebDAV Versioning, Memento, and Link Relations, a version is itself a resource with its own URI:

  - In WebDAV Versioning (RFC 3253), a "version resource" is an
    immutable HTTP resource at a server-assigned URL, created when a
    "version-controlled resource" is checked in.  You GET the version
    at its own URL.

  - In Memento (RFC 7089), a "Memento" (URI-M) is a resource that
    encapsulates a prior state of an "Original Resource" (URI-R).  It
    has its own URI, and you navigate to it via a TimeGate Resource.


The Memento protocol supports a range of scenarios, see https://datatracker.ietf.org/doc/html/rfc7089#section-4. These various scenarios are supported because, while devising the protocol, we found occurrences of each used on the web. We found that sometimes versions are assigned distinct URIs and sometimes they’re not. As such, the protocol supports scenarios in which a “Memento” has its own URI and scenarios in which it doesn’t.  

Cheers

Herbert 

Mike Bishop

unread,
Mar 10, 2026, 9:41:37 AMMar 10
to Michael Toomim, HTTP Working Group, Braid
I'm not sure this is an either/or.

The comparison to Content-Encoding or Content-Language is a good one. There is a base resource, which depending on the content negotiation, can have multiple representations. The Content-Location header tells you the URI of that particular representation if you want to reference it directly. So similarly there is a base resource, which might have had multiple versions over time; the version which was current at a particular time in the past is a property of that base resource which can be negotiated for, and its relationship to other versions is metadata of that version.

So while a version is a resource with a URL, the server manages the relationships — it's not up to the client to attempt to understand the URLs and how date might or might not be encoded into it.

(No hats)

From: Michael Toomim <too...@gmail.com>
Sent: Monday, March 9, 2026 10:01 PM
To: HTTP Working Group <ietf-h...@w3.org>; Braid <braid...@googlegroups.com>
Subject: What is a Version?
 

Roy T. Fielding

unread,
Mar 12, 2026, 3:03:25 PMMar 12
to Michael Toomim, HTTP Working Group, Braid
On Mar 9, 2026, at 7:01 PM, Michael Toomim <too...@gmail.com> wrote:
>
> Hey all,
>
> After surveying the different approaches to Versioning in HTTP for draft -04, I'm realizing there's an important design question for the group here:
>
> === Q. "What is a Version in HTTP?" ===
>
> Back in October 2024, Julian Reschke said "a version is a HTTP resource". This definition implied that, for instance, if a version doesn't exist, we could return 404:
>
> https://lists.w3.org/Archives/Public/ietf-http-wg/2024OctDec/0172.html
>
> This is interesting. Credit to Julian for recognizing the impact of this question, and suggesting we work to define how these basic concepts work in HTTP.
>
> When I surveyed the existing versioning approaches in -04, I found two distinct models:
>
> First, in WebDAV Versioning, Memento, and Link Relations, a version is itself a resource with its own URI:
>
> - In WebDAV Versioning (RFC 3253), a "version resource" is an
> immutable HTTP resource at a server-assigned URL, created when a
> "version-controlled resource" is checked in. You GET the version
> at its own URL.
>
> - In Memento (RFC 7089), a "Memento" (URI-M) is a resource that
> encapsulates a prior state of an "Original Resource" (URI-R). It
> has its own URI, and you navigate to it via a TimeGate Resource.
>
> - In Link Relations for Version Navigation (RFC 5829), the link
> relation types (predecessor-version, successor-version) assume
> you're navigating between version resources, each with its own URI.
>
> In all three, a version is a thing you dereference -- a resource at a URL. If it's missing, 404 is natural.

They each refer to a different (defined) conception of what a "version" may be. That's why they all differ in their own ways.

The concept of "resource" comes from the Web and IETF discussions around identifiers. The word is a generic in the sense that is a placeholder noun for any conceived mapping from an identifier to anything we might want to identify. But, at the same time, "resource" has the traditional meaning of something that (was/is/will be) available for use. Hence, something doesn't become a "resource" until it has been identified, even though that same thing exists before/during/after it has been identified. The philosophical conundrum is not a problem for the Web specs because we stick to the original design: All important resources must be identified by at least one URI. If it doesn't have a URI, then it isn't important, and we enforce that in HTTP by not performing operations on anything without its target URI. [This of course doesn't prevent people from doing private manipulation of other things via POST, but those interactions are decidedly not standardized as HTTP.]

As Julian just mentioned, a server provides resources by minting URIs. Many, many URIs. A single interesting representation might have dozens of different URIs associated with it, or parts of it, and the server uses links (in HTML or Link or Link-Template or custom fields, depending on history) to describe the relationships so that a client can find what it wants.

> Second, we have the Last-Modified and ETag headers, and the Version and Parents headers proposed in this draft. Instead of version being a resource with a URI, these simply identify a resource at a *point in time*. The version is a "coordinate of a resource."

The problem here is that this is using the term "resource" to mean the clump of data that happens to be the result of a prior GET, as it is defined in some of the browser-specific WHATWG specs. That's leading to confusion because we are not talking here about the internal storage context of a browser. No Web resources are ever transferred over HTTP. Only a representation is transferred.

Next, it assumes that each observable change in the representation of a resource is a version of that resource, which has two very big problems: 1) some changes are variants of the same response (for whatever reason the origin chooses) and some changes reflect change in the resource state (or implementation); and, 2) some new versions are minted without any change at all. The Last-Modified and ETag metadata are not identifiers of a representation/version. Last-Modified provides a time value within the accuracy of a second. A resource might have many different representations with the same Last-Modified. That's why we needed ETag for caching, but even ETag is not an identifier for a representation! It's a token that is presumed to be conflict-free among a set of representations for a resource over the scope of a given cache entry lifetime. There might be multiple ETags assigned to the same sequence of bytes, which may or may not be associated with the same representation (because of metadata).

> Last-Modified and ETag use coordinate as a condition for a request:
>
> - Last-Modified:
>
> GET /doc
> If-Unmodified-Since: Tue, 15 Oct 2024 12:00:00 GMT
>
> - ETag:
>
> GET /doc
> If-Match: "abc5"
>
> This draft extends that model by letting the coordinate select a representation directly:
>
> GET /doc
> Version: "abc5"
>
> or:
>
> GET /doc
> Parents: "abc4"
>
> This is analogous to how Range requests work. When a client sends `Range: bytes 500-1000`, it isn't requesting a different resource. The range is a coordinate within the resource. We do not give ranges their own URIs, such as `https://example.com/bytes/500-1000` or `urn:bytes:500-1000`.

A Range request has the target resource as a URI. The range refers only to a potentially successful response representation to GET on that URI. The If-Match and If-Range header fields are designed to fail safe if the server selects a sequence of bytes that is not the same (by ETag) as what the client expects. This is sufficient for preventing mismatch (if implemented correctly) but is not sufficient to identify the sequence of bytes beyond the scope of the past (partially stored) response and does not identify the representation because it doesn't prevent changes in metadata.

> Thus, it appears there are two models of "versioning" in HTTP:
>
> 1. A version is a HTTP resource
> 2. A version is a coordinate of a HTTP resource, in time
>
> So the question for the group: What is the right model for HTTP?
> What is a version?

I believe that was decided in 1994. Everything important is a resource. If you think versions are important for HTTP, then they must be resources. That's why the HTTP-related versioning extensions always end up referring to versions as resources and using some sort of link to describe their relationships. Because literally everything else in HTTP (access control, auth, caching, proxies, gateways, CDNs, etc.) depends on resources.

....Roy

Michael Toomim

unread,
Mar 16, 2026, 7:25:42 PMMar 16
to ietf-h...@w3.org, Braid
Thank you, everyone, for this great discussion! Let me summarize what
I've heard, what I've learned, and show a way forward.

As Mike Bishop and Mnot emphasize, the perspectives of (1) and (2) are
not themselves in conflict, but, as Julian and Nico point out, they use
similar words for different meanings. This can lead us to think we're
talking about one thing when there are actually two things.

I now see two separate concerns in "versioning":

(1) This draft *timestamps existing representation data* in existing
HTTP messages as they flow over the wire. These timestamps help
machines sync representations. They are not seen by humans. They
are not exposed in the resource model, and do not impact URIs.

(2) WebDAV, Memento, and Link Relations organize the *minting of new
version resources* with new URIs. They help humans and machines
manage resource versions via these version resources. They do
nothing to timestamp existing representations as they flow over the
wire in existing HTTP messages.

If we distinguish these two concerns, they actually fit together nicely.
But we need more precise language.

I realize that I actually created a bunch of confusion with the
terminology I chose at the root of this draft 04. Specifically, the
wording of the draft's title itself -- **HTTP Resource Versioning** --
creates confusion in three ways:

1. The word *"Version"* can ambiguously mean "a point in time", or "a
version of a resource at a point in time". This draft is talking
only about the "point in time", and the title could reduce
ambiguity by just saying "Time" directly instead of "Version."

2. The word *"Resource"* is incorrect. It should say
*"Representation"*; both in the title, and throughout the draft.
This draft says nothing normative about resources. All it does is
specify timestamps for representations as they are transferred.

Thanks to Roy for making this clear, with: "No Web resources are
ever transferred over HTTP. Only a representation is
transferred." Thanks Rahul for pointing out where RFC 9110
defines that representations change in time, and that it has yet
to define how to track the changes.

3. Finally, the word *"Versioning"* implies a much larger scope than
is necessary -- it brings to mind a plethora of things people do
with versions, like bookmarking them, navigating them, and
checking them in and out -- that are out of scope for this draft.
We just need to synchronize representations between machines.

Thank you Tim Bray, Carsten and Anders for making this clear.

I'm retitling this draft to something like "Time Semantics for HTTP
Message Representations."

There are basic semantics we need here. Roy pointed out that there's no
way in HTTP today to say:

"The representation data I just gave you, at time X, will always be
the same bytes when anyone references version X of the
representation."

ETags don't solve this, because they can change with a server reboot, or
across shards in a load balancer. This also isn't about the
*conceptual* resource version -- your Facebook profile might not have
changed in years, but the HTML representation changes frequently as new
features, ads, and A/B tests are implemented on the platform. To
implement data synchronization, we need to specify the
representation-level semantics for time.

A timestamped message for /doc could look like this:

200 OK
Content-Type: text/plain
Content-Length: 15
Content-Version: "foo-1"

Hello Everyone!

It implies the body will always be the same at "foo-1".

We can then build on that, by specifying:

- A partial ordering of time. This lets machines compare two
timestamps and know if one came after the other, or if they occurred
in parallel.

- Semantics for GET, PUT, POST, PATCH, HEAD and DELETE requests and
responses. A client needs to be able to say "give me updates since
version X", or "I'm writing against version Y." A server can say
"here is the representation at version Z, which descended from
versions Y1 and Y2."

- Extensibility in timestamps, so that implementations can pursue
optimizations such as lamport clocks, vector clocks, version
vectors, bytestreams, and run-length encodings without breaking the
basic semantics.

These are the basic definitions we need.

Now plugging this into (2) is simple -- as Julian points out, a server
can mint a URI for a representation version at any time, however it
wants, and convey it in the Content-Location header:

200 OK
Content-Type: text/plain
Content-Length: 15
Content-Version: "foo-1"
Content-Location: /doc?v=foo-1

Hello Everyone!

I hope this makes clear why I don't think these basic timestamps need to
be URIs. They identify points in time within the context of a HTTP
message stream for a known resource -- not across different contexts,
applications, or protocols. The analogous Content-Range: `bytes 50-100`
value doesn't need a URI, and neither does the Content-Length: `15`
value, nor does a Content-Version point in time. When an application
does want a URI for a version, it can put one into the Content-Location
header.

Does the group recognize (1) and (2) as separate concerns?

I am excited by this clarity. I would like to account for your thoughts
as I revise draft-05 now. Please also find me in person in Shenzhen to
chat!

Thank you!
Michael

Rahul Gupta

unread,
Mar 17, 2026, 2:54:53 AMMar 17
to Michael Toomim, ietf-h...@w3.org, Braid
Hi Mike,

I think you would benefit from using the word "revision" or another neutral term.

From what I understand, you are interested in tracking representations that are a consequence of changing state of the resource over time for the purpose of synchronization. It might even be the case that the representation generated or even the state might be the same but at different times. For example, the temperature at Laguna Beach might be 80F at 10:00 and then again 80F at 13:00 the same day. These will result in the same representation and have even the same underlying state, but they would then represent different revisions as they are a consequence of events at different times of the day; the two revisions would have different parents/history of states (One exception I can think of is an undo, where you might return to a previous revision as well at a later time, I am not sure how this would fit into the synchronization you wish to propose). You would still be transferring the same representation, as identified, say, by the same eTag, but the revision and parent metadata would be different (this is fine since the date field would be different irrespective).


I, however, do not think these revisions need to be timestamps, any scheme that identifies the progression of states for the resource should be good enough and will depend on the internal semantics of the resource.


Best Regards,
Rahul
publickey - cxres@protonmail.com - 0x0CEC7748.asc
signature.asc
Reply all
Reply to author
Forward
0 new messages