Skip to content
zit Artifact [6fdca05ae0]
Login

Artifact [6fdca05ae0]

Artifact 6fdca05ae05ae520e3d89212dc858b79f90644df83f095cd959d8b670daa1aa2:


Erratum — draft-fossil-artifact-format-00 §4.1.2

Document draft-fossil-artifact-format-00
Section 4.1.2 — Sterilization
Type Technical (three factual divergences from the reference implementation)
Status Draft — not yet applied
Reported 2026-08-08 (rejection mechanism corrected 2026-10-11)
Submitter Dan Mestas
Found via zit import-git mirror classifier, against a 36,383-commit SQLite Git mirror

1. Summary

§4.1.2 describes Fossil's sterilization marker — the comment line a writer adds to make content deliberately unparseable as a structural artifact. Three of its statements are wrong against the reference implementation: the marker's position, the gate that rejects the sterilized bytes, and the marker's literal text. The section is additionally self-contradictory: it says the marker is the first line, then says the signed form places it before the trailing signature block, which is after the cards.

The direction of the error is the dangerous one. An implementer following §4.1.2 as written builds a sterilizer that does not match Fossil's, and the mismatch is invisible until a sterilized artifact reaches another repository — where it surfaces as a foreign check-in rather than as a parse failure.

2. Location

§4.1.2 (Sterilization), the paragraph beginning "This subsection is informative." Current text:

To render content deliberately unparseable as a structural artifact, a writer inserts a comment line beginning with # (of the form # Remove this line to create a well-formed Fossil artifact.) as the first line, which causes the line-1 gate of Section 4.1 to fail. For PGP- or SSH-signed content, the marker line is instead spliced in immediately before the signature block…

3. The defect

Verified against Fossil 2.28 (52445a27f1) by source reading and by running the code.

3.1 Position — the marker is appended, never inserted first

sterilize_manifest() (src/manifest.c:2238) builds its output with blob_appendf. The marker lands after the final card, not before the first. Observed on a real checkout:

U tester
Z 633267631377a908d4e09def80ef9fd5
# Remove this line to create a well-formed Fossil manifest.

3.2 Failure mechanism — card ordering, not the line-1 gate

Because the marker is trailing, it never reaches the line-1 gate. Nor is it left over for §4.1's unconsumed-bytes rule: the card loop reads it as a line like any other, and its first byte, # (0x23), sorts below every card letter. §4.4's ordering rule rejects it there, as a card whose type sorts below the previous card's Z. Fossil 2.28 reports exactly this for a checked-out manifest:

$ fossil test-parse-manifest manifest
manifest_is_well_formed() reports the input contains errors
ERROR: line 7: cards not in lexicographical order

Line 7 is the marker; the same file with the marker removed parses. The order check runs first in manifest_parse()'s card loop (src/manifest.c:550), before the bad-card-type check (:563), so it is what fires. Gate 4 does not fire either: with the marker trailing, the final 35 bytes are not a Z card, so the checksum gate treats Z as absent and passes; the Z card is then read as an ordinary card, and the marker after it is out of order. A reader that implements §4.4 therefore rejects a sterilized artifact however it reads §4.1.2. The cost of naming the wrong gate falls on code that looks for the marker: a tool that strips or matches it at line 1 finds nothing to strip.

An earlier version of this erratum attributed the rejection to the unconsumed-bytes rule. That was wrong: the marker is consumed as a line and fails ordering. Corrected 2026-10-11 after go-libfossil's conformance suite compared the two against Fossil 2.28.

3.3 Literal text — the quoted string is never emitted

The format string (src/manifest.c:2242) is:

"# Remove this line to create a well-formed Fossil %s.\n"

%s is manifest for CFTYPE_MANIFEST and control artifact for every other type. The draft's …well-formed Fossil artifact. is emitted by no code path.

3.4 Internal contradiction

"as the first line" and "spliced in immediately before the signature block" cannot both hold: the signature block is trailing, so a marker before it is after the cards. The signed description is the accurate one, and it generalizes — the unsigned case is the same insertion point with an empty signature.

4. Impact

A parser is protected: §4.4's ordering rule rejects a sterilized artifact whatever §4.1.2 says (§3.2). The harm falls on code that writes or handles the marker.

A writer built to §4.1.2 diverges byte-for-byte from Fossil, so its output is not the artifact any Fossil-derived name refers to.

A tool that strips the marker misses it. Looking at line 1 for the draft's literal, it finds neither the position nor the text Fossil writes, and so cannot recover the artifact the sterilized file came from.

This is not theoretical, and the consumer-side cost is measurable. zit's Git import proves a mirror's manifest/manifest.uuid pair by stripping the sterilizer and checking the result against the name the mirror recorded. Written to the trailing-only reading of the implementation — which is correct for the unsigned case — it failed for every PGP-clearsigned check-in, because there the marker sits mid-blob, immediately before -----BEGIN PGP SIGNATURE-----. On a 36,383-commit SQLite mirror:

Proof outcome Count
Proven by trailing strip 35,307
Proven only once the marker is spliced from before the signature block 1,076
Unproven 0

All 1,076 date from 2009–2011, when SQLite's clearsign setting was on. Each had its mirror metadata retained permanently — the observable symptom of a correct implementation built against an incorrect description of where the marker goes.

5. Correction applied

Replace the first paragraph of §4.1.2 with text that states the position once, in the general (signature-aware) form, names the correct rejection mechanism, and quotes the two literals the implementation actually emits:

-   This subsection is informative.  To render content deliberately
-   unparseable as a structural artifact, a writer inserts a comment line
-   beginning with # (of the form # Remove this line to create a well-
-   formed Fossil artifact.) as the first line, which causes the line-1
-   gate of Section 4.1 to fail.  For PGP- or SSH-signed content, the
-   marker line is instead spliced in immediately before the signature
-   block, with the boundary recomputed by the same wrapper-detection
-   logic of Section 4.1.1, so the signature remains trailing.
+   This subsection is informative.  To render content deliberately
+   unparseable as a structural artifact, a writer appends a comment line
+   beginning with # after the final card, and before the signature block
+   when the content is PGP- or SSH-signed, so that a signature remains
+   trailing; the boundary is located by the same wrapper-detection logic
+   of Section 4.1.1.  The marker reads # Remove this line to create a
+   well-formed Fossil manifest. for a check-in and # Remove this line to
+   create a well-formed Fossil control artifact. for any other type.
+   Because the marker follows the cards, the line-1 gate never sees it.
+   It is rejected by the card-ordering rule of Section 4.4: the card
+   loop reads it as a line, and "#" sorts below every card letter,
+   including the "Z" before it.

6. Rationale

Note on the producer side

Fossil's own use of sterilize_manifest() narrowed in 722c248d53 (shipped 2.18, 2022-02-23), which removed it from tar.c and zip.c, so archive exports are no longer sterilized while checkouts and Git export still are. That is a producer-policy change and needs no draft text; it is recorded here because it makes §4.1.2's accuracy matter more, not less — consumers can no longer assume content arriving from a Fossil-derived tarball has been sterilized at all.


Prepared against the frozen final draft-fossil-artifact-format-00. The zit reference implementation already emits and strips the marker at the position described in §5; this erratum records the divergence so the draft can settle it.

7. Resolution verification