Artifact d5e1a04ed5d92d2415e9a8c5159bcc6f87d3d68f5d27f01aa14f1cb601ba2850:
- File docs/rfcs/erratum/sync-protocol-00-s6.4-login-failure-end-of-request.md — part of check-in [275e884be5] at 2026-10-11 21:47:48 on branch trunk — errata: sync-protocol behaviours where Fossil departs from the draft (user: dmestas, size: 6550) [annotate] [blame] [check-ins using]
Erratum — draft-fossil-sync-protocol-00 §6.4
| Document | draft-fossil-sync-protocol-00 |
| Section | 6.4 — Signature Computation (verification failure outcome); cross-references §3.2 and §10.1 |
| Type | Technical (the reply after a failed login is under-specified: end-of-request output is omitted) |
| Status | Proposed — operator decision pending |
| Reported | 2026-10-11 |
| Submitter | Dan Mestas |
| Found via | go-libfossil's sync conformance suite (case sync.6.4.failure-shape); reproduced on fossil 2.28 [52445a27f1] with fossil http and an application/x-fossil-debug request |
1. Summary
§6.4 ends with "Either failure keeps HTTP 200, discards staged output, emits
error login failed, records failure, and ends card processing." That is
true of the card loop, but a reader takes "ends card processing" as the end
of the reply. In Fossil 2.28 it is not. The server leaves the card loop and
then runs its end-of-request steps for whatever direction earlier cards
established: phantom gimme requests if a push was accepted, the igot
inventory if a pull was accepted, and in every case the closing
# timestamp ... errors N comment. The draft says none of this for the
login failure, or for any other site that ends the card loop.
In the canonical order (login card first, §3.2) no direction has been
established when the login fails, so the reply is only the error and the
timestamp comment. The extra output appears only when push or pull
precedes the login card, which the server accepts.
2. Location
- §6.4, last paragraph of the verification order (~line 1152-1153): "Either failure keeps HTTP 200, discards staged output, emits error login failed, records failure, and ends card processing."
- §6.4 baseline paragraph (~line 1125-1130): "failed login leaves baseline untouched but aborts."
- For comparison, unchanged: §3.2 (~line 401-403), "Ordinary completed replies ... end with the timestamp/error-count comment. Pre-dispatch and fail-fast server replies need not contain that comment."
3. The divergence
Fossil 2.28, src/xfer.c:
xfer.c:1603-1609: on a failedcheck_tail_hash()orcheck_login(),cgi_reset_content(), then@ error login\sfailed,nErr++,break. Thebreakleaves thewhile( blob_line(...) )card loop; it does not return frompage_xfer().xfer.c:1898-1906: after the loop,if( isPush )runs the push script andrequest_phantoms(&xfer, 500).xfer.c:1910-1925:if( isClone && nGimme==0 ) send_all(...), elseif( isPull ) { create_cluster(); send_unclustered(...); ... }.xfer.c:1968-1969:@ # timestamp %s(zNow) errors %d(nErr), thendb_commit_transaction(). The request transaction commits; nothing is rolled back.
The same break and the same end-of-request steps follow every other site
that ends the card loop (error wrong\sproject at xfer.c:1499,
error not\sauthorized\sto\sread at xfer.c:1507, the duplicate-login
error).
Observed with fossil http on a repository where nobody holds o:
request: pragma client-version 22800 20260311 113146
pull x PROJECTCODE
gimme HASH
login alice NONCE SIGNATURE (wrong signature)
# rnd 1
reply: error login\sfailed
igot 90c57917...
igot a57d4ad9...
# timestamp 2026-10-11T21:29:18 errors 1
The staged file for the gimme is discarded by the reset, as §6.4 says;
the inventory the pull asked for is still sent. With push and pull
before the failing login and nobody holding oi, an igot of an unknown
hash also produces a phantom on the server and a gimme for it after the
error. With the login card first, the reply is only error login\sfailed
and the timestamp comment.
4. Impact
Wire-visible, server side only. A server that stops the reply at the error
sends less than Fossil does when push or pull precedes the login card:
no inventory, no phantom requests, and possibly no timestamp comment, which
§3.2 expects of an ordinary completed reply (a login failure is not a
fail-fast path; nothing is rolled back). Clients are not affected: a client
stops its reply-card loop at the first error card (§10.1), so the trailing
cards are not acted on. Stock Fossil clients put the login card first and
never see the extra cards.
5. Correction
In §6.4, replace the last sentence of the verification order:
- Either failure keeps HTTP 200, discards staged output, emits error
- login failed, records failure, and ends card processing.
+ Either failure keeps HTTP 200, discards staged output, emits error
+ login failed, records failure, and ends card processing: later
+ request cards are not parsed. The request is not rolled back, and
+ the server still performs the end-of-request steps for any direction
+ an earlier card established (phantom gimme for push, igot inventory
+ for pull) and ends the reply with the timestamp/error-count comment
+ (Section 3.2). When the login card is first, as Section 3.2
+ requires of a client, no direction has been established and the
+ reply is the error and that comment alone.
In the baseline paragraph, replace "but aborts" with "but ends card processing as described below".
6. Rationale
- "Ends card processing" stays true. The correction only says what the phrase does not cover, which is the part a server implementer needs.
- Consistent with §3.2. A login failure is not a fail-fast path, so the reply keeps the timestamp comment §3.2 assigns to ordinary replies.
- No new client obligation. Clients already stop at the first
error; the correction describes server output only.
7. Verification
- Fossil 2.28 [52445a27f1], macOS arm64,
fossil http REPOwith anapplication/x-fossil-debugPOST to/xfer, three request shapes: pull then failing login (error, inventory, timestamp); push, pull, unknownigot, failing login (error,gimme, inventory, timestamp; the phantom row is committed); failing login first (error, timestamp). - Source references are to
src/xfer.cat tagversion-2.28. - go-libfossil conformance case
sync.6.4.failure-shapeasserts the error and the absence of the stagedfile; its comment records that Fossil still sends the inventory.
Prepared against draft-fossil-sync-protocol-00. The RFC remains the oracle; the reference implementation is used only to confirm the observable behavior.