Skip to content
zit Artifact [4df3672267]
Login

Artifact [4df3672267]

Artifact 4df36722673b33fdc81fdbff1f41c230fe231318520c211cf53891220159c733:


Erratum — draft-fossil-sync-protocol-00 §6.6

Document draft-fossil-sync-protocol-00
Section 6.6 — Capability Model (server refusal of push without Write); cross-references §8.5, §10.1 and §10.2
Type Technical (the server's refusal form changes: a message card, not an error card, when a pull is present)
Status Applied in draft-fossil-sync-protocol-01 (2026-10-12)
Reported 2026-10-11
Submitter Dan Mestas
Found via Reading §10.2 against the server side; reproduced on fossil 2.28 [52445a27f1] with fossil http and an application/x-fossil-debug request

1. Summary

§10.2 tells a client what to do when it receives message pull only *: turn push off for the session and hide the message. Nothing in the draft tells a server to send it. §6.6 says instead that "the server checks each card's required capability and emits error when lacking", and §10.1 lists "authorization" among the reset sites. A server built from the draft answers a push from a principal without Write with an error card.

Fossil 2.28 does that only when no pull card preceded the push in the request. When one did, it emits the nonfatal message pull\sonly\s-\snot\sauthorized\sto\spush, does not reset the response, and serves the pull. Fossil's own client sends pull before push in every round, so a stock fossil sync by a read-only user always gets the message, never the error. In neither form does the push refusal end card processing.

2. Location

3. The divergence

Fossil 2.28, src/xfer.c:1503-1524, the push/pull handler after the project-code check:

if( blob_eq(&xfer.aToken[0], "pull") ){
  if( !g.perm.Read ){ cgi_reset_content(); @ error not\sauthorized\sto\sread...
                      nErr++; break; }
  isPull = 1;
}else{
  if( !g.perm.Write ){
    if( !isPull ){
      cgi_reset_content();
      @ error not\sauthorized\sto\swrite%s(whyNotAuth())
      nErr++;
    }else{
      @ message pull\sonly\s-\snot\sauthorized\sto\spush%s(whyNotAuth())
    }
  }else{
    isPush = 1;
  }
}

Neither push branch executes break: card processing continues, and isPush stays clear, so later file/cfile cards are refused individually (xfer.c:1361-1363, 1382-1384). The client emits pull before push (xfer.c:2475-2480). whyNotAuth() appends a suffix only in specific configurations; it was empty in these tests.

Observed with fossil http, nobody holding o only:

Request operation cards Reply
pull, push message pull\sonly\s-\snot\sauthorized\sto\spush, the igot inventory, # timestamp ... errors 0
push, pull error not\sauthorized\sto\swrite, the igot inventory, # timestamp ... errors 1
push error not\sauthorized\sto\swrite, # timestamp ... errors 1

The second row shows that the error form also does not end card processing: the pull that follows it still produces the inventory.

4. Impact

Wire-visible. A server that answers a read-only user's sync with an error card makes a stock Fossil client stop its reply loop on every round (§10.1) and count an error, where against Fossil the same client quietly switches to pull-only and completes. (The client-side effect is taken from the draft's §10.1 and §10.2; only the server replies were reproduced.) Post-commit autopush hides part of this through the %not authorized to write% exception (§7.6), but fossil sync and fossil update with autosync do not. A server that treats the error as ending card processing also drops the pull's inventory when push precedes pull.

5. Correction

In §6.6, after the sentence ending "specifies silent behavior.", add:

+   A push card from a principal without Write is refused in one of two
+   forms, and neither ends card processing.  If a pull card was already
+   accepted earlier in the same request, the server MUST NOT reset
+   accumulated output and MUST emit the nonfatal message
+   "pull only - not authorized to push" (fossilized:
+   message pull\sonly\s-\snot\sauthorized\sto\spush), which a client
+   handles as Section 10.2 states.  Otherwise the server resets
+   accumulated output and emits error not authorized to write.  In both
+   forms push is not enabled for the request, so later file and cfile
+   cards are refused individually, and later cards, including a pull,
+   are still processed.  A client that sends both cards SHOULD send
+   pull before push, as the reference client does, so that a refused
+   push yields the message form.

In §10.1, amend the reset-site sentence:

-   At wrong-project, authorization, duplicate-login, unknown-card, and
-   other fatal reset sites, server discards partial output before error;
+   At wrong-project, authorization, duplicate-login, unknown-card, and
+   other reset sites, server discards partial output before error (the
+   push refusal of Section 6.6 has a non-reset message form, and both
+   push-refusal forms and the unknown-card error continue card
+   processing);

6. Rationale

7. Verification


Prepared against draft-fossil-sync-protocol-00. The RFC remains the oracle; the reference implementation is used only to confirm the observable behavior.