Network Working Group D. R. Hipp, Ed.
Internet-Draft Fossil
Intended status: Standards Track S. Beal
Expires: 30 January 2027 libfossil
D. Shearer
29 July 2026
The Fossil Distributed Version Control System: Sync Protocol
draft-fossil-sync-protocol-00
Abstract
Fossil repositories store project history as content-addressed
artifacts. The HTTP sync protocol replicates those artifacts and
unversioned files through framed card streams, authentication,
version negotiation, phantom discovery, clone, multi-round
continuation, and termination.
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 30 January 2027.
Copyright Notice
Copyright (c) 2026 IETF Trust and the persons identified as the
document authors. All rights reserved.
Hipp, et al. Expires 30 January 2027 [Page 1]
Internet-Draft Fossil Sync Protocol July 2026
This document is subject to BCP 78 and the IETF Trust's Legal
Provisions Relating to IETF Documents (https://trustee.ietf.org/
license-info) in effect on the date of publication of this document.
Please review these documents carefully, as they describe your rights
and restrictions with respect to this document. Code Components
extracted from this document must include Revised BSD License text as
described in Section 4.e of the Trust Legal Provisions and are
provided without warranty as described in the Revised BSD License.
Table of Contents
1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . 4
2. Conventions and Terminology . . . . . . . . . . . . . . . . . 4
3. Protocol Operation . . . . . . . . . . . . . . . . . . . . . 5
3.1. Round Structure . . . . . . . . . . . . . . . . . . . . . 6
3.1.1. Reply Processing Algorithm . . . . . . . . . . . . . 6
3.2. Endpoint State . . . . . . . . . . . . . . . . . . . . . 7
3.2.1. Round Message Layout . . . . . . . . . . . . . . . . 9
3.3. Phantom Record Lifecycle . . . . . . . . . . . . . . . . 11
3.4. The Clone Loop . . . . . . . . . . . . . . . . . . . . . 11
3.5. Request and Response Pragmas . . . . . . . . . . . . . . 11
3.6. Cookie Card . . . . . . . . . . . . . . . . . . . . . . . 11
3.7. Termination . . . . . . . . . . . . . . . . . . . . . . . 12
3.8. Client Protocol State Machine . . . . . . . . . . . . . . 13
3.9. Robustness Posture . . . . . . . . . . . . . . . . . . . 15
4. Transport . . . . . . . . . . . . . . . . . . . . . . . . . . 15
4.1. Message Compression and Media Types . . . . . . . . . . . 16
4.2. HTTP Round Framing . . . . . . . . . . . . . . . . . . . 17
4.3. Cookie Transport Note . . . . . . . . . . . . . . . . . . 17
5. Card Format . . . . . . . . . . . . . . . . . . . . . . . . . 17
5.1. Message Structure . . . . . . . . . . . . . . . . . . . . 17
5.2. Card Syntax . . . . . . . . . . . . . . . . . . . . . . . 18
5.2.1. Defined Operators . . . . . . . . . . . . . . . . . . 19
5.3. Comment Lines . . . . . . . . . . . . . . . . . . . . . . 19
6. Authentication . . . . . . . . . . . . . . . . . . . . . . . 20
6.1. Login Card . . . . . . . . . . . . . . . . . . . . . . . 20
6.2. Nonce Derivation . . . . . . . . . . . . . . . . . . . . 20
6.3. Shared Secret . . . . . . . . . . . . . . . . . . . . . . 20
6.4. Signature Computation . . . . . . . . . . . . . . . . . . 20
6.5. Cookie-Login Mode . . . . . . . . . . . . . . . . . . . . 21
6.6. Capability Model . . . . . . . . . . . . . . . . . . . . 22
7. Content Transfer . . . . . . . . . . . . . . . . . . . . . . 22
7.1. file Card . . . . . . . . . . . . . . . . . . . . . . . . 22
7.2. cfile Card . . . . . . . . . . . . . . . . . . . . . . . 22
7.3. uvfile Card . . . . . . . . . . . . . . . . . . . . . . . 22
7.4. Delta Sources and Phantom Creation . . . . . . . . . . . 24
7.5. Private Content . . . . . . . . . . . . . . . . . . . . . 25
7.6. SHA3-256 Peer-Version Gate . . . . . . . . . . . . . . . 25
Hipp, et al. Expires 30 January 2027 [Page 2]
Internet-Draft Fossil Sync Protocol July 2026
7.7. Hash-Policy Admission . . . . . . . . . . . . . . . . . . 26
8. Clone . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
8.1. Clone Protocol Version . . . . . . . . . . . . . . . . . 27
8.2. Sequence Numbers . . . . . . . . . . . . . . . . . . . . 27
8.3. First Clone Round . . . . . . . . . . . . . . . . . . . . 29
8.4. Configuration During Clone . . . . . . . . . . . . . . . 29
8.5. Push, Pull, and Sync . . . . . . . . . . . . . . . . . . 30
8.5.1. Unversioned-File Synchronisation . . . . . . . . . . 30
8.6. Announcement and Request Cards . . . . . . . . . . . . . 32
8.6.1. Derived Branch, Tag, Leaf, Closed, and Current-Trunk
Views . . . . . . . . . . . . . . . . . . . . . . . . 32
8.7. Phantom Records . . . . . . . . . . . . . . . . . . . . . 32
8.8. Gimme Eligibility Filters . . . . . . . . . . . . . . . . 32
8.9. Loop Termination . . . . . . . . . . . . . . . . . . . . 32
9. Pragmas . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
9.1. Version Negotiation . . . . . . . . . . . . . . . . . . . 33
9.2. Unversioned-File Pragmas . . . . . . . . . . . . . . . . 34
9.3. Configuration Exchange . . . . . . . . . . . . . . . . . 34
9.4. Commit-Lock Pragmas . . . . . . . . . . . . . . . . . . . 35
9.5. Private-Content and Discovery Pragmas . . . . . . . . . . 36
10. Error Handling . . . . . . . . . . . . . . . . . . . . . . . 37
10.1. Error Card . . . . . . . . . . . . . . . . . . . . . . . 37
10.2. Message Card . . . . . . . . . . . . . . . . . . . . . . 38
11. Security Considerations . . . . . . . . . . . . . . . . . . . 39
11.1. Credential Replay . . . . . . . . . . . . . . . . . . . 39
11.2. Private-Content Exposure and Protected Transport . . . . 40
11.3. Line-Length Bounds and Resource Exhaustion . . . . . . . 40
11.4. SHA3-256 Downgrade Prevention . . . . . . . . . . . . . 40
12. IANA Considerations . . . . . . . . . . . . . . . . . . . . . 40
12.1. application/x-fossil . . . . . . . . . . . . . . . . . . 40
12.2. application/x-fossil-debug . . . . . . . . . . . . . . . 41
12.3. application/x-fossil-uncompressed . . . . . . . . . . . 42
13. References . . . . . . . . . . . . . . . . . . . . . . . . . 42
13.1. Normative References . . . . . . . . . . . . . . . . . . 43
13.2. Informative References . . . . . . . . . . . . . . . . . 44
Appendix A. Examples . . . . . . . . . . . . . . . . . . . . . . 44
A.1. Basic Push/Pull Convergence (Two Rounds) . . . . . . . . 44
A.2. SHA3 Version Gate (Clone Reply) . . . . . . . . . . . . . 46
A.3. Private Content Suppression . . . . . . . . . . . . . . . 46
A.4. Comment Lines and Clock-Skew Timestamp . . . . . . . . . 49
A.5. Authentication Example . . . . . . . . . . . . . . . . . 49
Appendix B. ABNF . . . . . . . . . . . . . . . . . . . . . . . . 50
Appendix C. Anomalies . . . . . . . . . . . . . . . . . . . . . 54
Authors' Addresses . . . . . . . . . . . . . . . . . . . . . . . 54
Hipp, et al. Expires 30 January 2027 [Page 3]
Internet-Draft Fossil Sync Protocol July 2026
1. Introduction
Fossil repositories hold grow-only sets of hash-named artifacts
[FossilArtifact]. Sync exchanges the set difference so peers
converge on the union independent of direction or timing [GSET]. A
known hash without content is a _phantom_. Rounds announce artifacts,
create and request phantoms, transfer content, and repeat while
Section 3.7 requires progress. The client drives HTTP request/reply
rounds; the server retains no per-session state.
The rules cover transport, authentication, artifact and unversioned-
file transfer, clone, configuration framing, and termination. Table-
group row serialisation for /user, /reportfmt, /shun, /concealed, and
/subscriber is outside this protocol profile. Implementations MAY
omit those table groups; config /GROUPNAME SIZE framing alone does
not define their payloads. Sections 3-11 are normative; Section 11
contains the security requirements.
Omitting the optional /shun table group omits its configuration
exchange, not the repository-local shun set defined in Section 3.3.
That set exists independently for the request and admission rules in
Sections 3.3 and 7.7.
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.
* Artifact, hash, manifest, cluster: as defined in [FossilArtifact].
* Phantom: a known artifact hash whose content is absent;
Section 3.3 governs it.
* Branch, tag, closed marker, leaf, current-trunk: views derived
after artifact parsing and crosslink/tag-state processing. T
syntax is in [FossilArtifact]; derivation, including current-trunk
using the local main-branch setting, is in [FossilRepoState].
Sync transports only their source artifacts.
* Unversioned file: repository-local state outside the content-
addressed artifact set, synchronised using uv-hash, uvigot,
uvgimme, and uvfile; unversioned files are not artifacts and do
not participate in the artifact hash-policy or tag-state rules.
Hipp, et al. Expires 30 January 2027 [Page 4]
Internet-Draft Fossil Sync Protocol July 2026
* Card: an LF-terminated operator line, optionally followed by a
byte-counted payload.
* Client; server; round: the client initiates and carries session
state. The server independently processes each request. One
request/reply pair is a round; body exhaustion ends it without a
terminal card.
* servercode; projectcode: a repository instance identifier and
shared project identifier. Servercode is informational. A push
or pull PROJECTCODE mismatch MUST be refused.
* pragma: a pragma directive. Unknown pragma names are silently
ignored. For an unknown non-pragma operator, the server emits
error bad command and continues its request-card loop; the client
stops its current reply-card loop, then evaluates ordinary post-
reply continuation.
* igot; gimme: artifact possession announcement and content request.
* Fossil release integer: MAJOR*10000 + MINOR*100 + PATCH; 2.0 is
20000. It is distinct from clone VERSION.
* Clone protocol version: the clone VERSION selecting transfer
encoding; version 3 uses cfile.
* fossilize encoding: the escaping scheme in [FossilArtifact]
Section 4.
3. Protocol Operation
This section defines the sync loop before listing its implementation
state. A client sends one HTTP request body containing sync cards;
the server processes that body independently and returns one reply
body containing sync cards. The client then uses the reply to build
the next request, if any. There is no terminal wire card: a round
ends when its body is exhausted, and the session ends when the
continuation rules in Section 3.7 select no next round.
The loop has four moving parts:
1. Request construction: seed the next body with authentication,
version, and the selected operation; append remembered state such
as cookie; append responsive cards produced by the previous
reply; end with randomness.
Hipp, et al. Expires 30 January 2027 [Page 5]
Internet-Draft Fossil Sync Protocol July 2026
2. Card exchange: igot announces known artifacts, gimme requests
missing ones, file and cfile carry content, and the UV cards
perform the same inventory/request/content cycle for unversioned
files.
3. Phantom repair: a peer that learns a hash without content records
a phantom; later rounds request eligible phantoms until no
continuation condition remains.
4. Continuation: after reply processing, the client increments its
round number and applies Section 3.7. Positive work, not server
acknowledgement, drives another round.
3.1. Round Structure
Every request MUST contain pragma client-version. It MUST precede
content-requesting cards so the server can gate SHA3 at handler time.
Exceptions affecting the beginning are: a body login precedes the
nonce-covered body; initial private sync places send-private before
client-version, later rounds place it after. The first round-trip of
a modern clone suppresses both body and HTTP cookie login.
Push, pull, and sync requests carry push, pull, or both each round.
Modern clone starts with clone 3 1; later clone reissue requires both
a well-formed push in the just-received reply and a positive recorded
clone sequence. Cleanup after zero has no operation card. After a
matched message pull only *, Section 10.2 governs operation
suppression.
Requests MAY also contain eligible gimme; responsive or pushed file/
cfile; igot; configuration and UV cards; returned state cookie; and
MUST end with randomness comment(s). Ordinary completed replies MAY
contain responding pragmas, repository push/pull, igot, requested
content, gimme, UV cards, clone sequence, messages/errors, and end
with the timestamp/error-count comment. Pre-dispatch and fail-fast
server replies need not contain that comment.
3.1.1. Reply Processing Algorithm
After transport decoding, the client processes reply cards in order:
1. For push or pull, record peer identifiers and project code; do
not change the selected local operation.
2. For igot, apply Section 3.3.
Hipp, et al. Expires 30 January 2027 [Page 6]
Internet-Draft Fossil Sync Protocol July 2026
3. For file or cfile, apply Section 7; set content-received only
when the card stores content or creates a dangling-delta source
phantom.
4. For gimme, add peer-lacks; if push remains enabled, content is
held, and policy permits, append content to the next request.
5. For uvigot, apply the server-to-client unversioned-file state
machine.
6. For uvgimme, treat it as wrong-direction input and apply
Section 5.2.
7. For uvfile, apply Section 7.3 and the unversioned-file state
machine.
8. For clone_seqno N, record N when it is decimal; otherwise leave
the recorded sequence unchanged.
9. For error, apply Section 10.1.
10. For cookie, replace the stored cookie.
11. For #..., apply Section 5.3.
12. For any other known card, apply that card's owning rule.
After the reply-card loop ends, increment the round number, evaluate
Section 3.7, and only then discard round-scoped state.
3.2. Endpoint State
The client carries the following state; the server carries persistent
repository data plus the request-scoped parser, catalog, and
transaction state explicitly listed below.
+===================+============+=================================+
| State | Scope | Meaning |
+===================+============+=================================+
| projectcode | persistent | Required shared identifier |
+-------------------+------------+---------------------------------+
| peer version/date | session | Last server-version; before |
| | | one, VERSION is the client |
| | | release VERSION and DATE/TIME |
| | | are 00000000/000000 |
+-------------------+------------+---------------------------------+
| phantom set | persistent | Known hashes lacking content |
+-------------------+------------+---------------------------------+
Hipp, et al. Expires 30 January 2027 [Page 7]
Internet-Draft Fossil Sync Protocol July 2026
| clone sequence | session | Positive cursor; zero |
| number | | permanently bars later clone |
| | | cards |
+-------------------+------------+---------------------------------+
| clone received- | session | Raw parser artifact-card count |
| artifact count | | during clone; it increments |
| | | after every file, cfile, |
| | | uvfile, or config handler |
| | | invocation |
+-------------------+------------+---------------------------------+
| sync cookie | persistent | Opaque server token |
+-------------------+------------+---------------------------------+
| round number | session | Incremented after reply |
| | | processing, immediately before |
| | | Section 3.7 |
+-------------------+------------+---------------------------------+
| UV catalog-sent | server | Whether the clone handler |
| flag | request | emitted its immediate catalog |
| | | in the current request |
+-------------------+------------+---------------------------------+
| UV send table | session | Eligible local non-tombstone |
| | | names and mtimeOnly |
+-------------------+------------+---------------------------------+
| UV upload-enabled | session | Set by uv-push-ok / uv-pull- |
| / pull-only | | only |
+-------------------+------------+---------------------------------+
| outbound send | session | Single accumulating next- |
| buffer | | request buffer |
+-------------------+------------+---------------------------------+
| pending private | client | Single-use marker for the next |
| marker | session | file/cfile. The client sets it |
| | parser; | on private; the server sets it |
| | server | only when private is |
| | request | authorized. Handler entry |
| | parser | clears it. An unconsumed |
| | | client marker survives a normal |
| | | reply boundary until consumed |
| | | or session end; a server marker |
| | | ends with its current request |
+-------------------+------------+---------------------------------+
| new-phantom flag | round | An igot created a phantom |
+-------------------+------------+---------------------------------+
| content-received | round | A file/cfile, including |
| flag | | dangling delta, stored bytes |
+-------------------+------------+---------------------------------+
| peer-has / peer- | round | Hashes announced/sent or |
| lacks | | requested by peer |
+-------------------+------------+---------------------------------+
Hipp, et al. Expires 30 January 2027 [Page 8]
Internet-Draft Fossil Sync Protocol July 2026
| UV-gimme count | round | uvgimme cards emitted in this |
| | | request |
+-------------------+------------+---------------------------------+
| UV-content count | round | uvfile cards received in its |
| | | reply |
+-------------------+------------+---------------------------------+
| UV-upload count | round | uvfile cards appended to the |
| | | next request buffer |
+-------------------+------------+---------------------------------+
| private-igot | round | Client-emitted igot HASH 1 |
| counter | | cards |
+-------------------+------------+---------------------------------+
Table 1
Only round-scoped entries are reset at round start; they remain until
Section 3.7 is evaluated, then are discarded. Session entries
survive boundaries.
The outbound buffer invariant is: dispatch current bytes; reset
exactly once to begin constructing the next request; seed client-
version and fixed pull/push operation cards; for later seeded rounds,
append stored state cookie immediately afterward. In the initial
non-clone request, applicable client-url and req-links cards precede
the stored cookie after the operation. Process the reply and append
responsive content, UV cards, and an eligible clone reissue; append
uvgimme while processing each qualifying uvigot, then remaining igot,
gimme, and randomness; dispatch without another reset. Clone reissue
is reply-dependent, not fixed preamble. File-send and delta-send
counts in Section 3.7(b) count content appended during this reply-
processing window and clear with the next reset.
3.2.1. Round Message Layout
+================+================================================+
| Request card | Presence/order |
+================+================================================+
| body login | Absent in first clone; otherwise first body |
| | card when named body authentication applies |
+----------------+------------------------------------------------+
| send-private | Before client-version initially; after it |
| | later |
+----------------+------------------------------------------------+
| client-version | Every request; before content requests |
+----------------+------------------------------------------------+
| fixed push/ | Required as above; absent in clone cleanup and |
| pull operation | post-pure-push suppression |
+----------------+------------------------------------------------+
Hipp, et al. Expires 30 January 2027 [Page 9]
Internet-Draft Fossil Sync Protocol July 2026
| clone 3 NEXT | Reply-dependent; append during reply |
| reissue | processing as Section 3.1 specifies, after |
| | seeded request content and before dispatch |
+----------------+------------------------------------------------+
| state cookie | Later seeded requests: immediately after fixed |
| TEXT | operation, or after client-version if no fixed |
| | operation. Initial non-clone request: after |
| | any applicable client-url and req-links cards. |
+----------------+------------------------------------------------+
| gimme; file/ | As applicable |
| cfile; config/ | |
| UV | |
+----------------+------------------------------------------------+
| igot | As applicable; never applicable during clone |
| | (Section 8.2) |
+----------------+------------------------------------------------+
| # randomness | Every request, last |
+----------------+------------------------------------------------+
Table 2
Cookie-login instead places login in HTTP Cookie: x-f-l-c= and leaves
no body login. The first clone sends neither body nor cookie login.
State cookie TEXT is independent.
+====================+================================+
| Reply card | Presence/order |
+====================+================================+
| pragmas | As applicable; server-version |
| | precedes clone-specific cards |
+--------------------+--------------------------------+
| push/pull, igot, | As applicable |
| content, gimme, UV | |
+--------------------+--------------------------------+
| clone accepting | zero or more clone cfile cards |
| sequence | and per-artifact errors, then |
| | clone_seqno NEXT, then push |
+--------------------+--------------------------------+
| clone | push, then error; no cfile or |
| authorization | clone_seqno |
| refusal | |
+--------------------+--------------------------------+
| closing comment | Last on ordinary completed |
| | replies; fail-fast replies may |
| | omit it |
+--------------------+--------------------------------+
Table 3
Hipp, et al. Expires 30 January 2027 [Page 10]
Internet-Draft Fossil Sync Protocol July 2026
An SHA3 gate emits a standalone error at its attempted send site; it
is not a cfile artifact card and does not increment the client's raw
artifact-card count.
3.3. Phantom Record Lifecycle
An igot HASH creates a phantom only when HASH is absent and the
receiver can obtain it in the current pull, clone, or push-receive
direction. A delta-form file/cfile whose DELTASRC is absent creates
a source phantom.
Each round, peer-has records hashes announced or successfully sent by
the peer; peer-lacks records gimme requests. Newly created phantoms
become gimme-eligible. The ordinary phantom scan excludes peer-lacks
hashes, names in the repository shun table, and private hashes unless
private sync is enabled; shun-sha1 does not itself suppress an
otherwise eligible gimme.
Receiving gimme HASH records peer-lacks. If content is held and
direction and policy permit, send it now or subsequently. If
direction forbids sending, emit neither content nor error and do not
defer. If absent, silently skip.
Successful storage deletes HASH's phantom. For a dangling delta,
retain the received delta under HASH, delete HASH's phantom, create
DELTASRC's phantom, and set content-received; HASH is not requested
again. Reconstruction becomes possible on demand after recursive
basis availability propagates to a fixpoint.
3.4. The Clone Loop
Modern clone uses the ordinary dispatch and Section 3.7, with clone 3
SEQNO during transfer and operation-less cleanup after sequence zero.
Sections 8.1 through 8.4 are authoritative.
3.5. Request and Response Pragmas
Pragmas are ordered body cards. Unknown names MUST be ignored;
unknown non-pragma operators follow Section 5.2. Section 9 is
authoritative.
3.6. Cookie Card
A server MAY emit cookie TEXT; the client MUST replace its stored
value and MAY return it later. Servers MUST NOT depend on it, MUST
tolerate foreign and multiple cookies, and clients need not return
one.
Hipp, et al. Expires 30 January 2027 [Page 11]
Internet-Draft Fossil Sync Protocol July 2026
3.7. Termination
After reply processing and round-number increment, the client MUST
issue another round iff:
*(a)* (file/cfile, including dangling delta, stored content *or* igot
created a phantom) *and* the phantom table remains non-empty.
Applies to every operation.
*(b)* During construction of the next-request buffer while processing
this reply, at least one full-content card (file-send count) or delta
card (delta-send count) was appended, *or* the UV upload phase is
selected. Section 8.5.1 clears that phase after dry-run, revert, or
a completed scan, and preserves it through an output-threshold
continuation. uvfile count is not an independent condition.
*(c)* The current request emitted at least one private igot HASH 1
and the post-increment round number is exactly 1, *or* processing
server-version in this reply made private inventory eligible and the
post-reply remaining-inventory step appended at least one such igot.
The latter continuation dispatches the newly appended first private
inventory exactly once; the former permits only one first-cycle
discovery continuation.
*(d)* Either the current request emitted at least one uvgimme and
either its reply contained at least one uvfile or the post-increment
round number is less than 3, *or* processing this reply appended at
least one uvgimme to the next-request buffer. The latter condition
dispatches that newly appended request; this is a global cycle
condition, not a per-NAME retry policy.
*(e)* The operation is modern clone, none of (a)-(d) fired, and at
least one holds: this was the first clone round; this round stored
one or more file, cfile, or dangling-delta deliveries; or the
recorded sequence remains positive and the clone received-artifact
count increased during the round.
A positive clone sequence alone does not continue a non-advancing
session. Sequence zero bars later clone but does not terminate if
another limb requires cleanup. When continuation holds but clone
reissue is barred, send the Section 8.2 cleanup request.
Remaining phantoms alone MUST NOT continue or cause error. With no
true condition, stop; the server sends no completion card.
Hipp, et al. Expires 30 January 2027 [Page 12]
Internet-Draft Fossil Sync Protocol July 2026
3.8. Client Protocol State Machine
This state model uses the named-state and carried-state terminology
of TCP [RFC9293] and the request/response organization of IMAP
[RFC9051]. It restates requirements owned by the controlling
sections; where they differ, the controlling rule governs. The
internal round counter starts at zero immediately before the first
dispatch and increments only after the reply is consumed. Its round-
zero label is independent of the clone 3 1 sequence argument.
+==============+=====================================+
| State | Meaning |
+==============+=====================================+
| INIT | Initialized; no round sent |
+--------------+-------------------------------------+
| CLONE_ROUND0 | First modern-clone round; no normal |
| | body or cookie login, no config/UV |
| | hash; received server error ignored |
+--------------+-------------------------------------+
| EXCHANGE | Normal authenticated loop |
+--------------+-------------------------------------+
| TERMINATED | No continuation condition |
+--------------+-------------------------------------+
| ABORTED | Fatal failure |
+--------------+-------------------------------------+
Table 4
+------+
| INIT |
+--+---+
clone 3 SEQNO / \ push / pull / sync
v \
+--------------+ \
| CLONE_ROUND0 | \
+------+-------+ \
reply; | |
errors ignored | |
v v
+----------------+ ---.
| EXCHANGE | | any 3.7(a)..(e)
+----------------+ <--'
| |
none | | fatal
v v
+------------+ +---------+
| TERMINATED | | ABORTED |
+------------+ +---------+
Hipp, et al. Expires 30 January 2027 [Page 13]
Internet-Draft Fossil Sync Protocol July 2026
+==============+===============+===================+==============+
| From | Guard | Action | To |
+==============+===============+===================+==============+
| INIT | modern clone | send client- | CLONE_ROUND0 |
| | | version, clone 3 | |
| | | 1, randomness; no | |
| | | normal body or | |
| | | cookie login and | |
| | | no config/UV hash | |
+--------------+---------------+-------------------+--------------+
| INIT | push/pull/ | authenticate; | EXCHANGE |
| | sync | send version and | |
| | | operation | |
+--------------+---------------+-------------------+--------------+
| CLONE_ROUND0 | any server | record preceding | EXCHANGE |
| | error | project-code push | |
| | | when present; | |
| | | ignore error, | |
| | | continue parsing, | |
| | | and force second | |
| | | clone round | |
+--------------+---------------+-------------------+--------------+
| CLONE_ROUND0 | completed | record sequence | EXCHANGE |
| | reply without | and continue | |
| | error | ordinary first- | |
| | | round processing | |
+--------------+---------------+-------------------+--------------+
| CLONE_ROUND0 | transport or | abort | ABORTED |
| | process-fatal | | |
| | failure | | |
+--------------+---------------+-------------------+--------------+
| EXCHANGE | any | send next request | EXCHANGE |
| | Section 3.7 | | |
| | condition | | |
+--------------+---------------+-------------------+--------------+
| EXCHANGE | none | stop | TERMINATED |
+--------------+---------------+-------------------+--------------+
| EXCHANGE | fatal error | reset partial | ABORTED |
| | | output where | |
| | | required; abort | |
+--------------+---------------+-------------------+--------------+
Table 5
Unknown or locally malformed reply cards record a local warning and
end only the current reply-card loop. Effects of the successfully
parsed prefix remain, then the client increments the cycle and
evaluates Section 3.7; CLONE_ROUND0 therefore still performs its
Hipp, et al. Expires 30 January 2027 [Page 14]
Internet-Draft Fossil Sync Protocol July 2026
forced second clone round. On a server request, an unknown non-
pragma operator resets the response, emits error bad command: LINE,
and continues later request cards. SHA3 refusal is per-artifact at
its sender: server-sent error is handled by Section 10.1; client-sent
direction-invalid error by Section 7.6.
3.9. Robustness Posture
Receiver tolerance is limited to explicitly defined cases: unknown
pragmas; the known-pragma argument and visible-surplus tolerance in
Section 9; visible surplus tokens on private and igot; one post-
payload byte in config that the reference receiver consumes without
validation; optional Fossil-whitespace acceptance; foreign cookies;
first-clone errors; and invisible tokens beyond the fixed parser
vector. An unknown non-pragma request card produces error bad
command and continues; an unknown or locally malformed reply card
stops only that reply-card loop before ordinary continuation is
evaluated. This protocol deliberately rejects the receiver-side
liberal-acceptance principle associated with TCP [RFC9293]: guessing
at malformed input lets implementations diverge. Fossil tokenization
exposes at most six tokens. Senders MUST NOT emit a seventh token; a
receiver conforming to Fossil behaviour ignores otherwise invisible
seventh-and-later tokens rather than creating another card condition.
4. Transport
Sync uses HTTP POST [RFC9110] to the repository URL path unchanged;
empty path becomes /. The current Fossil reference client emits POST
<path> HTTP/1.0. Clients MUST NOT append /xfer. application/x-fossil
and application/x-fossil-debug dispatch to sync regardless of path.
Non-POST requests are redirected home. Every request has exact
Content-Length; sync bodies are nonempty.
SSH adds X-Fossil-Transport: SSH. Ordinary Content-Type, Content-
Length, User-Agent, Host, Proxy-Authorization, and Authorization:
Basic behave normally.
Every request MUST carry Content-Length. A reply MAY omit it, in
which case Fossil reads the entity through EOF. Fossil does not
treat Transfer-Encoding as a header error: it ignores that field, so
encoded or chunked bytes fail only if later body framing or payload
handling rejects them. Fossil parses Content-Length as an integer,
but imposes neither a separate 512 MiB entity limit nor a pre-
admission guarantee that an artifact or UV row can later fit in a
complete entity.
Hipp, et al. Expires 30 January 2027 [Page 15]
Internet-Draft Fossil Sync Protocol July 2026
Follow at most 19 redirects with status 301, 302, 307, or 308; the
twentieth is fatal. Never downgrade HTTPS to HTTP, nor redirect SSH
or local-file transports. Repeatedly strip trailing /xfer from the
redirect target, then parse the result as the new base; apply no
other path transformation. For an accepted redirect, Fossil parses
the target, refuses an HTTPS-to-HTTP downgrade and targets using SSH
or local-file transports, reloads HTTP authorization for the parsed
target, and recursively reuses the selected decompressed card body.
On that recursive exchange it reconstructs compression, HTTP headers,
and the applicable body or cookie login for the parsed target; it
does not rerun card selection. It has no trusted-origin allowlist.
An accepted redirect is not an acknowledgment of the dispatched
request. The redirect handler reuses the selected request body
rather than restoring a pre-dispatch protocol snapshot and
reselecting its cards.
Only HTTP 200 is a non-redirect protocol response; any other status
aborts before card parsing. For client-side reply handling, a
missing Content-Type is accepted as compressed application/x-fossil.
If the field is present, Fossil compares the value after Content-
Type: case-insensitively by expected-type prefix: an application/x-
fossil-debug or application/x-fossil-uncompressed prefix selects
uncompressed mode; any application/x-fossil prefix selects compressed
mode. Parameters and other suffixes are accepted by that prefix
behaviour; another present value is rejected. Server-side CGI
dispatch is also prefix-based for sync content types, but inbound
request decompression is narrower: after parameters are stripped,
compressed-body decoding is selected only by the exact type
application/x-fossil.
Changing scheme, host, or port does not cause the redirect handler to
reset protocol peer state or reselect the cards. Fossil reconstructs
the transport envelope and applicable body or cookie login for the
parsed target, and reloads HTTP authorization.
4.1. Message Compression and Media Types
* application/x-fossil: all production requests and ordinary
replies; body uses the compressed container below.
* application/x-fossil-debug: same decompressed content, no outer
compression; diagnostics only and MUST NOT be used in production.
* application/x-fossil-uncompressed: clone-protocol-version 3 or
greater replies only; version 3 is specified here. No outer
compression is applied because cfile payloads are stored
compressed.
Hipp, et al. Expires 30 January 2027 [Page 16]
Internet-Draft Fossil Sync Protocol July 2026
The compressed container is a 4-byte big-endian pre-compression input
length followed by a zlib stream [RFC1950]: 2-byte header, DEFLATE
body [RFC1951], and 4-byte Adler-32 trailer. It is neither raw
DEFLATE nor gzip. The sender MUST encode actual input length. On
malformed zlib, bad Adler-32, or decoded-capacity overflow, Fossil's
decompressor retains the original entity buffer; the CGI sync path
applies ordinary card processing to that retained buffer and does not
issue a separate pre-card-parsing rejection. Bytes after a
successfully decoded zlib stream are ignored. The advertised length
is a capacity, not a decode-success equality check: after a
successful decode, the receiver processes the actual decoded byte
count and MUST NOT reject solely because it is shorter than that
capacity. The length field is an unsigned 32-bit zlib output-
capacity value. Fossil attempts to resize temporary storage to nOut
+ 1 and passes nOut to zlib; an advertised 0xffffffff wraps that
resize operand, so reception does not guarantee allocation across
every advertised value. This protocol specifies no separate 512 MiB
limit.
Ordinary compression uses zlib default level. Inline body-login
concatenates login and body inputs into one container at DEFLATE
level 9; receivers MUST be level-independent. Stored blobs use the
same byte-identical container and are retransmitted verbatim.
4.2. HTTP Round Framing
A round is one POST/reply and ends at reply-body exhaustion. No done
or end exists; exhaustion is not error.
4.3. Cookie Transport Note
cookie TEXT is a sync card carried inside the decoded body; it is
distinct from HTTP cookie-login mode in Section 6.5.
5. Card Format
5.1. Message Structure
Bodies are sequences of LF-terminated lines. Byte-counted payloads
begin immediately after the line LF and may contain LF. They end
only at the declared count. A cfile sender appends one further LF
only when the counted compressed payload does not itself end in LF;
reference receivers consume that padding as an empty line. config
uses its specified trailing LF.
Hipp, et al. Expires 30 January 2027 [Page 17]
Internet-Draft Fossil Sync Protocol July 2026
5.2. Card Syntax
Canonical cards have one SP between tokens, no leading/trailing
whitespace, and one LF. Operators are lowercase ASCII letters,
except clone_seqno; fossilized arguments MUST be defossilized.
Receivers MUST accept canonical form and MAY tokenize runs of SP,
HTAB, VT, FF, or CR; LF remains terminator. Senders MUST emit
canonical form.
Card and comment lines are parsed through LF before payload framing.
This protocol imposes no separate maximum line length.
The reference parser exposes at most six tokens. A receiver does not
reject an otherwise valid six-token card merely because additional
tokens are present beyond that visible vector; the surplus is not
exposed to its card handler.
Unknown first token has precedence as unknown operator: server resets
partial output and emits error bad command: LINE, then continues
parsing later request cards. Client records a local unknown command:
[LINE] warning, emits no outbound error, and stops the current reply-
card loop; its processed prefix remains effective and ordinary post-
reply continuation is then evaluated.
For a defined operator, its visible-vector arity/argument rule
governs, except that receivers examine only the operator token of
private and ignore visible surplus tokens. An igot receiver accepts
a valid HASH with at least two visible tokens: an exact third token 1
marks it private, every other third token marks it public, and later
visible tokens are ignored. Canonical senders emit argument-free
private and the two Appendix B igot forms.
There is no single default disposition for malformed defined request
cards: the result depends on whether the line fails a dispatch-
recognition guard or enters a card handler and fails that handler's
validation.
If a request line matches no request-dispatch branch--including a
defined operator whose visible arity does not satisfy that branch--or
a defined non-pragma operator received in the wrong direction, the
server resets accumulated response output, emits error bad command:
LINE, and continues with later request cards. A client-sent error
reaches this catch-all path. That path does not itself roll back
earlier request mutations; Section 10.1 governs transaction effects.
Hipp, et al. Expires 30 January 2027 [Page 18]
Internet-Draft Fossil Sync Protocol July 2026
After dispatch enters the file, cfile, or uvfile handler, a
validation failure reported by that handler instead resets
accumulated response output, emits the handler's fossilized error,
increments the request error count, and stops request-card parsing.
Any single-use state consumed at handler entry remains consumed as
Section 7.5 specifies. This path likewise does not itself roll back
earlier request mutations. Duplicate or failed login, invalid clone
sequence, invalid config, and authorization failures use their
explicit owning rules; Section 10.1 governs their transaction
effects.
On a client reply, an unknown or locally malformed card records a
local warning and stops only the current reply-card loop; its
successfully processed prefix remains effective, and ordinary post-
reply continuation is then evaluated. The argument-insensitive
private marker in Section 7.5 and unknown or wrong-direction pragmas
in Section 9 are explicit exceptions. No "unknown message" text
exists.
5.2.1. Defined Operators
Defined operators are login, push, pull, cookie, clone, file, cfile,
uvfile, private, igot, uvigot, gimme, uvgimme, reqconfig, config,
clone_seqno, pragma, error, and message.
5.3. Comment Lines
A line beginning # is not dispatched. Servers silently skip it.
Clients likewise skip it, except they recognise:
# timestamp DATETIME errors N
DATETIME is exactly 19 bytes YYYY-MM-DDTHH:MM:SS, UTC, with fixed-
width zero-padded fields and no fraction/zone. N is unsigned decimal
with no leading zeros except 0. Senders MUST use this form.
Receivers read the first 19 bytes after # timestamp without requiring
additional internal validation; DATETIME estimates skew, while N is
informational and not a wire error.
For pragma req-clusters, a nonempty public-cluster igot run MUST be
immediately enclosed by # sending-clusters and # end-of-clusters;
emit neither for an empty run. Receivers ignore both, and pairing
has no receive-side semantics.
Every request ends with implementation-generated random bytes in a #
comment, varying the login nonce-covered tail.
Hipp, et al. Expires 30 January 2027 [Page 19]
Internet-Draft Fossil Sync Protocol July 2026
6. Authentication
6.1. Login Card
Exactly one login-card mechanism is allowed:
login USER NONCE SIGNATURE
USER is fossilized; NONCE and SIGNATURE are exactly 40 lowercase
hexadecimal characters. Duplicate body/header login is fatal: reset
partial output and emit error. anonymous, nobody, and SSH are exempt
from named authentication. The first clone sends neither body nor
cookie login.
6.2. Nonce Derivation
NONCE is SHA-1 [RFC3174] over every decompressed body byte after the
body login line's LF through body end. In cookie-login mode it
covers the body only, not the HTTP header.
6.3. Shared Secret
shared-secret = SHA1(project-code "/" login-name "/" plaintext-password)
The result is 40 lowercase hex. Login name is exactly USER. This
calculation applies when named authentication is constructed after
the project code is known. The initial clone sends no login card and
does not use an alternate plaintext-password branch on the wire.
6.4. Signature Computation
SIGNATURE = SHA1(NONCE-hex || shared-secret-hex)
Random comments vary independently generated nonces. Fossil has no
dedicated login replay cache and does not issue a dedicated
credential-replay rejection. A successful named login records the
current user and nonce in process state; when a later content
mutation initializes its received-from provenance, that nonce may be
inserted into the receipt table, whose nonce column is unique.
Login-only or pull-only replay is therefore not rejected by a login
replay cache, while a mutation replay can encounter receipt-
provenance uniqueness.
Hipp, et al. Expires 30 January 2027 [Page 20]
Internet-Draft Fossil Sync Protocol July 2026
Before parsing cards, the server establishes baseline capabilities
from normal session/cookie/HTTP Basic/remote-user machinery; this is
the only point at which capability state is reset from scratch.
Successful login only unions capabilities with that baseline and
cannot remove them; failed login leaves baseline untouched but
aborts.
Verification order:
1. nobody and anonymous succeed without signature; configured
matching remote-user is honored.
2. For named users, treat stored credential bytes directly as shared
secret and constant-time, case-sensitive compare SHA1(NONCE-
hex || stored-bytes) against SIGNATURE.
3. Only if that fails and stored credential length is not exactly 40
bytes, derive the secret from stored cleartext, name, and server
project code; retry without rewriting storage.
4. Independently, case-sensitively compare NONCE to SHA-1 of the
body tail. A stored credential of exactly 40 bytes never
receives cleartext fallback: for this protocol it is exclusively
the direct shared-secret value. Deployments that need cleartext
fallback MUST avoid storing such a credential at exactly 40
bytes.
Either failure keeps HTTP 200, discards staged output, emits error
login failed, records failure, and ends card processing.
6.5. Cookie-Login Mode
Use cookie login iff recorded server fields satisfy:
VERSION >= 22700 AND (DATE > 20250727 OR (DATE == 20250727 AND TIME >= 110500))
Then the client MUST omit body login and send:
Cookie: x-f-l-c=login+USER+NONCE+SIGNATURE
Spaces become +; URL-unsafe bytes in fossilized USER are percent-
encoded. The server URL-decodes and uses the body-login code path.
This standard Cookie header is the only login-cookie header; there is
no X-Fossil-* equivalent.
Hipp, et al. Expires 30 January 2027 [Page 21]
Internet-Draft Fossil Sync Protocol July 2026
If the gate is false or no server-version has been observed, a
request subject to named authentication uses body login. The
exemptions in Section 6.1 use neither form. Never send cookie login
to an ungated peer. Servers MUST accept body login regardless of
gate. Both forms together violate the one-login invariant.
6.6. Capability Model
Server capability letters include Read o for pull and discovery,
Write i for push, Clone g, Private x, and Admin a for configuration/
user management. The server checks each card's required capability
and emits error when lacking, except where an owning section
specifies silent behaviour. Commit-lock acquisition and release are
governed solely by Section 9.4's lock-table rules, not by an
additional Write-capability check.
7. Content Transfer
7.1. file Card
file HASH SIZE
file HASH DELTASRC SIZE
SIZE counts immediately following uncompressed opaque bytes. Full
form carries full artifact; delta form carries [FossilDelta] against
DELTASRC. No trailing separator follows. Push/pull use file; clone
3 server transfer does not.
7.2. cfile Card
cfile HASH USIZE CSIZE
cfile HASH DELTASRC USIZE CSIZE
CSIZE alone frames the following stored compressed bytes. USIZE is
the sender's full reconstructed artifact size, parsed syntactically
but not transfer-layer validated; in delta form it is not delta size.
Payload is the repository's stored compressed full artifact or stored
compressed delta, retransmitted verbatim. Clone 3 uses cfile without
size threshold, pragma, or capability negotiation; push/pull use
file.
7.3. uvfile Card
uvfile NAME MTIME HASH SIZE FLAGS
NAME is fossilized; MTIME is a nonnegative signed-64-bit decimal Unix
second. The 0..253402300799 subset renders as the exact 19-byte UTC
timestamp 1970-01-01T00:00:00 through 9999-12-31T23:59:59; this
Hipp, et al. Expires 30 January 2027 [Page 22]
Internet-Draft Fossil Sync Protocol July 2026
timestamp-renderability bound does not restrict uvfile reception.
HASH is content hash or -; SIZE describes current content; FLAGS is
non-negative decimal. 0x0001 marks deletion metadata; 0x0004 means
omitted content. Senders MUST clear all reserved bits. Receivers
reject negative FLAGS and ignore reserved bits completely (2 equals
0; 6 or 20 equals 4).
The canonical sender emits deletion as HASH -, SIZE 0, and FLAGS with
0x0001 set. On reception, HASH - deletes when SIZE is zero or either
0x0001 or 0x0004 suppresses payload/hash processing. Otherwise, a
positive SIZE supplies exactly SIZE bytes; verification against HASH
- fails, so the receiver rejects the card without storing or
deleting.
Immediately before applying every received uvfile, the receiver MUST
recompute status 0 through 5 from the then-current local row using
Section 8.5.1, steps 1 through 5; it MUST NOT reuse a status or
remote-wins decision computed for an earlier uvigot. Statuses 3
through 5 make no local-row change. Status 2 updates MTIME only.
For status 0 or 1, HASH - applies deletion and is a no-op when no row
exists; otherwise, the receiver replaces or creates the row using the
received metadata and content. When content is omitted by FLAGS
0x0004, or when SIZE is zero, the stored representation has size
zero, encoding zero, and NULL content. These rules also govern
omitted-content cards.
A non-deletion card with SIZE zero and no omitted-content flag
carries zero payload bytes. Fossil treats it as null content and
does not verify HASH against a zero-byte digest before applying the
normal unversioned status and storage path; a declared HASH can
therefore be stored with NULL content.
Hipp, et al. Expires 30 January 2027 [Page 23]
Internet-Draft Fossil Sync Protocol July 2026
uvigot NAME MTIME HASH SIZE is a server-to-client state announcement;
uvgimme NAME is a client-to-server request. Server receipt of
client-sent UV changes requires UV Write permission; unauthorized
receipt is error. A client receiving server-sent uvfile applies the
permitted update without a client-side UV Write check. Accepted
changes invalidate cached catalog hash. On a syntactically valid
server-received uvgimme NAME, the server MUST look up NAME. If NAME
is absent, it emits no card. If the named HASH is SHA3-256 and the
remote version is below 20000, it emits the SHA3-version error and no
UV card. Otherwise, if the response has already reached its ordinary
output cap, a non-tombstone emits uvigot NAME MTIME HASH SIZE, which
causes a later request. A tombstone instead emits uvigot NAME MTIME
SIZE with an empty HASH field; this malformed short line is not
accepted as uvigot. Without the cap, the server emits a full uvfile
NAME MTIME HASH SIZE FLAGS with FLAGS 0, or a tombstone with HASH -,
SIZE 0, and FLAGS 1. This request reads server state and has no
received-write-capability requirement.
7.4. Delta Sources and Phantom Creation
For a delta-form file or cfile, Fossil resolves or creates the
DELTASRC record before the storage operation can detect that HASH
already names a locally verified artifact. If HASH is later found to
be a duplicate target, the storage operation consumes the payload and
does not replace or downgrade the verified target, but any DELTASRC
phantom or source record created earlier by the delta-source handling
remains a possible side effect.
For an as-yet-unverified target, missing DELTASRC does not reject
content: Fossil retains the delta under HASH, deletes HASH's phantom,
and creates a DELTASRC phantom with the received content card's
private classification. There is no persistent deferred-verification
FIFO or later target-HASH-verification guarantee. At commit
verification, an unavailable record is skipped and the verification
bag is cleared; later dephantomization reconstructs and crosslinks
dependents without re-queuing their target hashes.
For a non-clone delta file with a missing basis, the receiver creates
the source phantom with the received privacy classification, stores
the received delta, removes the target phantom, and records the
target as dangling without immediate reconstruction. Once its basis
is available, Fossil can reconstruct and crosslink the dependent
target, but does not promise a later target-name hash verification or
an edge-specific recovery transaction.
Hipp, et al. Expires 30 January 2027 [Page 24]
Internet-Draft Fossil Sync Protocol July 2026
7.5. Private Content
Canonical private has no arguments, but a receiver ignores any
visible surplus tokens and treats it as the marker card. On a client
reply, private sets the marker for the next file or cfile; on a
server request, it sets the marker only when the sender is authorized
for Private. An unauthorized server-side private emits its private-
transfer authorization error and leaves no marker, so the following
content card is processed unmarked. Repetition leaves a set marker;
comments, other cards, and uvfile do not consume it. A client marker
persists across replies until consumed or session end; a server
marker ends with its current request.
Private admission is receiver- and card-specific. Both file and
cfile handler entries clear a pending marker. With an unauthorized
marked server file, the server extracts then discards the payload.
With an unauthorized marked server cfile, it returns before
extracting the compressed payload, leaving those bytes at the parser
cursor rather than silently discarding a complete card. The client
sets its marker upon private receipt; its subsequent content
admission follows its client receive handler rather than the server's
unauthorized-marker paths. A missing DELTASRC for a marked delta
creates a private phantom; a missing DELTASRC for an unmarked delta
creates a public phantom. A sender MUST NOT send a delta against a
private basis to a peer lacking private authorization; it sends full
content instead. Unauthorized private igot HASH 1 is not error: do
not create a phantom, but reclassify an existing HASH artifact or
phantom private; record peer possession and continue. Pulling
private content requires successful send-private.
For a received igot HASH 1, Fossil reclassifies an existing HASH
artifact or phantom private before applying the missing-artifact
permission check. If HASH is absent and Private capability is
absent, it creates no phantom; a later public igot HASH reclassifies
an existing HASH artifact or phantom public.
7.6. SHA3-256 Peer-Version Gate
Do not emit file/cfile whose target HASH is SHA3-256, or uvfile with
a 64-character lowercase hexadecimal HASH, to a recorded peer VERSION
below 20000. The server records unknown client VERSION as 0 until
client-version appears. The client initializes its recorded server
VERSION to its own release VERSION and replaces it when server-
version appears. At each independent send site emit:
error Fossil\sversion\s2.0\sor\slater\srequired.
Hipp, et al. Expires 30 January 2027 [Page 25]
Internet-Draft Fossil Sync Protocol July 2026
Abandon only that artifact; append the error and continue
constructing remaining cards. Never substitute SHA1.
Outside the first clone reply, a server-sent error ends the current
reply-card loop, preserves the successfully processed prefix,
increments the cycle, and evaluates Section 3.7; it terminates the
session only when no continuation condition holds. SYNC_IFABLE is a
client-side mode set only by post-commit autopush. In that mode, a
decoded error that matches SQLite's ASCII-case-insensitive LIKE
pattern %not authorized to write%, where % matches any sequence of
bytes, instead marks autopush failure, increments the error count,
and continues reply parsing; it creates no independent continuation
condition. In the first clone reply, the client records a push
wherever it is subsequently parsed, treats the error as nonterminal,
continues parsing permitted trailing cards, and issues its required
second clone request. A client-sent error is direction-invalid but
defined, not unknown: server resets accumulated response, emits error
bad command: LINE, and continues parsing remaining request cards. A
preceding server request-scoped private marker is consumed and
cleared by that substituted error handler.
7.7. Hash-Policy Admission
Admission applies to the received target HASH, not independently to
DELTASRC. Under shun-sha1, a non-clone file whose HASH is SHA1 is
consumed and discarded after its payload and private marker; it emits
no error, does not set content-received, and makes no repository
mutation. A cfile whose HASH is SHA1 is likewise consumed and
discarded even in clone; its raw parser artifact-card count
nevertheless increments after handler invocation. A file received in
clone bypasses that target check. For a non-SHA1 target with SHA1
DELTASRC, ordinary dangling-delta handling creates the SHA1 source
phantom. The ordinary phantom scan MAY emit one gimme for that
source; if its arriving source card is then discarded under shun-
sha1, the stored delta and source phantom remain and no non-delta
retry exists. Section 3.7 permits the session to end with that
unresolved phantom. This is source-faithful behaviour, not a
recovery path.
Integrity hashes reconstructed artifact bytes, not wire payload.
Pre-storage verification applies to reconstructable non-clone file.
Deferred deltas and transfer-layer cfile records whose complete bytes
are unavailable during the current verification pass gain no later-
verification guarantee from this protocol. A repository transaction-
commit hook MUST recompute each newly stored artifact whose complete
bytes are available in that pass and abort commit on mismatch.
Hipp, et al. Expires 30 January 2027 [Page 26]
Internet-Draft Fossil Sync Protocol July 2026
Only successful storage and verification of a SHA3-256-named artifact
under auto permanently promotes repository policy to sha3
[FossilArtifact].
8. Clone
Except when initial private sync places pragma send-private first,
clone begins with client-version, clone 3 SEQNO, and randomness;
first sequence is 1. The first clone request sends no login card. A
later clone request uses the applicable normal authentication
transport.
8.1. Clone Protocol Version
clone VERSION SEQNO
Version 3 is the interoperable profile specified here: server emits
storage-order cfile and uses application/x-fossil-uncompressed. The
current Fossil server accepts clone VERSION values greater than or
equal to 3 with the same cfile and uncompressed-reply behaviour, but
this document specifies VERSION 3 only. Clone VERSION is unrelated
to release VERSION.
For an otherwise authorized server clone request with exactly three
tokens, an integer VERSION of at least 2, and a SEQNO value whose
parsed result is no greater than zero, the server clears accumulated
response output, rolls back the request transaction, emits error
invalid clone sequence number, and returns without parsing later
request cards. Current Fossil does not reliably distinguish a
nondecimal SEQNO from a parsed nonpositive value on this path, so
clients MUST emit a positive decimal SEQNO and MUST NOT rely on
nondecimal SEQNO tolerance.
8.2. Sequence Numbers
SEQNO is a server blob-table RID. Iterate from supplied RID through
current maximum, silently skipping gaps; clients MUST NOT infer count
or size from range.
Hipp, et al. Expires 30 January 2027 [Page 27]
Internet-Draft Fossil Sync Protocol July 2026
An accepting clone reply emits zero or more clone cfile cards in RID
order, interspersed with any per-artifact SHA3 error, then
clone_seqno NEXT, push SERVERCODE PROJECTCODE, and final comment.
server-version precedes this sequence. NEXT zero signals exhaustion.
A canonical sender uses decimal NEXT. The client records a digit-
only NEXT and does not validate that a positive value advances from
the supplied SEQNO; a nondecimal but visible NEXT is a defined
receiver-tolerance exception that leaves the recorded sequence
unchanged and does not stop parsing later reply cards. A Clone-
authorization refusal resets the response, then emits push SERVERCODE
PROJECTCODE immediately before error with no cfile or clone_seqno.
For positive NEXT, Section 3.7 decides continuation; positive alone
is insufficient. The clone received-artifact count is the client's
raw handler count: every file, cfile, uvfile, or config handler
invocation increments it, including a verified duplicate or policy-
discarded cfile; an error card does not. A handler-reported client
error preserves the successfully processed reply prefix and then
follows ordinary cycle, continuation, and commit handling; only an
explicit fail-fast path rolls back and starts no further round. If
continuing, reissue clone 3 NEXT iff the just-received reply
contained a well-formed push and recorded NEXT is positive; otherwise
Section 3.7 may issue operation-less cleanup.
NEXT zero means exhausted. It permanently bars clone, pull, and push
additions to this clone session, but separately true Section 3.7
guards require a cleanup request. Cleanup MAY carry, in ordinary
request order, applicable normal body login (cookie/HTTP login
remains outside the body); mandatory client-version; applicable
pragmas; remaining-work reqconfig, gimme, or uvgimme; returned state
cookie; and trailing randomness. No operation card is allowed. In
that directionless cleanup, a gimme is recorded but MUST produce
neither file/cfile nor error: the server has no pull direction and
the client has no push enablement. Any still-unresolved phantom
therefore remains unresolved when cleanup terminates.
The recorded sequence persists until overwritten.
Hipp, et al. Expires 30 January 2027 [Page 28]
Internet-Draft Fossil Sync Protocol July 2026
SEQNO := 1
first request := [send-private if requested];
client-version; clone 3 1; randomness
loop:
send; process reply in order; remember whether well-formed push occurred
if first clone round and error: record any preceding push,
ignore error and continue parsing;
the second clone round is mandatory
if clone_seqno NEXT is decimal: SEQNO := NEXT
evaluate Section 3.7
if continue and push-seen and SEQNO>0: next includes clone 3 SEQNO
else if continue: next is operation-less cleanup
else stop
Clone rounds carry no push direction (Section 8.5), so a server
creates no phantoms from, and takes no other action on, client igot
cards received in a clone session; the accepting reply is driven
solely by the sequence cursor. The Section 3.2 permission for
request igot is therefore never applicable to clone rounds: a client
SHOULD NOT announce held artifacts during clone transfer or cleanup
rounds, and the reference client emits none. A server MUST accept
and ignore igot cards received without push enablement.
8.3. First Clone Round
The first request sends no normal login card. It uses client-
version, clone 3 1, and randomness; if private sync is requested,
pragma send-private precedes client-version. An unauthorized send-
private appends its own non-reset error and parsing continues; Clone
authorization is checked independently by the later clone card. Any
server error in this initial clone reply, including a private-
transfer, Clone, or SHA3 error, is ignored as an error condition
while the client consumes permitted trailing cards, records any push
it subsequently receives, retains its initial UV send table without
rebuild or requeue, and makes the mandatory second clone request.
8.4. Configuration During Clone
Defer all client reqconfig and, when requested, uv-hash until after
clone round 0. This deferral does not remove, rebuild, or requeue
the session's pre-seeded UV send table. Independently, after Clone
authorization succeeds, an enabled server UV sync with no catalog yet
sent in the current request emits pragma uv-pull-only and the full UV
catalog immediately in the clone reply; that clone-specific catalog
is not conditional on the deferred client uv-hash.
Hipp, et al. Expires 30 January 2027 [Page 29]
Internet-Draft Fossil Sync Protocol July 2026
8.5. Push, Pull, and Sync
push SERVERCODE PROJECTCODE, pull SERVERCODE PROJECTCODE, or both
select direction. Servercode is informational; project mismatch is
fatal. Pull creates client phantoms from server igot; push creates
server phantoms from client igot; gimme obtains content. Section 3.3
governs.
8.5.1. Unversioned-File Synchronisation
At session start seed the UV send table with every local row whose
hash is non-NULL and mtimeOnly=false; tombstones are excluded. On
the first eligible round send pragma uv-hash HASH exactly once; clone
round 0 is ineligible.
HASH is SHA-1 over one line for every non-tombstone row, sorted by
unsigned bytewise NAME bytes (BINARY collation):
NAME SP YYYY-MM-DD SP HH:MM:SS SP HASH LF
NAME is stored fossilized name bytes. Timestamp is the direct SQLite
datetime(MTIME,'unixepoch') value passed to printf('%s %s %s\n',
name, datetime, hash): it is the 19-byte UTC YYYY-MM-DD SP HH:MM:SS
rendering for 0..253402300799, and an empty field when SQLite returns
NULL. HASH is stored lowercase SHA-1 or SHA3-256 bytes. No CR or
trailing whitespace. Tombstones are excluded from equality
detection; when a mismatch causes a catalog response, its full
inventory still includes tombstones represented by HASH -.
If hashes match, server sends no UV catalog. Otherwise, with Read
permission and no catalog yet sent in the current request, it emits
uv-push-ok or uv-pull-only, then the *full inventory*, one uvigot
NAME MTIME HASH SIZE for every current unversioned entry, including
tombstones represented by HASH -. Duplicate suppression is request-
local only.
For each uvigot, compute:
1. No local row: status 0.
2. Else LOCAL-HASH is stored hash or -; MTIME-CMP compares local/
remote mtime; HASH-CMP is bytewise LOCAL-HASH/HASH comparison.
3. Equal hashes: status 3+MTIME-CMP (2 older, 3 identical, 4 newer).
4. Different hashes and (MTIME-CMP<0 or (MTIME-CMP=0 and HASH-
CMP<0)): status 1.
Hipp, et al. Expires 30 January 2027 [Page 30]
Internet-Draft Fossil Sync Protocol July 2026
5. Otherwise status 5.
Equal-time hash lexical order is deterministic tie-breaker. Revert
maps 4->2 and 5->1. Dry-run suppresses local changes and reply
messages.
Actions:
+========+===============================================+
| Status | Required action |
+========+===============================================+
| 0 | Remote wins; if HASH != -, append one uvgimme |
| | while processing this uvigot; if HASH is -, |
| | make no local-row change |
+--------+-----------------------------------------------+
| 1 | Remote wins; if content, append one uvgimme |
| | while processing this uvigot and immediately |
| | delete the existing local row; if -, store |
| | mtime and clear hash, size, encoding, content |
+--------+-----------------------------------------------+
| 2 | Same hash, remote newer: update mtime only |
+--------+-----------------------------------------------+
| 3 | Identical |
+--------+-----------------------------------------------+
| 4 | Local mtime newer: retain send-table row with |
| | mtimeOnly=true if upload allowed |
+--------+-----------------------------------------------+
| 5 | Local content/deletion wins: retain or insert |
| | row with mtimeOnly=false if upload allowed |
+--------+-----------------------------------------------+
Table 6
Afterward, 0-3 remove NAME from send table; 4/5 set markers. Pull-
only removes 4/5 too.
Each qualifying uvigot with non-- HASH appends one uvgimme NAME
during that reply's processing; there is no persistent per-NAME
request table, retry count, or protected old-row retention. A
repeated uvigot is independently recomputed and can append another
one-request response. For remote content wins, Fossil deletes the
old local row immediately; a later failed or absent transfer does not
restore it. Section 3.7(d) supplies the global cycle condition.
At next-request construction, an enabled UV upload phase visits
selected send-table rows and removes each row immediately after
invoking its send helper, regardless of whether that helper emits a
full, mtime-only, or tombstone uvfile, a reference-specific malformed
Hipp, et al. Expires 30 January 2027 [Page 31]
Internet-Draft Fossil Sync Protocol July 2026
short uvigot, a SHA3 error, or no card. That short uvigot is a
Fossil source defect: the server treats it as a wrong-direction
request and emits error bad command; a cleanroom sender MUST NOT emit
it. Dry-run or revert clears the phase immediately. Otherwise the
client clears the phase when its table scan reaches completion,
including an empty table; if the current output exceeds the ordinary
threshold, it stops with the phase still selected and Section 3.7(b)
dispatches exactly the next continuation. Section 3.7(d) governs the
global uvgimme cycle.
8.6. Announcement and Request Cards
igot HASH announces public possession; igot HASH 1 private
possession; gimme HASH requests content. Section 3.3 governs. No
ordering constraint exists between igot and gimme. For peer dates
before 2020-04-13, omit private igot entirely rather than sending
flag 1. Until server-version establishes the peer date, hold local
private inventory; if the reply establishes a date on or after
2020-04-13, append the eligible private igot cards to that reply's
next-request buffer, and let Section 3.7(c) dispatch that buffer.
8.6.1. Derived Branch, Tag, Leaf, Closed, and Current-Trunk Views
These views are not wire operations. After relevant artifacts and
bases are stored and crosslinked, derive them per [FossilArtifact]
and [FossilRepoState]. Current-trunk also uses each peer's local
main-branch setting, so synchronised peers may differ. With
unresolved phantoms, views cover only available closure and may
change later without sync error.
8.7. Phantom Records
Section 3.3 is authoritative.
8.8. Gimme Eligibility Filters
Apply Section 3.3's per-round peer-lacks, shun, and private filters;
never persist round filters.
8.9. Loop Termination
Section 3.7 is authoritative; the stateless server signals no
completion.
Hipp, et al. Expires 30 January 2027 [Page 32]
Internet-Draft Fossil Sync Protocol July 2026
9. Pragmas
pragma NAME [ARGUMENTS] is an ordinary body card. Unknown names MUST
be silently ignored; unlike unknown card operators, no pragma is
version-gated at the receiver, and every unrecognised pragma takes
that ignore path. Defined client-to-server names are send-private,
send-catalog, client-version, uv-hash, ci-lock, ci-unlock, client-
url, req-links, and req-clusters; server-to-client names are server-
version, uv-push-ok, uv-pull-only, ci-lock-fail, avoid-delta-
manifests, and link.
A known pragma received in the direction not assigned above is
silently ignored before argument parsing, exactly like an unknown
pragma, and MUST NOT change receiver state. A bare pragma has no
NAME and falls through to the unknown request-card branch: the server
resets accumulated response, emits error bad command: LINE, continues
parsing, and does not itself roll back earlier mutations. A
direction-correct known NAME enters its own handler; there is no
generic defined-card malformed outcome. The extension-pragma
production applies only to a NAME outside both defined-name lists in
this section; a known NAME MUST NOT fall through to that production
regardless of its arguments. send-private, send-catalog, req-links,
and req-clusters act despite visible surplus; client-version and
server-version act with at least three tokens and parse VERSION with
atoi, reading DATE/TIME only with at least five. A guarded named
handler whose predicate fails silently takes no action, emits no
error, does not reset response output, continues its card loop, and
leaves the transaction for ordinary commit. The client ci-lock-fail
handler has its own exception: on its exact four-token form, a parsed
negative MTIME that is earlier than current time invokes fatal error
invalid ci-lock-fail time; other time-parse failures remain nonfatal.
extension-pragma is reserved for a NAME not in the defined-name lists
above; a known NAME never falls through as an extension.
9.1. Version Negotiation
pragma client-version VERSION DATE TIME
Hipp, et al. Expires 30 January 2027 [Page 33]
Internet-Draft Fossil Sync Protocol July 2026
VERSION is the release integer. A full pragma client-version VERSION
DATE TIME carries DATE/TIME as YYYYMMDD/HHMMSS integers. On a full
client version, the server replaces request-local peer VERSION, DATE,
and TIME and emits full server-version VERSION DATE TIME; before one,
these server peer values are 0, 00000000, and 000000. A short three-
or four-token version pragma updates only VERSION and retains its
prior DATE/TIME; a short client-version emits no server-version. The
client applies the same full-versus-short update to received server-
version; before one, it records peer VERSION as its own
RELEASE_VERSION_NUMBER and DATE/TIME as 00000000/000000. These
values govern SHA3, private-igot, and cookie gates.
9.2. Unversioned-File Pragmas
uv-hash, catalog response, uv-push-ok, and uv-pull-only obey the
Unversioned-File Synchronisation rules. Client sends hash at most
once and not clone round 0. Matching hash produces no UV response.
On a mismatch with Read permission, the server sends exactly one uv-
push-ok when the authenticated principal has UV write permission;
otherwise it sends exactly one uv-pull-only. uv-push-ok enables
client upload; uv-pull-only disables it and causes the state machine
to remove local-only status-4/5 entries. In either case the server
sends the full catalog. Deduplication is request-local. Canonical
uv-hash emission is SHA-1. The server accepts an artifact-hash,
including SHA3-256, as received HASH; a nonmatching accepted value
takes the ordinary catalog-mismatch path.
9.3. Configuration Exchange
reqconfig NAME requires NAME beginning /. Without Read permission the
server silently ignores it. Clone defers requests. A client MAY
issue multiple reqconfig groups in one request. If Read is present
but /NAME matches no supported group, its group mask is zero and the
server emits no config cards; no error occurs.
Canonical config senders use config GROUPNAME SIZE, exactly SIZE
payload bytes, then one mandatory LF. A Fossil-compatible receiver
consumes one byte after the declared payload without checking that it
is LF.
Scalar settings always use config /config SIZE, regardless of
requested group. Payload is:
MTIME SP QUOTED-NAME SP value SP QUOTED-VALUE
Hipp, et al. Expires 30 January 2027 [Page 34]
Internet-Draft Fossil Sync Protocol July 2026
MTIME is decimal Unix seconds. Fossil quote encoding surrounds
values with ' and doubles internal '; receivers unquote and undouble.
A client receiving scalar /config evaluates its setting against the
union of all scalar groups it requested for the exchange; it does not
need or get per-card group identity.
Table groups /shun, /user, /reportfmt, /concealed, and /subscriber
use config /GROUPNAME SIZE; GROUPNAME remains the response
correlation; row serialisation is out of scope.
Scalar /config push payloads use the same payload and trailing LF.
The reference implementation also emits and accepts authorized /shun,
/user, /reportfmt, /concealed, and /subscriber table-group cards
using their out-of-scope row serialisations. Only exactly config
NAME SIZE with decimal SIZE enters the configuration handler. Other
malformed config forms, including signed or non-decimal SIZE, take
the unknown-card path, which resets output, emits error bad command:
LINE, continues parsing, and does not itself roll back earlier
mutations. For a recognised card, the server MUST require Admin;
otherwise emit error not authorized to push configuration and do not
apply. An authorized, well-formed card is applied to server
configuration after the server consumes its SIZE payload bytes and
single trailing LF.
The transfer-hook group contains exactly four server-side hook
settings, encoded as scalar /config; their execution and identities
are server-local and outside interoperability.
9.4. Commit-Lock Pragmas
pragma ci-lock HASH CLIENT-ID has exactly an artifact-hash HASH
argument and an opaque fossilized CLIENT-ID argument; the server does
not validate that fourth token as a hash name. On receipt, the
server scans all valid ci-lock-* rows. It deletes a row before any
conflict check when its MTIME is at least the configured lock-timeout
old or its HASH is no longer a leaf. It considers only a remaining
row named for the requested HASH; locks for other HASH values do not
conflict. If a matching row exists, it preserves that row without
refresh or replacement. If its stored CLIENT-ID differs from the
request CLIENT-ID, the server emits pragma ci-lock-fail LOGIN MTIME;
if it matches, it emits no lock result. In either case, a matching
live row prevents a new row.
If no matching live row remains, the server writes ci-lock-HASH with
the current server login value, CLIENT-ID, and current MTIME. It
emits no positive lock acknowledgement. The transfer dispatcher
applies this behaviour without a separate Write-capability or named-
principal test at this pragma; authentication and operation-card
Hipp, et al. Expires 30 January 2027 [Page 35]
Internet-Draft Fossil Sync Protocol July 2026
authorization remain governed by their own ordered cards. ci-lock-
fail is server-to-client and contains only the fossilized LOGIN and
unsigned-decimal MTIME. The reference client treats it as a
diagnostic for the current checkout, records the failure, and
continues reply processing; silence after ci-lock is not a
confirmation protocol.
pragma ci-unlock CLIENT-ID has one hash-name argument. It emits no
reply pragma or error. The server deletes every ci-lock-* row whose
JSON is invalid or whose stored CLIENT-ID equals the argument,
without an additional principal-ownership or Write-capability check.
The reference client emits 40 lowercase hexadecimal characters for
CLIENT-ID. A client that has a persisted CLIENT-ID but no current
check-in lock emits ci-unlock in the next request; because the
operation is unacknowledged, it may repeat in later requests.
9.5. Private-Content and Discovery Pragmas
send-private enables private sync only with x; otherwise append non-
reset error not authorized to sync private content. Clients repeat
it every private round; authorization is idempotent.
send-catalog unconditionally enables the server's resync state; it
has no Read-capability check at pragma processing. A subsequent
read-authorized transfer path requests igot for every eligible
holding after the Section 3.3 shun, phantom, private, and peer-has
filters; private inventory is separately added only when private sync
is enabled. Without Read, the server emits no inventory.
req-clusters has no Read-capability check in the reference server and
emits eligible public cluster igot announcements, enclosed as
Section 5.3 requires.
req-links is request-local: it permits zero or more pragma link URL
ARG MTIME only in the response to that same request; absent it,
server MUST emit no links. URL is fossilized HTTP(S) alternative
location for the current repository. ARG is fossilized opaque server
data: defossilize and store verbatim; MUST NOT parse, alter, or echo
it. Client acceptance is session-gated by local share-links mode and
additionally requires a parsed HTTP(S) URL and positive Unix-second
MTIME; otherwise discard all fields. An enabled client need not
separately remember whether the current request sent req-links.
Accepted triples are receiver-local. This OPTIONAL mechanism is not
artifact inventory, graph, delta, or content relationship.
client-url URL is a request pragma. Without Write i, the server
silently ignores it. With Write, exact lowercase http:// and
https:// forms enter HTTP(S) parsing and can be stored; exact
Hipp, et al. Expires 30 January 2027 [Page 36]
Internet-Draft Fossil Sync Protocol July 2026
lowercase ssh:// and file: forms are also recognised but are not
stored. Other forms fall through to local-path parsing: an existing
repository path gets protocol file and is not stored; otherwise the
server fails fatally as an unknown repository. An HTTP(S) port
consumes its run of decimal digits without range validation; parsing
fails only when the first following non-decimal byte is neither / nor
end-of-input. Fossil omits user information and stores an HTTP(S)
URL when its parsed host pointer is non-NULL, including an empty
host, unless the host matches SQLite's ASCII-case-insensitive LIKE
pattern %localhost%, where % matches any sequence of bytes. Its
canonical serializer lowercases and percent-decodes the host;
percent-decodes the path; retains no separate fragment delimiter, so
# is path text; omits a scheme-default port; and fossilizes output
fields. It splits the query only on &, ignores every field whose
name is not the case-sensitive fossil, and, for repeated fossil
fields, keeps the last: its value is percent-decoded then fossilized
as the sole ?fossil=VALUE query field. The server stores
syncfrom:CANONICAL with value {} and current mtime. A later req-
links response MAY select recent eligible syncfrom:http* entries as
link candidates; client-url itself emits no immediate link response.
Applicable clients send client-url once before the loop and req-links
in every request for which they want links.
avoid-delta-manifests is a server-to-client control pragma. Upon
receipt, the client removes manifest-delta candidates from any not-
yet-dispatched seeded request and sends full manifests for the
remainder of the current sync session; it does not affect non-
manifest artifacts, already-dispatched data, or persisted repository
state. The flag resets when the session ends.
10. Error Handling
10.1. Error Card
error TEXT
TEXT is fossilized and MUST be defossilized. Outside the first clone
reply, an ordinary server error ends the current reply-card loop
without alternate encoding/hash retry, records the error, increments
the cycle, and then evaluates Section 3.7; the session ends only if
that evaluation finds no continuation condition. The Section 7.6
SYNC_IFABLE post-commit-autopush exception instead marks autopush
failure, increments the error count, and continues parsing without an
independent continuation rule. Every first-clone error is silently
discarded as an error condition but does not terminate parsing: the
client consumes permitted trailing cards, records any push it
subsequently receives, and attempts the mandatory second clone round.
Hipp, et al. Expires 30 January 2027 [Page 37]
Internet-Draft Fossil Sync Protocol July 2026
Client reply processing preserves the successfully processed prefix
of a reply. Receipt of an ordinary error card commits that prefix
and ends only the reply-card loop, after which it increments the
cycle and evaluates Section 3.7. A client-local unknown or card-
parse failure records a warning and ends only the reply-card loop;
its prefix remains effective, then the client increments the cycle
and evaluates Section 3.7, including the forced second clone round.
A handler-reported content or integrity error warns, preserves and
commits the processed prefix, and follows ordinary continuation.
Explicit fail-fast paths roll back transaction-visible current-round
state and start no further round. A pending private marker is client
session parser state (and server request parser state), not
transaction-visible: handler entry clears it before validation; an
ordinary reply end leaves an unconsumed client marker set, and a
rollback never restores a marker already consumed at handler entry.
At wrong-project, authorization, duplicate-login, unknown-card, and
other fatal reset sites, server discards partial output before error;
later authorized cards may still follow where explicitly defined,
e.g. clone refusal resets then writes push before error. SHA3 gate
and unauthorized send-private are non-reset append sites.
Unauthorized marked private content has the receiver- and card-
specific behaviour in Section 7.5. Every non-reset append site
continues parsing in the same request transaction; prior and later
authorized mutations commit at ordinary request exhaustion unless a
later fail-fast rollback occurs. HTTP status remains 200.
For each request that reaches request-card processing, the server
begins one database write transaction. It returns before opening
that transaction for pre-dispatch failures such as a schema-out-of-
date response. It rolls back an opened transaction for the wire-
visible fail-fast errors error invalid clone sequence number and
error invalid config record. Other server card-error paths that
reset partial output, including error bad command: LINE, do not by
themselves roll back prior request mutations; at ordinary request
end, those prior mutations commit.
10.2. Message Card
message TEXT
Exactly two visible tokens are required. Defossilize TEXT. It is
nonfatal.
Hipp, et al. Expires 30 January 2027 [Page 38]
Internet-Draft Fossil Sync Protocol July 2026
If SYNC_PUSH is set and decoded bytes match SQLite GLOB pull only *,
case-sensitively, clear SYNC_PUSH for the session and suppress
display. The literal prefix is pull only followed by one ASCII
space; * then matches zero or more bytes. No trimming occurs, so a
payload ending immediately after that single ASCII space matches.
Otherwise MAY display verbatim and MUST change no state.
Clearing does not edit the already seeded next-request buffer.
Earlier reply cards' push-derived appends and its seeded push remain
in that buffer. Later cards in the same reply observe cleared
SYNC_PUSH; in particular, later gimme MUST NOT append content, while
earlier gimme content remains. If another Section 3.7 condition
starts a further round, that round dispatches the retained buffer.
On the reset/reseed following any such subsequent dispatch, omit push
and other SYNC_PUSH-gated versioned cards. Clearing SYNC_PUSH does
not clear SYNC_UNVERSIONED or uvDoPush: uvfile and push-side UV cards
remain eligible. If SYNC_PULL remains, seed pull and continue pull
work. If original intent was pure push, seed no operation or
directional versioned content; Section 3.7 alone decides whether that
request exists. A matching text received while SYNC_PUSH is already
clear follows the ordinary advisory path.
11. Security Considerations
The body-login mechanism sends USER, NONCE, and a calculated
SIGNATURE rather than raw password bytes. HTTP Basic and proxy
credentials are transport-layer mechanisms outside that body-login
property. Random comments vary generated nonce-covered tails. The
server maintains no dedicated login replay cache, though a content
mutation can persist the login nonce as received-from provenance.
A sender MUST enforce the 20000 SHA3 gate and MUST NOT truncate,
rename, or downgrade SHA3 identifiers.
11.1. Credential Replay
Each client-generated request ends with a fresh random comment; when
body login is used, that changes its nonce. The server validates the
signature but does not look up a nonce in a dedicated login replay
cache, so a byte-for-byte replay with a valid authentication token is
not rejected by that cache. A successful named login can supply the
nonce later stored with mutation receipt provenance, whose uniqueness
is distinct from a protocol-level replay decision.
Hipp, et al. Expires 30 January 2027 [Page 39]
Internet-Draft Fossil Sync Protocol July 2026
11.2. Private-Content Exposure and Protected Transport
The protocol implementation does not impose a transport-scheme guard:
plain HTTP can carry authenticated bodies, private artifacts, and
cookie login. Such traffic exposes its contents to readable
intermediaries. Deployments SHOULD use HTTPS, SSH, or an equivalent
confidential, integrity-protected, server-authenticated transport.
11.3. Line-Length Bounds and Resource Exhaustion
This protocol defines no separate per-line byte limit before LF.
Receivers parse a line through its LF; entity and payload limits
remain governed by their declared counts and storage policy.
11.4. SHA3-256 Downgrade Prevention
SHA3 refusal is explicit; implementations MUST NOT truncate or re-
identify SHA3 as SHA1. See [FossilArtifact] Section 7.
12. IANA Considerations
This protocol requests these application media-type registrations.
No port or service-name registration is required.
12.1. application/x-fossil
Type name: application
Subtype name: x-fossil
Required parameters: none
Optional parameters: none
Encoding considerations: binary; zlib-compressed Fossil sync,
arbitrary octets after decompression
Security considerations: Section 11; may carry authentication
material, private artifacts, and repository content
Interoperability considerations: ordinary requests and non-clone
replies per Section 4.1
Published specification: this document
Applications that use this media type: Fossil sync clients, servers,
and compatible replication tools
Hipp, et al. Expires 30 January 2027 [Page 40]
Internet-Draft Fossil Sync Protocol July 2026
Fragment identifier considerations: none
Additional information: deprecated alias names, magic numbers, file
extensions, and Macintosh file type codes are all none
Person and email address to contact for further information: Dan
Shearer, dan@shearer.org (mailto:dan@shearer.org)
Intended usage: COMMON
Restrictions on usage: none
Author: D. Richard Hipp, Stephan Beal, Dan Shearer
Change controller: IETF
12.2. application/x-fossil-debug
Type name: application
Subtype name: x-fossil-debug
Required parameters: none
Optional parameters: none
Encoding considerations: binary; uncompressed sync with arbitrary
payload octets
Security considerations: Section 11; exposed diagnostic traffic may
carry authentication material and repository content, and therefore
requires an appropriately protected deployment transport
Interoperability considerations: decompressed-wire-compatible with
application/x-fossil; diagnostics/testing only
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, magic numbers, file
extensions, and Macintosh file type codes are all none
Person and email address to contact for further information: Dan
Shearer, dan@shearer.org (mailto:dan@shearer.org)
Hipp, et al. Expires 30 January 2027 [Page 41]
Internet-Draft Fossil Sync Protocol July 2026
Intended usage: LIMITED USE
Restrictions on usage: debugging and testing only
Author: D. Richard Hipp, Stephan Beal, Dan Shearer
Change controller: IETF
12.3. application/x-fossil-uncompressed
Type name: application
Subtype name: x-fossil-uncompressed
Required parameters: none
Optional parameters: none
Encoding considerations: binary; uncompressed outer clone reply with
arbitrary cfile octets
Security considerations: Section 11; clone replies carry repository
content and authentication-adjacent state
Interoperability considerations: version-3 clone replies only
Published specification: this document
Applications that use this media type: Fossil bootstrap servers,
clients, and compatible tools
Fragment identifier considerations: none
Additional information: deprecated alias names, magic numbers, file
extensions, and Macintosh file type codes are all none
Person and email address to contact for further information: Dan
Shearer, dan@shearer.org (mailto:dan@shearer.org)
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
13. References
Hipp, et al. Expires 30 January 2027 [Page 42]
Internet-Draft Fossil Sync Protocol July 2026
13.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>.
[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>.
Hipp, et al. Expires 30 January 2027 [Page 43]
Internet-Draft Fossil Sync Protocol July 2026
[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>.
13.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>.
Appendix A. Examples
fossil-sync fences contain decompressed bodies; http fences include
framing. Binary omissions are described outside fences. The current
Fossil reference client emits HTTP/1.0 request lines; Section 4 is
normative.
A.1. Basic Push/Pull Convergence (Two Rounds)
Repositories share project-code
aa87ea41b0bb05ba528f7a3bdba700d7e23d6318.
*Round 1 request* (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* (application/x-fossil-debug, 367 bytes body):
Hipp, et al. Expires 30 January 2027 [Page 44]
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 client creates two phantoms.
*Round 2 request* (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 first payload is 272 bytes including seven terminating LFs; the
second is 26 bytes including its LF.
*Round 2 reply* (533 bytes body):
pragma server-version 22800 20260311 113146
file 4361252d0f450879dcd2e0b6a42b95ffe8a41d8e948130253ae8eefa7f4b79ea 270
C server:\sadd\sbeta.txt
D 2026-07-02T18:30:46.907
F beta.txt f5116517775075ea483901021282ba97eebe5a2b993a47eb78198adc10efe128
P c5b2e0ecb779a7616b32553d973bc9c4ae5a07569a2cee18001386da6bd9ae57
R 5aa7ac70dd267efe64fe354554a33895
U drh
Z 7330b90458172595afd5c246c62c096d
file f5116517775075ea483901021282ba97eebe5a2b993a47eb78198adc10efe128 31
beta content added server-side
# timestamp 2026-07-02T18:31:13 errors 0
Hipp, et al. Expires 30 January 2027 [Page 45]
Internet-Draft Fossil Sync Protocol July 2026
The payloads are exactly 270 and 31 bytes. No Round 3 is needed. No
login appears because anonymous push/pull is allowed; no delta
appears; production uses application/x-fossil; 22800 means 2.28.00.
A.2. SHA3 Version Gate (Clone Reply)
*Request* (Content-Type: application/x-fossil-debug, Content-Length:
97):
pragma client-version 19999 20260702 182649
clone 3 1
# 3A8C1D4EE99F1B0782F7C4A67B39D0C512E7F540
*Response* (HTTP 200, Content-Type: application/x-fossil-
uncompressed, Content-Length: 235):
pragma server-version 22800 20260311 113146
error Fossil\sversion\s2.0\sor\slater\srequired.
clone_seqno 0
push 34302acad64fe2397bd907704dec457899e2f9a3 c85e3efddf993e86e8325d08672ea94bdba6c2c4
# timestamp 2026-07-02T18:26:49 errors 0
Decoded error: Fossil version 2.0 or later required. No SHA1
fallback occurs. The SHA3 refusal occupies its RID position before
clone_seqno and the later push. A first-round clone client treats
this error as nonterminal, continues parsing, records that later
project code, and makes the required second clone request.
A.3. Private Content Suppression
*Scenario 1 -- Natural pull (anonymous): private artifacts absent*
Request (174-byte body):
pragma client-version 20028 20260702 183000
pull cccccccccccccccccccccccccccccccccccccccc ea2e766b68008b6c3b3ca547df136ded07c7f921
# 9B21F7CE5A48D3126FE0B48A0D5C71D3F26AA810
Response (295-byte body):
pragma server-version 22800 20260311 113146
igot 23813573dea5ec687052a067866751f481e58ef4c478939ac601587a43f50b35
igot 2c215398c6ea4e3c0994d41918f632d5208d6a24da3ac894c9d9d167a711f30b
igot ed76b084599a372a5a17232f0bb67211145c0bde6b5a5ff542813d60fa07d3dc
# timestamp 2026-07-02T18:33:01 errors 0
*Scenario 2 -- Crafted gimme: date-gated private announcement*
Hipp, et al. Expires 30 January 2027 [Page 46]
Internet-Draft Fossil Sync Protocol July 2026
Request (245-byte body):
pragma client-version 20028 20260702 183000
pull cccccccccccccccccccccccccccccccccccccccc ea2e766b68008b6c3b3ca547df136ded07c7f921
gimme da4b6b0ea2b060ff6bf29c16a3b10be05afc9c1a75d605a45cf7267ea3e74091
# C7D40B21E5F84A9308B21A5F63C4A9E70152D3B4
Response (367-byte body):
pragma server-version 22800 20260311 113146
igot da4b6b0ea2b060ff6bf29c16a3b10be05afc9c1a75d605a45cf7267ea3e74091 1
igot 23813573dea5ec687052a067866751f481e58ef4c478939ac601587a43f50b35
igot 2c215398c6ea4e3c0994d41918f632d5208d6a24da3ac894c9d9d167a711f30b
igot ed76b084599a372a5a17232f0bb67211145c0bde6b5a5ff542813d60fa07d3dc
# timestamp 2026-07-02T18:33:01 errors 0
*Scenario 3 -- Unauthorized send-private*
This crafted order is not the normative initial private-sync order.
Request (194-byte body):
pragma client-version 20028 20260702 183000
pragma send-private
pull cccccccccccccccccccccccccccccccccccccccc ea2e766b68008b6c3b3ca547df136ded07c7f921
# 6E1A97F2B4D08C51A73F0592E4C68B10D97AC531
Response (345-byte body):
pragma server-version 22800 20260311 113146
error not\sauthorized\sto\ssync\sprivate\scontent
igot 23813573dea5ec687052a067866751f481e58ef4c478939ac601587a43f50b35
igot 2c215398c6ea4e3c0994d41918f632d5208d6a24da3ac894c9d9d167a711f30b
igot ed76b084599a372a5a17232f0bb67211145c0bde6b5a5ff542813d60fa07d3dc
# timestamp 2026-07-02T18:33:01 errors 0
*Scenario 4 -- Authorized private pull*
Round 1 request (455 bytes total):
Hipp, et al. Expires 30 January 2027 [Page 47]
Internet-Draft Fossil Sync Protocol July 2026
POST / HTTP/1.0
Host: 127.0.0.1:9872
User-Agent: Fossil/2.28 (2026-03-11 11:31:46 [52445a27f1])
Content-Type: application/x-fossil-debug
Content-Length: 291
login privuser ee71329ef84fb77b428c6f626ad0103a426e0652 c3ee0be781c9ebcfc6e24756cbb75e6319ca255b
pragma send-private
pragma client-version 22800 20260311 113146
pull 3921d47b51aad4027faac6467b4a87b4e7951c64 ea2e766b68008b6c3b3ca547df136ded07c7f921
# 7258235C6A6B727E5C0F6E2E2576D36A234E63A3
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
Round 2 request (614 bytes total):
POST / HTTP/1.0
Host: 127.0.0.1:9872
User-Agent: Fossil/2.28 (2026-03-11 11:31:46 [52445a27f1])
Cookie: x-f-l-c=login+privuser+653828a8fc753c1685501de457c104f0b20938c9+bec32fbc6a2589413ee6d1f47a4ae0098dafcad2
Content-Type: application/x-fossil-debug
Content-Length: 336
pragma client-version 22800 20260311 113146
pragma send-private
pull 3921d47b51aad4027faac6467b4a87b4e7951c64 ea2e766b68008b6c3b3ca547df136ded07c7f921
gimme 393953a9803595823fb560ddbe333767840a5ad89748a12fc2cd00f533c6df8a
gimme da4b6b0ea2b060ff6bf29c16a3b10be05afc9c1a75d605a45cf7267ea3e74091
# 4D85B916A821F5198416D9F01D4F75F45714F45C
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 LF and is immediately followed by:
Hipp, et al. Expires 30 January 2027 [Page 48]
Internet-Draft Fossil Sync Protocol July 2026
private
file da4b6b0ea2b060ff6bf29c16a3b10be05afc9c1a75d605a45cf7267ea3e74091 ed76b084599a372a5a17232f0bb67211145c0bde6b5a5ff542813d60fa07d3dc 317
Exactly 317 binary delta bytes follow, possibly containing LF, then:
igot 23813573dea5ec687052a067866751f481e58ef4c478939ac601587a43f50b35
igot 2c215398c6ea4e3c0994d41918f632d5208d6a24da3ac894c9d9d167a711f30b
igot ed76b084599a372a5a17232f0bb67211145c0bde6b5a5ff542813d60fa07d3dc
igot 393953a9803595823fb560ddbe333767840a5ad89748a12fc2cd00f533c6df8a 1
igot da4b6b0ea2b060ff6bf29c16a3b10be05afc9c1a75d605a45cf7267ea3e74091 1
# timestamp 2026-07-02T18:34:48 errors 0
A.4. Comment Lines and Clock-Skew Timestamp
Client randomness:
# F2A895EF75B47B7157664A7FEAC978EFFFDB01C4
# F155AB07186D7F394649AB08DB88BD4179159D07
Server comments:
# timestamp 2026-07-02T18:31:13 errors 0
# timestamp 2026-07-02T18:31:13 errors 0
Random lines are ignored as cards; timestamp is used for skew and
errors 0 is informational.
A.5. Authentication Example
+==============+===========================================+
| Field | Value |
+==============+===========================================+
| USER | alice |
+--------------+-------------------------------------------+
| password | secret-passphrase |
+--------------+-------------------------------------------+
| project code | c1b2a3d4e5f60718293a4b5c6d7e8f9012345678 |
+--------------+-------------------------------------------+
| covered body | 26 ASCII bytes example-request-body-bytes |
+--------------+-------------------------------------------+
Table 7
Hipp, et al. Expires 30 January 2027 [Page 49]
Internet-Draft Fossil Sync Protocol July 2026
NONCE = SHA1("example-request-body-bytes")
= 5dfa03bd8645938b4e312a4fcdad5ebe6d4d4840
shared-secret = SHA1("c1b2a3d4e5f60718293a4b5c6d7e8f9012345678"
"/alice/secret-passphrase")
= d7a235115bf97d126c42140196c4fc6087312353
SIGNATURE = SHA1(NONCE-hex || shared-secret-hex)
= e3dba1c99442823b76ae64a8afca940e0327f9f8
Appendix B. ABNF
ABNF follows [RFC5234]. Prose enforces exact payload arithmetic.
; Section B.1 Core tokens
LF = %x0A
SP = %x20
ALPHA-LC = %x61-7A
DIGIT = %x30-39
HEXDIG-LC = DIGIT / %x61-66
HEXDIG-UC = DIGIT / %x41-46
HEXDIG-CI = DIGIT / %x41-46 / %x61-66
sha1-hash = 40HEXDIG-LC
sha3-hash = 64HEXDIG-LC
artifact-hash = sha1-hash / sha3-hash
decimal = 1*DIGIT
signed-decimal = [ "-" ] decimal
NZDIGIT = %x31-39
canonical-decimal = "0" / ( NZDIGIT *DIGIT )
fossil-token = 1*( ALPHA-LC / DIGIT / %x5F )
fossilized = 1*( %x21-FF ) ; non-SP fossilized octets;
; \s \t \n \r \f \v \0 \\ per
; [FossilArtifact] Section 4
config-name = "/" 1*( ALPHA-LC / DIGIT / "-" / "_" )
pragma-name = 1*( ALPHA-LC / DIGIT / "-" )
ci-lock-client-id = fossilized
ci-unlock-client-id = artifact-hash
non-lf-byte = %x00-09 / %x0B-FF
qchar = %x20-26 / %x28-7E / %x80-FF
fossil-quoted = %x27 *( qchar / %x27 %x27 ) %x27
timestamp = 4DIGIT "-" 2DIGIT "-" 2DIGIT %x54 2DIGIT ":" 2DIGIT ":" 2DIGIT
; 19-byte fixed UTC YYYY-MM-DDTHH:MM:SS
; per Section 5.3
uv-hash-field = artifact-hash / "-"
payload-octets = *OCTET
; Section B.2 Message-body structure
sync-message = 1*( sync-element )
Hipp, et al. Expires 30 January 2027 [Page 50]
Internet-Draft Fossil Sync Protocol July 2026
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 [ LF ]
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.
; Section B.3 Comment lines (Section 5.3)
comment-line = timestamp-comment / sending-clusters-comment /
end-of-clusters-comment / nonce-comment /
generic-comment
timestamp-comment = "# timestamp" SP timestamp SP "errors" SP canonical-decimal LF
sending-clusters-comment = "# sending-clusters" LF
end-of-clusters-comment = "# end-of-clusters" LF
nonce-comment = "#" SP 40HEXDIG-UC LF
generic-comment = "#" *non-lf-byte LF
; Section B.4 Transport and authentication (Sections 4 and 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
; Section B.5 Operation and announcement cards (Sections 3, 8, and 9)
push-card = "push" SP servercode SP projectcode LF
pull-card = "pull" SP servercode SP projectcode LF
servercode = fossil-token
projectcode = artifact-hash
Hipp, et al. Expires 30 January 2027 [Page 51]
Internet-Draft Fossil Sync Protocol July 2026
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
; Section B.6 Content-transfer cards (Section 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
; Section B.7 Configuration and pragma cards (Section 9)
reqconfig-card = "reqconfig" SP config-name LF
config-card = "config" SP config-name SP decimal LF
config-scalar-payload = decimal SP fossil-quoted SP "value" SP fossil-quoted
; payload form used by config /config
pragma-card = client-version-pragma / server-version-pragma /
uv-hash-pragma / uv-push-ok-pragma /
uv-pull-only-pragma / ci-lock-pragma /
ci-lock-fail-pragma / ci-unlock-pragma /
send-private-pragma / send-catalog-pragma /
client-url-pragma / req-links-pragma /
req-clusters-pragma / link-pragma /
avoid-delta-manifests-pragma / extension-pragma
; no-argument productions below are canonical sender
; forms; Section 9 defines correct-direction surplus tolerance
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 =
Hipp, et al. Expires 30 January 2027 [Page 52]
Internet-Draft Fossil Sync Protocol July 2026
"pragma" SP "uv-pull-only" LF
ci-lock-pragma = "pragma" SP "ci-lock" SP artifact-hash
SP ci-lock-client-id LF
ci-lock-fail-pragma =
"pragma" SP "ci-lock-fail" SP fossilized SP signed-decimal LF
; canonical senders use nonnegative decimal; receiver
; detects a negative value as its defined fatal error
ci-unlock-pragma = "pragma" SP "ci-unlock" SP ci-unlock-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
; client-to-server; request-local opt-in to
; `link` cards in this request's response; a
; client emits it in every request for which it
; wants links and has local `share-links` mode
req-clusters-pragma =
"pragma" SP "req-clusters" LF
link-pragma = "pragma" SP "link" SP link-url SP link-arg SP link-mtime LF
; server-to-client; response is permitted only
; when its request carried `pragma req-links`.
; Client-side acceptance requires local `share-links`
; mode, a parsed HTTP(S) URL, and positive MTIME; it
; does not depend on whether its request carried
; `req-links`. Otherwise silently discard every `link`
; and record no fields.
; This card carries no artifact-graph or
; content-relationship semantics.
link-url = fossilized ; fossilize-encoded http(s) URL naming
; an alternative location for the
; current repository
link-arg = fossilized ; fossilize-encoded server-generated
; opaque token; the receiver stores the
; defossilized bytes verbatim, MUST NOT
; parse them, and MUST NOT echo them
; onward on the wire
link-mtime = decimal ; Unix seconds since the epoch; the
; client stores only values greater than 0
avoid-delta-manifests-pragma =
"pragma" SP "avoid-delta-manifests" LF
extension-pragma = "pragma" SP pragma-name *( SP fossilized ) LF
; receivers inspect the name and silently ignore unknown
; extensions despite surplus tokens beyond the fixed vector
; Section B.8 Error signalling (Section 10)
Hipp, et al. Expires 30 January 2027 [Page 53]
Internet-Draft Fossil Sync Protocol July 2026
error-card = "error" SP fossilized LF
message-card = "message" SP fossilized LF
Appendix C. Anomalies
* "Unknown message" response: no such wire text exists. Server-side
unknown operators reset and emit error bad command: LINE, then
continue later request cards. Clients only warn locally, stop the
reply-card loop, and then evaluate ordinary continuation.
Section 5.2 governs.
* Cumulative multi-login permissions: withdrawn. A second login in
one request is fatal. One successful login unions capabilities
only with the request's baseline.
Authors' Addresses
D. Richard Hipp (editor)
Fossil
Stephan Beal
libfossil
Dan Shearer
Email: dan@shearer.org
URI: https://shearer.org
Hipp, et al. Expires 30 January 2027 [Page 54]