Skip to content
zit Artifact [6fd03646c9]
Login

Artifact [6fd03646c9]

Artifact 6fd03646c920580c58f102f0ebab8cf8a0ee90051057a2e793b84e7406aa8dac:






Network Working Group                                    D. R. Hipp, Ed.
Internet-Draft                                                    Fossil
Intended status: Standards Track                                 S. Beal
Expires: 7 January 2027                                        libfossil
                                                              D. Shearer
                                                             6 July 2026


      The Fossil Distributed Version Control System: Sync Protocol
                     draft-fossil-sync-protocol-00

Abstract

   Fossil is a distributed version-control system in which each
   repository holds a full copy of a project's history as a set of
   content-addressed artifacts.  Its sync protocol replicates that
   artifact set between two repositories over HTTP.  This document
   specifies the wire transport, message format, authentication, the
   card vocabulary, and the multi-round synchronisation algorithm: the
   round structure, version negotiation, phantom discovery and
   resolution, the clone procedure, and the termination condition.

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 7 January 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



Hipp, et al.             Expires 7 January 2027                 [Page 1]

Internet-Draft            Fossil Sync Protocol                 July 2026


   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  . . . . . . . . . . . . . . . . . . . . . . . .   3
   2.  Conventions and Terminology . . . . . . . . . . . . . . . . .   5
   3.  Protocol Operation  . . . . . . . . . . . . . . . . . . . . .   7
     3.1.  Endpoint State  . . . . . . . . . . . . . . . . . . . . .   7
     3.2.  Round Structure . . . . . . . . . . . . . . . . . . . . .  12
     3.3.  Phantom Record Lifecycle  . . . . . . . . . . . . . . . .  15
     3.4.  The Clone Loop  . . . . . . . . . . . . . . . . . . . . .  16
     3.5.  Request and Response Pragmas  . . . . . . . . . . . . . .  17
     3.6.  Cookie Card . . . . . . . . . . . . . . . . . . . . . . .  17
     3.7.  Termination . . . . . . . . . . . . . . . . . . . . . . .  17
     3.8.  Client Protocol State Machine . . . . . . . . . . . . . .  20
     3.9.  Robustness Posture  . . . . . . . . . . . . . . . . . . .  24
     3.10. Round Message Layout  . . . . . . . . . . . . . . . . . .  24
   4.  Transport . . . . . . . . . . . . . . . . . . . . . . . . . .  28
     4.1.  Message Compression and Media Types . . . . . . . . . . .  29
     4.2.  Round Structure . . . . . . . . . . . . . . . . . . . . .  30
     4.3.  Cookie Card . . . . . . . . . . . . . . . . . . . . . . .  31
   5.  Card Format . . . . . . . . . . . . . . . . . . . . . . . . .  31
     5.1.  Message Structure . . . . . . . . . . . . . . . . . . . .  31
     5.2.  Card Syntax . . . . . . . . . . . . . . . . . . . . . . .  31
       5.2.1.  Defined Operators . . . . . . . . . . . . . . . . . .  33
     5.3.  Comment Lines . . . . . . . . . . . . . . . . . . . . . .  34
   6.  Authentication  . . . . . . . . . . . . . . . . . . . . . . .  35
     6.1.  Login Card  . . . . . . . . . . . . . . . . . . . . . . .  35
     6.2.  Nonce Derivation  . . . . . . . . . . . . . . . . . . . .  35
     6.3.  Shared Secret . . . . . . . . . . . . . . . . . . . . . .  35
     6.4.  Signature Computation . . . . . . . . . . . . . . . . . .  36
     6.5.  Cookie-Login Mode . . . . . . . . . . . . . . . . . . . .  38
     6.6.  Capability Model  . . . . . . . . . . . . . . . . . . . .  39
   7.  Content Transfer  . . . . . . . . . . . . . . . . . . . . . .  39
     7.1.  file Card . . . . . . . . . . . . . . . . . . . . . . . .  39
     7.2.  cfile Card  . . . . . . . . . . . . . . . . . . . . . . .  40
     7.3.  uvfile Card . . . . . . . . . . . . . . . . . . . . . . .  41
     7.4.  Delta Sources and Phantom Creation  . . . . . . . . . . .  42
     7.5.  Private Content . . . . . . . . . . . . . . . . . . . . .  43
     7.6.  SHA3-256 Peer-Version Gate  . . . . . . . . . . . . . . .  45
     7.7.  Hash-Policy Admission . . . . . . . . . . . . . . . . . .  46
   8.  Clone . . . . . . . . . . . . . . . . . . . . . . . . . . . .  47
     8.1.  Clone Protocol Version  . . . . . . . . . . . . . . . . .  47
     8.2.  Sequence Numbers  . . . . . . . . . . . . . . . . . . . .  48
     8.3.  First Clone Round . . . . . . . . . . . . . . . . . . . .  51



Hipp, et al.             Expires 7 January 2027                 [Page 2]

Internet-Draft            Fossil Sync Protocol                 July 2026


     8.4.  Configuration During Clone  . . . . . . . . . . . . . . .  52
   9.  Synchronisation Strategies  . . . . . . . . . . . . . . . . .  52
     9.1.  Push, Pull, and Sync  . . . . . . . . . . . . . . . . . .  52
       9.1.1.  Unversioned-File Synchronisation  . . . . . . . . . .  52
     9.2.  Announcement and Request Cards  . . . . . . . . . . . . .  56
       9.2.1.  Derived Branch, Tag, Leaf, Closed, and Current-Trunk
               Views . . . . . . . . . . . . . . . . . . . . . . . .  56
     9.3.  Phantom Records . . . . . . . . . . . . . . . . . . . . .  57
     9.4.  Gimme Eligibility Filters . . . . . . . . . . . . . . . .  57
     9.5.  Loop Termination  . . . . . . . . . . . . . . . . . . . .  57
   10. Pragmas . . . . . . . . . . . . . . . . . . . . . . . . . . .  57
     10.1.  Version Negotiation  . . . . . . . . . . . . . . . . . .  58
     10.2.  Unversioned-File Pragmas . . . . . . . . . . . . . . . .  58
     10.3.  Configuration Exchange . . . . . . . . . . . . . . . . .  59
     10.4.  Commit-Lock Pragmas  . . . . . . . . . . . . . . . . . .  60
     10.5.  Private-Content and Discovery Pragmas  . . . . . . . . .  61
   11. Error Handling  . . . . . . . . . . . . . . . . . . . . . . .  63
     11.1.  Error Card . . . . . . . . . . . . . . . . . . . . . . .  63
     11.2.  Message Card . . . . . . . . . . . . . . . . . . . . . .  64
   12. Security Considerations . . . . . . . . . . . . . . . . . . .  67
     12.1.  Credential Replay  . . . . . . . . . . . . . . . . . . .  67
     12.2.  Private-Content Exposure and Protected Transport . . . .  68
     12.3.  Line-Length Bounds and Resource Exhaustion . . . . . . .  68
     12.4.  SHA3-256 Downgrade Prevention  . . . . . . . . . . . . .  68
   13. IANA Considerations . . . . . . . . . . . . . . . . . . . . .  69
   14. References  . . . . . . . . . . . . . . . . . . . . . . . . .  71
     14.1.  Normative References . . . . . . . . . . . . . . . . . .  71
     14.2.  Informative References . . . . . . . . . . . . . . . . .  72
   Appendix A.  Examples . . . . . . . . . . . . . . . . . . . . . .  73
     A.1.  Basic Push/Pull Convergence (Two Rounds)  . . . . . . . .  73
     A.2.  SHA3 Version Gate (Error Card)  . . . . . . . . . . . . .  76
     A.3.  Private Content Suppression . . . . . . . . . . . . . . .  77
     A.4.  Comment Lines and Clock-Skew Timestamp  . . . . . . . . .  80
     A.5.  Authentication Example  . . . . . . . . . . . . . . . . .  81
   Appendix B.  ABNF . . . . . . . . . . . . . . . . . . . . . . . .  82
   Appendix C.  Anomalies  . . . . . . . . . . . . . . . . . . . . .  86
   Authors' Addresses  . . . . . . . . . . . . . . . . . . . . . . .  86

1.  Introduction

   Fossil is a distributed version-control system: each participant
   keeps a full copy of a project's history, so those copies must be
   brought back into agreement as people work in parallel.  This
   document specifies how two Fossil repositories synchronise over HTTP
   — how each obtains the history the other holds, including the initial
   clone of a repository that starts from nothing.





Hipp, et al.             Expires 7 January 2027                 [Page 3]

Internet-Draft            Fossil Sync Protocol                 July 2026


   Two repositories of the same Fossil project hold sets of artifacts.
   Each artifact is named by the hash of its content [FossilArtifact],
   so an artifact is identical wherever it is held and the union of two
   repositories' artifact sets is well-defined.  Synchronisation brings
   the two sets into agreement: each side sends to the other the
   artifacts the other lacks.  Because the set is grow-only and order-
   independent, the result does not depend on direction or timing; the
   sets converge [GSET].

   A repository names an artifact it lacks but knows to exist as a
   phantom: a record carrying a hash but no content.  Synchronisation
   discovers phantoms when the remote side announces artifact hashes the
   local side does not hold, and resolves them by requesting the missing
   content, repeating until no new phantom arises and no outbound work
   remains.

   One repository acts as client and the other as server.  The client
   drives the exchange: it builds a request, the server replies, and the
   client decides from that reply what to send next.  The exchange is a
   sequence of such HTTP round trips.  This document specifies what each
   side sends, how the client updates its state from each reply, and
   when the client determines the sync is complete.

   This document fully specifies the current wire surface for artifact
   transfer and unversioned-file synchronisation: connection and
   framing, authentication, content transfer, phantom resolution, the
   unversioned-file catalog and request/response loop, clone, and
   termination.  One area is intentionally not fully specified here: the
   row-level byte serialization of the table-type configuration groups
   (/user, /reportfmt, /shun, /concealed, /subscriber) is out of scope;
   the config /GROUPNAME SIZE framing is defined but the payload layout
   is not.  An implementation conforming only to this document MAY omit
   table-type configuration exchange; interoperating on those groups
   requires a companion specification.  Where examples or capability
   descriptions touch that area, they illustrate the framing only and do
   not imply that the omitted serialization can be derived from this
   document alone.

   Section 1 is informative; normative wire-protocol requirements appear
   in Sections 3–11; Section 12 contains additional normative security
   and deployment requirements using BCP 14 keywords per Section 2.










Hipp, et al.             Expires 7 January 2027                 [Page 4]

Internet-Draft            Fossil Sync Protocol                 July 2026


2.  Conventions and Terminology

   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.

   The following terms are used throughout this document.

   Artifact, hash, manifest, cluster: :: As defined in [FossilArtifact].

   Phantom: :: A repository record for an artifact whose hash is
   referenced but whose content is not yet present.  Phantom records and
   their lifecycle are specified in Section 3.3.

   Branch, tag, closed marker, leaf, current-trunk: :: Branch, ordinary
   tag, leaf, and closed-leaf repository-state views are derived from
   stored artifacts after artifact-format parsing and repository
   crosslink/tag-state processing.  The Artifact Format defines the T
   card syntax; the derivation of those views is specified in
   [FossilRepoState].  Current-trunk uses those repository-state results
   together with the repository's local main-branch setting, also
   specified in [FossilRepoState].  The sync protocol does not carry a
   separate branch, tag, or current-trunk state channel; it discovers,
   transfers, stores, dephantomizes, and crosslinks the artifacts from
   which each peer computes those views.

   Unversioned file: :: Local repository state outside the content-
   addressed artifact set.  An unversioned-file sync uses pragma uv-
   hash, uvigot, uvgimme, and uvfile cards; the files are not artifacts
   and do not participate in the artifact hash-policy or tag-state
   rules.

   Card: :: One line of a sync message body.  A card begins with a
   single-token operator and is followed by space-separated arguments.
   The card line is terminated by a single LF (0x0a).  See Section 5 for
   the full card grammar.

   Client: :: The repository endpoint that initiates the exchange and
   drives successive rounds until synchronisation is complete.

   Server: :: The repository endpoint that replies to the client's
   requests.  The server is stateless across rounds; all inter-round
   state is maintained by the client.






Hipp, et al.             Expires 7 January 2027                 [Page 5]

Internet-Draft            Fossil Sync Protocol                 July 2026


   servercode: :: A repository instance identifier included in push and
   pull cards.  Each repository carries its own servercode.  The value
   is informational; the receiver does not act on it.

   projectcode: :: An identifier shared by all repositories of the same
   Fossil project.  The server MUST refuse to synchronise when the
   projectcode in a push or pull card does not match its own stored
   projectcode.

   round: :: One HTTP request and the corresponding HTTP response.  A
   round ends when the response body is fully consumed; there is no
   terminal card.

   pragma: :: A directive card whose operator is the literal token
   pragma.  Pragmas carry named parameters and govern protocol features
   such as version exchange, commit locks, and private-content enabling.
   Unknown pragma names are silently ignored; this is the forward-
   compatibility mechanism for the pragma namespace.  An unknown non-
   pragma card operator is a fatal protocol error.

   igot: :: A card by which one side announces that it holds a specific
   artifact.

   gimme: :: A card by which one side requests the content of a specific
   artifact.

   Fossil release integer: :: A decimal integer encoding the Fossil
   software version as MAJOR * 10000 + MINOR * 100 + PATCH.  Fossil 2.0
   produces the value 20000.  This integer is carried in version pragmas
   (Section 10.1) and governs version-gated send decisions.  It is
   distinct from the clone protocol version.

   Clone protocol version: :: A small literal integer argument to the
   clone card indicating the artifact transfer encoding the client
   requests.  Modern clients always send version 3, which causes the
   server to use the compressed cfile transfer path.  This integer is
   independent of the Fossil release integer.

   fossilize encoding: :: The character-escaping scheme defined in
   [FossilArtifact] Section 4, used to encode strings that may contain
   spaces or control characters as arguments in card lines.










Hipp, et al.             Expires 7 January 2027                 [Page 6]

Internet-Draft            Fossil Sync Protocol                 July 2026


3.  Protocol Operation

   A sync is a sequence of rounds.  The client builds a request, sends
   it, reads the reply, updates its state, and decides whether to issue
   another request.  This section specifies the state each side
   maintains, the structure of each round, the phantom discovery and
   resolution loop, the clone loop, the request/response pragma
   semantics, and the termination condition.

   The card formats referenced here are defined in Sections 5 through
   10.  The transport encoding is defined in Section 4.  Authentication
   is defined in Section 6.

3.1.  Endpoint State

   The client maintains the following carried state.  The server is
   stateless across rounds; it maintains no per-session state beyond
   persistent repository data.

   +=====================+==============+==============================+
   | State               | Scope        | Description                  |
   +=====================+==============+==============================+
   | projectcode         | persistent   | The project identifier both  |
   |                     |              | sides must share             |
   +---------------------+--------------+------------------------------+
   | peer version        | per-session  | The remote side's recorded   |
   |                     |              | Fossil release integer       |
   +---------------------+--------------+------------------------------+
   | peer date           | per-session  | The remote side's recorded   |
   |                     |              | version timestamp            |
   +---------------------+--------------+------------------------------+
   | phantom set         | persistent   | Artifact hashes known but    |
   |                     |              | whose content is absent      |
   +---------------------+--------------+------------------------------+
   | clone sequence      | per-session  | Progress cursor for the      |
   | number              |              | modern-clone artifact-       |
   |                     |              | transfer loop.  A positive   |
   |                     |              | value means the server has   |
   |                     |              | more blob-table RIDs to      |
   |                     |              | send and, subject to         |
   |                     |              | Section 8.2, drives a clone  |
   |                     |              | 3 SEQNO reissue in the next  |
   |                     |              | request when that section's  |
   |                     |              | reissue conditions hold; a   |
   |                     |              | value of zero is the         |
   |                     |              | terminator that Section 8.2  |
   |                     |              | signals with clone_seqno 0,  |
   |                     |              | after which the client MUST  |



Hipp, et al.             Expires 7 January 2027                 [Page 7]

Internet-Draft            Fossil Sync Protocol                 July 2026


   |                     |              | NOT reissue a clone card.    |
   +---------------------+--------------+------------------------------+
   | sync cookie         | persistent   | Opaque server token          |
   |                     |              | returned by the client each  |
   |                     |              | round                        |
   +---------------------+--------------+------------------------------+
   | round number        | per-session  | Count of completed request/  |
   |                     |              | reply rounds in this sync    |
   |                     |              | session.  Incremented once   |
   |                     |              | after every reply's cards    |
   |                     |              | have been processed and      |
   |                     |              | immediately before the       |
   |                     |              | Section 3.7 continuation     |
   |                     |              | evaluation of that round,    |
   |                     |              | so any evaluation of the     |
   |                     |              | completed round observes     |
   |                     |              | its post-increment value.    |
   +---------------------+--------------+------------------------------+
   | unversioned catalog | per-session  | Whether pragma uv-hash has   |
   | sent flag           |              | already been sent in this    |
   |                     |              | session                      |
   +---------------------+--------------+------------------------------+
   | unversioned send    | per-session  | Temporary table of local     |
   | table               |              | unversioned names still      |
   |                     |              | eligible to upload, with an  |
   |                     |              | mtimeOnly marker per row     |
   +---------------------+--------------+------------------------------+
   | unversioned upload- | per-session  | Set after pragma uv-push-    |
   | enabled flag        |              | ok; causes queued uvfile     |
   |                     |              | uploads to be emitted in     |
   |                     |              | later rounds                 |
   +---------------------+--------------+------------------------------+
   | unversioned pull-   | per-session  | Set after pragma uv-pull-    |
   | only latch          |              | only; suppresses client      |
   |                     |              | unversioned uploads for the  |
   |                     |              | rest of the session          |
   +---------------------+--------------+------------------------------+
   | outbound send       | per-session  | A single accumulating        |
   | buffer              |              | buffer that holds the body   |
   |                     |              | cards the client will        |
   |                     |              | dispatch as the next         |
   |                     |              | request.  The buffer is      |
   |                     |              | created once at session      |
   |                     |              | start, and each round        |
   |                     |              | follows a fixed sequence:    |
   |                     |              | (1) the accumulated bytes    |
   |                     |              | are dispatched as the        |
   |                     |              | current request body; (2)    |



Hipp, et al.             Expires 7 January 2027                 [Page 8]

Internet-Draft            Fossil Sync Protocol                 July 2026


   |                     |              | exactly once, at the start   |
   |                     |              | of building the next         |
   |                     |              | request, the buffer is       |
   |                     |              | reset; (3) the fixed         |
   |                     |              | request preamble is          |
   |                     |              | reseeded into the empty      |
   |                     |              | buffer — the mandatory       |
   |                     |              | pragma client-version, when  |
   |                     |              | applicable a cookie card     |
   |                     |              | returning any stored server  |
   |                     |              | cookie, any repeated         |
   |                     |              | pragmas that Section 10      |
   |                     |              | requires the client to       |
   |                     |              | repeat this round, and, for  |
   |                     |              | push, pull, and sync         |
   |                     |              | operations only, the fixed   |
   |                     |              | operation card (pull         |
   |                     |              | SERVERCODE PROJECTCODE,      |
   |                     |              | push SERVERCODE              |
   |                     |              | PROJECTCODE, or both);       |
   |                     |              | reply-dependent operation    |
   |                     |              | cards, in particular a       |
   |                     |              | modern-clone clone 3 SEQNO   |
   |                     |              | reissue card, are NOT part   |
   |                     |              | of this fixed preamble       |
   |                     |              | because their eligibility    |
   |                     |              | is only known from the       |
   |                     |              | just-received reply; (4) as  |
   |                     |              | the just-received reply is   |
   |                     |              | processed, reply-driven      |
   |                     |              | cards — for example file     |
   |                     |              | and cfile cards for content  |
   |                     |              | the peer requested with      |
   |                     |              | gimme, uvgimme cards         |
   |                     |              | emitted in response to       |
   |                     |              | uvigot, and, on modern-      |
   |                     |              | clone rounds, the clone 3    |
   |                     |              | SEQNO reissue card when the  |
   |                     |              | reissue guard of             |
   |                     |              | Section 8.2 holds — are      |
   |                     |              | appended; (5) any remaining  |
   |                     |              | next-request additions such  |
   |                     |              | as igot, gimme, and the      |
   |                     |              | trailing randomness comment  |
   |                     |              | are appended; and (6) the    |
   |                     |              | accumulated bytes are        |
   |                     |              | dispatched as the following  |
   |                     |              | request body.  Reply-driven  |



Hipp, et al.             Expires 7 January 2027                 [Page 9]

Internet-Draft            Fossil Sync Protocol                 July 2026


   |                     |              | cards appended during step   |
   |                     |              | (4) survive to the next      |
   |                     |              | dispatch because no second   |
   |                     |              | reset occurs between step    |
   |                     |              | (2) and the next dispatch;   |
   |                     |              | the single reset that opens  |
   |                     |              | each round is the only       |
   |                     |              | point at which the buffer's  |
   |                     |              | contents are discarded.      |
   +---------------------+--------------+------------------------------+
   | pending private     | per-session  | Single-use receive-side      |
   | marker              | (single-use) | private flag awaiting the    |
   |                     |              | next file or cfile card.     |
   |                     |              | It persists across           |
   |                     |              | successive reply bodies      |
   |                     |              | within a single sync         |
   |                     |              | session and is cleared only  |
   |                     |              | when a subsequent file or    |
   |                     |              | cfile card consumes it or    |
   |                     |              | the session ends.  Repeated  |
   |                     |              | private cards leave it set;  |
   |                     |              | intervening comment lines    |
   |                     |              | and other non-content cards  |
   |                     |              | do not consume it; only a    |
   |                     |              | file or cfile card clears    |
   |                     |              | it.  Server-side receive     |
   |                     |              | semantics of the private     |
   |                     |              | marker are scoped to a       |
   |                     |              | single request body and are  |
   |                     |              | specified in Section 7.5.    |
   +---------------------+--------------+------------------------------+
   | new-phantom flag    | per-round    | Set when any igot card       |
   |                     |              | creates a new phantom entry  |
   +---------------------+--------------+------------------------------+
   | content-received    | per-round    | Set when any file or cfile   |
   | flag                |              | card delivers stored bytes,  |
   |                     |              | whether on the normal-       |
   |                     |              | content path or the          |
   |                     |              | dangling-delta path of       |
   |                     |              | Section 3.3                  |
   +---------------------+--------------+------------------------------+
   | peer-has table      | per-round    | Artifact hashes the remote   |
   |                     |              | peer announced or sent in    |
   |                     |              | this round                   |
   +---------------------+--------------+------------------------------+
   | peer-lacks table    | per-round    | Artifact hashes the remote   |
   |                     |              | peer requested with gimme    |
   |                     |              | in this round                |



Hipp, et al.             Expires 7 January 2027                [Page 10]

Internet-Draft            Fossil Sync Protocol                 July 2026


   +---------------------+--------------+------------------------------+
   | unversioned-gimme   | per-round    | Count of uvgimme cards this  |
   | count               |              | round emitted in its own     |
   |                     |              | outbound request body.  It   |
   |                     |              | is a per-round counter, not  |
   |                     |              | a queue of pending names;    |
   |                     |              | each outbound uvgimme card   |
   |                     |              | is appended to the outbound  |
   |                     |              | send buffer as it is         |
   |                     |              | emitted, and this counter    |
   |                     |              | records how many such cards  |
   |                     |              | were emitted in the round.   |
   +---------------------+--------------+------------------------------+
   | unversioned-content | per-round    | Number of uvfile cards       |
   | count               |              | received in this round's     |
   |                     |              | reply                        |
   +---------------------+--------------+------------------------------+
   | private-igot        | per-round    | Number of private-flagged    |
   | counter             |              | (igot HASH 1) cards emitted  |
   |                     |              | by the client in this        |
   |                     |              | round's outbound request     |
   |                     |              | body.  Drives at most one    |
   |                     |              | additional first-cycle       |
   |                     |              | continuation under           |
   |                     |              | Section 3.7(c).              |
   +---------------------+--------------+------------------------------+

                                  Table 1

   When unversioned sync is enabled, the client initializes the
   unversioned send table at session start with every local unversioned
   row whose hash is not null, setting mtimeOnly false for each entry.
   Only the per-round entries of Table 1 — the new-phantom flag, the
   content-received flag, the peer-has table, the peer-lacks table, the
   unversioned-gimme count, the unversioned-content count, and the
   private-igot counter — MUST be created or reset at the start of each
   round, MUST remain available through the Section 3.7 termination
   evaluation of that round, and MUST be destroyed or discarded only
   after that evaluation completes; those per-round entries MUST NOT
   persist across round boundaries and MUST NOT be treated as persistent
   storage.  The per-session entries of Table 1 — including the outbound
   send buffer, the pending private marker, the unversioned send table,
   the unversioned upload-enabled flag, the unversioned pull-only latch,
   the unversioned catalog sent flag, the clone sequence number, the
   round number, the peer version, and the peer date — MUST NOT be reset
   or discarded at round boundaries; each such entry is created once at
   session start (or when the source condition that establishes it first
   fires) and evolves according to the rules given elsewhere in this



Hipp, et al.             Expires 7 January 2027                [Page 11]

Internet-Draft            Fossil Sync Protocol                 July 2026


   document.  The round number is incremented after all reply cards of
   the completed round have been processed and immediately before the
   Section 3.7 continuation evaluation, so any condition that examines
   the round number sees the count of the completed round.

3.2.  Round Structure

   Each request body MUST include a pragma client-version card
   (Section 10.1).  A current client normally begins the body-card
   sequence with that pragma and then writes the operation card.  Two
   transport/order exceptions apply: in body-login mode, when a body
   login card is used, that login card is prepended before the body
   bytes whose nonce it covers; and on the initial round of a private-
   content sync, pragma send-private is written before pragma client-
   version, while later private rounds write client-version first and
   then repeat send-private.  The first round of a modern clone
   suppresses the HTTP/body login card entirely (Section 8.3).  The
   server parses body cards in the order they appear and records the
   peer version when it processes the pragma client-version card.
   Because the server decides SHA3-256 content eligibility from the
   recorded peer version at the moment it processes each content-
   requesting card, a client that may receive SHA3-256-named content
   MUST place pragma client-version before any content-requesting card
   (for example clone or gimme) in the same request: a version pragma
   appearing only after such a card is recorded too late to govern it,
   and the server MAY refuse SHA3-256-named content to a peer whose
   version it has not yet recorded.  Subject to that ordering
   constraint, and to the login/send-private exceptions above, no
   further fixed body position is required beyond the pragma's presence
   in every request.

   The client includes, each round:

   *  pragma client-version naming the client release and, for current
      clients, the source timestamp (Section 10.1);

   *  an operation card when the current round requires one: pull
      SERVERCODE PROJECTCODE, push SERVERCODE PROJECTCODE, or both for a
      bidirectional sync, in every push, pull, and sync request; clone 3
      SEQNO in the initial modern-clone request and in any subsequent
      clone request that reissues after the server's prior reply
      included a push card while the recorded clone sequence number
      remains positive.  A modern-clone cleanup request issued after the
      client has received clone_seqno 0 carries no pull, push, or clone
      card at all (Section 8.2).  As a further narrow exception, in a
      session whose original operation intent was pure push, once a
      matched message pull only * advisory (Section 11.2) has cleared
      SYNC_PUSH while SYNC_PULL was not set, any request whose



Hipp, et al.             Expires 7 January 2027                [Page 12]

Internet-Draft            Fossil Sync Protocol                 July 2026


      Section 3.1 step-(3) preamble reseed occurs after that clear MUST
      omit every operation card and the client MUST NOT synthesize a
      pull, push, or clone card for it.  The immediate next request the
      client dispatches — the buffer whose step-(3) preamble reseed had
      already occurred before the match, when SYNC_PUSH was still set —
      retains its already-seeded push SERVERCODE PROJECTCODE operation
      card and is dispatched with that card unchanged; the operation-
      card omission above first applies to the request assembled after
      the following step-(2) reset (Section 11.2 item 3 details the
      sequence).  Whether any such further request is issued at all
      remains determined by Section 3.7.  The operation-card requirement
      above is otherwise unchanged for every other request type;

   *  gimme HASH cards for eligible phantoms it wants resolved when the
      operation can receive versioned artifacts;

   *  file or cfile cards carrying content the peer requested or that
      the client is pushing (Section 7);

   *  igot HASH cards announcing artifacts the client holds;

   *  reqconfig, configuration, and unversioned-file cards as the
      operation requires (Sections 7, 9, and 10);

   *  optionally, a stored cookie card returning a previously stored
      cookie (Section 3.6);

   *  one or more comment lines carrying per-round randomness
      (Section 5.3).

   For a current protocol-version-3 clone, the first request body is
   therefore the version pragma followed by clone 3 1 and the trailing
   randomness comment.  It contains no login card.  Configuration
   requests and pragma uv-hash are intentionally delayed until after
   clone round 0 (Section 8.4).

   The server reads the request body and replies with any applicable
   combination of:

   *  pragmas responding to client pragmas;

   *  a push SERVERCODE PROJECTCODE card, a pull SERVERCODE PROJECTCODE
      card, or both, when the server advertises its own repository
      identifiers;

   *  igot HASH cards for artifacts the server holds and the client has
      not announced;




Hipp, et al.             Expires 7 January 2027                [Page 13]

Internet-Draft            Fossil Sync Protocol                 July 2026


   *  the content of artifacts the client requested with gimme;

   *  gimme HASH cards for artifacts the client announced with igot that
      the server does not hold;

   *  unversioned-file catalog or content cards when an unversioned-file
      exchange is active;

   *  clone_seqno NEXT when the operation is a modern clone;

   *  message or error cards as needed (Section 11);

   *  a closing comment line carrying a timestamp and error count.

   The client processes each card in the server's reply in order:

  for each card C in the server reply body:
      if C is "push SERVERCODE PROJECTCODE" or
         C is "pull SERVERCODE PROJECTCODE":
          record the peer's advertised SERVERCODE and PROJECTCODE
          use PROJECTCODE as the peer project code for later authenticated rounds
          continue; only a separate rule such as `message pull only *`
          (Section 11.2) changes the selected operation
      else if C is "igot HASH":
          apply the igot/phantom rule of Section 3.3
      else if C is "file ..." or "cfile ...":
          store content (Section 7)
          set content-received flag
      else if C is "gimme HASH":
          record that the peer lacks HASH
          if operation includes push and HASH is held locally
             and policy permits:
              add HASH to the outbound send buffer (Section 3.1) as content
              for the next request
      else if C is "uvigot ..." or "uvfile ..." or "uvgimme ...":
          process under the unversioned-file algorithm of Section 9.1
      else if C is "clone_seqno N":
          set clone sequence number to N  (Section 8.2)
      else if C is "error MSG":
          if first clone round: discard silently  (Section 11.1)
          else: abort the sync  (Section 11)
      else if C is "cookie TEXT":
          store TEXT as the sync cookie for this server (Section 3.6)
      else if C begins with "#":
          process as comment line (Section 5.3); not a card dispatch
      # remaining card types per Sections 5 through 10





Hipp, et al.             Expires 7 January 2027                [Page 14]

Internet-Draft            Fossil Sync Protocol                 July 2026


   After processing all reply cards, the client increments the session
   round number (Section 3.1) and then evaluates the termination
   condition (Section 3.7).  The per-round tracking tables MUST remain
   available through that evaluation and are destroyed only after it
   completes; the client then builds the next request.  This ordering
   matches the per-round-state lifecycle stated in Section 3.1.

3.3.  Phantom Record Lifecycle

   This section is the authoritative phantom, igot, gimme, and phantom-
   related termination rule surface.  Later strategy sections summarize
   this behavior and cross-reference this section rather than restating
   an independent algorithm.

   A phantom is a blob-table entry whose hash is known but whose content
   is absent.  Phantoms arise in two ways:

   1.  An igot HASH card names an artifact the receiver does not hold,
       and the receiver is in an operation state that can obtain that
       artifact.  This covers a pulling or cloning client receiving
       server igot cards and a pushing server receiving client igot
       cards.

   2.  A file or cfile card arrives whose DELTASRC argument names a
       source artifact the receiver does not yet hold.

   Each round creates two temporary tracking tables.  The peer-has table
   records artifact hashes the remote peer announced or successfully
   sent in the round, so this side avoids sending the peer content it
   has just said it already holds.  The peer-lacks table records hashes
   the peer requested with gimme; the request itself is the evidence
   that the peer lacks HASH in this round, and the table prevents this
   side from immediately asking that same peer for the same HASH in the
   same round.  Both are the per-round tracking tables introduced in
   Section 3.1 (Endpoint State) and follow the lifecycle specified
   there.

   When an igot HASH card creates a phantom, the receiver MUST make that
   phantom eligible for gimme emission when constructing a pull, push-
   receive, or eligible clone message.  A side emits gimme for eligible
   phantoms subject to the filters below.  The eligibility filter is
   evaluated freshly each round and excludes:

   (a) hashes present in the peer-lacks table for this round;

   (b) shunned hashes; and





Hipp, et al.             Expires 7 January 2027                [Page 15]

Internet-Draft            Fossil Sync Protocol                 July 2026


   (c) private hashes unless private-content synchronisation is enabled
   for this session.

   A receiver that receives gimme HASH MUST record that the peer lacks
   HASH for this round.  If the receiver holds HASH, the current
   operation direction permits sending it, and hash-policy/private-
   content rules permit disclosure, the receiver sends the content in
   this or a subsequent round.  If the receiver holds HASH but the
   current operation direction does not permit sending it, the receiver
   emits no content and no error card, and does not defer the send to a
   later round.  If the receiver does not hold HASH, it MUST silently
   skip the request without emitting an error card.

   A phantom MUST be deleted when content for that artifact is
   successfully stored.  In the normal path, the content storage
   operation atomically updates the blob row and removes the phantom
   record.  In the dangling-delta path, when a file or cfile card
   arrives with a DELTASRC that is not yet available locally, the delta
   payload for HASH is stored as received, the phantom record for HASH
   is deleted, and a phantom record is created for DELTASRC.  Full delta
   reconstruction is unavailable until the source artifact arrives and
   is thereafter performed on demand.  Both paths set the per-round
   content-received flag defined in Section 3.1, which informs the
   Section 3.7 continuation/termination decision.  No per-artifact
   received state is recorded: the received-but-unreconstructed
   condition is implicit, since HASH stores the delta payload while its
   basis DELTASRC remains a phantom.  HASH is not re-requested because
   its removal from the phantom set suppresses gimme emission, and
   reconstructability is derived when the basis becomes available.

3.4.  The Clone Loop

   A modern clone is a normal sequence of sync rounds.  While artifact
   transfer is in progress the operation card is clone 3 SEQNO; a post-
   clone_seqno 0 cleanup request omits the operation card entirely
   (Section 8.2).  The detailed clone algorithm, including the required
   opening cards, clone_seqno continuation, and first-round login
   suppression, is specified once in Section 8.  This operation section
   only establishes that clone replies are processed by the same card
   dispatch loop and termination rule as other sync rounds.











Hipp, et al.             Expires 7 January 2027                [Page 16]

Internet-Draft            Fossil Sync Protocol                 July 2026


3.5.  Request and Response Pragmas

   Pragmas are ordinary body cards whose operator is pragma; they are
   parsed in round order along with all other body cards.  Unknown
   pragma names are silently ignored and MUST NOT be treated as protocol
   errors, while an unknown non-pragma operator remains a fatal protocol
   error.  The detailed, authoritative catalog of all defined pragma
   names, arguments, directions, duplication behavior, and operation-
   specific effects is Section 10.

3.6.  Cookie Card

   The server MAY emit a cookie TEXT card in its reply, where TEXT is an
   opaque value.  The client that receives such a card MUST store the
   value, replacing any previously held cookie for that server.  The
   client MAY include cookie TEXT in a subsequent request to return the
   stored value.

   The server MUST NOT depend on receiving a cookie card.  The server
   must tolerate cookie cards from a different server and must be
   prepared to receive more than one cookie card in a single request.
   The cookie mechanism is an optimisation only; a client that never
   returns a cookie is fully conformant.

3.7.  Termination

   The client MUST issue another round if and only if at least one of
   the following conditions holds after processing the previous round's
   reply:

   (a) Versioned artifact progress occurred — one or more file or cfile
   cards delivered stored content, including the dangling-delta path, or
   an igot card created a new phantom — and the phantom table is non-
   empty after the round.  Condition (a) is evaluated for every
   operation, including clone; condition (e) supplies additional clone-
   specific continuation rules that apply only when conditions (a)
   through (d) do not already require another round.

   (b) The per-round file-send count is greater than zero, or the per-
   round delta-send count is greater than zero, or a uvfile upload is
   pending against the per-session unversioned send table with the
   session's unversioned upload-enabled flag set (Section 9.1).  For the
   purpose of condition (b), the file-send count is the number of full-
   content file or cfile cards that the client appended to the outbound
   send buffer (Section 3.1) while processing the just-received reply,
   and the delta-send count is the number of delta-form file or cfile
   cards that the client appended to the outbound send buffer during
   that same reply-processing window; both counts are per-round



Hipp, et al.             Expires 7 January 2027                [Page 17]

Internet-Draft            Fossil Sync Protocol                 July 2026


   quantities scoped to that window and are cleared with the buffer's
   step-(2) reset that opens the following round.  Condition (b) reads
   the file-send count and the delta-send count at the moment §3.7 is
   evaluated, not any tally of cards already emitted in the completed
   outbound request body; the shift from cards already emitted in the
   completed request to cards appended while handling the just-received
   reply is what distinguishes condition (b) as a next-dispatch
   continuation trigger.  Because those counts are per-round quantities
   defined here at their point of use, no additional Table 1 row is
   introduced for them.

   (c) A private-content discovery round is required: the per-round
   private-igot counter is greater than zero — that is, the client
   emitted one or more private igot HASH 1 cards in this round's
   outbound request body — and the completed round-trip counter — the
   session round number (Section 3.1), taken after its post-reply
   increment and observed at the moment condition (c) is evaluated — is
   exactly one.  Under this condition the client issues one following
   round to carry any follow-up work arising from those client-emitted
   cards.  Only a round whose post-increment session round number equals
   one causes condition (c) to fire; later rounds do not re-trigger it
   on the private-igot counter alone, and no separate first-cycle flag
   is introduced.

   (d) Unversioned-file uvgimme work is in flight: the per-round
   unversioned-gimme count is greater than zero — that is, this round
   emitted one or more uvgimme cards in its own request body — and
   either one or more uvfile cards were received in the corresponding
   reply or the session round number, after incrementing for the
   completed round, is less than 3.  This is the current three-cycle
   continuation rule of the Fossil wire behavior: before the third
   completed round of the session, a round that emitted uvgimme forces
   one more request even if no uvfile has yet arrived, and on later
   rounds a uvgimme-only round does not by itself continue the exchange.
   Section 9.1 defines how uvgimme, uvfile, and repeated uvigot cards
   advance that state, and the pending-uvfile-upload branch of condition
   (b) covers the other unversioned continuation case, where the
   session's unversioned send table still holds rows eligible for
   upload.

   (e) The operation is a modern clone and none of conditions (a)
   through (d) already required another round; in that else-branch, at
   least one of the following clone-specific limbs holds — this is the
   first clone round; one or more file, cfile, or dangling-delta cards
   delivered stored content in this round; or the recorded clone
   sequence number (Section 3.1) remains positive after the round and
   the session's cumulative count of received artifacts is greater after
   the round than it was before the round.  This nested shape mirrors



Hipp, et al.             Expires 7 January 2027                [Page 18]

Internet-Draft            Fossil Sync Protocol                 July 2026


   the canonical client loop: the outer file/delta/ dangling-plus-
   phantom-remaining path of condition (a) is consulted first, and only
   when it and conditions (b) through (d) do not fire is this clone-
   specific disjunction consulted.  The second limb here does not carry
   condition (a)'s phantom-still-remaining requirement, and the third
   limb is a persisted-sequence-plus-artifact-counter progress check
   distinct from the second.

   Modern-clone artifact transfer therefore terminates on any
   completed round in which no Section 3.7 condition holds, even if
   the recorded clone sequence number remains positive. A client
   that observes a positive recorded clone sequence number together
   with no clone-branch progress this round and no other Section 3.7
   continuation condition has reached a non-advancing peer under
   Section 8.2 and terminates the modern-clone loop rather than
   reissuing `clone 3 SEQNO`; the client MUST NOT introduce a
   further round solely because `clone_seqno 0` has not yet been
   observed.

   Conversely, `clone_seqno 0` prevents the client from ever again
   issuing a `clone 3 SEQNO` card in this session, but it does not by
   itself terminate the exchange: any separately true continuation
   condition of Section 3.7 (a) through (e) — including this
   condition's own first-clone-round limb and its `file`/`cfile`/
   dangling-delta receipt limb, either of which can hold in the same
   round as `clone_seqno 0` — causes the client to issue a further
   round. Only this condition's third clone-branch limb (recorded
   clone sequence number positive together with cumulative-artifact-
   counter progress) cannot arise once `clone_seqno 0` has been
   recorded, by construction of the guard. When a continuation
   condition holds while the reissue guard bars a new `clone 3 SEQNO`
   card (in particular because the recorded clone sequence number is
   zero), the following request is the modern-clone cleanup form of
   Section 8.2 — `pragma client-version` plus the cards required for
   the pending work, and no `clone`, `pull`, or `push` operation
   card. Sections 3.7(e) and 8.2 name the same predicate.

   The client MUST NOT issue another round solely because phantom
   records remain in its local database.  Unresolved phantoms at
   termination are not a protocol error; they arise when a referenced
   artifact was not present on the peer or was excluded by policy.










Hipp, et al.             Expires 7 January 2027                [Page 19]

Internet-Draft            Fossil Sync Protocol                 July 2026


   When none of conditions (a) through (e) holds, the client stops.  The
   server signals no completion; each reply's closing comment line
   carries only a timestamp and an error count.  Stopping means no
   further protocol progress is required: when a round transfers no
   content in either direction, creates no new phantom, and leaves no
   outbound versioned, unversioned, clone, or private-discovery work
   pending, no additional rounds are indicated.

   A worked example of a complete exchange appears in Appendix A.

3.8.  Client Protocol State Machine

   The preceding subsections specify the client's behaviour round by
   round.  This subsection consolidates that behaviour into an explicit
   state machine, using the named-state and carried-state discipline of
   TCP [RFC9293] and the request/response-oriented organizational style
   of IMAP [RFC9051].  It is a restatement for implementers, not a new
   source of requirements: where this subsection and a cited section
   differ, the cited section governs.

   Because the server is stateless across rounds (Section 3.1), only the
   client runs a protocol state machine; the server reacts to each
   request independently.  The client occupies one of five states.




























Hipp, et al.             Expires 7 January 2027                [Page 20]

Internet-Draft            Fossil Sync Protocol                 July 2026


   +==============+===================================================+
   | State        | Meaning                                           |
   +==============+===================================================+
   | INIT         | Operation selected and endpoint state             |
   |              | initialised; no round sent yet.                   |
   +--------------+---------------------------------------------------+
   | CLONE_ROUND0 | The first round of a modern clone is in flight:   |
   |              | the login card is suppressed, configuration and   |
   |              | uv-hash are deferred, and an error reply is       |
   |              | ignored (Sections 8.3, 8.4, 11.1).                |
   +--------------+---------------------------------------------------+
   | EXCHANGE     | The steady round loop: an authenticated request   |
   |              | is sent, the reply is processed by the card-      |
   |              | dispatch loop of Section 3.2, and phantom,        |
   |              | outbound-send-buffer, unversioned-file,           |
   |              | configuration, and private-content work advances. |
   +--------------+---------------------------------------------------+
   | TERMINATED   | Clean stop: after the last round, none of the     |
   |              | continuation conditions of Section 3.7 held.      |
   +--------------+---------------------------------------------------+
   | ABORTED      | A fatal protocol error ended the sync without     |
   |              | convergence.                                      |
   +--------------+---------------------------------------------------+

                                 Table 2

                          +------+
                          | INIT |
                          +--+---+
           clone 3 SEQNO   /    \   push / pull / sync
                          v      \
                +--------------+  \
                | CLONE_ROUND0 |   \
                +------+-------+    \
              reply;   |            |
       errors ignored  |            |
           (11.1)      v            v
                     +----------------+ ---.
                     |    EXCHANGE    |    | any of 3.7 (a)..(e):
                     +----------------+ <--'  send next round
                        |          |
           none of 3.7  |          |  fatal protocol error
           (a)..(e)     |          |
                        v          v
                 +------------+  +---------+
                 | TERMINATED |  | ABORTED |
                 +------------+  +---------+




Hipp, et al.             Expires 7 January 2027                [Page 21]

Internet-Draft            Fossil Sync Protocol                 July 2026


   The transitions are:

   +============+=======================+================+============+
   |From        |Event or guard         |Action          |To          |
   +============+=======================+================+============+
   |INIT        |operation is a modern  |send the first  |CLONE_ROUND0|
   |            |clone (clone 3 SEQNO)  |clone request:  |            |
   |            |                       |clone 3 1, no   |            |
   |            |                       |login card,     |            |
   |            |                       |configuration   |            |
   |            |                       |and uv-hash     |            |
   |            |                       |deferred        |            |
   |            |                       |(Section 8.3)   |            |
   +------------+-----------------------+----------------+------------+
   |INIT        |operation is push,     |send the first  |EXCHANGE    |
   |            |pull, or sync          |request:        |            |
   |            |                       |authentication  |            |
   |            |                       |(login card or  |            |
   |            |                       |cookie,         |            |
   |            |                       |Section 6),     |            |
   |            |                       |pragma client-  |            |
   |            |                       |version, and the|            |
   |            |                       |operation card  |            |
   +------------+-----------------------+----------------+------------+
   |CLONE_ROUND0|reply processed        |record          |EXCHANGE    |
   |            |                       |clone_seqno     |            |
   |            |                       |(Section 8.2);  |            |
   |            |                       |any error card  |            |
   |            |                       |is discarded    |            |
   |            |                       |silently        |            |
   |            |                       |(Section 11.1)  |            |
   +------------+-----------------------+----------------+------------+
   |CLONE_ROUND0|transport failure or   |abort           |ABORTED     |
   |            |redirect limit exceeded|                |            |
   +------------+-----------------------+----------------+------------+
   |CLONE_ROUND0|any client-locally-    |abort           |ABORTED     |
   |            |detected fatal parse/  |                |            |
   |            |protocol error listed  |                |            |
   |            |in the Section 3.8     |                |            |
   |            |catalog (other than a  |                |            |
   |            |received wire error    |                |            |
   |            |card, which is         |                |            |
   |            |discarded per          |                |            |
   |            |Section 11.1)          |                |            |
   +------------+-----------------------+----------------+------------+
   |EXCHANGE    |at least one           |build and send  |EXCHANGE    |
   |            |continuation condition |the next request|            |
   |            |of Section 3.7 (a)-(e) |for the         |            |



Hipp, et al.             Expires 7 January 2027                [Page 22]

Internet-Draft            Fossil Sync Protocol                 July 2026


   |            |holds                  |operation       |            |
   +------------+-----------------------+----------------+------------+
   |EXCHANGE    |none of Section 3.7    |stop; the client|TERMINATED  |
   |            |(a)-(e) holds          |sends nothing   |            |
   |            |                       |further and the |            |
   |            |                       |server signals  |            |
   |            |                       |no completion   |            |
   +------------+-----------------------+----------------+------------+
   |EXCHANGE    |a fatal protocol error |reset partial   |ABORTED     |
   |            |occurs (below)         |output and abort|            |
   +------------+-----------------------+----------------+------------+

                                 Table 3

   A fatal protocol error is any of: an unknown non-pragma card operator
   (Section 5); a card or comment line that exceeds the Section 5
   maximum line length; a malformed file, cfile, or uvfile card
   (Section 7); a project-code mismatch (Section 9.1); an authentication
   failure (Section 6); a redirect limit exceeded (Section 4); or a
   clone_seqno value below zero (Section 8.2).  Of these, the client-
   locally-detected failures (an unknown non-pragma card operator, an
   overlength card or comment line, a malformed content card, a redirect
   limit exceeded, and a negative clone_seqno) are fatal in every client
   state, including CLONE_ROUND0.  An error card (Section 11) is also
   fatal in every state except CLONE_ROUND0, where the first clone
   reply's error card is silently discarded (Section 11.1).  Project-
   code and authentication failures remain fatal at the endpoint that
   detects them, and when such a failure is relayed to a peer as a wire
   error card the receiver's handling is governed solely by
   Section 11.1, including CLONE_ROUND0's first-reply discard.  The
   SHA3-256 peer-version refusal of Section 7.6 is distinct: it is per-
   artifact at the detecting sender and does not, by itself, terminate
   construction of the current request or reply; it manifests on the
   wire only as an outgoing error card appended to that message, whose
   receipt by the peer is direction-dependent (Section 7.6): when the
   sender is the server, the receiving client's handling is governed by
   Section 11.1 (including CLONE_ROUND0's first-reply discard); when the
   sender is the client, the direction-invalid error card in a client
   request is handled by the receiving server under Section 7.6's reset-
   and-continue rule rather than by Section 11.1.  Within EXCHANGE, the
   per-card processing of each reply is the dispatch loop of
   Section 3.2, and the guard between another EXCHANGE round and
   TERMINATED is the condition of Section 3.7.








Hipp, et al.             Expires 7 January 2027                [Page 23]

Internet-Draft            Fossil Sync Protocol                 July 2026


3.9.  Robustness Posture

   This protocol is intentionally strict about malformed input.  It does
   not adopt the receive-side half of the TCP robustness principle as
   summarized in [RFC9293] (liberal acceptance of input).  A conforming
   client is conservative in what it sends.  It emits only well-formed
   cards, in the order Section 3.2 requires.  A receiver MUST reject, as
   a fatal error, an unknown non-pragma card operator (Section 5) and a
   malformed content card (Section 7).

   The reason is interoperability.  A parser that guessed at malformed
   or unrecognised input would let two independent implementations
   diverge, one tolerating what the other rejects, and it would widen
   the input surface an attacker can probe.  The protocol is liberal
   only where liberality cannot cause divergence, and it names each such
   point.  Unknown pragma names are ignored (Section 10).  Receivers MAY
   accept runs of Fossil whitespace between tokens as a compatibility
   extension (Section 5), but senders MUST emit the canonical single-
   space form and MUST NOT rely on surplus trailing tokens being
   preserved.  A cookie card from an unexpected server is tolerated
   (Section 3.6).  An error card in the first round of a clone is
   discarded (Section 11.1).  At any other point, a receiver applies a
   defined rule or aborts.

3.10.  Round Message Layout

   This subsection consolidates, for quick reference, the cards that
   make up a request body and a reply body.  It restates the lists of
   Section 3.2; where it and Section 3.2, or the owning section of a
   card, differ, those sections govern.

   Request body, in the order a client writes it (a row that names an
   optional or conditional card is included only when the card is
   present in that request; the table lists cards in the wire order in
   which they appear when they do):

   +==================+=====================================+==========+
   | Card             | When present                        | Owning   |
   |                  |                                     | section  |
   +==================+=====================================+==========+
   | login card (body | authentication, when the body-      | Sections |
   | form)            | embedded login card of Section 6.1  | 6.1,     |
   |                  | is used — that is, cookie-login     | 6.5, 8.3 |
   |                  | mode (Section 6.5) is not in        |          |
   |                  | effect and the request is not the   |          |
   |                  | first modern-clone request of       |          |
   |                  | Section 8.3; when present, the      |          |
   |                  | body login card is the first card   |          |



Hipp, et al.             Expires 7 January 2027                [Page 24]

Internet-Draft            Fossil Sync Protocol                 July 2026


   |                  | of the request body, preceding      |          |
   |                  | pragma client-version               |          |
   +------------------+-------------------------------------+----------+
   | pragma client-   | every request, before any content-  | Section  |
   | version          | requesting card                     | 10.1     |
   +------------------+-------------------------------------+----------+
   | operation card   | every push, pull, or sync request;  | Sections |
   | (pull, push, or  | the initial modern-clone request;   | 3.1, 8,  |
   | clone 3 SEQNO)   | a subsequent modern-clone request   | 9        |
   |                  | that reissues only after a server   |          |
   |                  | push card while the recorded clone  |          |
   |                  | sequence number remains positive.   |          |
   |                  | A modern-clone cleanup request      |          |
   |                  | issued after clone_seqno 0 carries  |          |
   |                  | none of these cards.  In a session  |          |
   |                  | whose original operation intent     |          |
   |                  | was pure push, once a matched       |          |
   |                  | message pull only * advisory        |          |
   |                  | (Section 11.2) has cleared          |          |
   |                  | SYNC_PUSH while SYNC_PULL was not   |          |
   |                  | set, any further request whose      |          |
   |                  | issuance is required by the         |          |
   |                  | ordinary termination conditions of  |          |
   |                  | Section 3.7 MUST omit every         |          |
   |                  | operation card and the client MUST  |          |
   |                  | NOT synthesize a pull, push, or     |          |
   |                  | clone card for it.  Whether such a  |          |
   |                  | further request is issued at all    |          |
   |                  | remains determined by Section 3.7.  |          |
   |                  | All other request types remain      |          |
   |                  | subject to the requirement above.   |          |
   +------------------+-------------------------------------+----------+
   | cookie TEXT      | returning a stored per-server       | Section  |
   | (state cookie)   | state cookie previously delivered   | 3.6      |
   |                  | by the server as a reply-body       |          |
   |                  | cookie card (Section 3.6); when     |          |
   |                  | present, this card immediately      |          |
   |                  | follows the operation card (or,     |          |
   |                  | when no operation card is present,  |          |
   |                  | immediately follows pragma client-  |          |
   |                  | version).  This is the body state-  |          |
   |                  | cookie card and is distinct from    |          |
   |                  | the HTTP Cookie: x-f-l-c= cookie-   |          |
   |                  | login header of Section 6.5.        |          |
   +------------------+-------------------------------------+----------+
   | gimme HASH       | eligible phantoms to resolve        | Sections |
   |                  |                                     | 7, 9     |
   +------------------+-------------------------------------+----------+



Hipp, et al.             Expires 7 January 2027                [Page 25]

Internet-Draft            Fossil Sync Protocol                 July 2026


   | file / cfile     | content the client pushes or the    | Section  |
   |                  | peer requested                      | 7        |
   +------------------+-------------------------------------+----------+
   | igot HASH        | artifacts the client holds          | Section  |
   |                  |                                     | 9        |
   +------------------+-------------------------------------+----------+
   | reqconfig,       | as the operation requires           | Sections |
   | configuration,   |                                     | 7, 9, 10 |
   | unversioned-file |                                     |          |
   | cards            |                                     |          |
   +------------------+-------------------------------------+----------+
   | comment line (#) | every request, carrying per-round   | Section  |
   |                  | randomness                          | 5.3      |
   +------------------+-------------------------------------+----------+

                                  Table 4

   *Table 4 note — pragma send-private:* On the initial private-content
   round it precedes pragma client-version; on later private-content
   rounds it follows pragma client-version.  Section 3.2 is
   authoritative.

   Three distinct authentication states appear at the top of a request
   and MUST NOT be conflated: (i) body-embedded login, in which the
   request begins with the body login card of Section 6.1 and no Cookie:
   x-f-l-c= header is sent; (ii) cookie-login mode (Section 6.5), in
   which the login card is carried in the HTTP Cookie: x-f-l-c= header
   instead of the message body and no body login card is written — the
   request body therefore begins with pragma client-version rather than
   a body login card, and the HTTP Cookie: x-f-l-c= header is a
   transport-layer artefact outside this table; and (iii) first modern-
   clone request (Section 8.3), in which both the body login card and
   the Cookie: x-f-l-c= cookie-login header are suppressed because the
   client has not yet learned the peer identifiers required to construct
   a login.  The body state-cookie card cookie TEXT in the fourth row is
   a separate mechanism, driven by Section 3.6, and its presence or
   absence is independent of which of (i)–(iii) is in effect.

   Reply body, any applicable combination:

    +==================+==================================+==========+
    | Card             | When present                     | Owning   |
    |                  |                                  | section  |
    +==================+==================================+==========+
    | pragmas          | responding to client pragmas     | Section  |
    |                  |                                  | 10       |
    +------------------+----------------------------------+----------+
    | push / pull      | the server advertises its own    | Sections |



Hipp, et al.             Expires 7 January 2027                [Page 26]

Internet-Draft            Fossil Sync Protocol                 July 2026


    |                  | identifiers; the card is         | 3, 8.2,  |
    |                  | recorded, and only a separate    | 8.3, 9,  |
    |                  | rule such as message pull only * | 11       |
    |                  | changes the selected operation.  |          |
    |                  | In a modern-clone accepting or   |          |
    |                  | terminal response (Section 8.2)  |          |
    |                  | push SERVERCODE PROJECTCODE      |          |
    |                  | appears after clone_seqno NEXT   |          |
    |                  | and any preceding cfile artifact |          |
    |                  | cards; in a first-round refusal  |          |
    |                  | response (Section 8.3) push      |          |
    |                  | SERVERCODE PROJECTCODE appears   |          |
    |                  | before the refusal error card    |          |
    +------------------+----------------------------------+----------+
    | igot HASH        | artifacts the server holds that  | Section  |
    |                  | the client did not announce      | 9        |
    +------------------+----------------------------------+----------+
    | content (file /  | artifacts the client requested   | Sections |
    | cfile)           | with gimme; in a modern-clone    | 7, 8.2   |
    |                  | accepting or terminal response   |          |
    |                  | the reply carries zero or more   |          |
    |                  | cfile artifact cards in transfer |          |
    |                  | order, preceding clone_seqno     |          |
    |                  | NEXT (Section 8.2) — the         |          |
    |                  | version-3 clone-artifact path    |          |
    |                  | emits only cfile                 |          |
    +------------------+----------------------------------+----------+
    | gimme HASH       | artifacts the client announced   | Section  |
    |                  | that the server lacks            | 9        |
    +------------------+----------------------------------+----------+
    | unversioned-file | an unversioned-file exchange is  | Section  |
    | catalog /        | active                           | 9.1      |
    | content cards    |                                  |          |
    +------------------+----------------------------------+----------+
    | clone_seqno NEXT | the operation is a modern clone  | Section  |
    |                  | and the reply is an accepting or | 8.2      |
    |                  | terminal clone-card response;    |          |
    |                  | the reply ends the clone-card    |          |
    |                  | sequence with clone_seqno NEXT,  |          |
    |                  | then push SERVERCODE             |          |
    |                  | PROJECTCODE.  A first-round      |          |
    |                  | refusal response (Section 8.3)   |          |
    |                  | omits clone_seqno entirely       |          |
    +------------------+----------------------------------+----------+
    | message / error  | as needed                        | Section  |
    |                  |                                  | 11       |
    +------------------+----------------------------------+----------+
    | closing comment  | every reply, carrying a          | Section  |



Hipp, et al.             Expires 7 January 2027                [Page 27]

Internet-Draft            Fossil Sync Protocol                 July 2026


    | line             | timestamp and error count        | 5.3      |
    +------------------+----------------------------------+----------+

                                 Table 5

   *Table 5 note — clone-card responses:* A clone-card response
   constrains the relative ordering of its clone-specific cards, not the
   entire reply body (Section 8.2).  Other cards this document
   authorizes MAY appear in the same reply — a pragma server-version in
   response to the client's pragma client-version (Section 10.1)
   precedes the clone-specific cards; a message or error card MAY appear
   where its owning section authorizes it (Section 11); and the closing
   comment line carrying a timestamp and error count is always the last
   line of the reply body (Section 5.3).  In an accepting response
   (positive NEXT) or a terminal response (NEXT=0) the clone-specific
   cards appear in the order zero or more cfile artifact cards in
   transfer order, then clone_seqno NEXT, then push SERVERCODE
   PROJECTCODE.  If the SHA3-256 peer-version send gate (Section 7.6)
   fires during an attempted cfile send in such a response, an outgoing
   error card is written in the artifact-card slot for the gated
   artifact in place of that artifact's cfile card and precedes any
   later clone_seqno NEXT and push SERVERCODE PROJECTCODE in the same
   response; this per-artifact gate MAY repeat for further SHA3-named
   artifacts reached in the same round.  In a first-round refusal
   response (Section 8.3) the clone-specific cards are push SERVERCODE
   PROJECTCODE and then the refusal error card, with no cfile cards and
   no clone_seqno.  Outside the refusal position and the SHA3-gate
   position specified here, no requirement is placed on where an error
   card appears in a clone-card response, and a clone-card response is
   not required to carry any error card.  Section 8.2 is authoritative
   and governs where its wording differs from this note.

4.  Transport

   A sync message is carried in the body of an HTTP/1.1 POST request
   [RFC9110].  The request-target is the path of the repository URL
   unchanged.  The client MUST NOT append any path component, including
   /xfer, to the request-target; a URL whose path component is empty
   uses /.  Requests whose Content-Type is application/x-fossil or
   application/x-fossil-debug are dispatched to the sync handler
   regardless of request-target path; the bare repository URL is
   therefore sufficient.  application/x-fossil-uncompressed is a clone-
   reply media type, not a request media type.

   The message format is identical regardless of whether it is carried
   over HTTP, HTTPS, or SSH.  When the transport is SSH the client
   includes the header X-Fossil-Transport: SSH; this is the only
   protocol-defined non-standard header.  Standard HTTP headers —



Hipp, et al.             Expires 7 January 2027                [Page 28]

Internet-Draft            Fossil Sync Protocol                 July 2026


   Content-Type, Content-Length, User-Agent, and, when applicable,
   Proxy-Authorization, Authorization: Basic, and Host — are included as
   normal.

   A sync request is always an HTTP POST.  A sync message is never
   empty, so the empty-payload case that would otherwise be sent as a
   GET does not arise during synchronisation.  A server MUST NOT process
   sync cards from a request whose method is not POST; such a request is
   instead redirected to the repository home page.  The request MUST
   carry a Content-Length header giving the exact byte count of the
   payload that follows the header block.

   The client reads the reply body as delimited by the reply Content-
   Length header, consuming exactly that many payload bytes after the
   blank line that terminates the reply header block.  If the reply
   carries no Content-Length, the client instead reads until the
   connection closes, growing its receive buffer as needed up to a hard
   cap of 536,870,912 bytes (512 MiB); a reply that would exceed that
   cap MUST be rejected rather than buffered without bound.

   An HTTP transport exchange MUST NOT follow more than 19 HTTP
   redirects (status 301, 302, 307, or 308) for a single exchange; on
   encountering a twentieth redirect, the exchange MUST abort with a
   fatal redirect-limit error.  A transport exchange MUST NOT downgrade
   an HTTPS connection to plaintext HTTP as the result of following a
   redirect, and MUST NOT follow a redirect at all when the original
   transport is SSH or a local file path.  When following a redirect, a
   trailing "/xfer" path segment present in the target location MUST be
   stripped before the location is used as the new request base; this
   stripping is repeated while such a segment remains.  No other path
   transformation is applied, and the original repository URL's path is
   not consulted: after stripping, the (possibly shortened) target
   location is parsed as the new base URL.  ("/xfer" is the sync
   endpoint suffix a server appends when redirecting a client to its
   transfer endpoint; removing it recovers the base repository URL.)

4.1.  Message Compression and Media Types

   Three Content-Type values are defined:

   application/x-fossil :: Normal sync.  The message body is compressed
   with zlib [RFC1950].  This media type is used for all requests and
   for replies to pull, push, and sync operations.

   application/x-fossil-debug :: Diagnostic debugging and testing only.
   The message body is NOT compressed; content is otherwise identical to
   application/x-fossil.  This variant MUST NOT be used in production
   sync exchanges.



Hipp, et al.             Expires 7 January 2027                [Page 29]

Internet-Draft            Fossil Sync Protocol                 July 2026


   application/x-fossil-uncompressed :: Clone replies only.  The server
   uses this Content-Type for its response to a clone request when the
   clone protocol version is 3 (Section 8).  The response body is NOT
   zlib-compressed at the outer message level; this is because the cfile
   payloads it contains are already in compressed form as stored in the
   repository, and recompressing them would waste CPU without reducing
   size.

   These three media types are used only within the Fossil sync protocol
   context.  Their registration is requested in Section 13.

   Whole-payload transport compression and stored blob content share one
   compression primitive and container format.  The compressed container
   consists of a 4-byte big-endian field giving the uncompressed length
   of the input offered to the compressor by the sender, followed by a
   standard zlib [RFC1950] stream: a 2-byte zlib header, a DEFLATE-
   compressed body [RFC1951], and a 4-byte Adler-32 trailer.  This
   container is neither raw headerless DEFLATE nor gzip framing.

   A sender MUST encode the actual length of the pre-compression input
   in the 4-byte length field.  A receiver MUST use that field as the
   allocation capacity for its decompression output buffer, MUST reject
   the envelope when the zlib stream is malformed, when its Adler-32
   trailer does not verify, or when the decoder cannot fit its output
   within that capacity, and MUST NOT require the actual decoded length
   to equal the field value when the decoder succeeds within capacity.
   The actual decoded length is the number of payload bytes the receiver
   processes.

   The ordinary single-input compression path, used for a normal payload
   or a stored blob, uses the zlib default compression level.  When a
   login card is prepended ahead of the body in inline login mode
   (Section 6.1), the same container is produced over the two
   concatenated inputs and is compressed at DEFLATE level 9;
   implementations MUST NOT rely on a particular compression level,
   since the container is self-describing and the decompressor is level-
   independent.  Because stored blob content is written and
   retransmitted verbatim, the container used for wire transport and the
   container used for stored content are byte-identical.

4.2.  Round Structure

   A sync session consists of one or more rounds.  Each round is one
   HTTP POST request and its response; a round ends when the response
   body is exhausted.  There is no terminal card (no done or end
   operator) in the Fossil sync protocol; a receiver MUST NOT treat body
   exhaustion as an error.




Hipp, et al.             Expires 7 January 2027                [Page 30]

Internet-Draft            Fossil Sync Protocol                 July 2026


   Section 3.2 specifies the round model.  That model covers the cards
   each side emits each round, the client's reply-processing loop, and
   the per-round tracking-table requirements.

4.3.  Cookie Card

   The sync protocol defines a cookie TEXT wire card as a transport-
   layer optimisation.  Section 3.6 defines the storage, return, and
   server-tolerance rules.  This section records only the wire role: the
   card carries one opaque TEXT value, may appear in either a request or
   a response, and has no protocol-defined meaning beyond being returned
   by convention between the same peers.

5.  Card Format

5.1.  Message Structure

   A sync message body is a sequence of lines.  Lines are separated by a
   single LF byte (0x0a); there are no other line-ending conventions.
   Each line is either a card or a comment line (Section 5.3).

   Several cards carry a byte-counted payload (Sections 7 and 10).  For
   those cards the payload begins on the byte immediately after the card
   line's terminating LF; the payload MAY contain LF bytes.  A receiver
   MUST locate the end of the payload by its declared byte count and
   MUST NOT scan the payload for a separator.  A message parser that
   splits on LF without accounting for byte-counted payloads will
   misframe any payload containing a LF byte.

5.2.  Card Syntax

   A card is an operator token followed by zero or more argument tokens,
   terminated by a single LF byte.  The canonical wire form uses exactly
   one space character between adjacent tokens, with no leading or
   trailing whitespace on the line.  An operator token is a sequence of
   lowercase US-ASCII letters and, for clone_seqno, an underscore.  The
   character encoding scheme for argument values that contain spaces,
   tabs, newlines, or other special characters is the fossilize scheme
   defined in [FossilArtifact] Section 4; receivers MUST apply
   defossilize decoding to fossilize-encoded arguments before use.











Hipp, et al.             Expires 7 January 2027                [Page 31]

Internet-Draft            Fossil Sync Protocol                 July 2026


   On receipt, the reference parser tokenizes a card line on runs of
   Fossil whitespace inside the line: SP, HTAB, VT, FF, or CR.  LF
   remains the card-line terminator and is not part of any token.  A
   receiver MUST accept the canonical single-space form.  A receiver MAY
   additionally accept other Fossil-whitespace runs as a compatibility
   extension, interpreting each such run as a token separator; a sender
   MUST NOT rely on that extension and MUST NOT emit non-canonical
   separators.

   A receiver MUST impose a finite maximum length on each card line and
   comment line, measured from the first byte of the line through and
   including its terminating LF, and excluding any following byte-
   counted payload.  This specification sets that maximum to 65535
   bytes.  If the terminating LF is not found within that bound, the
   receiver MUST treat the line as malformed and abort the exchange
   before attempting payload framing.

   An unknown card operator (a first token that is not one of the
   defined operators listed in Section 5.2.1) is a fatal protocol error,
   and the receiver's response is endpoint-specific.  When the server
   observes such a card, it MUST reset its partial output and emit an
   outbound error card before aborting the exchange.  When the client
   observes such a card — for example while processing a server reply —
   it MUST NOT emit any outbound error card in response (the client-side
   reply-processing loop has no wire-outbound error-card path); instead
   it MUST record the condition as a local warning and MUST abort the
   current round without further reply processing, transitioning under
   the state machine of Section 3.8.  This rule is distinct from the
   handling of unknown pragma names, which are silently ignored
   (Section 10).  The fatal rule applies only when the first token is
   not a defined operator; a _defined_ operator that carries a malformed
   argument count is not treated as unknown, but is handled by that card
   type's own arity and error rule, described below.

   A card has an arity/malformed failure only when the token vector
   exposed to its handler fails that operator's arity or argument check.
   The reference parser tokenizes each card line into a fixed six-slot
   vector: at most the first six tokens of a card line participate in
   card processing, and a seventh or later token on the same line is not
   exposed to the per-card handler.  Interoperable senders MUST NOT emit
   surplus tokens.  Because a receiver following the fixed six-token
   parser cannot observe tokens beyond the sixth, this document does not
   require a receiver to detect unseen surplus tokens on such a line,
   and a card whose visible token vector satisfies its operator's arity
   and argument checks cannot exhibit the malformed/arity failure
   specified below merely because a seventh or later token is unseen on
   the same line.  The observable receive-side behavior described below
   arises only from the visible token vector.



Hipp, et al.             Expires 7 January 2027                [Page 32]

Internet-Draft            Fossil Sync Protocol                 July 2026


   When a fixed-slot parser truncates a surplus-token line to six
   tokens, the card type's own arity check produces the observable
   failure, and the receiver's response to that failure is endpoint-
   specific.  When the server observes such a failure, it MUST emit the
   relevant outbound error card and abort the exchange: for the content-
   bearing card types (file, cfile, and uvfile) the server emits error
   malformed CARD line, and for structurally simpler card types the
   server emits error bad command: LINE whose payload is the offending
   line.  In both cases, and for every malformed/arity failure the
   server observes, the server MUST reset its partial output before
   emitting the outbound error card and MUST abort the exchange after
   emitting it; no server-side malformed-card outcome is non-fatal.
   When the client observes such a failure — for example while
   processing a server reply — it MUST NOT emit any outbound error card;
   instead it records the condition as a local warning and MUST abort
   the current round without further reply processing, transitioning
   under the state machine of Section 3.8.  There is no "Unknown
   message" wire text: the framing for a server-emitted unrecognized-
   card response is exactly error bad command: followed by the offending
   line (Appendix C).

   The consequences for uvfile are direct.  A uvfile card is defined as
   uvfile NAME MTIME HASH SIZE FLAGS — the operator plus five argument
   tokens, a total of six tokens — so the visible token vector produced
   by the fixed six-slot parser is exactly those six tokens whenever the
   sender writes at least six tokens on the line.  If those six visible
   tokens satisfy the arity and argument-form checks of Section 7.3, no
   seventh or later invisible token on the same line can produce the
   malformed/arity failure described above: the surplus tokens are not
   exposed to the per-card handler and the arity check has already
   passed on the six visible tokens.  The sender-side prohibition on
   surplus tokens (this section, and the per-card sender rules of
   Sections 7 and 9) still applies unchanged; only the observable
   receive-side behavior is constrained to the visible token vector.

5.2.1.  Defined Operators

   This section specifies the operators covered by Sections 4 through 6
   of this document.  The complete operator catalog is assembled across
   all sections; operators not listed here are defined in Sections 7
   through 11.

   Client-to-server (this section): login (Section 6.1), push and pull
   (Section 3), cookie (Section 4.3).

   Server-to-client (this section): push (Section 3), cookie
   (Section 4.3).




Hipp, et al.             Expires 7 January 2027                [Page 33]

Internet-Draft            Fossil Sync Protocol                 July 2026


   The remaining operators — clone, file, cfile, uvfile, private, igot,
   uvigot, gimme, uvgimme, reqconfig, config, clone_seqno, pragma,
   error, and message — are defined in Sections 7 through 11.

5.3.  Comment Lines

   Any line in a sync message body whose first byte is ASCII # (0x23) is
   a comment line.  Comment lines are not card operators; they carry no
   argument tokens and are not dispatched by either parser.  The server
   skips all #-prefixed lines silently.

   The client skips #-prefixed lines in its receive loop without
   dispatching them, with one exception: before skipping, the client
   tests whether the line matches the subform

     # timestamp DATETIME errors N

   where DATETIME is the 19-byte fixed-width form YYYY-MM-DDTHH:MM:SS —
   four-digit year, zero-padded month, day, hour, minute, and second
   components, an ASCII T (0x54) between the date and the time, no
   fractional-second field, and no time-zone designator; the server
   clock is presented as UTC.  N is a non-negative decimal integer with
   no sign, no thousands separator, and no leading zeros (a single 0
   represents zero).  A sender MUST emit DATETIME in exactly this form;
   a receiver reads the first 19 bytes after the ASCII prefix #
   timestamp as DATETIME and MUST NOT require additional grammar within
   those 19 bytes.  If the line matches, the client reads DATETIME to
   compute clock skew relative to local time; the error count N is
   informational only and MUST NOT be treated as a wire-level error
   signal.  Other #-prefixed lines in the client receive loop are not
   semantically interpreted.

   A cluster igot is an igot card the server emits when handling a
   pragma req-clusters request (Section 10), naming a cluster artifact
   from its own cluster set.  Cluster igot cards are drawn from the
   public cluster set only; a server MUST NOT expose a private cluster
   artifact through this path.  When the server emits at least one
   cluster igot in a pragma req-clusters response, it MUST enclose the
   run in a matching pair of informational comment lines: # sending-
   clusters immediately before the first cluster igot of the run and #
   end-of-clusters immediately after the last cluster igot of the run.
   When the server emits no cluster igot for that request, it MUST NOT
   emit either comment line.  These pairing obligations bind the sender
   only.  Both comment lines are informational and MUST be ignored by
   the receiver under the generic comment-line handling rule of this
   section; they carry no wire-level state.  Their presence, absence, or
   apparent pairing has no effect on the receiver's disposition of the
   response or on its processing of the response's cards.



Hipp, et al.             Expires 7 January 2027                [Page 34]

Internet-Draft            Fossil Sync Protocol                 July 2026


   Each client request ends with a #-prefixed comment line carrying
   implementation-generated random bytes.  This ensures that the body
   bytes following the login card (from which the authentication nonce
   is derived; see Section 6.2) differ between rounds even when all
   other content is identical.

6.  Authentication

6.1.  Login Card

   When a request uses Fossil's sync login-card mechanism, it carries
   exactly one login card, either as a body card or in the cookie-login
   HTTP header (Section 6.5).  The body-card form is:

     login USER NONCE SIGNATURE

   USER is the login name encoded using the fossilize scheme of
   [FossilArtifact] Section 4.  NONCE and SIGNATURE are each exactly 40
   lowercase hexadecimal characters.  A request containing more than one
   login card is a fatal protocol error; this includes the case where
   one login card arrives through the cookie header and another appears
   in the body.  The receiver MUST reset its partial output and emit an
   error card.

   A login card MUST NOT be emitted when the user is anonymous or
   nobody; those identities carry their configured capabilities without
   challenge.  A login card is also not emitted when the transport is
   SSH; the SSH layer provides its own authentication.  The first round
   of a current protocol-version-3 clone also omits both body-login and
   cookie-login cards, as specified in Section 8.3.

6.2.  Nonce Derivation

   The NONCE value is the SHA-1 [RFC3174] digest of all message body
   bytes that follow the login card line — that is, the bytes beginning
   immediately after the LF that terminates the login card, through the
   end of the message body.  The server verifies the nonce by computing
   SHA-1 over the same byte range in the received (decompressed) body.

   In cookie-login mode (Section 6.5) the payload nonce covers only the
   body bytes; the HTTP header carrying the login card is not included
   in the nonce computation.

6.3.  Shared Secret

   The shared secret is derived locally on each side and is never
   transmitted.  It is computed as the SHA-1 digest of three fields
   concatenated with / separators:



Hipp, et al.             Expires 7 January 2027                [Page 35]

Internet-Draft            Fossil Sync Protocol                 July 2026


     shared-secret = SHA1( project-code "/" login-name "/"
                           plaintext-password )

   where project-code is the repository project identifier, login-name
   is the login name exactly as carried in USER, and plaintext-password
   is the user's password in plaintext form.  The result is 40 lowercase
   hexadecimal characters.

   A current protocol-version-3 clone does not use a plaintext-password
   substitute on its first round.  Instead, the client sends no login
   card at all in that round; after it learns the project code from the
   server's clone reply, later authenticated rounds use the normal
   shared-secret derivation and login-card transport rules.

6.4.  Signature Computation

   The SIGNATURE value is the SHA-1 digest of the NONCE hex string
   concatenated with the shared-secret hex string:

     SIGNATURE = SHA1( NONCE-hex || shared-secret-hex )

   Both inputs are 40-character lowercase hexadecimal strings.  The
   concatenation is byte-level with no separator, yielding a 80-byte
   input to SHA-1.  The result is 40 lowercase hexadecimal characters.

   The server authenticates a request by independently computing the
   same derivation — reconstructing the shared secret from its stored
   credential for the named user, combining it with the received NONCE,
   and comparing the result to the received SIGNATURE.

   The plaintext password, the shared secret, and any intermediate value
   derived from them MUST NOT appear on the wire in any form.  The NONCE
   is derived from the message-body bytes that follow the login card;
   SIGNATURE is derived from the NONCE and the shared secret.  Because
   each request appends an implementation-generated random comment line
   (Section 5.3), independently generated rounds produce distinct NONCE
   values.  The server recomputes the NONCE from the received body tail
   and the SIGNATURE from the received NONCE and the stored credential,
   but maintains no nonce cache and applies no timestamp freshness
   check.  A byte-for-byte replay of a previously authenticated request
   will therefore still pass server verification; protection against
   such replay MUST be provided by the transport layer (Section 12.1).

   Before any card is parsed, the server establishes a baseline
   capability set using the same session, cookie, HTTP Basic
   authentication, and web-server remote-user machinery it uses for
   ordinary web pages; this is the only point at which capability state
   is reset from scratch.  A login card, when present, is verified and



Hipp, et al.             Expires 7 January 2027                [Page 36]

Internet-Draft            Fossil Sync Protocol                 July 2026


   applied afterward and is purely additive: a successful login card can
   only add capability bits on top of the baseline, never remove any.
   The two mechanisms are sequential contributors to one capability set,
   combined by union, not related by precedence.  A login card that
   fails verification leaves the baseline untouched but MUST cause the
   exchange to abort.

   Server-side verification of a login card proceeds as follows:

   1.  Special-cased identities.  Logins of nobody or anonymous succeed
       unconditionally, with no signature check; anonymous
       synchronisation is always permitted.  A matching authenticated
       remote-user identity (HTTP Basic authentication), where the
       server is configured to accept it, is honoured next.

   2.  Signature check, primary form.  For a named user, the server
       treats the stored credential bytes directly as the shared secret,
       regardless of their stored format, computes SHA1( NONCE-hex ||
       stored-bytes ), and compares the result to the received SIGNATURE
       using a constant-time, case-sensitive comparison over the
       40-character ASCII values.

   3.  Signature check, stored-credential fallback.  Only if the primary
       comparison fails, and only if the stored credential is not
       exactly 40 bytes long (hence a cleartext stored credential by the
       storage convention), the server re-derives the shared secret from
       the stored cleartext password, the name, and its own project code
       (Section 6.3) and re-checks the SIGNATURE.  The server does not
       rewrite the stored credential as a side effect.

   4.  Tail-hash check.  Independent of the signature check, the server
       verifies that the NONCE equals a fresh SHA-1 hash of the
       remainder of the request body following the login line
       (Section 6.2), a case-sensitive comparison over the 40-character
       hash values.

   Login MUST be rejected if either the signature check or the tail-hash
   check fails.  On authentication failure the server MUST NOT change
   the HTTP status code, which remains 200; instead it discards any
   staged reply content, emits an error login failed card, records the
   failure, and breaks out of the card-processing loop, ending the
   exchange.  A failing login card therefore terminates the exchange at
   the protocol layer, not the HTTP layer.








Hipp, et al.             Expires 7 January 2027                [Page 37]

Internet-Draft            Fossil Sync Protocol                 July 2026


6.5.  Cookie-Login Mode

   The client transmits the login card via the standard HTTP Cookie
   header rather than in the message body if and only if the peer's
   recorded VERSION, DATE, and TIME (from a prior pragma server-version
   card, using the decimal-integer VERSION, the 8-digit YYYYMMDD decimal
   DATE, and the 6-digit HHMMSS decimal TIME formats defined in
   Section 10.1) satisfy the integer eligibility predicate

  VERSION >= 22700 AND (DATE > 20250727 OR (DATE == 20250727 AND TIME >= 110500))

   (equivalently, VERSION at least 22700 — Fossil 2.27 — and the
   recorded server-version timestamp at or after 2025-07-27 11:05:00).

   The cookie header carries the full login card text URL form-encoded,
   with each space replaced by +:

     Cookie: x-f-l-c=login+USER+NONCE+SIGNATURE

   The header value is the text login USER NONCE SIGNATURE with each SP
   converted to +; URL-unsafe bytes in the fossilize-encoded USER field
   are additionally percent-encoded.  The server URL-decodes the value
   and processes the resulting login card by the same code path as a
   body-embedded login card; the application-layer card semantics are
   unchanged.  The login card MUST NOT appear in the message body when
   cookie-login mode is in effect.  The standard HTTP Cookie header
   containing the x-f-l-c cookie-pair is the only cookie-related header
   used by the Fossil sync protocol; there is no X-Fossil-* header
   equivalent.






















Hipp, et al.             Expires 7 January 2027                [Page 38]

Internet-Draft            Fossil Sync Protocol                 July 2026


   This version gate applies only to the x-f-l-c cookie transport
   defined in this section.  When both conditions above are met for the
   current peer, the client MUST use the x-f-l-c cookie transport and
   MUST NOT include a body-embedded login card.  When either condition
   is not satisfied — including any round in which the peer's pragma
   server-version has not yet been observed — the client MUST fall back
   to the body-embedded login card defined in Section 6.1; the fallback
   is the ordinary Section 6.1 transport, unmodified by this section,
   except that the first round of a current protocol-version-3 clone
   (Section 8.3) omits both body-login and cookie-login transports and
   therefore carries neither login form.  A client MUST NOT emit the
   x-f-l-c cookie header against an ungated peer.  In the absence of an
   x-f-l-c cookie login, a server MUST accept a body-embedded login
   card, irrespective of whether the peer would also be gated for the
   cookie transport.  The one-login-per- request invariant of
   Section 6.1 is preserved: a request that carries both an x-f-l-c
   cookie login and a body-embedded login card is a protocol error under
   Section 11.1, independently of whether either form would otherwise be
   gated.

6.6.  Capability Model

   Users are assigned capability letters by the server.  Capabilities
   govern what operations the server will accept from the authenticated
   user: Read (o) permits pull; Write (i) permits push; Clone (g)
   permits clone; Private (x) permits private-content transfer;
   administrator capabilities permit configuration and user-management
   operations.  The server checks the capability appropriate to each
   card it processes and emits an error card for a request that lacks
   the required capability.

7.  Content Transfer

   Versioned artifact content is transferred by file and cfile cards.
   The uvfile card is specified in this section because it uses the same
   byte-counted framing model while carrying the protocol's unversioned-
   file synchronization state outside the artifact model.

7.1.  file Card

   A file card has one of two forms:

     file HASH SIZE
     file HASH DELTASRC SIZE

   HASH is the artifact name ([FossilArtifact], Section 3).  SIZE is the
   count of payload bytes that follow immediately after the card's
   newline.  The payload is uncompressed and is opaque: it MAY contain



Hipp, et al.             Expires 7 January 2027                [Page 39]

Internet-Draft            Fossil Sync Protocol                 July 2026


   the newline byte 0x0a.  A receiver MUST locate the end of the payload
   by the byte count and MUST NOT scan the payload for a separator.
   After exactly SIZE payload bytes, the next message byte, if any, is
   the first byte of the next card line; there is no trailing separator
   after the payload.

   The three-argument form carries the full artifact content.  The four-
   argument form carries a delta [FossilDelta] against the artifact
   named DELTASRC.

   file cards are used for push and pull transfers.  A file card is
   never emitted by the server on the clone 3 SEQNO path; Section 8
   defines that clone transfer path separately.

7.2.  cfile Card

   A cfile card has one of two forms:

     cfile HASH USIZE CSIZE
     cfile HASH DELTASRC USIZE CSIZE

   HASH is the artifact name.  In the four-argument form, DELTASRC is
   the artifact name of the delta basis.  USIZE is the sender's
   uncompressed size for the full artifact named by HASH after any delta
   reconstruction; in the delta form it is therefore the reconstructed
   target artifact size, not a byte count for the delta payload.  CSIZE
   is the exact count of payload bytes that follow immediately after the
   card's newline.  A receiver MUST locate the end of the payload by
   CSIZE and MUST NOT use USIZE as a framing boundary.  After exactly
   CSIZE payload bytes, the next message byte, if any, is the first byte
   of the next card line; no extra separator follows the payload.
   Current Fossil receivers parse USIZE for syntax but do not validate
   the received payload against it at the transfer layer.

   The payload is the artifact's stored repository content transmitted
   verbatim.  It is already in Fossil's stored compressed container.
   For a full-content cfile, the payload is the compressed full
   artifact.  For a delta cfile, the payload is the compressed delta
   stored for HASH against DELTASRC.  The sender does not re-compress
   the payload for the card; it writes the stored bytes.  When the
   enclosing response body uses the application/x-fossil-uncompressed
   media type (Section 4.1), the outer body is not zlib-compressed, so
   the cfile payloads inside are not double-compressed.

   cfile cards are emitted by the server on the clone 3 SEQNO path
   (Section 8).  No pragma, content-size threshold, or capability
   negotiation governs this choice; the clone card selects it.  Push and
   pull transfers use file cards.



Hipp, et al.             Expires 7 January 2027                [Page 40]

Internet-Draft            Fossil Sync Protocol                 July 2026


7.3.  uvfile Card

   A uvfile card carries unversioned-file synchronization state outside
   the artifact model [FossilArtifact] and outside the artifact hash-
   policy and tag-state rules.

     uvfile NAME MTIME HASH SIZE FLAGS

   NAME is the file name encoded using the fossilize character-escaping
   scheme ([FossilArtifact], Section 4).  MTIME is the modification time
   expressed as a decimal count of seconds since the Unix epoch.  HASH
   is the content hash of the file, or the literal - when the card
   represents a deletion.  SIZE is the byte count of the payload that
   follows immediately after the card's newline when content is present.
   FLAGS is a non-negative decimal integer interpreted as a bit field.
   Bit 0x0001 marks deletion; bit 0x0004 marks omitted content.  All
   other bits are reserved and have no defined semantics in this version
   of the protocol.  A sender MUST clear every reserved bit before
   emitting the card and MUST NOT overload any reserved bit with an
   implementation signal.  A receiver MUST reject a negative FLAGS value
   as a malformed uvfile line and MUST ignore every reserved bit in a
   non-negative FLAGS value: masking off the reserved bits MUST NOT
   change any subsequent receiver behavior.  For example, 2 (only the
   reserved 0x0002 bit set) is treated exactly as 0, and 6 (0x0002 |
   0x0004) or 20 (0x0010 | 0x0004) is treated exactly as 4.  The
   receiver decides deletion from the HASH token, not from the 0x0001
   bit: a deletion is signaled by HASH equal to - with SIZE equal to 0,
   and a sender emitting a deletion MUST also set the 0x0001 bit as a
   metadata marker.  A mtime-only or size-limited transfer uses the
   omitted-content flag and carries no payload bytes even though HASH
   and SIZE describe the sender's current file state.

   A uvfile card whose HASH is - and whose SIZE is greater than zero is
   malformed: the sentinel HASH - represents deletion of the named
   unversioned file (with the deletion carrying no payload and SIZE 0)
   and is incompatible with a positive SIZE that would otherwise require
   SIZE content bytes.  A receiver MUST reject a uvfile card that
   combines HASH - with SIZE greater than zero as a malformed uvfile
   line; the receiver's endpoint-specific response follows Section 5.2 —
   the server emits the relevant error card and aborts, and the client
   records a local warning and aborts the current round without emitting
   any outbound error card.  This constraint does not modify the
   deletion signalling above (HASH - with SIZE 0 and the 0x0001 FLAGS
   bit).  Omitted-content transfers are an independent FLAGS case (the
   0x0004 bit) that retain the sender's current HASH and SIZE values
   while carrying no payload bytes, and this constraint does not modify
   that case either.




Hipp, et al.             Expires 7 January 2027                [Page 41]

Internet-Draft            Fossil Sync Protocol                 July 2026


   When SIZE is greater than zero and neither the deletion flag nor the
   omitted-content flag is set, the payload contains exactly SIZE
   content bytes.  After exactly SIZE payload bytes, the next message
   byte, if any, is the first byte of the next card line.  When content
   is omitted, there are no payload bytes and the next card line begins
   immediately after this card's terminating LF.  A receiver MUST verify
   HASH against those bytes before storing the unversioned file.  When
   content is omitted, the receiver may use the card to update metadata
   only when the unversioned-file status algorithm permits that update;
   it does not obtain file content from the card.

   A companion uvigot NAME MTIME HASH SIZE card is the announcement
   form, carrying the same state without a payload.  A deleted
   unversioned file is announced or transferred with HASH equal to -.  A
   uvgimme NAME card requests a named unversioned file.  Only a receiver
   with unversioned-file write permission may land a uvfile change;
   lacking that permission is a protocol error for the uvfile receiver.
   Accepted unversioned-file changes clear the cached unversioned
   catalog hash.

7.4.  Delta Sources and Phantom Creation

   When a file or cfile card carries a DELTASRC argument that names an
   artifact the receiver does not currently hold, the receiver retains
   the delta payload against the named source and defers full
   reconstruction until the source arrives.  The receiver MUST create a
   phantom record for the named DELTASRC (Section 9.3) and MUST NOT
   reject the card.  When the source artifact is subsequently stored,
   that retained delta becomes available for reconstruction.

   Availability propagation is recursive.  Storing a source artifact
   makes each retained delta held against it available for
   reconstruction, and because such a delta may itself be named as the
   DELTASRC of another retained delta, availability propagates in turn.
   A receiver MUST propagate availability to a fixpoint so that every
   retained delta descending from the newly stored artifact is marked
   available for reconstruction.  Basis arrival does not separately
   materialize or store a new full-content artifact for each descendant;
   full bytes are reconstructed on demand by following the retained
   delta chain back to a stored full source.  For example, if a receiver
   has retained delta C against source A and delta A against source B
   before B arrives, then storing B makes A available for
   reconstruction, and, because A is now itself available as a source,
   makes C available for reconstruction; neither C nor A may be left
   unavailable.






Hipp, et al.             Expires 7 January 2027                [Page 42]

Internet-Draft            Fossil Sync Protocol                 July 2026


7.5.  Private Content

   A private card (no arguments) marks the next subsequently processed
   content-bearing file or cfile card as carrying private content.  The
   marker is a receiver-side pending flag: intervening comment lines and
   other non-content cards do not consume it, a repeated private card
   leaves the flag set, and the flag is consumed only when handling of
   the next file or cfile card begins.  A client receiver carries the
   pending marker across successive reply bodies within a single sync
   session; only a subsequent file or cfile card, or the end of that
   session, clears it.  A server receiver's pending marker is scoped to
   the processing of its current request body and ends when processing
   of that request body ends.  Reaching the end of a client reply body
   with the marker still pending is not by itself a fatal protocol
   error.  The asymmetric marker lifetime — client-session receive-state
   versus server-request receive-state — reflects the underlying session
   model of Section 3.1: the client is the session-holding endpoint
   whose per-session receive state persists across HTTP round-trips,
   while the server processes each request independently and holds no
   per-session receive state, so a pending private marker has no server
   context in which to survive between requests.  This rationale is non-
   normative; the normative marker-consumption rules are the sentences
   above.

   Private content is transferred only when the send-private pragma has
   been accepted by the server and the receiving user holds the Private
   capability.  A receiver MUST NOT store a private artifact unless the
   authenticated user holds that capability; a private artifact arriving
   without it is discarded without error.

   A uvfile card does not consume a pending private marker; a preceding
   private does not make that uvfile private and leaves the marker
   pending for the next file or cfile card; unversioned private-content
   suppression remains governed by Section 10's pragma exchange.

   On the sender side, the ordinary emission pattern is atomic at the
   wire: a sender that has decided to transmit a private artifact writes
   the private card immediately followed by the matching file or cfile
   card, with no intervening card of any kind — neither a comment line,
   another pragma, an announcement card, nor any other body card — so
   that the very next card the sender writes after private is the
   matching content card and the receiver-side pending marker of the
   paragraphs above is consumed by that card.  This sender-side
   adjacency requirement is separate from the receiver-side consumption
   rule stated earlier in this section, which continues to allow
   intervening comment lines and non-content cards on the wire to leave
   the pending marker set without consuming it.  The one legacy
   compatibility hole in this sender pattern is the compressed-SHA3 gate



Hipp, et al.             Expires 7 January 2027                [Page 43]

Internet-Draft            Fossil Sync Protocol                 July 2026


   of Section 7.6: if the sender has already written the private card
   when it discovers that the pending content is a SHA3-256-named cfile
   and the peer's recorded VERSION forbids that emission, the sender
   MUST emit the terminal error card required by Section 7.6 instead of
   the barred cfile.  The fate of the receiver-side pending private
   marker set by the preceding private at that terminal-error boundary
   is direction-dependent.  When the sender is the server, the client is
   the receiver, and Section 11.1 applies to that terminal error: the
   client aborts the exchange without constructing a further request,
   ending the session, so the client's session-scoped pending marker
   becomes moot with the session and is never subsequently bound to a
   content card by that sender.  When the sender is the client, the
   server is the receiver, and the direction-invalid error card in the
   client request is handled by the server under Section 7.6's reset-
   and- continue rule rather than by Section 11.1.  That reset clears
   only the server's accumulated response output for the current request
   and emits error bad command: LINE as the reset reply; it does not
   clear the server's own request-scoped pending private marker, which
   is receive-side state kept separately from the response output.  The
   next later file or cfile attempt that the same client request submits
   consumes and clears the pending marker at handler entry, before the
   server validates the attempt's arity, hash, or the client's Private
   authorization.  If the attempt is successfully accepted and
   authorized, it receives the private treatment described later in this
   section; if the attempt is malformed, rejected, or otherwise
   discarded after handler entry, the marker is already cleared and does
   not bind any later file or cfile attempt that the same request may
   subsequently submit.  Intervening comment lines and non-content cards
   do not consume the marker, following the receiver-side consumption
   rule stated earlier in this section.  If no file or cfile attempt
   arrives before the client request body ends, the marker dies
   unconsumed with that request, because the server's per-request
   receive state does not survive request boundaries as stated earlier
   in this section.  This server-side, request-scoped marker fate at the
   terminal-error boundary is distinct from the client-receiver's
   session-scoped marker, whose persistence across HTTP round-trips
   within the same sync session the earlier paragraph of this section
   governs.  This paragraph names only the terminal-error boundary at
   that specific hole; it does not assert that a sender always resolves
   the peer-version gate before writing private, and it does not assert
   that a sender never leaves a private marker unconsumed at the end of
   a reply body (the receiver-side rule at the end of the third
   paragraph of this section already covers that case as non-fatal).

   The private-content boundary also constrains delta transmission.  If
   the on-disk delta basis for an artifact is private but the artifact
   being sent is not, the sender MUST NOT send a delta that references
   that private basis to a peer that lacks private-content



Hipp, et al.             Expires 7 January 2027                [Page 44]

Internet-Draft            Fossil Sync Protocol                 July 2026


   authorization; it MUST instead send full, non-delta content, so that
   the existence or identity of the private basis artifact is not leaked
   through a delta reference (Section 7.4).  Authorization for private
   content applies in both directions, but a private announcement and
   private content are handled differently on the receive side.  A
   private file or cfile card pushed to a server MUST be rejected as a
   not-authorized error unless the pushing peer holds the Private
   capability on the server.  A private igot HASH 1 from a peer that
   lacks that capability MUST NOT be treated as an error.  The receiver
   does not create a phantom for HASH; but if it already holds HASH as a
   phantom, it MUST mark that phantom private, so that it stops re-
   requesting content it will never be sent.  The receiver records that
   the peer holds HASH and continues processing the message.  Pulling
   private content MUST be preceded by a successful send-private pragma,
   which is itself gated on the client holding that capability.

7.6.  SHA3-256 Peer-Version Gate

   A sender MUST NOT emit a file or cfile card whose HASH names a
   SHA3-256 artifact, nor a uvfile card whose HASH field carries a
   64-character lowercase hexadecimal content hash, to a peer whose
   recorded version-pragma VERSION (Section 10.1) is less than the
   decimal integer 20000.  The value 20000 corresponds to Fossil release
   2.0.

   When this restriction prevents a transfer, the sender MUST emit an
   error card (Section 11) whose decoded text is Fossil version 2.0 or
   later required.  This error card is appended to the message the
   sender is currently constructing; the sender abandons only that one
   artifact transfer and does not, by itself, terminate construction of
   the current message.  The sender continues emitting any remaining
   cards for the current message in the normal way.  Subsequent
   behaviour depends on role: for a client sender, the outcome of the
   session that follows this message is governed by the peer's reply and
   by Sections 11.1 and 3.7, not by an immediate sender-side abort; for
   a server sender, later response cards MAY still be emitted in the
   same reply.  No automatic substitution of a SHA1-named artifact or
   alternate hash form is performed.  Receipt of this error card is
   direction-dependent, and Section 11.1 governs only client receipt of
   an error card in a server reply.  When the sender is the server, the
   receiving client applies Section 11.1 and aborts the sync without
   retry, except during the first round of a clone where the error card
   is silently discarded.  When the sender is the client, the error card
   appears in a client request; this is direction-invalid.  Because the
   error operator is defined, this case is not the unknown-operator
   failure of Section 5.2 and the server MUST NOT abort the exchange on
   that ground.  The receiving server MUST reset any response output it
   has accumulated for the current request, emit error bad command: LINE



Hipp, et al.             Expires 7 January 2027                [Page 45]

Internet-Draft            Fossil Sync Protocol                 July 2026


   (Section 5.2) — reusing that response form with LINE set to the
   offending request line — as the reset reply, and continue parsing any
   remaining cards in the same client request under their own handlers.

   This gate applies at three independent send sites: the file send
   path, the cfile send path, and the uvfile send path.  A sender MUST
   apply the relevant gate at each site independently.

   This restriction does not define new card syntax.  It is a
   precondition on emission, enforced before the card is written.

7.7.  Hash-Policy Admission

   Hash-policy admission and content-integrity verification are distinct
   mechanisms that apply at different points in the receive path.  They
   MUST NOT be conflated.

   Hash-policy admission is a gate applied to the HASH name of an
   inbound artifact, governed by the receiving repository's configured
   hash policy ([FossilArtifact], Section 7).  Under the shun-sha1
   policy, a receiver MUST reject any inbound artifact whose HASH is a
   40-character (SHA1) name before storage.  This gate applies to
   inbound file and cfile cards, including cfile cards received on the
   clone 3 SEQNO path.

   Content-integrity verification recomputes the artifact hash over the
   reconstructed artifact bytes — the full content named by HASH after
   applying any delta source, not the received wire payload — and
   confirms that the result matches HASH.  Pre-storage verification
   applies to a non-clone file card whose reconstructed bytes are
   available.  A Section 3.3 dangling-delta file card has no
   reconstructable bytes at receipt because its delta basis is not yet
   held locally; its payload is stored under Section 3.3 without a pre-
   storage hash check.  For cfile cards, pre-storage integrity
   verification does not apply in the transfer layer.  In addition to
   those pre-storage checks, a conforming receiver MUST recompute and
   compare the artifact hash from a hook the receiver registers on its
   repository transaction commit: for every artifact that the current
   sync transaction newly stored and whose complete bytes are available
   at commit, the hook MUST recompute the artifact hash from those
   stored complete bytes and MUST abort the commit when the recomputed
   hash does not match HASH.  Later availability of a dangling delta's
   basis does not constitute a new card receipt: this document does not
   specify a second transfer-layer pre-storage check, nor an on-demand
   hash verification, of the already-retained dangling payload when its
   basis becomes available.  The retained delta is subsequently
   reconstructable on demand under Section 3.3, but no further transfer-
   layer integrity check on that retained payload is defined here.



Hipp, et al.             Expires 7 January 2027                [Page 46]

Internet-Draft            Fossil Sync Protocol                 July 2026


   When the receiving repository operates under the auto hash policy and
   successfully stores a SHA3-256-named artifact, the policy is
   permanently promoted to sha3 as a side effect of that storage
   operation.  This promotion is persisted to the repository
   configuration immediately; it applies to all subsequent operations in
   the same and future sessions and is not reversible within a session
   ([FossilArtifact], Section 7).

8.  Clone

   A clone is the procedure by which a client obtains a copy of a
   server's repository from an empty local repository.  A current client
   uses the modern protocol-version-3 algorithm: it sends pragma client-
   version VERSION DATE TIME, then clone 3 SEQNO, then the normal
   trailing randomness comment in each clone artifact-transfer request
   for which SEQNO is positive.  The first modern clone request uses
   SEQNO 1 and carries no login card (Section 8.3).

8.1.  Clone Protocol Version

   The clone card carries a protocol version argument:

     clone VERSION SEQNO

   VERSION is a small decimal integer that selects the clone transfer
   path.  This document defines the current interoperable clone path:

   Version 3: :: The client sends clone 3 SEQNO for a new clone.  The
   server emits cfile cards (Section 7.2) for each artifact, in storage-
   sequence order.  The response body uses the media type application/x-
   fossil-uncompressed.  This is the clone path defined by this
   document.

   Any other VERSION value is outside the interoperable surface defined
   here.

   The clone protocol VERSION is orthogonal to the Fossil software
   release integer carried in version pragmas (Section 10.1).  A client
   may send clone 3 while reporting any release integer; the value 20000
   in the SHA3-256 send gate (Section 7.6) refers to the release
   integer, not to the clone version.










Hipp, et al.             Expires 7 January 2027                [Page 47]

Internet-Draft            Fossil Sync Protocol                 July 2026


8.2.  Sequence Numbers

   The SEQNO argument to the clone card is a blob-table row identifier
   in the server's repository.  The server iterates row identifiers
   sequentially from the client-supplied SEQNO to the maximum identifier
   present, emitting one artifact per identifier.  Gaps in the blob
   table are skipped silently; a client MUST NOT infer artifact count or
   total artifact size from the SEQNO range.

   The server replies with a clone_seqno NEXT card after the clone-path
   artifact cards, ending the clone-specific artifact sequence; the
   reply's push SERVERCODE PROJECTCODE card and closing # timestamp
   comment follow:

   *  When NEXT is greater than zero, the server has not yet exhausted
      its blob table.  The client evaluates the Section 3.7 termination
      rule to decide whether to issue another round.  Outer condition
      (a) is tested first and fires when both parts of its canonical
      predicate hold: this round advanced the received count via one or
      more file, cfile, or dangling-delta receipts, or an igot card
      created a new phantom during this round; and the phantom table is
      non-empty after the round.  Only when that outer predicate does
      not fire, and none of conditions (b) through (d) fires either, is
      the clone-specific disjunction of condition (e) consulted; that
      disjunction fires when this is the first clone round, when this
      round's file/cfile/ dangling-delta receive progress was positive
      (that is, the round's file, delta, and dangling-delta receive
      counts sum to a strictly positive value), or when the recorded
      clone sequence number remains positive and the session's
      cumulative count of received artifacts advanced during the round.
      The clone sequence number is the client-side progress cursor of
      Section 3.1; its positive value after the round makes the third
      clone-branch limb of Section 3.7(e) available.  When the client
      continues under Section 3.7 and the client guard below is
      satisfied (this reply carried a well-formed push card and the
      recorded clone sequence number is greater than zero), the
      following request includes pragma client-version and clone 3 NEXT.
      A positive NEXT value does not by itself require another round
      when no Section 3.7 continuation condition holds.  A conforming
      server upholds the matching liveness invariant: whenever it
      returns clone_seqno NEXT with NEXT greater than zero and blob RIDs
      at or above NEXT remain unsent, it MUST advance the sequence in
      that round — a positive NEXT is accompanied by content for at
      least one artifact whenever content remains to be sent.  A client
      that receives a positive NEXT together with no new artifact and no
      other continuation condition has therefore reached a non-advancing
      peer and terminates the clone rather than looping.




Hipp, et al.             Expires 7 January 2027                [Page 48]

Internet-Draft            Fossil Sync Protocol                 July 2026


   *  When NEXT equals zero, the server has sent all artifacts up to the
      maximum row identifier.  The client MUST NOT send another clone
      card after receiving clone_seqno 0, and it MUST NOT introduce a
      pull or push operation card into a modern-clone session at this
      point; the modern-clone loop never transforms mid-session into a
      push/pull loop.  Receipt of clone_seqno 0 bars any subsequent
      clone card in the session but does not by itself terminate the
      exchange: any separately true continuation condition of
      Section 3.7 (a) through (e) — including Section 3.7(e)'s first-
      clone-round limb and Section 3.7(e)'s file/cfile/dangling-delta
      receipt limb, either of which can hold in the same round as
      clone_seqno 0 — causes the client to issue a further round.  Only
      Section 3.7(e)'s third limb (recorded clone sequence number
      positive together with cumulative-artifact-counter progress)
      cannot arise once clone_seqno 0 has been recorded, by construction
      of the guard.  That further round is a modern-clone cleanup
      request: an ordinary request that carries no pull, push, or clone
      operation card.  The cards it MAY carry are the mandatory pragma
      client-version of Section 10.1; any other pragmas permitted by
      Section 10 for the pending work (for example pragma send-private
      when private discovery remains, or pragma uv-hash when unversioned
      exchange is still eligible); the request cards for the remaining
      work (reqconfig, gimme, and uvgimme); a cookie card returning a
      stored cookie (Section 3.6); and the trailing randomness comment
      line of Section 5.3.  When no Section 3.7 condition (a) through
      (e) holds after clone_seqno 0, the client terminates without
      issuing a further round.

   *  A clone_seqno value less than zero is a fatal protocol error; the
      receiver MUST abort.

   The normal accepting/terminal path and the first-round refusal path
   each impose relative ordering constraints on the clone-specific cards
   of a clone-card response; those constraints govern the ordering of
   the clone-specific cards within the response, not the entire reply
   body.  Other cards this document authorizes MAY also appear in the
   same reply — a pragma server-version in response to the client's
   pragma client-version (Section 10.1) precedes the clone-specific
   cards; a message or error card MAY appear where its owning section
   authorizes it (Section 11); and the closing comment line carrying a
   timestamp and error count is always the last line of the reply body
   (Section 5.3).  In an accepting response (positive NEXT) or a
   terminal response (NEXT=0) the clone-specific cards appear in the
   order zero or more cfile artifact cards in transfer order, then
   clone_seqno NEXT, then push SERVERCODE PROJECTCODE; the version-3
   clone-artifact path emits cfile only.  When the SHA3-256 peer-version
   send gate (Section 7.6) fires during an attempted cfile send in such
   a response, an outgoing error card is written in the artifact-card



Hipp, et al.             Expires 7 January 2027                [Page 49]

Internet-Draft            Fossil Sync Protocol                 July 2026


   slot for the gated artifact in place of that artifact's cfile card
   and precedes any later clone_seqno NEXT and push SERVERCODE
   PROJECTCODE in the same response; this per-artifact gate MAY repeat
   for further SHA3-named artifacts reached in the same round.  In a
   first-round refusal response (Section 8.3) the clone-specific cards
   are push SERVERCODE PROJECTCODE and then the refusal error card, with
   no cfile cards and no clone_seqno.  Outside the refusal position and
   the SHA3-gate position specified here, no requirement is placed on
   where an error card may appear in a clone-card response, and a clone-
   card response is not required to carry any error card.  Because a
   well-formed continuing accepting reply carries both clone_seqno NEXT
   with NEXT greater than zero and push SERVERCODE PROJECTCODE, the
   client guard stated below never blocks a legitimate reissue against a
   conforming peer.

   The client records NEXT by updating the Section 3.1 clone sequence
   number to the NEXT value carried by any well-formed clone_seqno NEXT
   card in the just-received reply, and the recorded value persists
   across subsequent replies until the next clone_seqno NEXT card
   overwrites it.  When the Section 3.7 evaluation says the client
   continues, the client reissues a clone 3 SEQNO card, with SEQNO equal
   to the recorded clone sequence number, in a subsequent request if and
   only if both of the following hold: the just-received reply included
   a well-formed push SERVERCODE PROJECTCODE card, and the recorded
   clone sequence number is greater than zero.  A push card in a reply
   while the recorded clone sequence number is zero MUST NOT cause a
   further clone reissue.

   The modern clone loop can then be summarised by the following
   pseudocode.  The pseudocode is illustrative only; the normative
   requirements are the surrounding prose of Sections 3.2, 3.7, and 8.1
   through 8.3, and where the pseudocode differs from that normative
   prose the normative prose governs:


















Hipp, et al.             Expires 7 January 2027                [Page 50]

Internet-Draft            Fossil Sync Protocol                 July 2026


  SEQNO := 1
  build first request:
      pragma client-version VERSION DATE TIME
      clone 3 1
      # RANDOM
      no login card
  loop:
      reply_included_push := false
      send request; read reply
      for each card C in the reply:
          accept cfile cards and other reply cards in order
          if C is "push SERVERCODE PROJECTCODE":
              reply_included_push := true
      if clone_seqno NEXT was received:
          if NEXT < 0: abort
          SEQNO := NEXT
      evaluate Section 3.7
      if continuing and reply_included_push and SEQNO > 0:
          next request includes pragma client-version and clone 3 SEQNO
      else if continuing:
          next request includes pragma client-version but no clone, pull, or push operation card (Section 8.2 cleanup form)
      else:
          stop

8.3.  First Clone Round

   During the first round of a current protocol-version-3 clone
   exchange, the client does not yet know the repository's project code
   and suppresses both body-login and cookie-login transport.  The first
   request body begins with pragma client-version, then clone 3 1, and
   ends with the normal random comment.  It contains no login card and
   therefore transmits neither a shared-secret value nor a plaintext-
   password substitute.

   The server processes that request using its baseline credentials and
   clone permission checks.  A server that refuses the unauthenticated
   clone MUST still emit push SERVERCODE PROJECTCODE before the error
   card that conveys the refusal; this is the refusal shape of
   Section 8.2, and it delivers the peer's project code to the client in
   the same reply that declines the round.  A client MUST read the
   project code from a push or pull card it receives in a reply and
   record it as the peer's project code (Section 6.3); a clone client
   obtains its project code this way from the first clone round's reply,
   whether that reply accepts the round or refuses it.  For this reason
   a client silently discards error cards received during the first
   clone round; subsequent clone rounds process error cards normally.





Hipp, et al.             Expires 7 January 2027                [Page 51]

Internet-Draft            Fossil Sync Protocol                 July 2026


   After the artifact-transfer phase completes (upon receiving
   clone_seqno 0), the client resolves any remaining phantoms using the
   normal phantom-resolution procedure of Section 3.3 and the
   termination rule of Section 3.7.

8.4.  Configuration During Clone

   The client defers all reqconfig cards until after clone round 0.
   Current clients also defer pragma uv-hash until after clone round 0
   when unversioned sync is requested.  This delay keeps the first clone
   exchange limited to version announcement, clone pagination, and
   project-code discovery; configuration and unversioned-file exchange
   then proceed through the normal reqconfig/config and unversioned-file
   mechanisms described in Sections 10.3 and 9.1.

9.  Synchronisation Strategies

9.1.  Push, Pull, and Sync

   A push request carries a push SERVERCODE PROJECTCODE card.  A pull
   request carries a pull SERVERCODE PROJECTCODE card.  A combined sync
   carries both.  SERVERCODE is the client's own repository instance
   identifier (informational).  PROJECTCODE is the identifier shared by
   all repositories of the same project.  The server MUST refuse to
   synchronise when its stored project code does not match the received
   PROJECTCODE; such a mismatch is a fatal protocol error.

   A pull advances by the client creating phantoms from the server's
   igot announcements and requesting them with gimme cards.  A push
   advances by the server creating phantoms from the client's igot
   announcements and requesting them with gimme cards; the client then
   sends the named content.  A sync performs both directions in each
   round.  The authoritative phantom and gimme rules are Section 3.3.

9.1.1.  Unversioned-File Synchronisation

   An unversioned-file sync runs inside the same request/reply rounds as
   artifact sync, but it exchanges local unversioned-file state rather
   than artifacts.  At session start, the client initializes the per-
   session unversioned send table with every local unversioned row whose
   hash is not null and sets the session's unversioned catalog-sent flag
   false.  When unversioned sync is enabled and the exchange is
   eligible, the client sends pragma uv-hash HASH exactly once per
   session; during a clone this pragma is not sent on round 0.







Hipp, et al.             Expires 7 January 2027                [Page 52]

Internet-Draft            Fossil Sync Protocol                 July 2026


   The HASH in pragma uv-hash is the unversioned catalog hash.  It is
   computed as the SHA-1 [RFC3174] digest of the concatenation, in
   bytewise ascending name order, of one line per unversioned row whose
   hash is not null.  Each line has the exact byte sequence

     NAME SP YYYY-MM-DD SP HH:MM:SS SP HASH LF

   where SP is ASCII 0x20 and LF is ASCII 0x0A.  NAME is the row's
   fossilize-encoded file name bytes ([FossilArtifact], Section 4) as
   stored.  YYYY-MM-DD SP HH:MM:SS is the 19-byte fixed-width UTC
   rendering of the row's Unix-epoch mtime — four-digit year, zero-
   padded month, day, hour, minute, and second components, an ASCII SP
   (0x20) between the date and the time, no fractional-second field, and
   no time-zone designator.  HASH is the row's stored hash bytes as
   recorded for the row (lowercase hexadecimal, either SHA-1 or SHA3-256
   per the row's stored form).  There is no CR before the LF and no
   trailing whitespace inside the line.  Bytewise ascending name order
   means the rows are ordered by unsigned bytewise comparison of the
   NAME field bytes (equivalent to SQL ORDER BY name under the default
   BINARY collation), not by any locale-dependent, case-insensitive, or
   Unicode collation.  Deleted unversioned rows are excluded from the
   catalog hash; deletion is represented in inventories and transfers by
   HASH equal to -, not by adding a deleted row to the catalog hash
   input.

   If the server's catalog hash matches the client's value, the server
   sends no unversioned-file catalog for that request.  If the hashes
   differ and the client has read permission, the server sends either
   pragma uv-push-ok when it will accept client unversioned uploads, or
   pragma uv-pull-only when the client may pull but may not upload
   unversioned files.  It then sends uvigot NAME MTIME HASH SIZE entries
   describing its unversioned files.  A server suppresses duplicate
   catalog emission only within a single request; a later request may
   receive another catalog only if the protocol path explicitly emits it
   again.

   Upon receiving pragma uv-push-ok, the client sets the session's
   unversioned upload-enabled flag.  Upon receiving pragma uv-pull-only,
   the client sets the session's unversioned pull-only latch and MUST
   NOT upload unversioned files for the remainder of the session; this
   does not prevent pull-side uvgimme/uvfile transfer from server to
   client.

   For each uvigot NAME MTIME HASH SIZE, the client computes the
   unversioned status code against its local row for NAME as follows:

   1.  If no local row exists for NAME, status is 0.




Hipp, et al.             Expires 7 January 2027                [Page 53]

Internet-Draft            Fossil Sync Protocol                 July 2026


   2.  Otherwise, let LOCAL-HASH be the local hash value, or - when the
       local row has a null hash.  Let MTIME-CMP be -1, 0, or +1
       according to whether the local mtime is less than, equal to, or
       greater than the received MTIME.  Let HASH-CMP be the bytewise
       string comparison of LOCAL-HASH against HASH.

   3.  If HASH-CMP is 0, status is 3 + MTIME-CMP: status 2 when the
       local copy is older, status 3 when the two states are identical,
       and status 4 when the local copy is newer.

   4.  Otherwise, if MTIME-CMP is less than 0, or MTIME-CMP is 0 and
       HASH-CMP is less than 0, status is 1.

   5.  Otherwise, status is 5.

   When mtimes are equal but hashes differ, the hash string is therefore
   the deterministic tie-breaker.  In revert mode, the client maps
   status 4 to status 2 and status 5 to status 1 before applying the
   actions below.  In dry-run mode, it suppresses local changes and
   reply messages.

   The status codes drive the client action normatively:

   *  status 0 means NAME is absent locally and the remote state wins;
      when HASH is not -, the client queues uvgimme NAME and removes any
      local row that would be replaced before the incoming uvfile lands;

   *  status 1 means the remote state wins over an existing local row;
      when HASH is not -, the client queues uvgimme NAME and deletes the
      local row before the replacement arrives, and when HASH is -, the
      client records the deletion locally by storing the received mtime
      and clearing hash, size, encoding, and content;

   *  status 2 means the content hash matches but the remote mtime is
      newer, so the client updates local mtime only;

   *  status 3 means local and remote state are identical;

   *  status 4 means the content hash matches but the local mtime is
      newer, so the client retains NAME in the unversioned send table
      with mtimeOnly true if uploads are permitted;

   *  status 5 means local content or deletion overrides the remote
      state, so the client retains or re-inserts NAME in the unversioned
      send table with mtimeOnly false if uploads are permitted.






Hipp, et al.             Expires 7 January 2027                [Page 54]

Internet-Draft            Fossil Sync Protocol                 July 2026


   After those actions, the client updates the unversioned send table:
   statuses 0 through 3 remove NAME from the table; status 4 sets
   mtimeOnly true; status 5 sets mtimeOnly false.  If the session is
   pull-only, statuses 4 and 5 also remove NAME from the table instead
   of keeping it queued for upload.

   A uvgimme NAME requests one named unversioned file.  The receiver
   answers with uvfile NAME MTIME HASH SIZE FLAGS, or with uvigot NAME
   MTIME HASH SIZE again if the send-size limit prevents including the
   content in that response.  The uvfile receiver rechecks the same
   unversioned-status function before landing the change and accepts it
   only when its write-permission and status checks allow the update
   (Section 7.3).

   The unversioned exchange advances across rounds as follows.  The
   first eligible round sends uv-hash and receives either no unversioned
   response or a uv-push-ok/uv-pull-only pragma plus uvigot catalog
   entries.  The next round sends any queued uvgimme cards and, when
   uploads are permitted, any queued uvfile uploads from the unversioned
   send table.  A later round repeats that pattern for any repeated
   uvigot or received uvfile result.  Unversioned-file continuation is
   therefore not open-ended: it depends on the Section 3.7(d)
   termination rule, which continues the session exactly while queued
   uploads remain or while a round that queued uvgimme either also
   received uvfile or ended before the session reached round 3.

   When the client emits a uvfile card from the unversioned send table —
   in any of its three defined forms: full-content, mtime-only (the
   row's mtimeOnly marker set), or tombstone (deletion with HASH - and
   SIZE 0) — the client MUST remove the corresponding row from the
   unversioned send table in the same request-building step, before the
   Section 3.7 termination condition is evaluated for that round.  The
   removal is unconditional at the client: no server acknowledgement,
   echoed uvigot, echoed uvfile, or later response card is required to
   complete it, and the client MUST NOT wait for one.  The client
   routine that walks the unversioned send table and emits its uvfile
   cards during request building — uvDoPush in Fossil's reference
   implementation — is the per-round unversioned push loop referenced in
   this section.  When the accumulating outbound send buffer reaches its
   per-request output-size limit before uvDoPush has visited every
   eligible row, uvDoPush MUST stop for the round with the residual un-
   emitted rows still present in the unversioned send table; those rows
   persist across the round boundary as ordinary per-session state, and
   uvDoPush continues over them at the next round's request-building
   step until either the send table is exhausted or the output cap is
   hit again.  No separate replay state is recorded, no row is
   duplicated, and the session's unversioned upload-enabled flag remains
   set.  Row removal on emission and residual persistence on output-cap



Hipp, et al.             Expires 7 January 2027                [Page 55]

Internet-Draft            Fossil Sync Protocol                 July 2026


   termination are therefore the two invariants under which uvDoPush
   progresses each round: it advances the send table exactly by the rows
   it emits, and each round it terminates by exhausting the send table
   or by hitting the output cap.  The initial per-session seed of the
   unversioned send table (specified at the top of this section)
   includes only local unversioned rows whose hash is not null; a
   locally deleted (tombstone-only) row that has no hash is not inserted
   at seed time and enters the send table only when a later uvigot
   response drives a status-5 outcome for its NAME.

9.2.  Announcement and Request Cards

   An igot HASH card announces that the sender holds the artifact
   identified by HASH.  An igot HASH 1 card announces that the artifact
   is private.  A gimme HASH card requests the artifact identified by
   HASH.  Creation of phantoms from igot, handling of private igot,
   gimme eligibility, missing-content behavior, and per-round duplicate
   suppression are specified in Section 3.3.  There is no ordering
   constraint between igot and gimme cards within a single round.

   Private-artifact igot suppression applies when the sender's recorded
   peer date is before 2020-04-13: for such peers, a private artifact is
   omitted from the igot announcements entirely, rather than announced
   with the 1 flag.

9.2.1.  Derived Branch, Tag, Leaf, Closed, and Current-Trunk Views

   The artifact inputs from which branch, ordinary tag, leaf, closed-
   leaf, and current-trunk views are derived are synchronized by
   transferring the artifacts that carry the corresponding artifact-
   format tag cards.  The sync protocol MUST NOT treat an igot, gimme,
   file, or cfile card as an independent branch/tag operation, and it
   does not define separate wire cards for those views.  The Artifact
   Format owns T card syntax and the tag-operation vocabulary
   ([FossilArtifact], Section 4.7.16); the derivation of repository tag-
   state views (branch, ordinary tag, leaf, closed-leaf, and current-
   trunk) from those crosslinked artifacts is specified in
   [FossilRepoState].  For current-trunk specifically, the protocol
   transports only the artifact inputs; each peer derives current-trunk
   locally, using its own main-branch setting as specified in
   [FossilRepoState].  Current-trunk is therefore not a pure wire-
   synchronized value: two synchronized peers can resolve different
   current-trunks when their main-branch settings differ.

   A receiving implementation may rely on branch, tag, leaf, closed, and
   current-trunk answers for the synchronized scope only after the
   artifacts that carry the relevant structural cards have been stored,
   required delta bases have been resolved or retained as pending



Hipp, et al.             Expires 7 January 2027                [Page 56]

Internet-Draft            Fossil Sync Protocol                 July 2026


   phantoms, and repository crosslink/tag-state processing has run for
   the stored artifacts.  Before that point, the wire stream only proves
   that the peer announced or sent artifact hashes; it does not prove a
   final open-leaf, leaf, closed-marker, or current-trunk view.

   If sync terminates with unresolved phantoms, derived views are
   complete only for the artifact closure actually available in the
   local repository.  Later receipt of missing artifacts can change
   branch membership, symbolic tag visibility, closed-leaf state, leaf
   sets, and current-trunk answers through the artifact crosslink/tag-
   state machinery.  This is not a separate sync error; it follows from
   the normal phantom termination rule in Section 3.7.

9.3.  Phantom Records

   Phantom records are specified in Section 3.3.  This section
   intentionally does not restate the creation, deletion, private-
   filtering, or dangling-delta rules.  A strategy that needs missing
   artifact content uses igot to create phantoms and gimme to request
   eligible phantoms under that single rule surface.

9.4.  Gimme Eligibility Filters

   gimme eligibility filters are specified in Section 3.3.
   Implementations MUST apply the per-round peer-lacks, shun, and
   private-content filters described there, and MUST NOT persist those
   per-round filters across rounds.

9.5.  Loop Termination

   Loop termination follows the normative rule in Section 3.7, including
   the clone-specific continuation condition in Section 3.7(e) and the
   requirement that the client stop when none of those conditions holds.
   The server holds no per-session state and does not detect
   termination; it processes each request independently and signals no
   completion, as described in Section 3.7.

10.  Pragmas

   A pragma card has the form pragma NAME [ARGUMENTS].  Pragma cards are
   processed within the normal sync request/response body; no out-of-
   band negotiation is required.  A pragma name the receiver does not
   recognise MUST be silently ignored; this is the forward-compatibility
   mechanism for the pragma namespace.  Unknown pragma names differ from
   unknown card operators: an unknown card operator is a fatal protocol
   error (Section 11).





Hipp, et al.             Expires 7 January 2027                [Page 57]

Internet-Draft            Fossil Sync Protocol                 July 2026


   Fifteen pragma names are defined.  Nine are client-to-server: send-
   private, send-catalog, client-version, uv-hash, ci-lock, ci-unlock,
   client-url, req-links, and req-clusters.  Six are server-to-client:
   server-version, uv-push-ok, uv-pull-only, ci-lock-fail, avoid-delta-
   manifests, and link.  No pragma is version-gated at the receiver; all
   pragmas are forwarded to the ignore path when not recognised.

10.1.  Version Negotiation

   The client MUST include the client-version pragma in every request
   body:

     pragma client-version VERSION DATE TIME

   VERSION is the Fossil release integer, encoded as MAJOR * 10000 +
   MINOR * 100 + PATCH.  Fossil release 2.0 produces the value 20000;
   any release before 2.0 produces a value less than 20000.  DATE is a
   decimal integer in YYYYMMDD form; TIME is a decimal integer in HHMMSS
   form.  The DATE and TIME arguments are OPTIONAL; a client MAY send
   pragma client-version VERSION with VERSION only.  Current clients
   send DATE and TIME on every round, including every clone round.

   Each received client-version pragma updates the server's recorded
   peer release integer and timestamp for the current request.  Before a
   client-version pragma is received, the server treats the peer's
   VERSION as 0.  The server MUST reply with pragma server-version
   VERSION DATE TIME if and only if the received client-version pragma
   included DATE and TIME (that is, the pragma had at least five tokens
   total).  If the client sent client-version with VERSION only, the
   server MUST NOT emit server-version.

   Each received server-version pragma updates the client's recorded
   peer release integer and timestamp for the session.  Before a server-
   version pragma is received, the client treats the peer's VERSION as
   its own release integer.  Recorded peer VERSION values feed the
   SHA3-256 send gates in Section 7.6.

10.2.  Unversioned-File Pragmas

   pragma uv-hash HASH carries the client's catalog hash over its
   unversioned-file set.  The catalog-hash computation and the resulting
   announce/request/send loop are specified in Section 9.1.  A client
   sends uv-hash at most once per client sync session and does not send
   it on clone round 0.

   When the server's own catalog hash matches HASH, the server sends no
   unversioned-file response for that request.  When the hashes differ
   and the client has read permission, the server replies with pragma



Hipp, et al.             Expires 7 January 2027                [Page 58]

Internet-Draft            Fossil Sync Protocol                 July 2026


   uv-push-ok if the client may upload unversioned files, or pragma uv-
   pull-only if the client may pull but not upload them, followed by
   uvigot catalog entries.  Server-side catalog emission is deduplicated
   only within the current request.

   pragma uv-push-ok is sent server-to-client to indicate the server
   accepts unversioned-file push.  pragma uv-pull-only is sent server-
   to-client to indicate the server will not accept unversioned-file
   push; the client MUST treat the session as pull-only for unversioned-
   file uploads upon receiving this pragma.  Pull-side unversioned-file
   transfer remains permitted when otherwise authorized.

10.3.  Configuration Exchange

   Repository configuration settings are exchanged through reqconfig and
   config cards.  This exchange is a library component invoked from
   within the normal sync round loop; there is no pre-round capability
   advertisement or separate configuration handshake.

   A client sends reqconfig NAME to request a configuration group, where
   NAME MUST begin with /.  The server silently ignores reqconfig cards
   when the requesting user lacks Read permission; no error card is
   emitted for an unauthorized request.  During clone, clients defer
   reqconfig until after clone round 0 (Section 8.4).

   The server replies with config GROUPNAME SIZE cards followed by SIZE
   content bytes.  The GROUPNAME field encodes the configuration group
   as follows:

   *  Scalar repository settings travel as config /config SIZE cards
      regardless of which group the client requested.  The /config name
      is an encoding marker, not a group identifier.  The SIZE bytes of
      content have the form:

        MTIME SP QUOTED-NAME SP value SP QUOTED-VALUE

      where MTIME is the modification time as a decimal epoch-second
      count, QUOTED-NAME is the setting name in Fossil quote encoding,
      the literal token value appears verbatim, and QUOTED-VALUE is the
      setting value in Fossil quote encoding.  Fossil quote encoding
      encloses the token in single-quote (') characters and doubles any
      single-quote it contains, using SQL-style single-quoted string-
      literal escaping; a reader removes the surrounding quotes and
      undoubles internal quotes to recover the raw name or value.  A
      client receiving /config cards MUST track which groups it
      requested by its own internal state; it cannot determine the
      source group from the card name alone.




Hipp, et al.             Expires 7 January 2027                [Page 59]

Internet-Draft            Fossil Sync Protocol                 July 2026


   *  Table-type configuration entries (covering the shun table, user
      table, report-format table, concealed table, and subscriber table)
      travel as config /GROUPNAME SIZE cards, where GROUPNAME identifies
      the table.  This document specifies the config /GROUPNAME SIZE
      card framing and the request/response flow for these groups, but
      the internal byte serialization of each table-type payload is *out
      of scope for this document*; the scalar /config payload defined
      above is fully specified, whereas a client that must interoperate
      on the specific table groups requires that additional row-encoding
      definition.

   A single newline byte follows the content payload of every config
   card before the next card begins; receivers MUST consume this
   trailing newline.

   Configuration also flows in the reverse direction.  A client that
   holds the Admin capability on the server MAY push a scalar setting
   with a config NAME SIZE card followed by SIZE content bytes and the
   trailing newline, using the same /config payload form specified
   above.  The server MUST require the Admin capability for a pushed
   config card: a client without it receives an error card with the text
   not authorized to push configuration, and the card is not applied.  A
   pushed config card whose SIZE is negative is a fatal protocol error,
   and the server aborts the transaction.  A well-formed config NAME
   SIZE card from an authorized client is applied to the server's
   configuration; the server consumes the SIZE payload bytes and the
   single trailing newline.  Only the scalar /config form is pushed by
   this mechanism; table-type configuration groups are not.

   The transfer-hook configuration group covers exactly four server-side
   hook settings.  These settings travel as scalar config /config cards.
   Their content is executed server-side only and is not part of the
   interoperability surface of this document.

10.4.  Commit-Lock Pragmas

   pragma ci-lock HASH CLIENT-ID requests an exclusive commit lock on
   the check-in identified by HASH.  CLIENT-ID is an opaque per-checkout
   identifier maintained by the client.  The server stores the lock; if
   the lock is already held by a different client, the server replies
   with pragma ci-lock-fail LOGIN MTIME, where LOGIN is the current lock
   holder and MTIME is the lock acquisition time expressed as a non-
   negative decimal integer count of seconds since the Unix epoch
   (1970-01-01T00:00:00 UTC), with no sign, no fractional-second field,
   no time-zone designator, no thousands separator, and no leading zeros
   (a single 0 represents zero).





Hipp, et al.             Expires 7 January 2027                [Page 60]

Internet-Draft            Fossil Sync Protocol                 July 2026


   On receipt of pragma ci-lock-fail, a client MUST record the fault
   against the pending commit — associating LOGIN and MTIME with the
   check-in that pragma ci-lock requested — and MUST continue processing
   the remainder of the round; the pragma is not by itself a wire-layer
   abort of the sync exchange.  A receiver MAY treat a syntactically
   invalid MTIME (in particular, a negative or non-integer MTIME) as a
   fatal protocol error under Section 11.1 and terminate the round; a
   non-negative MTIME MUST NOT be treated as a wire-layer error
   regardless of its magnitude.

   The recorded fault constrains the client's later commit attempt, not
   its handling of the current round.  When the recorded fault is
   present, a subsequent commit against the same check-in MUST NOT be
   completed if it would create a fork against the locked leaf, unless
   the user has explicitly requested an operation that removes the fork-
   avoidance guard (for example, committing on a named branch, marking
   the commit private, forcing the commit, allowing a fork, or
   overriding the lock).  A client that offers such user-directed
   overrides MUST surface the recorded fault to the user before applying
   the override.  Nothing in this section forbids further pull-side
   traffic or later pragma ci-lock attempts in the same session; whether
   the client retries is a policy decision left to the implementation.

   pragma ci-unlock CLIENT-ID releases a previously acquired commit
   lock.  Clients with a known client identifier send ci-unlock in later
   rounds when no commit lock is pending for the current exchange.

10.5.  Private-Content and Discovery Pragmas

   pragma send-private signals that the client wishes to synchronise
   private artifacts.  The server enables private-content transfer for
   this session only when the requesting user holds the Private (x)
   capability.  If the user lacks that capability, the server emits an
   error card with the text not authorized to sync private content
   rather than silently ignoring the pragma; this is a non-reset error
   site (Section 11.1).  A client performing private sync repeats send-
   private in each private-sync round; repeated authorized occurrences
   are idempotent.

   pragma send-catalog requests that the server emit igot cards for
   every artifact it holds.  This is the recovery path for a full
   resynchronisation.

   pragma req-clusters requests that the server emit igot cards for its
   cluster artifacts.  Only public cluster artifacts are eligible for
   this path; a server MUST NOT expose a private cluster artifact
   through pragma req-clusters.  When the server emits at least one
   cluster igot in the response, it MUST enclose the run in the paired



Hipp, et al.             Expires 7 January 2027                [Page 61]

Internet-Draft            Fossil Sync Protocol                 July 2026


   informational comment lines # sending-clusters and # end-of-clusters
   defined in Section 5.3, one immediately before the first cluster igot
   and one immediately after the last; when the server emits no cluster
   igot for that request, it MUST NOT emit either comment line.  Pairing
   binds the sender only; a receiver MUST ignore both comment lines
   under the generic comment-line handling rule of Section 5.3; their
   presence, absence, or apparent pairing has no effect on the
   receiver's disposition of the response or on its processing of the
   response's cards.  pragma req-links requests that the server emit
   alternative-URL announcements for the current repository.  The server
   MAY emit zero or more pragma link URL ARG MTIME cards, and MUST emit
   none absent a preceding pragma req-links from the client in this
   session; the emission is not a solicited-reply obligation but a
   conditional-on-request one, so a server that has nothing to advertise
   emits no pragma link even after req-links.  URL is a fossilize-
   encoded http(s) URL naming an alternative location at which the
   current repository is reachable.  ARG is a fossilize-encoded, server-
   generated opaque token whose byte content is not parsed by the
   receiver; after applying fossilize decoding the receiver stores the
   resulting bytes verbatim, and no wire round-trip ever echoes ARG
   onward to the server or to any other peer.  Fossil's reference
   implementation publishes a JSON object as ARG today, but the wire
   treats ARG as an opaque argument and interoperating implementations
   MUST NOT rely on any internal grammar of ARG, MUST NOT parse it, and
   MUST NOT alter it for storage.  MTIME is a non-negative decimal count
   of seconds since the Unix epoch (1970-01-01T00:00:00 UTC).  The five-
   token grammar pragma, link, URL, ARG, MTIME appears in Appendix B.
   Client-side opt-in to this mechanism is the conjunction of two
   conditions that MUST both hold for the current session: the client's
   local share-links mode is engaged, AND the client has emitted its own
   pragma req-links in this session.  A client for which either leg of
   that conjunction does not hold — including a share-links client that
   has not yet emitted pragma req-links — MUST discard every pragma link
   card it observes and MUST NOT record any of its fields.  An opted-in
   client MAY store each observed (URL, ARG, MTIME) triple as receiver-
   local link state and MAY offer those URLs as alternative locations
   for the current repository; that store is receiver-local, and no wire
   card echoes ARG or the stored triples back to any peer.  This
   mechanism is OPTIONAL: a conforming implementation MAY omit the
   share-links opt-in, the pragma req-links emission, and any handling
   of pragma link entirely, and its absence has no wire-observable
   effect for peers that do not use it.  pragma link carries no
   artifact-graph, delta-basis, or content-relationship semantics: it
   names alternative URLs at which the current repository is reachable,
   not links between artifacts, and its presence MUST NOT be interpreted
   as inventory of the peer's artifact set or as a claim about any
   repository other than the one being synchronized.  pragma client-url
   URL communicates the client's canonical URL to the server.  Current



Hipp, et al.             Expires 7 January 2027                [Page 62]

Internet-Draft            Fossil Sync Protocol                 July 2026


   clients emit client-url and req-links, when applicable, once before
   the round loop rather than repeating them each round.

   pragma avoid-delta-manifests is sent server-to-client to indicate the
   server prefers receiving full-content manifests rather than delta
   manifests.

11.  Error Handling

11.1.  Error Card

   An error card is a fatal protocol signal except for the first round
   of a clone:

     error TEXT

   TEXT is the error message encoded using the fossilize character-
   escaping scheme ([FossilArtifact], Section 4), with spaces encoded as
   \s.  A receiver MUST apply defossilize decoding before displaying or
   logging the text.

   Upon receiving an error card outside the first clone round, the
   client MUST stop processing the response body and MUST terminate the
   sync loop.  The client does not retry the failed transfer using an
   alternative encoding or hash algorithm.

   Before emitting an error card, the server's behavior depends on the
   error origin.  At fatal protocol-error sites — wrong project,
   authorization failures, duplicate login card, and unknown card
   operator — the server discards any prior partial output before
   emitting the error card.  Note that discarding prior output does not
   guarantee an error-only response body: the unauthorized-clone site,
   for example, emits a push card after the reset and before the error
   card.  At the SHA3-256 peer-version gate (Section 7.6) and the
   unauthorized send-private and private card sites, the server appends
   the error card directly to the already-built response; other response
   cards (such as clone_seqno, push, and the closing timestamp comment)
   may therefore appear in the same response body alongside the error
   card.  In all cases the HTTP response status is 200; the error is
   signalled entirely within the message body.

   The exception to immediate client termination is the first round of a
   clone exchange (Section 8.3): error cards received during that round
   are silently discarded.  Current clients send no login card in that
   first clone round, and a server may need to return push SERVERCODE
   PROJECTCODE before the client can form later authenticated requests.





Hipp, et al.             Expires 7 January 2027                [Page 63]

Internet-Draft            Fossil Sync Protocol                 July 2026


   The exact decoded text Fossil version 2.0 or later required. is the
   error message emitted when the SHA3-256 peer-version gate fires
   (Section 7.6).

11.2.  Message Card

   A message card is a non-fatal advisory from the server:

     message TEXT

   The card MUST consist of exactly two whitespace-separated tokens: the
   literal operator message and the single argument TEXT.  TEXT is
   fossilize-encoded on the wire ([FossilArtifact], Section 4), using
   the same encoding as an error card's payload; the receiver MUST apply
   defossilize decoding to TEXT before any further processing.  Upon
   receiving a message card, both the server and the client continue
   processing: the session is not terminated, and neither peer emits an
   error card or aborts the exchange in response to this card alone.

   A client that receives a message card selects its behavior from the
   decoded payload as follows.

   1.  Pull-only suppression path.  When the client is currently
       operating with push intent for this session — the client emitted
       a push SERVERCODE PROJECTCODE operation card in the current round
       (equivalently, the client's internal SYNC_PUSH mode flag is set)
       — AND the decoded payload matches the SQLite GLOB predicate pull
       only *, the client MUST clear its push intent for the remainder
       of the session, MUST suppress any user-visible display of the
       decoded payload for this card, and MUST NOT emit an error card or
       otherwise abort the exchange.  Cleared push intent persists
       across rounds for the rest of the session; a later round MUST NOT
       reinstate push based on this card alone.  The pattern is
       evaluated with SQLite GLOB semantics: the comparison is case-
       sensitive; * matches any run of zero or more bytes; the literal
       ASCII sequence pull only (the two lowercase words pull and only
       separated by a single SP and followed by a mandatory trailing SP)
       is the fixed prefix, and any suffix — including the empty suffix
       — satisfies the predicate.  A decoded payload of exactly pull
       only (with the trailing SP and no further bytes) therefore
       matches, as do payloads such as pull only for now or pull only
       pushing disabled.  No trimming of surrounding whitespace is
       performed before the match.

   2.  Advisory display path.  In every other case — either the client
       does not currently hold push intent for the session, or the
       decoded payload does not match the pull only * predicate under
       the semantics above — the client MAY display the decoded payload



Hipp, et al.             Expires 7 January 2027                [Page 64]

Internet-Draft            Fossil Sync Protocol                 July 2026


       to the user verbatim as a non-fatal advisory message.  Non-
       matching payloads MUST NOT change any per-session flag, MUST NOT
       be treated as protocol errors, and MUST NOT abort the exchange.

   3.  Post-match request effects.  A match under item 1 clears the per-
       session SYNC_PUSH flag as a side effect on that one flag.  It
       MUST NOT rewind or edit any bytes already written to the outbound
       send buffer.  In particular, when the matching message card is
       processed, the Section 3.1 step-(3) preamble reseed for the
       request now under construction has already placed a push
       SERVERCODE PROJECTCODE operation card in that buffer, and any
       earlier card in this same server reply that caused a client-side
       handler to append a push-derived file, cfile, igot, or push-side
       unversioned announcement or request card has already appended it;
       every such byte remains and is dispatched with the immediate next
       request unchanged.  Two distinct effect windows follow from the
       clear.

       a.  Later-in-this-reply SYNC_PUSH-gated processing.  Any
       remaining card in this same server reply whose client-side
       handler tests SYNC_PUSH before appending to the outbound send
       buffer now observes the cleared flag and MUST NOT append.  The
       concrete case established elsewhere in this document is a later
       gimme HASH card in the same reply: its client-side handler would
       otherwise append a responsive file or cfile card carrying local
       versioned content, and once SYNC_PUSH is clear it MUST NOT do so.
       A gimme HASH that appears earlier in the reply's card sequence
       than the matching message card is handled before the clear fires:
       the client has already appended the responsive file or cfile card
       to the outbound send buffer, and that append is retained.  The
       ordering of a gimme card relative to the matching message in the
       reply body therefore determines whether the corresponding content
       card is dispatched with the immediate next request.  Per-session
       flags other than SYNC_PUSH are unaffected by the clear.

















Hipp, et al.             Expires 7 January 2027                [Page 65]

Internet-Draft            Fossil Sync Protocol                 July 2026


       b.  Request construction after the next Section 3.1 step-(2)
       buffer reset.  The immediate next request the client dispatches —
       the buffer under construction when the match fired — carries the
       already-seeded push operation card and every pre-match reply-
       derived append described in subitem a, and is dispatched under
       Section 3.1's single- reset invariant with no additional mid-
       round reset.  The first request whose Section 3.1 step-(3)
       preamble reseed observes the cleared SYNC_PUSH is the one built
       after the following step-(2) reset — the reset that opens the
       round after the round now in flight.  That reseed omits the push
       SERVERCODE PROJECTCODE operation card, and during the
       construction of that request every SYNC_PUSH-gated appending site
       (file, cfile, uvfile, igot, and push-side unversioned
       announcement and request cards) is likewise suppressed.

       c.  If SYNC_PULL remains set in the original operation intent (a
       sync operation reduced by the match to pull-only), the request
       assembled after that following step-(2) reset emits a pull
       SERVERCODE PROJECTCODE operation card in its step-(3) preamble
       reseed and continues pull-side work (gimme, reqconfig, uvgimme,
       and pull-side pragmas) under Sections 3, 7, 9, and 10.

       d.  If SYNC_PULL is not set — the original intent was pure push —
       the request assembled after that following step-(2) reset has no
       operation card and carries no push- or pull-side content work;
       ordinary loop termination under Section 3.7 decides whether that
       further request is issued at all, because the reseeded outbound
       buffer for it contains only the fixed request preamble.  The
       Section 3.1 operation-card omission this branch cross-references
       applies from that reseeded request onward and does not touch the
       immediate next dispatch, whose seeded push card was placed before
       the match cleared SYNC_PUSH.

       e.  A message that matches pull only GLOB under item 1 while the
       client holds pure pull intent (SYNC_PUSH already clear) does not
       enter this branch: SYNC_PUSH being clear excludes the item 1
       predicate, and the client falls through to the advisory display
       path of item 2.  The GLOB match semantics and prefix rules of
       item 1 are unchanged; item 3 only names the request-construction
       consequences of a match already classified there.











Hipp, et al.             Expires 7 January 2027                [Page 66]

Internet-Draft            Fossil Sync Protocol                 July 2026


12.  Security Considerations

   The Fossil login and shared-secret mechanism does not transmit the
   user password or the shared secret in any form; the shared secret is
   computed locally on each side (Section 6.3) and never carried on the
   wire.  Standard HTTP authentication headers (Authorization: Basic,
   Proxy-Authorization) that may appear at the transport layer are
   outside the scope of this mechanism (Section 4).  Authentication
   nonces are derived from the message payload; each freshly generated
   request produces a distinct nonce (Section 6.2, Section 5.3).  Full
   authentication-flow security properties and transport requirements
   are described in Section 12.1.  Implementations MUST NOT store a
   shared secret or its equivalent in a cookie, URI, or any session
   state that is visible to intermediaries.

   A sender MUST NOT transmit a SHA3-256 artifact (a 64-character
   lowercase hexadecimal hash) to a peer whose reported release version
   is less than 20000.  When this condition applies, the sender MUST
   emit an error card (Section 11); no automatic downgrade to SHA1
   naming occurs.  Implementations MUST NOT silently truncate a SHA3-256
   artifact identifier or re-identify it as a SHA1 artifact in any
   protocol path.  See [FossilArtifact] §7 for hash-policy state
   definitions.

12.1.  Credential Replay

   The Fossil login mechanism transmits neither the user password nor
   the shared secret derived from it; the shared secret is derived
   locally from the password (Section 6.3), and neither is carried on
   the wire.  HTTP authentication headers (Authorization: Basic, Proxy-
   Authorization), when present, are transport-layer credentials outside
   the scope of this mechanism (Section 4).  The NONCE in a login card
   is the SHA-1 digest of all message-body bytes that follow the card
   (Section 6.2).  Because each request appends an implementation-
   generated random comment line (Section 5.3), independently generated
   rounds produce distinct nonces.  The server maintains no nonce cache
   and applies no timestamp freshness check, however; a complete request
   — body bytes and login card, or in cookie-login mode (Section 6.5)
   the Cookie: x-f-l-c= header together with the body — can be replayed
   byte-for-byte and will pass server verification.











Hipp, et al.             Expires 7 January 2027                [Page 67]

Internet-Draft            Fossil Sync Protocol                 July 2026


12.2.  Private-Content Exposure and Protected Transport

   Confidentiality, server authentication, and protection against replay
   of credential-bearing traffic MUST be provided by the transport
   layer.  A deployment that carries authenticated sync traffic,
   private-content transfers (send-private pragma, Section 10), or
   cookie-login credentials (Cookie: x-f-l-c, Section 6.5) MUST use
   HTTPS, SSH, or an equivalent protected transport that provides
   confidentiality, integrity, and server authentication.  Plaintext
   HTTP does not provide these properties and SHOULD NOT be used in
   production for such traffic.  An intermediary that can read a
   private-content sync session can read the private artifact payloads
   it transfers.

   A shared secret MUST NOT be stored in a cookie, URI, or session state
   visible to intermediaries.

12.3.  Line-Length Bounds and Resource Exhaustion

   Card and comment lines are terminated by LF, while several card types
   carry byte-counted payloads after that LF.  A receiver that scans for
   LF without a bound can therefore be forced to buffer or scan
   arbitrarily large attacker-made lines before it reaches the payload
   framing decision.  To limit this exposure, conforming receivers MUST
   enforce the Section 5 maximum of 65535 bytes for each card line or
   comment line, measured through the terminating LF and excluding any
   following byte-counted payload.

   Exceeding that bound is a fatal protocol error.  The receiver MUST
   abort the exchange before attempting to interpret the over-length
   line as a card or to consume any subsequent payload bytes.  This
   bound applies only to line framing; it does not cap the SIZE or CSIZE
   payloads of file, cfile, uvfile, or config cards, which remain
   governed by their own declared byte counts and by implementation
   storage policy.

12.4.  SHA3-256 Downgrade Prevention

   SHA3-256-named artifacts are refused to peers with a release integer
   less than 20000 via explicit error card emission; no silent downgrade
   of the artifact identifier to SHA1 is performed.  Implementations
   MUST NOT silently truncate or re-identify a SHA3-256 artifact as SHA1
   on any protocol path.  See [FossilArtifact], Section 7 for the hash-
   policy definitions that govern this gate.







Hipp, et al.             Expires 7 January 2027                [Page 68]

Internet-Draft            Fossil Sync Protocol                 July 2026


13.  IANA Considerations

   This document requests registration of the following media types in
   the application media type registry.  No port number or service-name
   registration is required for this protocol.

   application/x-fossil  Type name: application

      Subtype name: x-fossil

      Required parameters: none

      Optional parameters: none

      Encoding considerations: binary; the body is a zlib-compressed
      Fossil sync message and can contain arbitrary octets after
      decompression.

      Security considerations: see Section 12.  Fossil sync exchanges
      can carry authentication material, private artifacts, and
      repository content.

      Interoperability considerations: this media type is used for
      ordinary Fossil sync requests and for non-clone replies as
      specified in Section 4.1.

      Published specification: this document

      Applications that use this media type: Fossil sync clients,
      servers, and compatible repository replication tools

      Fragment identifier considerations: none

      Additional information: * Deprecated alias names for this type:
      none * Magic number(s): none * File extension(s): none * Macintosh
      file type code(s): none

      Person and email address to contact for further information: Dan
      Shearer, [email protected] (mailto:[email protected])

      Intended usage: COMMON

      Restrictions on usage: none

      Author: D.  Richard Hipp, Stephan Beal, Dan Shearer

      Change controller: IETF




Hipp, et al.             Expires 7 January 2027                [Page 69]

Internet-Draft            Fossil Sync Protocol                 July 2026


   application/x-fossil-debug  Type name: application

      Subtype name: x-fossil-debug

      Required parameters: none

      Optional parameters: none

      Encoding considerations: binary; the body is an uncompressed
      Fossil sync message and can contain arbitrary octets in card
      payloads.

      Security considerations: see Section 12.  This diagnostic form
      exposes the sync message body without outer zlib compression and
      MUST NOT be used on an unprotected production transport.

      Interoperability considerations: this media type is wire-
      compatible with application/x-fossil after decompression, but it
      is intended only for diagnostic debugging and testing as described
      in Section 4.1.

      Published specification: this document

      Applications that use this media type: Fossil debugging, test
      harnesses, and compatible debugging tools

      Fragment identifier considerations: none

      Additional information: * Deprecated alias names for this type:
      none * Magic number(s): none * File extension(s): none * Macintosh
      file type code(s): none

      Person and email address to contact for further information: Dan
      Shearer, [email protected] (mailto:[email protected])

      Intended usage: LIMITED USE

      Restrictions on usage: debugging and testing only

      Author: D.  Richard Hipp, Stephan Beal, Dan Shearer

      Change controller: IETF

   application/x-fossil-uncompressed  Type name: application

      Subtype name: x-fossil-uncompressed

      Required parameters: none



Hipp, et al.             Expires 7 January 2027                [Page 70]

Internet-Draft            Fossil Sync Protocol                 July 2026


      Optional parameters: none

      Encoding considerations: binary; the outer message body is not
      zlib- compressed and clone replies can contain arbitrary octets in
      cfile payloads.

      Security considerations: see Section 12.  Clone replies can carry
      repository content and authentication-adjacent state exactly as
      ordinary sync replies do.

      Interoperability considerations: this media type is used only for
      clone replies negotiating protocol version 3, as specified in
      Section 4.1 and Section 8.

      Published specification: this document

      Applications that use this media type: Fossil repository-bootstrap
      servers, clients, and compatible repository bootstrap tools

      Fragment identifier considerations: none

      Additional information: * Deprecated alias names for this type:
      none * Magic number(s): none * File extension(s): none * Macintosh
      file type code(s): none

      Person and email address to contact for further information: Dan
      Shearer, [email protected] (mailto:[email protected])

      Intended usage: COMMON

      Restrictions on usage: reply media type only; not used for
      requests

      Author: D.  Richard Hipp, Stephan Beal, Dan Shearer

      Change controller: IETF

14.  References

14.1.  Normative References

   [FossilArtifact]
              Hipp, D. R., Ed., Beal, S., and D. Shearer, "The Fossil
              Distributed Version Control System: Artifact Format", Work
              in Progress, Internet-Draft, draft-fossil-artifact-format-
              00, 2026, <https://datatracker.ietf.org/doc/html/draft-
              fossil-artifact-format-00>.




Hipp, et al.             Expires 7 January 2027                [Page 71]

Internet-Draft            Fossil Sync Protocol                 July 2026


   [FossilDelta]
              Hipp, D. R., Ed., Beal, S., and D. Shearer, "The Fossil
              Delta Format", Work in Progress, Internet-Draft, draft-
              fossil-delta-format-00, 2026,
              <https://datatracker.ietf.org/doc/html/draft-fossil-delta-
              format-00>.

   [FossilRepoState]
              Hipp, D. R., Ed., Beal, S., and D. Shearer, "The Fossil
              Distributed Version Control System: Repository State and
              Check-in Resolution", Work in Progress, Internet-Draft,
              draft-fossil-repo-state-00, 2026,
              <https://datatracker.ietf.org/doc/html/draft-fossil-repo-
              state-00>.

   [RFC1950]  Deutsch, P. and J. Gailly, "ZLIB Compressed Data Format
              Specification version 3.3", RFC 1950,
              DOI 10.17487/RFC1950, May 1996,
              <https://www.rfc-editor.org/rfc/rfc1950>.

   [RFC1951]  Deutsch, P., "DEFLATE Compressed Data Format Specification
              version 1.3", RFC 1951, DOI 10.17487/RFC1951, May 1996,
              <https://www.rfc-editor.org/rfc/rfc1951>.

   [RFC2119]  Bradner, S., "Key words for use in RFCs to Indicate
              Requirement Levels", BCP 14, RFC 2119,
              DOI 10.17487/RFC2119, March 1997,
              <https://www.rfc-editor.org/rfc/rfc2119>.

   [RFC3174]  Eastlake 3rd, D. and P. Jones, "US Secure Hash Algorithm 1
              (SHA1)", RFC 3174, DOI 10.17487/RFC3174, September 2001,
              <https://www.rfc-editor.org/rfc/rfc3174>.

   [RFC5234]  Crocker, D., Ed. and P. Overell, "Augmented BNF for Syntax
              Specifications: ABNF", STD 68, RFC 5234,
              DOI 10.17487/RFC5234, January 2008,
              <https://www.rfc-editor.org/rfc/rfc5234>.

   [RFC8174]  Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC
              2119 Key Words", BCP 14, RFC 8174, DOI 10.17487/RFC8174,
              May 2017, <https://www.rfc-editor.org/rfc/rfc8174>.

   [RFC9110]  Fielding, R., Ed., Nottingham, M., Ed., and J. Reschke,
              Ed., "HTTP Semantics", STD 97, RFC 9110,
              DOI 10.17487/RFC9110, June 2022,
              <https://www.rfc-editor.org/rfc/rfc9110>.

14.2.  Informative References



Hipp, et al.             Expires 7 January 2027                [Page 72]

Internet-Draft            Fossil Sync Protocol                 July 2026


   [GSET]     Shapiro, M., Preguica, N., Baquero, C., and M. Zawirski,
              "A comprehensive study of Convergent and Commutative
              Replicated Data Types", INRIA RR-7506, January 2011,
              <https://hal.inria.fr/inria-00555588>.

   [RFC9051]  Melnikov, A., Ed. and B. Leiba, Ed., "Internet Message
              Access Protocol (IMAP) - Version 4rev2", RFC 9051,
              DOI 10.17487/RFC9051, August 2021,
              <https://www.rfc-editor.org/rfc/rfc9051>.

   [RFC9293]  Eddy, W., Ed., "Transmission Control Protocol (TCP)",
              STD 7, RFC 9293, DOI 10.17487/RFC9293, August 2022,
              <https://www.rfc-editor.org/rfc/rfc9293>.

Appendix A.  Examples

   This appendix presents concrete sync exchanges and worked protocol
   instances.  Request Content-Type may be application/x-fossil-debug
   (uncompressed body); server clone replies may use application/x-
   fossil-uncompressed; surrounding prose identifies the applicable
   Content-Type where it matters.  Section 4.1 defines the protocol
   minimum as HTTP/1.1; Appendix A preserves the concrete request lines
   used in these worked instances, including HTTP/1.0 where shown.

   Two fence types appear in this appendix.  A fossil-sync fence
   contains the decompressed sync-message body only — card lines,
   comment lines, and any printable payload bytes included in the
   example, with no HTTP framing.  An http fence contains a full HTTP
   message: request or response start line, headers, a blank line, and
   then the decompressed body; http fences are used where HTTP headers
   carry protocol-relevant information (for example, the Cookie:
   x-f-l-c= cookie-login header or the Content-Type selection).

   Non-printable binary payloads and any printable content deliberately
   omitted from a fence are described in editorial notes immediately
   adjacent to that fence; no placeholder appears inside a fence.
   Examples are illustrative; in any conflict with the protocol
   definition in Sections 3 through 11, the normative prose takes
   precedence.

A.1.  Basic Push/Pull Convergence (Two Rounds)

   Two local repositories share project-code
   aa87ea41b0bb05ba528f7a3bdba700d7e23d6318.  Before the sync each side
   held one checkin the other lacked; the common ancestor was shared by
   clone.





Hipp, et al.             Expires 7 January 2027                [Page 73]

Internet-Draft            Fossil Sync Protocol                 July 2026


   *Round 1 request* (client → server; Content-Type: application/x-
   fossil-debug, 471 bytes body):

   pragma client-version 22800 20260311 113146
   pull da3e69ab0655a7dd2aba2520b2d3302a97c85787 aa87ea41b0bb05ba528f7a3bdba700d7e23d6318
   push da3e69ab0655a7dd2aba2520b2d3302a97c85787 aa87ea41b0bb05ba528f7a3bdba700d7e23d6318
   igot 8e0933ed1ef9aa0051d185c8123f0b9ac79865e4b70098b877543fca0092c259
   igot b1c1b05fea4f52bd90b15fdffd13f5c91dce6505f498eeb79b4952c2ea8d4d15
   igot c5b2e0ecb779a7616b32553d973bc9c4ae5a07569a2cee18001386da6bd9ae57
   # F2A895EF75B47B7157664A7FEAC978EFFFDB01C4

   *Round 1 reply* (server → client; Content-Type: application/x-fossil-
   debug, 367 bytes body):

   pragma server-version 22800 20260311 113146
   gimme 8e0933ed1ef9aa0051d185c8123f0b9ac79865e4b70098b877543fca0092c259
   gimme b1c1b05fea4f52bd90b15fdffd13f5c91dce6505f498eeb79b4952c2ea8d4d15
   igot 4361252d0f450879dcd2e0b6a42b95ffe8a41d8e948130253ae8eefa7f4b79ea
   igot f5116517775075ea483901021282ba97eebe5a2b993a47eb78198adc10efe128
   # timestamp 2026-07-02T18:31:13 errors 0

   The server does not gimme c5b2e0ecb779… because it already holds that
   artifact.  After Round 1 the client holds two new phantoms
   (4361252d0f… and f5116517…) created from the server's igot cards.

   *Round 2 request* (client → server; 1058 bytes body):

   pragma client-version 22800 20260311 113146
   pull da3e69ab0655a7dd2aba2520b2d3302a97c85787 aa87ea41b0bb05ba528f7a3bdba700d7e23d6318
   push da3e69ab0655a7dd2aba2520b2d3302a97c85787 aa87ea41b0bb05ba528f7a3bdba700d7e23d6318
   file 8e0933ed1ef9aa0051d185c8123f0b9ac79865e4b70098b877543fca0092c259 272
   C client:\sadd\salpha.txt
   D 2026-07-02T18:30:46.860
   F alpha.txt b1c1b05fea4f52bd90b15fdffd13f5c91dce6505f498eeb79b4952c2ea8d4d15
   P c5b2e0ecb779a7616b32553d973bc9c4ae5a07569a2cee18001386da6bd9ae57
   R 6b3e7488d36fc39ea8093fdea87c3641
   U drh
   Z ed00cdc67449c3949587aa83b3fd8a67
   file b1c1b05fea4f52bd90b15fdffd13f5c91dce6505f498eeb79b4952c2ea8d4d15 26
   alpha content from client
   gimme 4361252d0f450879dcd2e0b6a42b95ffe8a41d8e948130253ae8eefa7f4b79ea
   gimme f5116517775075ea483901021282ba97eebe5a2b993a47eb78198adc10efe128
   igot 8e0933ed1ef9aa0051d185c8123f0b9ac79865e4b70098b877543fca0092c259
   igot b1c1b05fea4f52bd90b15fdffd13f5c91dce6505f498eeb79b4952c2ea8d4d15
   igot c5b2e0ecb779a7616b32553d973bc9c4ae5a07569a2cee18001386da6bd9ae57
   # F155AB07186D7F394649AB08DB88BD4179159D07





Hipp, et al.             Expires 7 January 2027                [Page 74]

Internet-Draft            Fossil Sync Protocol                 July 2026


   The 272-byte payload of the first file card is the Fossil checkin
   manifest shown above (seven lines of Fossil manifest notation,
   totalling 272 bytes including terminating LFs).  The \s sequences in
   the C card are the Fossil fossilize encoding for spaces.  The 26-byte
   payload of the second file card is the text alpha content from client
   followed by a single LF.  Both file cards use the full-content (two-
   argument) form file HASH SIZE.

   *Round 2 reply* (server → client; 533 bytes body):

   pragma server-version 22800 20260311 113146
   file 4361252d0f450879dcd2e0b6a42b95ffe8a41d8e948130253ae8eefa7f4b79ea 270
   C server:\sadd\sbeta.txt
   D 2026-07-02T18:30:46.907
   F beta.txt f5116517775075ea483901021282ba97eebe5a2b993a47eb78198adc10efe128
   P c5b2e0ecb779a7616b32553d973bc9c4ae5a07569a2cee18001386da6bd9ae57
   R 5aa7ac70dd267efe64fe354554a33895
   U drh
   Z 7330b90458172595afd5c246c62c096d
   file f5116517775075ea483901021282ba97eebe5a2b993a47eb78198adc10efe128 31
   beta content added server-side
   # timestamp 2026-07-02T18:31:13 errors 0

   The 270-byte payload of the first file card is the Fossil checkin
   manifest for the beta commit.  The 31-byte payload of the second file
   card is the text beta content added server-side followed by a single
   LF.

   *Termination*

   No Round 3 is issued.  Round 2 delivered content, but the server's
   reply contained no igot cards, so no new phantoms were created.
   Neither continuation condition of Section 3.7 holds prospectively;
   the client stops.

   *Notes*

   1.  *No login card* appears in this example because the server
       accepts unauthenticated push/pull.  The login card form is
       documented in Section 6.

   2.  *Delta-form file cards* (file HASH DELTASRC SIZE) do not appear:
       the two commits are fully independent with no common file content
       enabling a delta.  The delta form is defined in Section 7.

   3.  *application/x-fossil-debug* is used throughout these two rounds;
       the production compressed media type is application/x-fossil.




Hipp, et al.             Expires 7 January 2027                [Page 75]

Internet-Draft            Fossil Sync Protocol                 July 2026


   4.  *Version integer 22800* encodes Fossil 2.28.00 as 2×10000 +
       28×100 + 0 = 22800.

A.2.  SHA3 Version Gate (Error Card)

   This example uses pragma client-version 19999 to fall below the SHA3
   support threshold of 20000.  The server has one blob with a
   64-character SHA3-256 hash; the gate fires when the server attempts
   to emit that artifact.

   *Request* (Content-Type: application/x-fossil-debug, Content-Length:
   81):

   pragma client-version 19999
   clone 3 1
   # 3A8C1D4EE99F1B0782F7C4A67B39D0C512E7F540

   *Response* (HTTP 200, Content-Type: application/x-fossil-
   uncompressed, Content-Length: 191):

   error Fossil\sversion\s2.0\sor\slater\srequired.
   clone_seqno 0
   push 34302acad64fe2397bd907704dec457899e2f9a3 c85e3efddf993e86e8325d08672ea94bdba6c2c4
   # timestamp 2026-07-02T18:26:49 errors 0

   Decoded error text: Fossil version 2.0 or later required.  The
   response contains three protocol cards (error, clone_seqno 0, push)
   plus a closing # timestamp comment line (not a protocol card).  The
   SHA3 peer-version gate is a per-artifact abandonment site: the server
   does not reset the response before emitting the error, so prior
   partial output is preserved and the remaining cards follow.
   clone_seqno 0 signals that the server's blob-table RID range is
   exhausted; no artifact-transfer cards were emitted in this particular
   response.  No SHA1 retry or silent downgrade occurs.

   A clone client receiving this response as the reply to its first
   clone round would silently discard the error card rather than
   aborting: first-round clone errors are ignored because the project
   code needed for correct authentication is returned in the push card
   in the same response (Section 11.1).  This example therefore shows
   the server's three-card response and the absence of any SHA1
   fallback; the client-side discard is a consequence of Section 11.1.









Hipp, et al.             Expires 7 January 2027                [Page 76]

Internet-Draft            Fossil Sync Protocol                 July 2026


A.3.  Private Content Suppression

   Server: three public blobs (RIDs 1–3) and two private blobs (RIDs
   4–5) in the private(rid) table.  User anonymous has caps og (no
   Private/x); user privuser has caps ogx.  Full 64-character SHA3-256
   hashes shown.  Scenarios 1–3 isolate public/private announcement and
   authorization behavior; Scenario 4 shows the authenticated private-
   pull path.

   *Scenario 1 — Natural pull (anonymous): private artifacts absent*

   Request (174-byte body):

   pragma client-version 20028 20260702 183000
   pull cccccccccccccccccccccccccccccccccccccccc ea2e766b68008b6c3b3ca547df136ded07c7f921
   # 9B21F7CE5A48D3126FE0B48A0D5C71D3F26AA810

   Response (295-byte body):

   pragma server-version 22800 20260311 113146
   igot 23813573dea5ec687052a067866751f481e58ef4c478939ac601587a43f50b35
   igot 2c215398c6ea4e3c0994d41918f632d5208d6a24da3ac894c9d9d167a711f30b
   igot ed76b084599a372a5a17232f0bb67211145c0bde6b5a5ff542813d60fa07d3dc
   # timestamp 2026-07-02T18:33:01 errors 0

   Three public igot cards; private RIDs 4–5 are completely absent from
   the wire.

   *Scenario 2 — Crafted gimme for private hash: date-gated igot HASH 1*

   Request (245-byte body):

   pragma client-version 20028 20260702 183000
   pull cccccccccccccccccccccccccccccccccccccccc ea2e766b68008b6c3b3ca547df136ded07c7f921
   gimme da4b6b0ea2b060ff6bf29c16a3b10be05afc9c1a75d605a45cf7267ea3e74091
   # C7D40B21E5F84A9308B21A5F63C4A9E70152D3B4

   Response (367-byte body):

   pragma server-version 22800 20260311 113146
   igot da4b6b0ea2b060ff6bf29c16a3b10be05afc9c1a75d605a45cf7267ea3e74091 1
   igot 23813573dea5ec687052a067866751f481e58ef4c478939ac601587a43f50b35
   igot 2c215398c6ea4e3c0994d41918f632d5208d6a24da3ac894c9d9d167a711f30b
   igot ed76b084599a372a5a17232f0bb67211145c0bde6b5a5ff542813d60fa07d3dc
   # timestamp 2026-07-02T18:33:01 errors 0






Hipp, et al.             Expires 7 January 2027                [Page 77]

Internet-Draft            Fossil Sync Protocol                 July 2026


   igot HASH 1 — private-flag form — signals the artifact exists and is
   private; no file card follows because private-content sync is not
   enabled.  The peer's recorded date (20260702) ≥ 20200413 satisfies
   the date gate.

   *Scenario 3 — Crafted server-path probe: unauthorized send-private
   (anonymous, no x capability)*

   _This scenario isolates the server's authorization check.  The card
   ordering shown — pragma client-version before pragma send-private —
   is not the normative client order; Section 3.2 places pragma send-
   private before pragma client-version on the initial round of a
   private-content sync._

   Request (194-byte body):

   pragma client-version 20028 20260702 183000
   pragma send-private
   pull cccccccccccccccccccccccccccccccccccccccc ea2e766b68008b6c3b3ca547df136ded07c7f921
   # 6E1A97F2B4D08C51A73F0592E4C68B10D97AC531

   Response (345-byte body):

   pragma server-version 22800 20260311 113146
   error not\sauthorized\sto\ssync\sprivate\scontent
   igot 23813573dea5ec687052a067866751f481e58ef4c478939ac601587a43f50b35
   igot 2c215398c6ea4e3c0994d41918f632d5208d6a24da3ac894c9d9d167a711f30b
   igot ed76b084599a372a5a17232f0bb67211145c0bde6b5a5ff542813d60fa07d3dc
   # timestamp 2026-07-02T18:33:01 errors 0

   Decoded: not authorized to sync private content.  An unauthorized
   send-private pragma emits an explicit error card.  This is a non-
   reset path: public igot cards follow the error card in the same
   response.

   *Scenario 4 — Authorized private pull (privuser, x capability)*

   Round 1 request (455 bytes total):













Hipp, et al.             Expires 7 January 2027                [Page 78]

Internet-Draft            Fossil Sync Protocol                 July 2026


   POST / HTTP/1.0
   Host: 127.0.0.1:9872
   User-Agent: Fossil/2.28 (2026-03-11 11:31:46 [52445a27f1])
   Content-Type: application/x-fossil-debug
   Content-Length: 291

   login privuser ee71329ef84fb77b428c6f626ad0103a426e0652 c3ee0be781c9ebcfc6e24756cbb75e6319ca255b
   pragma send-private
   pragma client-version 22800 20260311 113146
   pull 3921d47b51aad4027faac6467b4a87b4e7951c64 ea2e766b68008b6c3b3ca547df136ded07c7f921
   # 7258235C6A6B727E5C0F6E2E2576D36A234E63A3

   login USER NONCE SIGNATURE — body-embedded login card.

   Round 1 reply (content-length 439 bytes):

   pragma server-version 22800 20260311 113146
   igot 23813573dea5ec687052a067866751f481e58ef4c478939ac601587a43f50b35
   igot 2c215398c6ea4e3c0994d41918f632d5208d6a24da3ac894c9d9d167a711f30b
   igot ed76b084599a372a5a17232f0bb67211145c0bde6b5a5ff542813d60fa07d3dc
   igot 393953a9803595823fb560ddbe333767840a5ad89748a12fc2cd00f533c6df8a 1
   igot da4b6b0ea2b060ff6bf29c16a3b10be05afc9c1a75d605a45cf7267ea3e74091 1
   # timestamp 2026-07-02T18:34:48 errors 0

   Private artifacts announced as igot HASH 1.

   Round 2 request (614 bytes total):

   POST / HTTP/1.0
   Host: 127.0.0.1:9872
   User-Agent: Fossil/2.28 (2026-03-11 11:31:46 [52445a27f1])
   Cookie: x-f-l-c=login+privuser+653828a8fc753c1685501de457c104f0b20938c9+bec32fbc6a2589413ee6d1f47a4ae0098dafcad2
   Content-Type: application/x-fossil-debug
   Content-Length: 336

   pragma client-version 22800 20260311 113146
   pragma send-private
   pull 3921d47b51aad4027faac6467b4a87b4e7951c64 ea2e766b68008b6c3b3ca547df136ded07c7f921
   gimme 393953a9803595823fb560ddbe333767840a5ad89748a12fc2cd00f533c6df8a
   gimme da4b6b0ea2b060ff6bf29c16a3b10be05afc9c1a75d605a45cf7267ea3e74091
   # 4D85B916A821F5198416D9F01D4F75F45714F45C

   Cookie: x-f-l-c=… replaces the body login card from Round 2 onward.

   Round 2 reply (content-length 1033 bytes):






Hipp, et al.             Expires 7 January 2027                [Page 79]

Internet-Draft            Fossil Sync Protocol                 July 2026


   pragma server-version 22800 20260311 113146
   private
   file 393953a9803595823fb560ddbe333767840a5ad89748a12fc2cd00f533c6df8a 49
   Private content — must not reach x-less peers.

   The 49-byte payload ends with a single LF.  In the binary stream it
   is immediately followed by the next private card with no separator.

   private
   file da4b6b0ea2b060ff6bf29c16a3b10be05afc9c1a75d605a45cf7267ea3e74091 ed76b084599a372a5a17232f0bb67211145c0bde6b5a5ff542813d60fa07d3dc 317

   317 bytes of binary delta payload follow; payload contains embedded
   LF bytes and MUST be consumed by byte count, not newline scanning.

   igot 23813573dea5ec687052a067866751f481e58ef4c478939ac601587a43f50b35
   igot 2c215398c6ea4e3c0994d41918f632d5208d6a24da3ac894c9d9d167a711f30b
   igot ed76b084599a372a5a17232f0bb67211145c0bde6b5a5ff542813d60fa07d3dc
   igot 393953a9803595823fb560ddbe333767840a5ad89748a12fc2cd00f533c6df8a 1
   igot da4b6b0ea2b060ff6bf29c16a3b10be05afc9c1a75d605a45cf7267ea3e74091 1
   # timestamp 2026-07-02T18:34:48 errors 0

   Each private artifact is immediately preceded by a private card.  The
   second round completes the exchange: the client requests the two
   announced private artifacts and receives both.

A.4.  Comment Lines and Clock-Skew Timestamp

   This example uses the same two-round push/pull exchange as A.1.  The
   # sending-clusters / # end-of-clusters comment pair was not triggered
   in this exchange; no cluster transmission occurred.

   *Client comment lines* (random nonce):

   Round 1 request, final line:

   # F2A895EF75B47B7157664A7FEAC978EFFFDB01C4

   Round 2 request, final line:

   # F155AB07186D7F394649AB08DB88BD4179159D07

   Both lines begin with # (0x23) followed by a 40-character uppercase
   hex nonce; they are not dispatched as protocol cards and the server
   skips them silently.  A different nonce appears in each round.

   *Server timestamp comment lines*:

   Round 1 reply, final line:



Hipp, et al.             Expires 7 January 2027                [Page 80]

Internet-Draft            Fossil Sync Protocol                 July 2026


   # timestamp 2026-07-02T18:31:13 errors 0

   Round 2 reply, final line:

   # timestamp 2026-07-02T18:31:13 errors 0

   Both match the # timestamp DATETIME errors N subform.  The client
   reads DATETIME to compute clock skew; N = 0 indicates no protocol
   errors were signalled in that round.

A.5.  Authentication Example

   This non-normative example reproduces a full login signature end to
   end.  All digests are SHA-1 [RFC3174]; each value recomputes from the
   inputs shown, so an implementer can check an implementation against
   it.  The authentication formulas are specified in Section 6.

   Inputs:

     +====================+==========================================+
     | Field              | Value                                    |
     +====================+==========================================+
     | login name (USER)  | alice                                    |
     +--------------------+------------------------------------------+
     | plaintext password | secret-passphrase                        |
     +--------------------+------------------------------------------+
     | project code       | c1b2a3d4e5f60718293a4b5c6d7e8f9012345678 |
     +--------------------+------------------------------------------+
     | NONCE-covered body | the 26 ASCII bytes example-request-body- |
     |                    | bytes                                    |
     +--------------------+------------------------------------------+

                                  Table 6

   Derivation:

   NONCE         = SHA1("example-request-body-bytes")
                 = 5dfa03bd8645938b4e312a4fcdad5ebe6d4d4840
   shared-secret = SHA1("c1b2a3d4e5f60718293a4b5c6d7e8f9012345678"
                        "/alice/secret-passphrase")
                 = d7a235115bf97d126c42140196c4fc6087312353
   SIGNATURE     = SHA1( NONCE-hex || shared-secret-hex )  ; 80 bytes
                 = e3dba1c99442823b76ae64a8afca940e0327f9f8

   These populate the login card's USER, NONCE, and SIGNATURE fields
   (Section 6); NONCE and SIGNATURE are the 40-character lowercase
   hexadecimal strings shown.




Hipp, et al.             Expires 7 January 2027                [Page 81]

Internet-Draft            Fossil Sync Protocol                 July 2026


Appendix B.  ABNF

   This appendix gives the canonical line grammar for every sync body-
   card operator named in Sections 3 through 11, plus the cookie-login
   HTTP header.  ABNF notation follows [RFC5234].  Byte-counted payload
   lengths and the required equality between a card's declared SIZE or
   CSIZE field and the following octet count are normative rules of
   Sections 5, 7, and 10; the productions below name where those payload
   octets occur, but the arithmetic constraint itself is enforced by
   prose rather than ABNF.

  ; § B.1  Core tokens

  LF               = %x0A
  SP               = %x20
  ALPHA-LC         = %x61-7A
  DIGIT            = %x30-39
  HEXDIG-LC        = DIGIT / %x61-66
  HEXDIG-UC        = DIGIT / %x41-46
  HEXDIG-CI        = DIGIT / %x41-46 / %x61-66
  sha1-hash        = 40HEXDIG-LC
  sha3-hash        = 64HEXDIG-LC
  artifact-hash    = sha1-hash / sha3-hash
  decimal          = 1*DIGIT
  fossil-token     = 1*( ALPHA-LC / DIGIT / %x5F )
  fossilized       = 1*( %x21-7E )   ; printable ASCII, no SP;
                                      ; \s \t \n \r \f \v \0 \\ per
                                      ; [FossilArtifact] §4
  config-name      = "/" 1*( ALPHA-LC / DIGIT / "-" / "_" )
  pragma-name      = 1*( ALPHA-LC / DIGIT / "-" )
  client-id        = fossilized
  non-lf-byte      = %x01-09 / %x0B-FF
  qchar            = %x20-26 / %x28-7E / %x80-FF
  fossil-quoted    = %x27 *( qchar / %x27 %x27 ) %x27
  timestamp        = 4DIGIT "-" 2DIGIT "-" 2DIGIT %x54 2DIGIT ":" 2DIGIT ":" 2DIGIT
                                      ; 19-byte fixed UTC YYYY-MM-DDTHH:MM:SS
                                      ; per §5.3
  uv-hash-field    = artifact-hash / "-"
  payload-octets   = *OCTET

  ; § B.2  Message-body structure

  sync-message     = *( sync-element )
  sync-element     = bare-card / file-transfer / cfile-transfer /
                     uvfile-transfer / config-transfer / comment-line

  bare-card        = cookie-card / login-card / push-card / pull-card /
                     clone-card / private-card / igot-card / gimme-card /



Hipp, et al.             Expires 7 January 2027                [Page 82]

Internet-Draft            Fossil Sync Protocol                 July 2026


                     uvigot-card / uvgimme-card / pragma-card /
                     reqconfig-card / clone-seqno-card / error-card /
                     message-card

  file-transfer    = file-card payload-octets
  cfile-transfer   = cfile-card payload-octets
  uvfile-transfer  = uvfile-card [ payload-octets ]
  config-transfer  = config-card payload-octets LF

  ; payload-octets is consumed by the exact SIZE or CSIZE value declared
  ; in the immediately preceding card line.

  ; § B.3  Comment lines (§5.3)

  comment-line             = timestamp-comment / sending-clusters-comment /
                             end-of-clusters-comment / nonce-comment /
                             generic-comment
  timestamp-comment        = "# timestamp" SP timestamp SP "errors" SP decimal LF
  sending-clusters-comment = "# sending-clusters" LF
  end-of-clusters-comment  = "# end-of-clusters" LF
  nonce-comment            = "#" SP 40HEXDIG-UC LF
  generic-comment          = "#" *non-lf-byte LF

  ; § B.4  Transport and authentication (§§4, 6)

  cookie-card       = "cookie" SP fossilized LF
  login-card        = "login" SP user SP nonce SP signature LF
  user              = fossilized
  nonce             = sha1-hash
  signature         = sha1-hash

  cookie-login-header = "Cookie: x-f-l-c=" url-login-value
  url-login-value     = "login+" pct-user "+" nonce "+" signature
  pct-user            = 1*( unreserved / pct-encoded )
  unreserved          = ALPHA / DIGIT / "-" / "." / "_" / "~"
  pct-encoded         = "%" HEXDIG-CI HEXDIG-CI

  ; § B.5  Operation and announcement cards (§§3, 8, 9)

  push-card         = "push" SP servercode SP projectcode LF
  pull-card         = "pull" SP servercode SP projectcode LF
  servercode        = fossil-token
  projectcode       = fossil-token

  clone-card        = "clone" SP decimal SP decimal LF
  clone-seqno-card  = "clone_seqno" SP decimal LF
  private-card      = "private" LF




Hipp, et al.             Expires 7 January 2027                [Page 83]

Internet-Draft            Fossil Sync Protocol                 July 2026


  igot-card         = "igot" SP artifact-hash LF /
                      "igot" SP artifact-hash SP "1" LF
  gimme-card        = "gimme" SP artifact-hash LF
  uvigot-card       = "uvigot" SP fossilized SP decimal SP uv-hash-field
                      SP decimal LF
  uvgimme-card      = "uvgimme" SP fossilized LF

  ; § B.6  Content-transfer cards (§7)

  file-card         = "file" SP artifact-hash SP decimal LF /
                      "file" SP artifact-hash SP artifact-hash SP decimal LF
  cfile-card        = "cfile" SP artifact-hash SP decimal SP decimal LF /
                      "cfile" SP artifact-hash SP artifact-hash SP decimal
                      SP decimal LF
  uvfile-card       = "uvfile" SP fossilized SP decimal SP uv-hash-field
                      SP decimal SP decimal LF

  ; § B.7  Configuration and pragma cards (§10)

  reqconfig-card    = "reqconfig" SP config-name LF
  config-card       = "config" SP config-name SP decimal LF
  config-scalar-payload = decimal SP fossil-quoted SP "value" SP fossil-quoted
                          ; payload form used by config /config

  pragma-card       = client-version-pragma / server-version-pragma /
                      uv-hash-pragma / uv-push-ok-pragma /
                      uv-pull-only-pragma / ci-lock-pragma /
                      ci-lock-fail-pragma / ci-unlock-pragma /
                      send-private-pragma / send-catalog-pragma /
                      client-url-pragma / req-links-pragma /
                      req-clusters-pragma / link-pragma /
                      avoid-delta-manifests-pragma / extension-pragma

  client-version-pragma =
                     "pragma" SP "client-version" SP decimal
                     [ SP 8DIGIT SP 6DIGIT ] LF
  server-version-pragma =
                     "pragma" SP "server-version" SP decimal SP 8DIGIT
                     SP 6DIGIT LF
  uv-hash-pragma    = "pragma" SP "uv-hash" SP artifact-hash LF
  uv-push-ok-pragma = "pragma" SP "uv-push-ok" LF
  uv-pull-only-pragma =
                     "pragma" SP "uv-pull-only" LF
  ci-lock-pragma    = "pragma" SP "ci-lock" SP artifact-hash SP client-id LF
  ci-lock-fail-pragma =
                     "pragma" SP "ci-lock-fail" SP fossilized SP decimal LF
  ci-unlock-pragma  = "pragma" SP "ci-unlock" SP client-id LF
  send-private-pragma =



Hipp, et al.             Expires 7 January 2027                [Page 84]

Internet-Draft            Fossil Sync Protocol                 July 2026


                     "pragma" SP "send-private" LF
  send-catalog-pragma =
                     "pragma" SP "send-catalog" LF
  client-url-pragma = "pragma" SP "client-url" SP fossilized LF
  req-links-pragma  = "pragma" SP "req-links" LF
                       ; client-to-server; opt-in to reception of
                       ; server `link` cards; the client emits this
                       ; pragma only while its local `share-links`
                       ; mode is engaged for the current session
  req-clusters-pragma =
                     "pragma" SP "req-clusters" LF
  link-pragma       = "pragma" SP "link" SP link-url SP link-arg SP link-mtime LF
                       ; server-to-client; a server emits zero or
                       ; more `link` cards in the same response,
                       ; but MUST emit none absent a preceding
                       ; client `pragma req-links` in this session.
                       ; Client-side opt-in is the conjunction of
                       ; two conditions that MUST both hold for the
                       ; current session: the client's local
                       ; `share-links` mode is engaged, AND the
                       ; client has emitted its own `pragma
                       ; req-links` in this session.  A receiver
                       ; for which either leg of that conjunction
                       ; does not hold — including a `share-links`
                       ; client that has not yet emitted
                       ; `req-links` — MUST silently discard every
                       ; `link` card it observes and MUST NOT
                       ; record any of its fields.  This card
                       ; carries no artifact-graph or
                       ; content-relationship semantics.
  link-url          = fossilized     ; fossilize-encoded http(s) URL naming
                                     ; an alternative location for the
                                     ; current repository
  link-arg          = fossilized     ; fossilize-encoded server-generated
                                     ; opaque token; the receiver stores the
                                     ; defossilized bytes verbatim, MUST NOT
                                     ; parse them, and MUST NOT echo them
                                     ; onward on the wire
  link-mtime        = decimal        ; non-negative seconds since the
                                     ; Unix epoch (1970-01-01T00:00:00 UTC)
  avoid-delta-manifests-pragma =
                     "pragma" SP "avoid-delta-manifests" LF
  extension-pragma  = "pragma" SP pragma-name *( SP fossilized ) LF

  ; § B.8  Error signalling (§11)

  error-card        = "error" SP fossilized LF
  message-card      = "message" SP fossilized LF



Hipp, et al.             Expires 7 January 2027                [Page 85]

Internet-Draft            Fossil Sync Protocol                 July 2026


Appendix C.  Anomalies

   The following statements found in existing descriptions of the
   protocol do not match the behaviour specified in the body of this
   document.  The body is definitive; the divergent statements are
   recorded here for reference.

   "Unknown message" response. :: Some descriptions refer to an "Unknown
   message" reply for an unrecognized card.  No such wire text exists.
   On the server side, an unrecognized card is answered with an error
   bad command: card followed by the offending line, and a malformed
   content card is answered with an error malformed CARD line card, both
   emitted as outbound cards before the server aborts the exchange.  On
   the client side (for example while processing a server reply) no such
   outbound error card is emitted: the client instead records the
   condition as a local warning and aborts the current round without
   further reply processing, transitioning under the state machine of
   Section 3.8.  This endpoint asymmetry is the normative rule of
   Section 5.2 and Section 11.1.

   Cumulative multi-login permissions. :: An earlier server behaviour
   accumulated capability grants across multiple login cards presented
   in a single request.  That behaviour has been withdrawn: a second
   login card in a request is a fatal protocol error that aborts the
   exchange, and capability grants from a login card are additive to the
   baseline within a single verification only, never cumulative across
   multiple cards (Section 6.1, Section 6.4).

Authors' Addresses

   D. Richard Hipp (editor)
   Fossil


   Stephan Beal
   libfossil


   Dan Shearer
   Email: [email protected]
   URI:   https://shearer.org










Hipp, et al.             Expires 7 January 2027                [Page 86]