Internet-Draft M. Toomim Expires: 6 May 2027 Invisible College Intended status: Proposed Standard 2 November 2026 Resource Versioning for HTTP draft-toomim-httpbis-braid-versions-00 Abstract HTTP resources change over time. Each change to a resource creates a new "version": a new point in time, at which the resource has a new 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. It also provides an upgrade path for legacy intermediaries. This document is one of the Braid-HTTP extensions, which work alone or together; together they make HTTP a state synchronization protocol. About This Document This note is to be removed before publishing as an RFC. Discussion of this document takes place on the HTTP Working Group mailing list (ietf-http-wg@w3.org). The source of this document, and an issue tracker, are at https://github.com/braid-org/braid-spec. Status of this Memo This Internet-Draft is submitted in full conformance with the provisions of BCP 78 and BCP 79. Internet-Drafts are working documents of the Internet Engineering Task Force (IETF). Note that other groups may also distribute working documents as Internet-Drafts. The list of current Internet-Drafts is at https://datatracker.ietf.org/drafts/current/. Internet-Drafts are draft documents valid for a maximum of six months and may be updated, replaced, or obsoleted by other documents at any time. It is inappropriate to use Internet-Drafts as reference material or to cite them other than as "work in progress." This Internet-Draft will expire on 6 May 2027. Copyright Notice Copyright (c) 2026 IETF Trust and the persons identified as the document authors. All rights reserved. This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info) in effect on the date of publication of this document. Please review these documents carefully, as they describe your rights and restrictions with respect to this document. Code Components extracted from this document must include Revised BSD License text as described in Section 4.e of the Trust Legal Provisions and are provided without warranty as described in the Revised BSD License. Table of Contents 1. Introduction .................................................5 2. HTTP Resource Versioning .....................................6 2.1. Model of time ..............................................6 2.1.1. Terminology ..............................................7 2.2. Version and Parents headers ................................8 2.2.1. Formatting of versions in versioning headers .............8 2.2.2. Definition of Events and Versions .......................10 2.2.3. Computing the current version ...........................10 2.3. Using Versioning with HTTP Methods ........................11 2.3.1. GET the current version .................................11 2.3.2. GET a specific version ..................................12 2.3.3. PUT a new version .......................................13 2.3.4. GET a span of history ...................................13 2.3.5. Rules for Version and Parents headers ...................15 2.3.6. Interaction with Conditional Request Headers ............16 2.4. Status 432: Version Not Found .............................17 2.5. The Current-Version header ................................17 2.6. Version Types .............................................18 2.7. Versioning through intermediaries .........................18 2.7.1. Detecting legacy intermediaries .........................20 3. Analysis of existing versioning approaches in HTTP ..........21 3.1. Approaches with versions in headers .......................21 3.1.1. Last-Modified ...........................................21 3.1.2. ETag ....................................................22 3.2. Approaches defining versions as new resources .............22 3.2.1. Creating additional "Version Resources" .................23 3.2.2. Standardizing versions as external resources ............23 3.3. Requirements for general versioning .......................23 3.3.1. Distributed Time ........................................24 3.3.2. Included within existing HTTP requests and responses ....24 3.3.3. Generalizable Timestamps ................................25 3.4. Feature table of existing approaches ......................25 4. Informative Use-Cases .......................................26 4.1. Incremental RSS Subscription ..............................26 4.2. Hosting git via HTTP ......................................27 4.3. Resumable uploads protocol ................................30 4.4. Distributed collaborative editing .........................31 4.5. Improved Header Compression with runs .....................33 4.6. Run-Length Compression without Header Compression .........36 5. Acknowledgements ............................................37 6. Conventions .................................................37 7. IANA Considerations .........................................37 7.1. HTTP Field Registrations ..................................37 7.2. Version Type Registry .....................................38 7.2.1. Procedure ...............................................38 7.2.2. Version Type Registrations ..............................39 7.2.3. Comments on Version Type Registrations ..................40 7.2.4. Change Procedures .......................................40 8. Implementation Status .......................................41 9. Security Considerations .....................................42 10. References .................................................43 10.1. Normative References .....................................43 10.2. Informative References ...................................43 1. Introduction HTTP resources change over time. Any single computer on the network can observe a resource changing, over time, in a linear sequence of versions: o <-- oldest version | o | o | o <-- newest version We call this a "linear" history. However, when multiple networked computers change a resource "simultaneously", before their edits propagate to one another, the version history as a whole forks into a DAG, or "partial order": o <-- oldest version / \ o o \ / o | o <-- newest version HTTP systems often need to specify, identify, and navigate these histories of versions in order to (1) implement cache consistency, (2) create history archives, (3) settle race conditions, (4) request incremental updates to resources, (5) interpret incremental updates to versions, or (6) merge parallel mutations in distributed collaborative editing algorithms. Section 4 demonstrates these use-cases concretely. Each of these systems needs a way to represent *time*, but different systems have different requirements for representing it. This document enumerates these needs, and proposes a general versioning system to satisfy them. (Note that this document does NOT speak to the versioning of HTTP APIs, only to the versioning of the HTTP resources used within them.) 2. HTTP Resource Versioning This section defines the core concepts and mechanisms for HTTP Resource Versioning. 2.1. Model of time This specification adds a dimension of *time* to HTTP resources. A client queries it with the Version header, as it queries a resource's dimension of *space* with the Range header: == Variant, Time, and Space as dimensions of Resources == Variant <-- Content Negotiation ^ | | | Time <-- Version: header | / | / | / | / +----------------> Space <-- Range: header So a client can query any resource in both time and space, with a Version and a Range: GET /foo Version: ("v1.2.3") Range: bytes=500-1000 In this model, the term "Version" is synonymous with "timestamp", and a Version represents a point in distributed time. So on its own, a Version identifies a point in time for the whole universe of resources. We can use a Version for a single resource, or for several resources, to identify their states at the same point in time. A peer can specify a Version without reference to any particular resource. It follows that a resource has a state at each Version, and that each variant of the resource, at a Version, is a representation: a particular body, or "snapshot." Some other mechanisms, such as WebDAV Versioning, use the term Version for a copy of a resource's content. In this document, a Version is a point in time, and content is *at* a Version. A resource's "Version History" is the partial order (or DAG) of the versions that its state has passed through. For example, this history has one fork and one merge: "1" | Time / \ | "2" "3" | \ / | ("2" "3") <-- merge | | | "4" V A Merge Type [Merge-Types] specifies the semantics of a merge. 2.1.1. Terminology This document uses the following terms. Throughout, a version is a point in time, and anything with content, such as a resource's state or a representation, is *at* a version. Version: A point in time (Section 2.1). Event: A single moment at a single place, as in physics: a point in time at one peer. Every mutation occurs at an event (Section 2.2.2). Timeline: A set of events, named by a URI, that form a DAG (Section 2.2.1). Peer: Any client, server, or intermediary that holds a resource's state. State: What a resource is at a version. Variant: One way of representing a resource, such as its JSON or HTML, as selected by its URI, Accept, and other request headers, apart from time. Variants exist at multiple versions. Representation: A variant at a point in time (version), which always has the same bytes. (Section 3.2 of [RFC9110]) Variant Key: The request's method and URI, and the request headers that select a variant. Cache Key: The variant key, plus the request's Version and Parents (Section 2.7). 2.2. Version and Parents headers This specification defines two new HTTP headers, Version and Parents, which peers send in requests and responses. The Version header names the point in time that the message describes: Version: ("dkn7ov2vwg") The Parents header names the version just before it, which the message's update builds on: Parents: ("ajtva12kid" "cmdpvkpll2") A peer can send these headers in PUT and DELETE requests, and in GET and HEAD responses, to identify the versions just before and just after the Update that the message contains. (See [Updates].) A client can also send them in GET and HEAD requests, to ask a server for a specific version or span of history. If an Update (a PUT or DELETE request, or a GET or HEAD response) has no Version header, the recipient MAY generate a new Event ID and assign it to the Update. If an Update has no Parents header, the recipient MAY presume that its current version is the new version's Parents. To describe a merger, a version MAY contain several Event IDs: Version: ("dkn7ov2vwg" "v2vwgdkn7o") 2.2.1. Formatting of versions in versioning headers The Version and Parents headers are Simpler Fields [SIMPLER-FIELDS], a profile of Structured Fields [RFC9651]. Each Version or Parents field line holds one version, formatted as an Inner List of Strings. We call each String an Event ID: Version: Parents: : ( ...) : The Inner List is the set of events that identifies the version (Section 2.2.2), so the order of the Event IDs within it has no meaning. A peer SHOULD sort the Event IDs lexicographically whenever it receives or sends a version, so that each set has a unique serialization, e.g. as a unique cache key. An Event ID may occur at most once within a version. An empty Inner List, "()", names the version before any event. Like every String in a Simpler Field, each Event ID is percent-encoded, so a peer sends the Event ID "a,b" as: Version: ("a%2Cb") A version can have two parameters: - "type", a Token naming the Version Type of its Event IDs (Section 2.6). A Version Type's modifiers are further parameters, with no value: Version: ("x82ha-344");type=peer-counter;bytestream - "timeline", a String that names the timeline of its Event IDs. The String holds a URI reference, which a recipient resolves against the target URI (Section 5 of [RFC3986]). Without this parameter, the timeline is the target URI itself, so each resource has a timeline of its own. A timeline's events form a DAG, which forks wherever peers act in parallel (Section 2.1). The URI reference can be a fragment, for a timeline local to the resource, such as one peer's clock; a path, for one shared by the resources of a site, such as a transaction across several of them; or an absolute URI, for one shared across sites: Version: ("alice-7");timeline="#alice" Version: ("tx-12");timeline="/tx/g24397" A message can name one point in time on several timelines, with one field line for each: Version: ("3nf3tsbk" "27phny49tgl") Version: ("1780178921376000001");type=localtime;timeline="/" Any recipient can join field lines into one, separated by commas (Section 5.3 of [RFC9110]), so a recipient also accepts several versions in one field line, as a Structured Fields List. All the Event IDs on one timeline have the same Version Type, so a peer specifies each timeline's type once in a message, on any of its Version, Parents, or Current-Version field lines. 2.2.2. Definition of Events and Versions An "Event" is a single moment at a single place, as an event is in physics: a point in time at one peer. Every mutation occurs at an event. Peers generally define events at state mutations, but a peer MAY also define one at a "no-op mutation", when it wants a unique identifier to mark time. Each event has a unique Event ID, and any peer can define an event, by generating a unique ID. Conceptually, for any event E, ancestors(E) is the set of all events that the peer defining E had observed before it defined E. The frontier of this set, frontier(ancestors(E)), is the set of events in ancestors(E) that are not in ancestors(e') for any other event e' in ancestors(E). We define parents(E) as this frontier(ancestors(E)). Likewise, we define the Version V at any point in time as frontier(observed_events), the frontier of the set of events observed by that time. Thus, the version immediately after any event E is simply the set {E}, and the version after the merger of two parallel events E1 and E2 is the set {E1, E2}. As a result, the version immediately after a mutation contains just a single Event ID: Version: ("foo-123") The version after a merger generally contains several: Version: ("foo-123" "bar-abc") A peer can also define a no-op event that represents the merger as a single Event ID: Parents: ("foo-123" "bar-abc") Version: ("[foo-123 bar-abc]") 2.2.3. Computing the current version A peer tracks its current version for a resource as a set of Event IDs, from the updates it receives. It uses the current version to resume a subscription where it left off, to send a Current-Version header, to compare with a Current-Version it receives (Section 2.5), and to know whether a request names the version it holds. Before any update, the peer doesn't know its current version, unless it has stored a copy of the resource with a Version header. Then, on each update: - If the update has a Parents header, the peer removes each Event ID in its Parents from the set, and then adds each Event ID in its Version. - If the update has no Parents header, it builds on everything the peer holds (Section 2.2), so the set becomes the update's Version. - If the update has no Version header, the current version becomes unknown, and the peer cannot resume by version until an update names one. For a linear history, the set holds a single Event ID. While it holds more than one, the history has parallel branches, and the peer needs a merge (Section 2.1) to build the state at its current version. This computation only compares Event IDs for equality, as opaque strings, so a peer can do it without knowing the Version Type. A peer tracks a separate current version for each timeline. 2.3. Using Versioning with HTTP Methods A peer can send the Version and Parents headers in requests and responses of these methods: - GET - HEAD - PUT - DELETE For each of these methods, if the request has a Version or Parents header, the response should have it too. If the request lacks one, the server may still send it in its response. We now detail the ways in which these headers modify requests and responses for these methods. 2.3.1. GET the current version If a GET request has no Version header, the server responds with the state at the current version, as usual: Request: GET /chat Response: HTTP/1.1 200 OK Version: ("ej4lhb9z78") Parents: ("oakwn5b8qh" "uc9zwhw7mf") Content-Type: application/json Content-Length: 64 [{"text": "Hi, everyone!", "author": {"link": "/user/tommy"}}] The server MAY include a Version and/or Parents header in the response, to indicate the current version and its parents. A client can send a HEAD request to get the Version and Parents without downloading the body: Request: HEAD /chat Response: HTTP/1.1 200 OK Version: ("ej4lhb9z78") Parents: ("oakwn5b8qh" "uc9zwhw7mf") Content-Type: application/json 2.3.2. GET a specific version A server can respond to GET requests for a resource at historical versions, by honoring the Version and Parents headers. A client names the version it wants in the Version header: Request: GET /chat Version: ("ej4lhb9z78") Response: HTTP/1.1 200 OK Version: ("ej4lhb9z78") Parents: ("oakwn5b8qh" "uc9zwhw7mf") Content-Type: application/json Content-Length: 64 [{"text": "Hi, everyone!", "author": {"link": "/user/tommy"}}] 2.3.3. PUT a new version When a client changes a resource's state with a PUT, it can specify the version that the new state is at, and the version that the new state builds on: Request: PUT /chat Version: ("ej4lhb9z78") Parents: ("oakwn5b8qh" "uc9zwhw7mf") Content-Type: application/json Content-Length: 64 [{"text": "Hi, everyone!", "author": {"link": "/user/tommy"}}] Response: HTTP/1.1 200 OK The Version and Parents headers are optional. If a PUT has no Version header, the recipient may assign new Event IDs. If it has no Parents header, the recipient may assume that its current version is the new version's parents. 2.3.4. GET a span of history A client can request a span of history by sending a Parents and a Version header together. The Parents marks the beginning of the span (the oldest versions), and the Version marks its end (the newest versions). Request: GET /chat Version: ("3") Parents: ("1a" "1b") Response: HTTP/1.1 209 Multiresponse Content-Type: application/http-history Cache-Control: no-store Current-Version: ("3") HTTP/1.1 200 OK Version: ("2") Parents: ("1a" "1b") Content-Type: application/json Content-Length: 64 [{"text": "Hi, everyone!", "author": {"link": "/user/tommy"}}] HTTP/1.1 200 OK Version: ("3") Parents: ("2") Content-Type: application/json Content-Length: 117 [{"text": "Hi, everyone!", "author": {"link": "/user/tommy"}} {"text": "Yo!", "author": {"link": "/user/yobot"}] Here the server responds with the 209 Multiresponse status code and the application/http-history media type, both defined in [Multiresponse]. Each sub-response responds to an implied request [Multiresponse]: the request for the span, with its Parents and Version replaced by the sub-response's own. The first sub-response above responds to a request for the span from ("1a" "1b") to ("2"), and the second to a request from ("2") to ("3"). A cache can store each one as the response to its request. The implied request has the sub-response's Version as well as its Parents, because each sub-response holds just one step of the span. If the client just wants the version identifiers for a span of history, it can send its GET with "Method-Override: HEAD". Then the server sends each response in the span as a response to a HEAD, with no content, and sends the same header in its 209 [Multiresponse]: Request: GET /chat Parents: ("ej4lhb9z78") Version: ("oakwn5b8qh") Method-Override: HEAD Response: HTTP/1.1 209 Multiresponse Content-Type: application/http-history Cache-Control: no-store Method-Override: HEAD HTTP/1.1 200 OK Parents: ("ej4lhb9z78") Version: ("i8qa3vu1s0k") HTTP/1.1 200 OK Parents: ("i8qa3vu1s0k") Version: ("v1voi252yr") HTTP/1.1 200 OK Parents: ("i8qa3vu1s0k") Version: ("jn4zuwo0wum") ... 2.3.5. Rules for Version and Parents headers If a GET request contains a Version header: - If the Parents header is absent, the server SHOULD return a single response, containing the resource at the requested version in its body, with the Version response header set to the same version. - If the server does not support historical versions, it MAY ignore the Version header and respond as usual, but MUST NOT include the Version header in its response. If a GET request contains a Parents header: - The server SHOULD send the set of updates from the specified Parents to the specified Version. If the request has no Version, the server should send the updates up to its current version. - If every event in the requested Version, or in the server's current version when the request has no Version, is among the Parents or their ancestors, then the server responds with 304 Not Modified, with a Version header naming that version. - If the server does not support historical versions, then it MAY ignore the Parents header, but MUST NOT include the Parents header in its response. A server does not need to honor historical version requests for every resource, or for all of history. If a server doesn't have the history it needs to honor a request, it responds with status 432 Version Not Found. 2.3.6. Interaction with Conditional Request Headers The Version and Parents headers work alongside the conditional request headers (If-Match, If-None-Match, If-Modified-Since, If-Unmodified-Since) defined in [RFC9110] Section 13. Conditional headers contain entity-tags (ETags) or modification dates, while Version and Parents contain versions. An ETag identifies a representation, which is a variant at a version. A version is a point in time, across all of a resource's variants. So a resource at one version can have several ETags, one for each variant, and one ETag can recur at several versions, when the state returns to an earlier value (Section 3.1.2). With Parents or If-None-Match, a client says that it already holds something, so the server needn't send it again. They differ in what they name, and in how the server can respond: - If-None-Match names representations, and its list of ETags gives alternatives, any one of which can match. The server responds with a 304, or with the whole representation. - Parents names a point in time, across every variant, and the Event IDs in it together make up one version. The server can respond with only what came after it, as patches or as a snapshot. A GET's Parents header also works as a precondition. When the requested version is already among the Parents or their ancestors, the server responds with 304 Not Modified (Section 2.3.5). When a request has both versioning and conditional headers, the server SHOULD first resolve the requested version, and then evaluate any preconditions. In mutation requests (PUT and DELETE), the Parents header serves a purpose related to If-Match's, but distinct. With If-Match, the server rejects the request if the resource's current state does not match. With Parents, the client says which version the mutation builds on, and the server may use it to detect conflicts, perform merges, or construct the version history. 2.4. Status 432: Version Not Found If a server does not have a version that a request needs, it should respond with status 432 Version Not Found, and include copies of the request header(s) with versions it could not satisfy. For example, if a client sends: GET /foo Version: ("alice-44") Parents: ("bob-32") ...but the server does not have the version ("alice-44"), it should respond with: 432 Version Not Found Version: ("alice-44") The server can also include a Current-Version header naming its current version, so that the client knows where it can resume. Peers can drop history at any time. A client cannot rely on any particular portion of history existing on a server or intermediary when it makes a request. We define a new status code because a 404 Not Found is heuristically cacheable (Section 15.1 of [RFC9110]). If a cache ignores the Version header, it could store a 404 for a missing version as the response for the resource itself, and then tell every client that the resource doesn't exist. A cache stores a 432 only when the response has explicit caching information (Section 3 of [RFC9111]), and if a client doesn't know the 432, it treats it as a 400 (Section 15 of [RFC9110]). 2.5. The Current-Version header When a server or client sends historical versions, it can send its current version in a Current-Version header, which has the syntax of a version (Section 2.2.1). The recipient can use it to know when it has caught up. A client also sends Current-Version to start a resumable upload (Section 4.3). A client compares the Current-Version it receives with its own current version (Section 2.2.3), and when they match, it has caught up. So even when no updates arrive, the client can tell whether it is up to date, or still waiting for them. 2.6. Version Types The optional "type" parameter of a version specifies constraints on the format and interpretation of its Event IDs. Peers can use these constraints for optimizations and specialized versioning schemes. For example: Version: ("9531a97");type=git This type could indicate that the Event IDs are git-style hashes, branches, or tags. Peers could verify that the entire repository at a given version hashes to the specified ID. Another example: Version: ("3f84786c-57");type=peer-counter;text-runs Diamond-Types, Automerge, and other algorithms use this format to compress history metadata through run-length encoding of consecutive insertions. With it, a peer stores a run of 50 inserted characters as 50 bytes plus one Event ID. Without it, the run takes 50 bytes plus 50 Event IDs, each of several bytes. Implementers may define custom Version Types to suit specific needs. A Version Vector Event ID might look like this: Version: ("[peerid1:3 peerid2:2]");type=version-vector With Version Vectors, a peer can compute the partial order between any two Event IDs directly, without examining the full version history graph. (To know the order between two Version Vectors A and B, one needs only to compare each peer's counter between A and B. If A dominates across all peers, it is newer. If B dominates, then it is newer. Otherwise, neither came before the other, and they happened in parallel.) 2.7. Versioning through intermediaries An intermediary can use versions to identify, store, and serve each of a resource's states and updates across its history. To distinguish versions, it must add the Version and Parents headers to its cache keys, as this Vary header would: Vary: version, parents A cache can do this without knowing any Version Type: it only compares versions for equality, as sets of opaque Event IDs, after sorting them (Section 2.2.1). A representation at a version does not change (Section 2.1.1), so a response to a GET with a Version header, and without a Parents header, does not go out of date. An origin server can say so with a long freshness lifetime and "Cache-Control: immutable" [RFC8246]. An intermediary SHOULD behave as if every response passing through it had Version and Parents in its Vary header. To support legacy intermediaries that don't know versioning, the origin server is RECOMMENDED to add "version, parents" to the Vary header of all its responses, unless it is certain that no legacy intermediary will process the response. 2.7.1. Detecting legacy intermediaries If a legacy intermediary *does* process a versioned response without the Vary header, a client can detect it, by noticing that the response lacks Event IDs that were in its request's Version and Parents. Here is an example: Presume we start with two versions: PUT /foo Version: ("1") Hello PUT /foo Version: ("2") Hello World! Now, if someone GETs the old version: GET /foo Version: ("1") Then a versioning-aware origin server can return it: HTTP/1.1 200 OK Version: ("1") Hello However, a versioning-unaware intermediary will cache this old response as if it is the current state. This breaks when a client requests the *newest* version: GET /foo Version: ("2") And the cache ignores the Version header and returns what it has most recently seen: HTTP/1.1 200 OK Version: ("1") Hello To detect this, the client SHOULD check that the Version and Parents headers in the response contain a superset of the Event IDs specified in the request. (For this check, the client treats a missing Version or Parents header as an empty set.) When a response lacks the request's Event IDs, the client can warn the programmer, so that he or she knows to add Vary to the server's responses. 3. Analysis of existing versioning approaches in HTTP Existing approaches to versioning in HTTP address disparate use-cases, but have limitations and trade-offs. The Last-Modified and ETag headers were invented for cache consistency, but a peer can't use them to order versions through time, or to represent forks and merges in distributed time. Other versioning systems, such as WebDAV and Link Relations, support forks and merges by defining a new resource to hold an existing resource's state at each version. A client then needs additional network round trips to discover and query these version resources. No existing HTTP versioning system can express custom formats of distributed timestamps, such as version vectors. We now describe each existing approach, and then summarize their limitations for a general versioning system. 3.1. Approaches with versions in headers The "Last-Modified" and "ETag" headers are by far the most commonly used versioning system in HTTP today. They specify a resource's version directly in request and/or response headers. This is simple and efficient, since each version arrives in the message itself, with no extra round trips, and it is very useful in practice for caching and conditional requests. However, their values don't give a peer enough information to compute an accurate order of versions, so they cannot represent distributed time. 3.1.1. Last-Modified The Last-Modified header specifies a wallclock timestamp that caches and clients can use to know when a change has occurred: Last-Modified: Sat, 6 Jul 2024 07:28:00 GMT This header is useful for caching and conditional requests (using the If-Modified-Since header). However, it has several limitations: 1. It is limited to the one-second precision of an HTTP date. If a resource changes within the same second, the Last-Modified date won't change, and caches can become inconsistent. 2. It stores only linear time. It cannot represent distributed (partial order) time, or the ambiguity of forked time. 3. It is susceptible to clock skew in distributed systems, potentially leading to inconsistencies across different servers. 3.1.2. ETag The ETag header is more precise. It specifies a string that uniquely identifies a cacheable representation: ETag: "2u34fa7yorz0" ETags can be strong or weak, with weak ETags prefixed by W/: ETag: W/"2u34fa7yorz0" Clients send ETags in conditional requests, with the If-None-Match and If-Match headers, and can use them for optimistic concurrency control. However: 1. ETags help with cache validation, but do not mark time accurately. A peer has no way to order versions by ETag, or to know which version came before another. 2. An ETag identifies content. So the same ETag can recur over time, if the resource's state returns to an earlier value. 3. A strong ETag depends on the Content-Encoding. If a server sends a resource at one version with different Content-Encodings (e.g., gzip), each one has a different strong ETag. So one version can have several ETags, and one ETag can recur at several versions. 3.2. Approaches defining versions as new resources A second set of approaches creates a new "version resource" to hold an original resource's state at each version, and adds ways to navigate the version resources. These approaches range from hand-copied "foo-v2.html" URIs to standards like WebDAV. However, they all require additional round trips, with new URIs or new methods, to discover and navigate version information. This reduces performance, adds complexity, and makes it hard for intermediaries to use versioning information without making additional network requests of their own. 3.2.1. Creating additional "Version Resources" Application programmers often define copies of a resource, at various versions, by encoding a version identifier into its URI: https://unpkg.com/braid-text@0.0.18/index.js This approach is common, but has several limitations: 1. It loses the semantics of a "resource changing over time", and creates multiple version resources for each resource. 2. It then needs standards that, given a URI, extract the current version of the resource, get the previous and next versions, interpret the format of the versions (e.g., major.minor.patch), and create new versions. We discuss these standards next. 3. It can lead to URI proliferation, complicating a web application's URI space, and potentially impacting caching strategies and SEO. 3.2.2. Standardizing versions as external resources Several standards organize and navigate a set of "version resources." Memento [RFC7089] provides read-only access to prior states through datetime negotiation, but defines no mechanism for creating new versions. WebDAV Versioning [RFC3253] provides a full lifecycle through CHECKOUT/CHECKIN methods, but requires server-assigned version identifiers. Link Relations for Version Navigation [RFC5829] defines link relation types for navigating between version resources, but leaves the creation and management of those versions to other protocols. WebDAV and Link Relations also support a partial order of distributed time, whereas Memento is limited to linear wallclock time. However, with each of these approaches, a client discovers version information by dereferencing additional URIs, with additional network round trips. The headers of a normal GET or PUT to the resource don't have it, so an intermediary can't organize the version history without making network requests of its own. Furthermore, although WebDAV supports forking and merging history, the methods it provides to manipulate the history (CHECKIN, CHECKOUT, VERSION-CONTROL, LOCK, UNLOCK) require a centralized server. 3.3. Requirements for general versioning This section defines the requirements for a general versioning standard. It then provides a feature table showing which requirements each existing approach supports. We derived these requirements from the use-cases in Section 4, which together span the range of versioning needs we have identified in HTTP systems, from cache consistency and incremental updates to distributed collaborative editing and hosting version-control systems like git. 3.3.1. Distributed Time Distributed systems (see the examples in Section 4) require support for distributed time, including: (a) A comparable partial order: Any two Versions (timestamps) A and B must be comparable, such that either: - A > B: A came after B - A < B: A came before B - A <> B: Neither A nor B came before the other (b) Immune to clock skew or limited precision: The ordering must not depend on the skew or precision of a CPU's wallclock. (c) Merges must be specifiable without central authority: Any peer must be able to identify the version resulting from the merger of two parallel versions A and B, without relying on a central authority to assign a new version identifier AB. 3.3.2. Included within existing HTTP requests and responses A general versioning system should include versions within existing HTTP requests and responses, wherever possible, with no additional requests and round trips. This simplifies implementation and improves performance. An intermediary can then understand and accurately cache the version history of state as it passes through, without making additional requests of its own. And an application can add versioning information to its existing resources, and leave its existing URI space unchanged. Changing the URI space would be an unnecessary burden for many applications. We enumerate these requirements as follows: (a) Includes versioning in every existing request and response where state is queried or mutated. (b) Does not require additional round-trips. (c) Does not require polluting the application's URL namespace. 3.3.3. Generalizable Timestamps Some systems have requirements for Version timestamps themselves: (a) Extensible timestamp formats: Advanced distributed systems often devise special formats for partially-ordered timestamps that allow inferences for improved performance, such as lamport clocks, vector clocks, version vectors, hash histories, hybrid logical clocks, and append-only-log indices. Implementations can rely on information embedded in these timestamps to compress history metadata, optimize partial-order computations, or infer the value of state. (b) Independent of resource: Some applications need to assign a single version to multiple resources, so that one can refer to multiple resources at the same point in time. For instance, a git repository (see section 4.2) commits multiple files at the same version. A general versioning system must provide version identifiers that are independent of any particular resource, so that multiple resources can be versioned and referred to at the same points in time. 3.4. Feature table of existing approaches This table shows which requirements each existing approach meets. An X marks a requirement that the approach meets, a ~ one that it partly meets, and a - one that it does not: | 1a | 1b | 1c | 2a | 2b | 2c | 3a | 3b | ------------------------------------------------------------ Last-Modified | - | - | - | X | X | X | - | X | ETags | - | - | - | X | X | X | - | - | Memento | - | ~ | - | - | - | ~ | - | - | WebDAV Versioning | X | X | - | - | - | - | - | - | Link Relations | X | X | - | X | - | ~ | - | - | ------------------------------------------------------------ Because no existing versioning approach satisfies all needs, programmers today must implement multiple approaches to versioning in their applications, each with subtly different logic, and cannot implement common infrastructure for distributed versioning, archiving, and collaborative editing that works across HTTP systems. This document specifies a general versioning model that satisfies all 8 requirements above, by adding versions to HTTP requests and responses (Section 2). 4. Informative Use-Cases A general mechanism for versioning HTTP resources could enable a number of new use-cases: - RSS clients could request incremental updates when polling, and download only the feed items that changed - Servers could accept incoming patches based on old or parallel versions of history, and even rebase those patches for other clients, at other points in history - Collaborative editing could be built directly into HTTP resources, providing the abilities of Google Docs at any URL - Git repositories could be hosted directly over HTTP, with their versioning information in HTTP headers, where git today embeds it within opaque blobs that use HTTP just as a transport - Caches and archives could hold and serve a resource at multiple versions, enabling audits and distributed backups - Distributed databases could standardize network APIs to HTTP, while retaining distributed consistency guarantees This section shows how to support some of these use-cases. 4.1. Incremental RSS Subscription An RSS reader polls its server, and often downloads the entire feed when only a small part of it has changed. With HTTP Resource Versioning, the reader can download just the changes. A client can specify its last known version using the Parents header: Request: GET /feed.rss Accept: application/rss+xml Parents: ("4") The server can then respond with only the changes since that version: Response: HTTP/1.1 200 OK Content-Type: application/rss+xml+patch Version: ("5") Parents: ("4") My RSS Feed This is a new entry Incremental update example http://www.example.com/blog/post/1 This approach significantly reduces bandwidth usage and processing time for both client and server. Peers can use any patch format, such as those in [Updates] or [Range-Patch]. 4.2. Hosting git via HTTP We can host a git repository directly through HTTP, where each file corresponds to a resource, and all have a version history. Git identifies each version with a hash. The server can say so with a "type=git" parameter: Request: GET /repo/readme.md Response: HTTP/1.1 200 OK Content-Type: text/markdown Version: ("9531a9702af0d90dd489050ed8e25f87912a9252");type=git Parents: ("3a4c361f8e0349fe4b25c1ff46ebec1cec66e60f") ... Git also names versions with short strings, such as "HEAD", or the name of any tag or branch. We can request the head of the "development" branch with: Request: GET /repo/readme.md Version: ("development") Response: HTTP/1.1 200 OK Content-Type: text/markdown Version: ("9e26e8837a4f6a4445e74eed744fe8af85efd0c2");type=git Parents: ("1d5f89f8843b33b91d62bf95877e46b23fd86741") ... A client can also request a file at the release tagged "1.3.5": Request: GET /repo/readme.md Version: ("1.3.5") A client can clone a repo by asking for every version from the root to HEAD: Request: GET /repo/readme.md Version: ("HEAD") Parents: ("ROOT") Response: HTTP/1.1 209 Multiresponse Content-Type: application/http-history Cache-Control: no-store HTTP/1.1 200 OK Content-Type: text/markdown Version: ("9e26e8837a4f6a4445e74eed744fe8af85efd0c2");type=git Parents: ("1d5f89f8843b33b91d62bf95877e46b23fd86741") Content-Length: 190 ... HTTP/1.1 200 OK Content-Type: text/markdown Version: ("1d5f89f8843b33b91d62bf95877e46b23fd86741");type=git Parents: ("1cf6ab4ed836d4d7308ac93edbc6fd18a69ef88f") Content-Length: 192 ... In fact, git itself already supports two HTTP protocols: a "dumb" and a "smart" protocol. The dumb protocol uses plain HTTP, but doesn't support incremental updates, so each pull can re-download whole pack files. With the smart protocol, the client specifies the version it has, and the version it wants: 0054want 31f1c37dfa1bf983e4d67e06fac28e8e6f 00093bd7884 HEAD@{1} 0032have e68fe437718c37155c7e3e5f4a3ff17c4f476940 0000 We can express this with HTTP Versioning as: Request: GET /repo/readme.md Version: ("31f1c37dfa1bf983e4d67e06fac28e8e6f") Parents: ("e68fe437718c37155c7e3e5f4a3ff17c4f476940") This expresses aspects of the "smart" git protocol over plain HTTP. 4.3. Resumable uploads protocol With Resource Versioning, a client and server can implement resumable uploads efficiently, which offers an alternative perspective to [Resumable Upload]. To initiate an upload, the client specifies the expected final version, and its Version Type, using the Current-Version header: Request: PUT /something Current-Version: ("abwejf-900");type=peer-counter;bytestream Content-Length: 900 For a successful upload, the server responds as usual: Response: 200 OK If the upload is interrupted, the client can query the server's current state: Request: HEAD /something Parents: ("abwejf-0") The client's next action depends on the server's response: A. Upload complete: Response: 200 OK Parents: ("abwejf-0") Version: ("abwejf-900") B. Partial upload: Response: 206 Partial Content Parents: ("abwejf-0") Version: ("abwejf-400") C. No upload progress: Response: 416 Range Not Satisfiable Based on the response, the client proceeds as follows: - Case A: Upload is complete, no further action needed. - Case B: Resume the upload from the last received byte: Request: PUT /something Current-Version: ("abwejf-900") Parents: ("abwejf-400") Content-Range: bytes 400-900/900 Content-Length: 500 - Case C: Restart the upload from the beginning. This protocol relies on the general semantics of versions. So if a server implements HTTP Resource Versioning with the "peer-counter" Version Type and its "bytestream" modifier, it supports resumable uploads inherently. 4.4. Distributed collaborative editing With other extensions, such as those of [Braid-HTTP], this versioning system can also support full CRDT and OT collaborative editing, so that every URL can gain the functionality of Google Docs. The [Braid-Text] project implements a very efficient style of this. When a client first loads a resource, the server sends its state at a single version: Request: GET https://braid.org/test Accept: text/plain Subscribe: stream Response: HTTP/1.1 209 Multiresponse Subscribe: stream Content-Type: application/http-history Cache-Control: no-store Incremental: ?1 HTTP/1.1 200 OK Version: ("2agvvzgccrq-5");type=peer-counter;text-runs Merge-Type: simpleton Content-Length: 12 Hello world! Then the server sends each update as a patch: Response (continued): HTTP/1.1 200 OK Version: ("4590r8uwm63-18") Parents: ("2agvvzgccrq-5") Content-Length: 1 Content-Range: text [12:12] : HTTP/1.1 200 OK Version: ("4590r8uwm63-19") Parents: ("4590r8uwm63-18") Content-Length: 1 Content-Range: text [13:13] ) This versioning system supports multiple Merge Types [Merge-Types], and they can even co-exist for the same resource. For instance, braid-text supports three merge types at once: - The "simpleton" merge type requires the server to rebase all edits for the client - The "dt" merge type uses a fully peer-to-peer merge algorithm called Diamond-Types - The "yjs" merge type uses Yjs, another peer-to-peer merge algorithm A client can connect with any of these merge types, and can even change merge type on the fly, since they all use the same version history. 4.5. Improved Header Compression with runs A header compression scheme can use the patterns of a Version Type to compress messages significantly. For example, suppose a peer inserts 4 characters in a collaborative editor, with four PUTs that take 480 bytes uncompressed: PUT /some.txt Version: ("3f84786c-57") Parents: ("3f84786c-56") Content-Length: 1 Content-Range: text [471:471] a PUT /some.txt Version: ("3f84786c-58") Parents: ("3f84786c-57") Content-Length: 1 Content-Range: text [472:472] s PUT /some.txt Version: ("3f84786c-59") Parents: ("3f84786c-58") Content-Length: 1 Content-Range: text [473:473] d PUT /some.txt Version: ("3f84786c-60") Parents: ("3f84786c-59") Content-Length: 1 Content-Range: text [474:474] f HTTP's HPACK and QPACK can compress these headers by identifying repeated strings and assigning them codes: U = "PUT /some.txt" W = "Version: (\"3f84786c-{ }\")" X = "Parents: (\"3f84786c-{ }\")" Y = "Content-Length: 1" Z = "Content-Range: text [{ }]" This compression reduces the messages to: U W{57} X{56} Y Z{471:471} a U W{58} X{57} Y Z{472:472} s U W{59} X{58} Y Z{473:473} d U W{60} X{59} Y Z{474:474} f This initial compression reduces the 480 bytes to about 140 bytes. We can compress further, since the numbers in each message's headers increase by 1 from the previous message. The Version Type "type=peer-counter;text-runs" specifies this pattern, so we can use run-length encoding. We compress the entire run of insertions with a new compression function, RUN(a, b, insertions): RUN(a, b, insertions) = for i, char in insertions return ` U W{a+i} X{a+i-1} Y Z{b+i:b+i} char ` Where the parameters are: a: Initial version number b: Initial content range insertions: String of characters to be inserted We could then compress the 480 bytes down to a single 20-byte message: RUN(57, 471, 'asdf') Giving up human readability, we can encode this even more compactly, in 12 bytes: R57,471,asdf We can compress further by encoding the numbers as variable-length integers (such as LEB128 or Protocol Buffers' varint). That could reduce the encoding to 7 bytes for these 4 insertions, just 1.41% of the uncompressed 495 bytes. 4.6. Run-Length Compression without Header Compression In practice, peers can also apply run-length compression without header compression. When an update has the Version Type: type=peer-counter;text-runs ...any peer can receive a series of N insertions as a single update, and infer that the string in the update was composed of N separate insertions. For instance, a peer can compress the four PUT requests of the previous example into a single update, without any header compression: PUT /some.txt Version: ("3f84786c-60");type=peer-counter;text-runs Parents: ("3f84786c-56") Content-Length: 4 Content-Range: text [471:471] asdf This single request expresses the change from version 3f84786c-56 to 3f84786c-60, implicitly including the three intermediate versions (3f84786c-57, 3f84786c-58, and 3f84786c-59). If a client or server understands the "peer-counter" Version Type with "text-runs", it can infer these intermediate versions when it needs them, by slicing the content "asdf" to the appropriate length for each version. For example: - Version 3f84786c-57 would contain "a" - Version 3f84786c-58 would contain "as" - Version 3f84786c-59 would contain "asd" - Version 3f84786c-60 would contain the full "asdf" This approach significantly reduces the number of HTTP requests, and the amount of data, for a series of small, consecutive insertions, and every peer can still identify each intermediate version and reconstruct its state. 5. Acknowledgements This document is derived from the prior draft [Braid-HTTP], written by: - Michael Toomim - Greg Little - Raphael Walker - Bryn Bellomy - Joseph Gentle It also incorporates ideas from: - Rahul Gupta - Duane Johnson - Mitar Milutinovic - Paul Kuchenko 6. Conventions The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as shown here. 7. IANA Considerations 7.1. HTTP Field Registrations This document registers the following fields in the "Hypertext Transfer Protocol (HTTP) Field Name Registry", maintained at , as described in Section 16.3.1 of [RFC9110]: Field Name: Version Status: permanent Structured Type: List Reference: Section 2.2 of this document Field Name: Parents Status: permanent Structured Type: List Reference: Section 2.2 of this document Field Name: Current-Version Status: permanent Structured Type: List Reference: Section 2.5 of this document This document also registers the following status code in the "Hypertext Transfer Protocol (HTTP) Status Code Registry", maintained at , as described in Section 16.2.1 of [RFC9110]: Value: 432 Description: Version Not Found Reference: This document 7.2. Version Type Registry The "Version Type Registry" defines the namespace for the version type names and refers to their corresponding specifications. The registry will be created and maintained at . 7.2.1. Procedure Registration of a Version Type MUST include the following fields: o Type name o Required parameters o Optional parameters o Description o Security considerations o Interoperability considerations o Obsolete/Non-obsolete status o Published specification o Person & email address to contact for further information IANA adds values to this namespace under the "Specification Required" policy (see [RFC8126], Section 4.6), so that anyone can register a Version Type defined outside the IETF. 7.2.2. Version Type Registrations The "peer-counter" Version Type specifies that each Event ID names a peer, and a counter that only increases, with this syntax: Version: ("-");type=peer-counter Or, with a modifier: Version: ("-");type=peer-counter;text-runs Version: ("-");type=peer-counter;bytestream Each peer guarantees that its counter will always increase in subsequent versions. So a recipient can order any two Event IDs from the same peer in time, just by examining their counters. The modifier "text-runs" further constrains the behavior of the counter to enable run-length encoding. Specifically, each Unicode code point that a peer inserts or deletes takes the next value of that peer's counter, counting from zero, and an update's Event ID has the counter of the last code point that it inserts or deletes. So if a peer's last Event ID was "3f84786c-56", and it then inserts 4 code points, its new Event ID is "3f84786c-60". With "text-runs", a recipient can also infer the intermediate versions between any two versions sent explicitly, by assuming that each insertion inserts its characters one by one, left to right, by unicode codepoint, and that each deletion deletes its characters right to left, by unicode codepoint. The "bytestream" modifier specifies that the resource can be considered as an append-only bytestream: Version: ("-");type=peer-counter;bytestream At the version -, the resource then has bytes. For example, "x82ha-344" names the state of the resource when it held 344 bytes, a state that peer x82ha created. This approach creates a direct correspondence between time and space: each version increment represents one additional byte in the stream. 7.2.3. Comments on Version Type Registrations Comments on registered Version Types may be submitted by members of the community to the IANA at iana@iana.org. These comments will be reviewed by the Version Types reviewer and then passed on to the "owner" of the Version Type if possible. Submitters of comments may request that their comment be attached to the Version Type registration itself; if the IANA, in consultation with the Version Types reviewer, approves, the comment will be made accessible in conjunction with the type registration. 7.2.4. Change Procedures Once a Version Type has been published by the IANA, the owner may request a change to its definition. The same procedure that would be appropriate for the original registration request is used to process a change request. Version Type registrations may not be deleted; Version Types that are no longer believed appropriate for use can be declared OBSOLETE; such Version Types will be clearly marked in the list published by the IANA. Significant changes to a Version Type's definition should be requested only when there are serious omissions or errors in the published specification. When review is required, a change request may be denied if it renders entities that were valid under the previous definition invalid under the new definition. The owner of a Version Type may pass responsibility to another person or agency by informing the IANA; this can be done without discussion or review. The IESG may reassign responsibility for a Version Type. The most common case of this will be to enable changes to be made to types where the author of the registration has died, moved out of contact, or is otherwise unable to make changes that are important to the community. 8. Implementation Status This section records the known implementations of this document, as [RFC7942] describes. It is to be removed before publishing as an RFC. Each implementation below sends Version and Parents in an earlier syntax, as quoted strings, and not yet as the Inner Lists of Section 2.2.1. - The reference JavaScript library [Braid-HTTP-JS] sends and parses Version and Parents in requests and responses, and reads Current-Version. - braid-text [Braid-Text] responds to a GET for a specific version, or for the updates since its Parents, and names each event with an agent and a sequence number. - braid-blob [Braid-Blob] responds with 432 (Version Not Found) when it does not have a requested version, and sends Current-Version. - braidfs [Braidfs] synchronizes a folder of local files with HTTP resources, and sends Version and Parents with each PUT. - With braid-chrome [Braid-Chrome], a browser extension for Chrome and Firefox, a developer can view and navigate the version history of a resource. - Editor plugins for Emacs [Braid-Emacs], Neovim [Braid-Neovim], and VS Code [Braid-VSCode] send Version and Parents with each PUT. - The Braid-HTTP polyfill for Cloudflare [CDN-Polyfills] serves a cached copy to a request that names a version only when the copy is at exactly that version (Section 2.7). 9. Security Considerations Event IDs may reveal information about the structure and activity of a resource's editors. For instance, the "peer-counter" Version Type encodes a peer identifier and a monotonically increasing counter, from which an observer can infer who is editing and how frequently. If a server exposes version history, it SHOULD apply the same access control to versioned requests as it does to current-state requests, and SHOULD consider whether the Event IDs themselves leak information that the resource body does not. By requesting a span of history, with Parents and Version headers together, a client could probe for the existence of versions it is not authorized to see. A server MUST NOT reveal version history to a client that is not authorized to access the resource. If acknowledging that a version exists would itself leak information, a server MAY respond with 403 Forbidden or 404 Not Found in place of 432 Version Not Found. If a client requests a long span of history, the server could need significant resources to assemble and send it. A server SHOULD impose limits on the size of its version history responses, and MAY refuse a request that would exceed them: with a 403 (Forbidden), when it won't fulfill the request, or with a 503 (Service Unavailable) and a Retry-After header, when the limit depends on its current load. A server MAY also rate-limit version history requests. As described in Section 2.7.1, a versioning-unaware intermediary may store a response at a historical version, and serve it in response to a request for a different version. If the resource is access-controlled, a client could then receive the resource at a version it was not meant to see, such as a version predating the revocation of sensitive content. An origin server SHOULD include "Vary: version, parents" in its responses, and SHOULD consider adding "Cache-Control: no-store" or "Cache-Control: private" to versioned responses containing sensitive content. Since any peer can generate Event IDs, a malicious client could craft Event IDs that collide with or mimic another peer's IDs. This could corrupt the version history, or cause a server to misattribute edits. If a system depends on the integrity of its version history, it SHOULD validate Event IDs against the format of their Version Type, and SHOULD treat Event IDs from untrusted clients as opaque, unverified claims. 10. References 10.1. Normative References [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate Requirement Levels", BCP 14, RFC 2119, March 1997. [RFC3986] Berners-Lee, T., Fielding, R., and L. Masinter, "Uniform Resource Identifier (URI): Generic Syntax", STD 66, RFC 3986, January 2005. [RFC8126] Cotton, M., Leiba, B., and T. Narten, "Guidelines for Writing an IANA Considerations Section in RFCs", BCP 26, RFC 8126, June 2017. [RFC8174] Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words", BCP 14, RFC 8174, May 2017. [RFC9110] Fielding, R., Ed., Nottingham, M., Ed., and J. Reschke, Ed., "HTTP Semantics", STD 97, RFC 9110, June 2022. [RFC9111] Fielding, R., Ed., Nottingham, M., Ed., and J. Reschke, Ed., "HTTP Caching", STD 98, RFC 9111, June 2022. [RFC9651] Nottingham, M. and P-H. Kamp, "Structured Field Values for HTTP", RFC 9651, September 2024. [Multiresponse] Toomim, M., "Multiresponses for HTTP", draft-toomim-httpbis-braid-multiresponse-00, work in progress, November 2026. [SIMPLER-FIELDS] Toomim, M., "Simpler Fields for HTTP", draft-toomim-httpbis-simpler-fields-00, work in progress, November 2026. 10.2. Informative References [Braid-HTTP] Toomim, M., Little, G., Walker, R., Bellomy, B., and J. Gentle, "Braid-HTTP: State Synchronization for HTTP", draft-toomim-httpbis-braid-http-05, work in progress, November 2026. [Merge-Types] Toomim, M., Milutinovic, M., and B. Bellomy, "Consistent Merge Types for HTTP", draft-toomim-httpbis-braid-merge-types-00, work in progress, November 2026. [Updates] Toomim, M., "Replayable Updates for HTTP", draft-toomim-httpbis-braid-updates-00, work in progress, November 2026. [Range-Patch] Milutinovic, M., Toomim, M., and B. Bellomy, "Range Patch", draft-toomim-httpbis-range-patch-00, work in progress, May 2020. [Resumable Upload] Kleidl, M., Zhang, G., and L. Pardue, "Resumable Uploads for HTTP", draft-ietf-httpbis-resumable-upload, work in progress. [Braid-Text] braid.org, "braid-text", . [RFC7089] Van de Sompel, H., Nelson, M., and R. Sanderson, "HTTP Framework for Time-Based Access to Resource States -- Memento", RFC 7089, December 2013. [RFC3253] Clemm, G., Amsden, J., Ellison, T., Kaler, C., and J. Whitehead, "Versioning Extensions to WebDAV (Web Distributed Authoring and Versioning)", RFC 3253, March 2002. [RFC5829] Brown, A., Clemm, G., and J. Reschke, Ed., "Link Relation Types for Simple Version Navigation between Web Resources", RFC 5829, April 2010. [RFC8246] McManus, P., "HTTP Immutable Responses", RFC 8246, September 2017. [RFC7942] Sheffer, Y. and A. Farrel, "Improving Awareness of Running Code: The Implementation Status Section", BCP 205, RFC 7942, July 2016. [Braid-HTTP-JS] braid.org, "braid-http", . [Braid-Blob] braid.org, "braid-blob", . [Braidfs] braid.org, "braidfs", . [CDN-Polyfills] braid.org, "Braid Polyfills for CDNs", . [Braid-Chrome] braid.org, "braid-chrome", . [Braid-Emacs] braid.org, "braid-emacs", . [Braid-Neovim] braid.org, "braid-neovim", . [Braid-VSCode] braid.org, "braid-vscode", . Authors' Addresses For more information, the authors of this document are best contacted via Internet mail: Michael Toomim Invisible College, Berkeley 2053 Berkeley Way Berkeley, CA 94704 EMail: toomim@gmail.com Web: https://invisible.college/@toomim