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:

```c
"# 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:

```diff
-   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

- **One statement of position.** The contradiction exists because position is
  asserted twice. Stating it once, in the form that covers both cases, removes
  it.
- **The mechanism is load-bearing.** Naming the wrong gate sends an
  implementer to check the wrong place, on exactly the input sterilization
  targets. This is the most consequential of the three defects. The rule that
  does the work, §4.4's ordering, is already normative; the correction only
  points to it.
- **The literals are quoted because they are compared.** Consumers match the
  marker to strip it; a consumer matching the draft's string matches nothing
  Fossil writes.
- **Informative, not normative.** §4.1.2 does not impose requirements — the
  correction changes only what the section describes, not what any reader or
  writer is required to do.

### 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

- All three defects verified twice: by reading Fossil 2.28 source
  (`src/manifest.c:2238`, `:2242`) and by observing a live checkout's `manifest`
  file and a published SQLite mirror's blobs.
- The rejection mechanism verified by running Fossil 2.28's own parser
  (`fossil test-parse-manifest`) on a checked-out manifest: it fails at the
  marker's line with "cards not in lexicographical order", and the same bytes
  without the marker parse. The order check is the first test in the card loop
  (`src/manifest.c:550`).
- The 1,076/35,307/0 split is reproducible: export `sqlite.fossil` with
  `fossil git export`, then for each commit compare `manifest` stripped both
  ways against `manifest.uuid`.
- Clean-room effect: an implementer no longer has to discover, from a foreign
  check-in appearing in an unrelated repository, that the marker is trailing
  rather than leading and that the text varies by artifact type.