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 . . . . . . . . . . . . . . . . . 4
3. Protocol Operation . . . . . . . . . . . . . . . . . . . . . 6
3.1. Endpoint State . . . . . . . . . . . . . . . . . . . . . 7
3.2. Round Structure . . . . . . . . . . . . . . . . . . . . . 9
3.3. Phantom Record Lifecycle . . . . . . . . . . . . . . . . 11
3.4. The Clone Loop . . . . . . . . . . . . . . . . . . . . . 13
3.5. Request and Response Pragmas . . . . . . . . . . . . . . 13
3.6. Cookie Card . . . . . . . . . . . . . . . . . . . . . . . 13
3.7. Termination . . . . . . . . . . . . . . . . . . . . . . . 13
3.8. Client Protocol State Machine . . . . . . . . . . . . . . 15
3.9. Robustness Posture . . . . . . . . . . . . . . . . . . . 18
3.10. Round Message Layout . . . . . . . . . . . . . . . . . . 19
4. Transport . . . . . . . . . . . . . . . . . . . . . . . . . . 20
4.1. Message Compression and Media Types . . . . . . . . . . . 21
4.2. Round Structure . . . . . . . . . . . . . . . . . . . . . 23
4.3. Cookie Card . . . . . . . . . . . . . . . . . . . . . . . 23
5. Card Format . . . . . . . . . . . . . . . . . . . . . . . . . 23
5.1. Message Structure . . . . . . . . . . . . . . . . . . . . 23
5.2. Card Syntax . . . . . . . . . . . . . . . . . . . . . . . 23
5.2.1. Defined Operators . . . . . . . . . . . . . . . . . . 25
5.3. Comment Lines . . . . . . . . . . . . . . . . . . . . . . 25
6. Authentication . . . . . . . . . . . . . . . . . . . . . . . 26
6.1. Login Card . . . . . . . . . . . . . . . . . . . . . . . 26
6.2. Nonce Derivation . . . . . . . . . . . . . . . . . . . . 27
6.3. Shared Secret . . . . . . . . . . . . . . . . . . . . . . 27
6.4. Signature Computation . . . . . . . . . . . . . . . . . . 27
6.5. Cookie-Login Mode . . . . . . . . . . . . . . . . . . . . 29
6.6. Capability Model . . . . . . . . . . . . . . . . . . . . 30
7. Content Transfer . . . . . . . . . . . . . . . . . . . . . . 30
7.1. file Card . . . . . . . . . . . . . . . . . . . . . . . . 30
7.2. cfile Card . . . . . . . . . . . . . . . . . . . . . . . 31
7.3. uvfile Card . . . . . . . . . . . . . . . . . . . . . . . 31
7.4. Delta Sources and Phantom Creation . . . . . . . . . . . 33
7.5. Private Content . . . . . . . . . . . . . . . . . . . . . 33
7.6. SHA3-256 Peer-Version Gate . . . . . . . . . . . . . . . 34
7.7. Hash-Policy Admission . . . . . . . . . . . . . . . . . . 34
8. Clone . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
8.1. Clone Protocol Version . . . . . . . . . . . . . . . . . 35
8.2. Sequence Numbers . . . . . . . . . . . . . . . . . . . . 36
8.3. First Clone Round . . . . . . . . . . . . . . . . . . . . 37
Hipp, et al. Expires 7 January 2027 [Page 2]
Internet-Draft Fossil Sync Protocol July 2026
8.4. Configuration During Clone . . . . . . . . . . . . . . . 38
9. Synchronisation Strategies . . . . . . . . . . . . . . . . . 38
9.1. Push, Pull, and Sync . . . . . . . . . . . . . . . . . . 38
9.1.1. Unversioned-File Synchronisation . . . . . . . . . . 38
9.2. Announcement and Request Cards . . . . . . . . . . . . . 41
9.2.1. Derived Branch, Tag, Leaf, Closed, and Current-Trunk
Views . . . . . . . . . . . . . . . . . . . . . . . . 41
9.3. Phantom Records . . . . . . . . . . . . . . . . . . . . . 42
9.4. Gimme Eligibility Filters . . . . . . . . . . . . . . . . 42
9.5. Loop Termination . . . . . . . . . . . . . . . . . . . . 42
10. Pragmas . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
10.1. Version Negotiation . . . . . . . . . . . . . . . . . . 43
10.2. Unversioned-File Pragmas . . . . . . . . . . . . . . . . 44
10.3. Configuration Exchange . . . . . . . . . . . . . . . . . 44
10.4. Commit-Lock Pragmas . . . . . . . . . . . . . . . . . . 46
10.5. Private-Content and Discovery Pragmas . . . . . . . . . 46
11. Error Handling . . . . . . . . . . . . . . . . . . . . . . . 47
11.1. Error Card . . . . . . . . . . . . . . . . . . . . . . . 47
11.2. Message Card . . . . . . . . . . . . . . . . . . . . . . 48
12. Security Considerations . . . . . . . . . . . . . . . . . . . 48
12.1. Credential Replay . . . . . . . . . . . . . . . . . . . 49
12.2. Private-Content Exposure and Protected Transport . . . . 49
12.3. Line-Length Bounds and Resource Exhaustion . . . . . . . 50
12.4. SHA3-256 Downgrade Prevention . . . . . . . . . . . . . 50
13. IANA Considerations . . . . . . . . . . . . . . . . . . . . . 50
14. References . . . . . . . . . . . . . . . . . . . . . . . . . 53
14.1. Normative References . . . . . . . . . . . . . . . . . . 53
14.2. Informative References . . . . . . . . . . . . . . . . . 54
Appendix A. Examples . . . . . . . . . . . . . . . . . . . . . . 55
A.1. Basic Push/Pull Convergence (Two Rounds) . . . . . . . . 55
A.2. SHA3 Version Gate (Error Card) . . . . . . . . . . . . . 57
A.3. Private Content Suppression . . . . . . . . . . . . . . . 58
A.4. Comment Lines and Clock-Skew Timestamp . . . . . . . . . 61
A.5. Authentication Example . . . . . . . . . . . . . . . . . 62
Appendix B. ABNF . . . . . . . . . . . . . . . . . . . . . . . . 63
Appendix C. Anomalies . . . . . . . . . . . . . . . . . . . . . 67
Authors' Addresses . . . . . . . . . . . . . . . . . . . . . . . 67
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.
This section is informative. Requirements appear in Sections 3
through 11.
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.
Hipp, et al. Expires 7 January 2027 [Page 4]
Internet-Draft Fossil Sync Protocol July 2026
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.
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.
Hipp, et al. Expires 7 January 2027 [Page 5]
Internet-Draft Fossil Sync Protocol July 2026
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.
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.
Hipp, et al. Expires 7 January 2027 [Page 6]
Internet-Draft Fossil Sync Protocol July 2026
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 | Next blob-table RID for the |
| number | | server to send during a |
| | | clone |
+---------------------+-------------+-----------------------------+
| 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 |
+---------------------+-------------+-----------------------------+
| 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- |
Hipp, et al. Expires 7 January 2027 [Page 7]
Internet-Draft Fossil Sync Protocol July 2026
| only latch | | only; suppresses client |
| | | unversioned uploads for the |
| | | rest of the session |
+---------------------+-------------+-----------------------------+
| send queue | per-round | Artifacts the client will |
| | | push this round |
+---------------------+-------------+-----------------------------+
| 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 |
| | | content |
+---------------------+-------------+-----------------------------+
| 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 |
+---------------------+-------------+-----------------------------+
| unversioned-request | per-round | Number of uvgimme cards |
| count | | queued while processing |
| | | this round's reply |
+---------------------+-------------+-----------------------------+
| unversioned-content | per-round | Number of uvfile cards |
| count | | received in this round's |
| | | reply |
+---------------------+-------------+-----------------------------+
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.
The per-round tables and counters MUST be created or reset at the
start of each round and MUST be destroyed or discarded at the end of
that round; they carry no state across round boundaries and MUST NOT
be treated as persistent storage.
Hipp, et al. Expires 7 January 2027 [Page 8]
Internet-Draft Fossil Sync Protocol July 2026
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: pull SERVERCODE PROJECTCODE, push SERVERCODE
PROJECTCODE, both for a bidirectional sync, or clone 3 SEQNO for a
modern clone (Section 8);
* 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);
Hipp, et al. Expires 7 January 2027 [Page 9]
Internet-Draft Fossil Sync Protocol July 2026
* 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;
* 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:
Hipp, et al. Expires 7 January 2027 [Page 10]
Internet-Draft Fossil Sync Protocol July 2026
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 send queue for next round
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
The round's per-round tracking tables are destroyed after all reply
cards have been processed. The client then evaluates the termination
condition (Section 3.7) before building the next request.
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:
Hipp, et al. Expires 7 January 2027 [Page 11]
Internet-Draft Fossil Sync Protocol July 2026
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
(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
Hipp, et al. Expires 7 January 2027 [Page 12]
Internet-Draft Fossil Sync Protocol July 2026
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 deferred until the source artifact arrives. The
content-received flag is set in both paths.
3.4. The Clone Loop
A modern clone is a normal sequence of sync rounds whose operation
card is clone 3 SEQNO. 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.
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:
Hipp, et al. Expires 7 January 2027 [Page 13]
Internet-Draft Fossil Sync Protocol July 2026
(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. This phantom-table guard applies to non-clone
operations; clone continuation is governed by condition (e).
(b) Versioned content is queued for outbound push: the send queue is
non-empty because the peer requested artifacts or local push work
remains.
(c) A private-content discovery round is required: private igot
information was observed in the first round of a private sync and a
second round is needed to request or suppress the private artifacts
under the negotiated authorization.
(d) Unversioned-file work is pending. This condition is true if
either:
1. the per-session unversioned send table is non-empty and the session's
unversioned upload-enabled flag is set; or
2. one or more `uvgimme` cards were queued while processing this round's
reply, and either one or more `uvfile` cards were received in that same
reply or the session round number, after incrementing for the completed
round, is less than 3.
Clause (d)(2) is the current three-cycle continuation rule of the Fossil
wire behavior. Before the third completed round of the session, a round that
queued `uvgimme` forces one more request even if no `uvfile` has yet arrived.
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.
(e) The operation is a modern clone and at least one clone-specific
continuation condition holds: this is the first clone round; at least
one artifact was received this round; or the received clone_seqno
NEXT is positive and the total artifact count grew this round
(Section 8.2).
(f) Configuration exchange is pending: a reqconfig deferred during
clone (Section 8.4), or otherwise required by the client, has not yet
been sent and answered.
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 14]
Internet-Draft Fossil Sync Protocol July 2026
When none of conditions (a) through (f) 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 15]
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, |
| | send-queue, 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)..(f):
+----------------+ <--' send next round
| |
none of 3.7 | | fatal protocol error
(a)..(f) | |
v v
+------------+ +---------+
| TERMINATED | | ABORTED |
+------------+ +---------+
Hipp, et al. Expires 7 January 2027 [Page 16]
Internet-Draft Fossil Sync Protocol July 2026
The transitions are:
+==============+==============+=====================+==============+
| From | Event or | Action | To |
| | guard | | |
+==============+==============+=====================+==============+
| INIT | operation is | send the first | CLONE_ROUND0 |
| | a modern | clone request: | |
| | clone (clone | clone 3 1, no login | |
| | 3 SEQNO) | card, configuration | |
| | | and uv-hash | |
| | | deferred | |
| | | (Section 8.3) | |
+--------------+--------------+---------------------+--------------+
| INIT | operation is | send the first | EXCHANGE |
| | push, pull, | request: | |
| | or sync | authentication | |
| | | (login card or | |
| | | cookie, Section 6), | |
| | | pragma client- | |
| | | version, and the | |
| | | operation card | |
+--------------+--------------+---------------------+--------------+
| CLONE_ROUND0 | reply | record clone_seqno | EXCHANGE |
| | processed | (Section 8.2); any | |
| | | error card is | |
| | | discarded silently | |
| | | (Section 11.1) | |
+--------------+--------------+---------------------+--------------+
| CLONE_ROUND0 | transport | abort | ABORTED |
| | failure or | | |
| | redirect | | |
| | limit | | |
| | exceeded | | |
+--------------+--------------+---------------------+--------------+
| EXCHANGE | at least one | build and send the | EXCHANGE |
| | continuation | next request for | |
| | condition of | the operation | |
| | Section 3.7 | | |
| | (a)-(f) | | |
| | holds | | |
+--------------+--------------+---------------------+--------------+
| EXCHANGE | none of | stop; the client | TERMINATED |
| | Section 3.7 | sends nothing | |
| | (a)-(f) | further and the | |
| | holds | server signals no | |
| | | completion | |
+--------------+--------------+---------------------+--------------+
Hipp, et al. Expires 7 January 2027 [Page 17]
Internet-Draft Fossil Sync Protocol July 2026
| EXCHANGE | a fatal | reset partial | ABORTED |
| | protocol | output and abort | |
| | error occurs | | |
| | (below) | | |
+--------------+--------------+---------------------+--------------+
Table 3
A fatal protocol error, in any state other than CLONE_ROUND0, is any
of: an error card (Section 11); 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); a
clone_seqno value below zero (Section 8.2); or a SHA3-256 peer-
version refusal (Section 7.6). 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.
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.
Hipp, et al. Expires 7 January 2027 [Page 18]
Internet-Draft Fossil Sync Protocol July 2026
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:
+=========================+==============================+==========+
| Card | When present | Owning |
| | | section |
+=========================+==============================+==========+
| pragma client-version | every request, before any | Section |
| | content-requesting card | 10.1 |
+-------------------------+------------------------------+----------+
| operation card (pull, | every request | Sections |
| push, or clone 3 SEQNO) | | 8, 9 |
+-------------------------+------------------------------+----------+
| login card | authentication, unless | Section |
| | cookie mode or clone | 6 |
| | round 0 | |
+-------------------------+------------------------------+----------+
| gimme HASH | eligible phantoms to | Sections |
| | resolve | 7, 9 |
+-------------------------+------------------------------+----------+
| file / cfile | content the client pushes | Section |
| | or the peer requested | 7 |
+-------------------------+------------------------------+----------+
| igot HASH | artifacts the client | Section |
| | holds | 9 |
+-------------------------+------------------------------+----------+
| reqconfig, | as the operation requires | Sections |
| configuration, | | 7, 9, 10 |
| unversioned-file cards | | |
+-------------------------+------------------------------+----------+
| cookie TEXT | returning a stored cookie | Section |
| | | 3.6 |
+-------------------------+------------------------------+----------+
| comment line (#) | every request, carrying | Section |
| | per-round randomness | 5.3 |
+-------------------------+------------------------------+----------+
Table 4
Reply body, any applicable combination:
Hipp, et al. Expires 7 January 2027 [Page 19]
Internet-Draft Fossil Sync Protocol July 2026
+==================+==================================+==========+
| Card | When present | Owning |
| | | section |
+==================+==================================+==========+
| pragmas | responding to client pragmas | Section |
| | | 10 |
+------------------+----------------------------------+----------+
| push / pull | the server advertises its own | Sections |
| | identifiers; the card is | 3, 9, 11 |
| | recorded, and only a separate | |
| | rule such as message pull only * | |
| | changes the selected operation | |
+------------------+----------------------------------+----------+
| igot HASH | artifacts the server holds that | Section |
| | the client did not announce | 9 |
+------------------+----------------------------------+----------+
| content (file / | artifacts the client requested | Section |
| cfile) | with gimme | 7 |
+------------------+----------------------------------+----------+
| 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 |
| | | 8.2 |
+------------------+----------------------------------+----------+
| message / error | as needed | Section |
| | | 11 |
+------------------+----------------------------------+----------+
| closing comment | every reply, carrying a | Section |
| line | timestamp and error count | 5.3 |
+------------------+----------------------------------+----------+
Table 5
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-
Hipp, et al. Expires 7 January 2027 [Page 20]
Internet-Draft Fossil Sync Protocol July 2026
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 —
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, if
the target location's path ends with the path component of the
ORIGINAL repository URL (the "repository-path segment"), that suffix
MUST be stripped from the target before the redirect is followed;
otherwise the target is used unchanged. The repository-path segment
is the full path of the repository URL the exchange began with, not
merely its final slash-delimited element, so a multi-segment mount
(for example /cgi-bin/fossil/proj) is stripped whole.
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.
Hipp, et al. Expires 7 January 2027 [Page 21]
Internet-Draft Fossil Sync Protocol July 2026
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.
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.
Hipp, et al. Expires 7 January 2027 [Page 22]
Internet-Draft Fossil Sync Protocol July 2026
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.
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 23]
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;
the receiver MUST reset its partial output and emit an error card.
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 carrying more tokens than the defined form for its operator is
malformed. Current Fossil implementations expose at most the first
six parsed tokens to the per-card handler; seventh and later tokens
can therefore be discarded before card-specific validation runs.
Interoperable senders MUST NOT emit surplus tokens. A receiver that
detects surplus tokens SHOULD treat the line as malformed. A
receiver that follows the fixed six-token parser MAY instead produce
the observable failures described below when truncation leaves the
handler with an arity error.
Hipp, et al. Expires 7 January 2027 [Page 24]
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. For the content-bearing card types (file, cfile, and
uvfile), a failed arity check produces an error malformed CARD line
card and the exchange is aborted. For structurally simpler card
types, a failed arity check causes the line to be handled as an
unrecognized card, answered with an error bad command: LINE card
whose payload is the offending line; this outcome is fatal to the
exchange when it occurs on the client side and non-fatal when it
occurs on the server side. There is no "Unknown message" wire text:
the framing for an unrecognized card is exactly error bad command:
followed by the offending line (Appendix C).
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): cookie (Section 4.3).
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
Hipp, et al. Expires 7 January 2027 [Page 25]
Internet-Draft Fossil Sync Protocol July 2026
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.
Both client and server emit the informational comment lines #
sending-clusters and # end-of-clusters when transmitting cluster igot
cards; these are advisory and carry no normative semantics.
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.
Hipp, et al. Expires 7 January 2027 [Page 26]
Internet-Draft Fossil Sync Protocol July 2026
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:
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.
Hipp, et al. Expires 7 January 2027 [Page 27]
Internet-Draft Fossil Sync Protocol July 2026
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
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.
Hipp, et al. Expires 7 January 2027 [Page 28]
Internet-Draft Fossil Sync Protocol July 2026
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.
6.5. Cookie-Login Mode
When both of the following conditions hold, the client transmits the
login card via the standard HTTP Cookie header rather than in the
message body:
1. The peer's recorded VERSION (from a prior pragma server-version
card) is at least 22700 (Fossil 2.27); and
2. The peer's recorded timestamp is on 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.
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
Hipp, et al. Expires 7 January 2027 [Page 29]
Internet-Draft Fossil Sync Protocol July 2026
is the ordinary Section 6.1 transport, unmodified by this section. 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
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.
Hipp, et al. Expires 7 January 2027 [Page 30]
Internet-Draft Fossil Sync Protocol July 2026
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.
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
Hipp, et al. Expires 7 January 2027 [Page 31]
Internet-Draft Fossil Sync Protocol July 2026
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.
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.
Hipp, et al. Expires 7 January 2027 [Page 32]
Internet-Draft Fossil Sync Protocol July 2026
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,
the deferred reconstruction is completed at that time.
Completion is recursive. Completing a deferred reconstruction stores
the reconstructed artifact, and that artifact may itself be the
DELTASRC of another retained delta; storing it therefore completes
those deltas in turn. A receiver MUST run completion to a fixpoint
over newly stored artifacts. For example, if a receiver has retained
delta C against source A and delta A against source B before B
arrives, then storing B completes A, and the storing of A completes
C; neither C nor A may be left unreconstructed.
7.5. Private Content
A private card (no arguments) MUST immediately precede the file or
cfile card carrying private content. A private card that is not
followed immediately by a content-bearing file or cfile card is a
protocol error.
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 private card preceding a uvfile card is not defined; private
content suppression for unversioned files is governed by the pragma
exchange in Section 10.
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
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
Hipp, et al. Expires 7 January 2027 [Page 33]
Internet-Draft Fossil Sync Protocol July 2026
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. The sender abandons that transfer for this round.
No automatic substitution of a SHA1-named artifact or alternate hash
form is performed. The client that receives this error card aborts
the sync without retry, except during the first round of a clone
where the error card is silently discarded (Section 11.1).
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.
Hipp, et al. Expires 7 January 2027 [Page 34]
Internet-Draft Fossil Sync Protocol July 2026
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. This check applies pre-
storage on non-clone file cards and at commit time on all paths. For
cfile cards, pre-storage integrity verification does not apply in the
transfer layer; commit-time verification still applies.
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.
Hipp, et al. Expires 7 January 2027 [Page 35]
Internet-Draft Fossil Sync Protocol July 2026
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.
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 at the end of each
modern clone artifact-transfer round:
* When NEXT is greater than zero, the server has not yet exhausted
its blob table. If the client also received at least one artifact
in this round, if the total artifact count grew, or if this is the
first clone round, the client continues under the Section 3.7
termination rule. A following modern clone request that continues
artifact transfer includes pragma client-version and clone 3 NEXT.
A positive NEXT value does not by itself require another round
when no clone progress or other 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.
* 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. It may still issue cleanup
rounds for configuration, unversioned files, private discovery, or
phantom resolution if Section 3.7 requires them. A cleanup round
is an ordinary request that omits the clone card and carries only
the pragmas and the request cards (reqconfig, gimme, and
unversioned-file cards) for the remaining work; a client with no
remaining work terminates without issuing a further round.
* A clone_seqno value less than zero is a fatal protocol error; the
receiver MUST abort.
Hipp, et al. Expires 7 January 2027 [Page 36]
Internet-Draft Fossil Sync Protocol July 2026
The modern clone loop is therefore:
SEQNO := 1
build first request:
pragma client-version VERSION DATE TIME
clone 3 1
# RANDOM
no login card
loop:
send request; read reply
accept cfile cards and other reply cards in order
if clone_seqno NEXT was received:
if NEXT < 0: abort
SEQNO := NEXT
evaluate Section 3.7
if continuing 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 card
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 may still include push SERVERCODE PROJECTCODE before an error
card, giving the client the project code needed to form later
authenticated requests. 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 in the first clone round. For this reason a client silently
discards error cards received during the first clone round;
subsequent clone rounds process error cards normally.
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.
Hipp, et al. Expires 7 January 2027 [Page 37]
Internet-Draft Fossil Sync Protocol July 2026
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.
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
Hipp, et al. Expires 7 January 2027 [Page 38]
Internet-Draft Fossil Sync Protocol July 2026
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.
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.
Hipp, et al. Expires 7 January 2027 [Page 39]
Internet-Draft Fossil Sync Protocol July 2026
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.
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.
Hipp, et al. Expires 7 January 2027 [Page 40]
Internet-Draft Fossil Sync Protocol July 2026
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.
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
Hipp, et al. Expires 7 January 2027 [Page 41]
Internet-Draft Fossil Sync Protocol July 2026
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
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.
Hipp, et al. Expires 7 January 2027 [Page 42]
Internet-Draft Fossil Sync Protocol July 2026
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).
Fourteen 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. Five are server-to-client:
server-version, uv-push-ok, uv-pull-only, ci-lock-fail, and avoid-
delta-manifests. 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.
Hipp, et al. Expires 7 January 2027 [Page 43]
Internet-Draft Fossil Sync Protocol July 2026
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
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
Hipp, et al. Expires 7 January 2027 [Page 44]
Internet-Draft Fossil Sync Protocol July 2026
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.
* 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.
Hipp, et al. Expires 7 January 2027 [Page 45]
Internet-Draft Fossil Sync Protocol July 2026
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).
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
Hipp, et al. Expires 7 January 2027 [Page 46]
Internet-Draft Fossil Sync Protocol July 2026
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. pragma req-links requests link-graph data.
pragma client-url URL communicates the client's canonical URL to the
server. Current 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
Hipp, et al. Expires 7 January 2027 [Page 47]
Internet-Draft Fossil Sync Protocol July 2026
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.
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
TEXT is encoded identically to an error card. Upon receiving a
message card, both the server and the client continue processing; the
session is not terminated.
When the decoded text of a message card matches the pattern pull only
*, the client MUST silently clear any push intent and treat the
session as pull-only for this round, without displaying the message
to the user. In all other cases the client MAY display the message
to the user.
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.
Hipp, et al. Expires 7 January 2027 [Page 48]
Internet-Draft Fossil Sync Protocol July 2026
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.
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.
Hipp, et al. Expires 7 January 2027 [Page 49]
Internet-Draft Fossil Sync Protocol July 2026
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.
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
Hipp, et al. Expires 7 January 2027 [Page 50]
Internet-Draft Fossil Sync Protocol July 2026
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
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-
Hipp, et al. Expires 7 January 2027 [Page 51]
Internet-Draft Fossil Sync Protocol July 2026
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
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
Hipp, et al. Expires 7 January 2027 [Page 52]
Internet-Draft Fossil Sync Protocol July 2026
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>.
[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>.
Hipp, et al. Expires 7 January 2027 [Page 53]
Internet-Draft Fossil Sync Protocol July 2026
[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
[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>.
Hipp, et al. Expires 7 January 2027 [Page 54]
Internet-Draft Fossil Sync Protocol July 2026
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.
*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):
Hipp, et al. Expires 7 January 2027 [Page 55]
Internet-Draft Fossil Sync Protocol July 2026
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
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):
Hipp, et al. Expires 7 January 2027 [Page 56]
Internet-Draft Fossil Sync Protocol July 2026
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.
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.
Hipp, et al. Expires 7 January 2027 [Page 57]
Internet-Draft Fossil Sync Protocol July 2026
*Request* (Content-Type: application/x-fossil-debug, Content-Length:
38):
pragma client-version 19999
clone 3 1
*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.
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 (131-byte body):
pragma client-version 20028 20260702 183000
pull cccccccccccccccccccccccccccccccccccccccc ea2e766b68008b6c3b3ca547df136ded07c7f921
Hipp, et al. Expires 7 January 2027 [Page 58]
Internet-Draft Fossil Sync Protocol July 2026
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 (202-byte body):
pragma client-version 20028 20260702 183000
pull cccccccccccccccccccccccccccccccccccccccc ea2e766b68008b6c3b3ca547df136ded07c7f921
gimme da4b6b0ea2b060ff6bf29c16a3b10be05afc9c1a75d605a45cf7267ea3e74091
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
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 (151-byte body):
pragma client-version 20028 20260702 183000
pragma send-private
pull cccccccccccccccccccccccccccccccccccccccc ea2e766b68008b6c3b3ca547df136ded07c7f921
Hipp, et al. Expires 7 January 2027 [Page 59]
Internet-Draft Fossil Sync Protocol July 2026
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):
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):
Hipp, et al. Expires 7 January 2027 [Page 60]
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])
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):
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.
Hipp, et al. Expires 7 January 2027 [Page 61]
Internet-Draft Fossil Sync Protocol July 2026
*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:
# 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:
Hipp, et al. Expires 7 January 2027 [Page 62]
Internet-Draft Fossil Sync Protocol July 2026
+====================+==========================================+
| 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.
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
Hipp, et al. Expires 7 January 2027 [Page 63]
Internet-Draft Fossil Sync Protocol July 2026
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
ts-token = 1*( %x21-7E / %x80-FF )
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 /
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 ts-token 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
Hipp, et al. Expires 7 January 2027 [Page 64]
Internet-Draft Fossil Sync Protocol July 2026
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
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
Hipp, et al. Expires 7 January 2027 [Page 65]
Internet-Draft Fossil Sync Protocol July 2026
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 /
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 fossilized LF
ci-unlock-pragma = "pragma" SP "ci-unlock" SP client-id LF
send-private-pragma =
"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
req-clusters-pragma =
"pragma" SP "req-clusters" LF
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 66]
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.
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 (Section 5.2,
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 67]