Skip to content
zit Erratum — draft-fossil-sync-protocol-00 §4 (redirect, single /xfer strip)
Login

Erratum — draft-fossil-sync-protocol-00 §4 (redirect, single /xfer strip)

Document draft-fossil-sync-protocol-00
Section 4 — Transport (redirect following)
Type Technical (correction — Fossil strips one trailing /xfer, not repeatedly)
Status Proposed — operator decision pending
Reported 2026-10-11
Submitter Dan Mestas
Found via go-libfossil's conformance suite against Fossil 2.28 (scope note SP-4-6, case sync.4.redirect-strips-xfer); confirmed by reading Fossil's source and by redirecting a Fossil client
Amends sync-protocol-00-s4-redirect-xfer-strip.md (2026-07-10), whose "repeated while such a segment remains" wording zit's draft carries

1. Summary

§4 says to "Repeatedly strip trailing /xfer from the redirect target". Fossil strips exactly one: a redirect to /b/xfer/xfer is followed to /b/xfer. The source has a while loop, but the loop cannot run twice, so the observable rule is a single strip.

The 10 July erratum that introduced the /xfer rule said the strip "is repeated while such a segment remains". It was right about /xfer and wrong about the repetition; zit's draft and the 29 July copy both carry that wording.

2. Location

§4, redirect paragraph (~lines 847-849):

Repeatedly strip trailing /xfer from the redirect target, then parse the result as the new base; apply no other path transformation.

3. The divergence

Source, Fossil 2.28 http_exchange() (src/http.c:633-637):

j = strlen(zLine) - 1;
while( j>4 && fossil_strcmp(&zLine[j-4],"/xfer")==0 ){
   j -= 4;
   zLine[j] = 0;
}

j starts on the last character, and the test compares the five characters ending at j. A match truncates at the /, which is j-4, but leaves j on the new terminator rather than the new last character. The next test then compares a four-character tail with the five-character "/xfer", which can never match. One strip, whatever the target.

Run: a scripted HTTP peer answered the first POST of fossil pull with 307 Location: http://127.0.0.1:PORT/b/xfer/xfer. Fossil printed redirect with status 307 to http://127.0.0.1:PORT/b/xfer and sent its second POST to /b/xfer.

4. Impact

Only targets ending in /xfer/xfer are affected, which in practice means a repository whose own URL ends in /xfer. For those, a client following the draft goes one level too far (/b) where Fossil goes to /b/xfer. Fossil's result is the one that keeps the repository path: only the endpoint suffix the server appended is removed.

5. Correction

-   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
+   or local-file transports.  Strip one trailing /xfer, if present, from
+   the redirect target, then parse the result as the new base; apply no
+   other path transformation.  For an accepted redirect, Fossil parses

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.