Fahrtenbuch selbst prüfen: die Prüfkette nachrechnen
Ein Fahrtenbuch ist ein Aufbewahrungsdokument. Es kann Jahre nach der letzten Fahrt gebraucht werden, und dann stellt sich eine einzige Frage: Ist das noch derselbe Stand, oder hat jemand nachträglich etwas geändert?
Viele Apps beantworten diese Frage mit einem Siegel — einem Wort auf der Produktseite, das man glauben muss. Diese Seite beantwortet sie anders: Sie beschreibt das Verfahren so genau, dass du es auf deinem eigenen Rechner nachrechnen kannst, ohne uns und ohne Internetverbindung. Ein Prüfweg, der vom Hersteller abhängt, prüft den Hersteller nicht mit.
Sie ist zugleich die Seite, auf die der Prüfsummen-Abschnitt eines exportierten Fahrtenbuchs verweist.
Was die Kette leistet — und was sie nicht leistet
Jede Zeile im Änderungsprotokoll trägt eine Prüfsumme, die den eigenen Inhalt und die Prüfsumme der Vorgängerzeile abdeckt. Ein einziges geändertes Zeichen bricht deshalb nicht nur seine Zeile, sondern jede Zeile danach.
Was das belegt: Keine einzelne Aufzeichnung wurde nachträglich verändert, eingefügt oder herausgenommen, ohne dass es sichtbar wird.
Was es nicht belegt: dass niemand die Kette als Ganzes neu berechnet hat. Die Kette kommt ohne Schlüssel aus — wer die Software kennt, kann sie vollständig nachbauen. Das ist keine Schlamperei, sondern die Lage bei lokaler Speicherung: Ein Schlüssel, der auf demselben Gerät liegt wie die Daten, sichert nichts, was das Gerät nicht ohnehin hat.
Für den steuerlichen Zweck reicht das: Verlangt wird, dass Änderungen dokumentiert sind — verworfen wird die unsichtbare Änderung.
Die Grenze, die du kennen musst
Es gibt eine Manipulation, die diese Prüfung nicht findet, und sie steht hier oben und nicht im Kleingedruckten.
Werden Zeilen am Ende der Kette entfernt, fällt das beim Nachrechnen nicht auf. Was übrig bleibt, beginnt weiterhin bei 1, ist lückenlos und ergibt an jeder Stelle die richtige Prüfsumme — eine gekürzte Kette ist eine gültige kürzere Kette. Das wiegt schwerer, als es klingt, denn am Ende stehen die neuesten Einträge und Korrekturen.
Der Grund ist grundsätzlich und lässt sich nicht wegprogrammieren: Eine gekürzte Kette ist kein Fälschungsprodukt, sondern ein echter Ausschnitt — Zeichen für Zeichen der Zustand, den die Kette zu einem früheren Zeitpunkt wirklich hatte. Auch eine Signatur hilft dagegen nicht, denn was damals unterschrieben wurde, war damals wahr (siehe unten).
Zwei Dinge engen die Lücke ein:
- Der Export nennt seine eigene Länge und die Prüfsumme der letzten Zeile. Wer kürzt, braucht damit einen zweiten Eingriff, um unauffällig zu bleiben. Das ist schwaches Zeugnis — die Angabe steht in derselben Datei —, aber es macht aus einem stillen Weglassen eine bewusste Tat und fängt den Versehensfall ganz.
- Ein Beleg außerhalb des Geräts wirkt wirklich. Jeder Bericht, den du einreichst, nennt seine Kettenposition. Wer später etwas vorlegt, das kürzer ist als ein früher eingereichter Bericht, widerspricht einem Dokument, das jemand anderes bereits in der Hand hat.
Die Zahl auf dem Bericht ist die Position in der Kette. Wer sie mit einem späteren Auszug vergleicht, sieht, ob etwas fehlt.
Beide Einschränkungen dazu, unverkürzt: Der Anker wirkt erst ab dem ersten eingereichten Bericht, und er wirkt nur, wenn jemand die beiden Zahlen vergleicht. Vorher und ohne Vergleich bleibt das Kürzen des Endes unentdeckbar.
Die Signatur — was sie sagt und was nicht
Die Spitze der Kette kann vom Gerät signiert werden: ECDSA über einen Schlüssel im hardwaregestützten Schlüsselspeicher des Telefons. Signatur, öffentlicher Schlüssel und Verfahrensname reisen mit dem Export, die Prüfung braucht also nichts von uns.
Signaturen sind Kontrollpunkte: Eine Signatur über die Spitze deckt alles davor mit ab, weil die Spitze jede Vorgängerzeile bindet. Dass eine Signatur fehlt, ist ein gültiger Zustand und kein Defekt — der Schlüssel kann das Gerät nicht verlassen und übersteht keinen Gerätewechsel. Nach einer Wiederherstellung kommt die Kette also unbeschädigt und ab dieser Stelle unsigniert an; ältere Signaturen bleiben gegen den Schlüssel prüfbar, mit dem sie entstanden sind.
Zwei Grenzen, ausdrücklich:
- Ohne Schlüsselattestierung belegt die Signatur den Besitz eines Schlüssels, nicht, dass dieser in manipulationsresistenter Hardware liegt. Die Attestierung würde das schließen, bräuchte dafür aber beim Prüfer eine Zertifikatskette aus dem Netz — und einem Leser ohne Internet „nicht prüfbar" zu melden, wäre eine schlechtere Antwort als die ehrliche Grenze.
- Eine Signatur allein sagt nichts darüber, ob du das Ganze siehst. Weil ältere Kontrollpunkte aufbewahrt werden und nur die Spitze signiert wird, findet eine am Ende gekürzte Kette eine gültige Signatur vor. Deshalb ist die brauchbare Angabe nicht „gerätesigniert bis ⟨Datum⟩", sondern „gerätesigniert bis Position ⟨Zahl⟩": Eine Position kannst du gegen einen früheren Bericht halten, ein Datum nicht.
Welche Fassung des Formats du vor dir hast
Der Export nennt zwei Nummern, und sie beantworten verschiedene Fragen:
| Feld | sagt |
|---|---|
format_version | wie die Datei aufgebaut ist — welche Blöcke es gibt und wie sie heißen (zum Lesen) |
chain_format_version | wie eine gehashte Zeile aufgebaut ist — Feldreihenfolge, Schreibweisen, welche Felder es gibt (zum Nachrechnen) |
Diese Seite beschreibt Kettenform 5. Ein Export, der gar keine Nummer nennt, ist Form 1 — die gab es nur vor der Veröffentlichung.
Für das Nachrechnen der Zeilen macht das nichts aus: Form 1 bis 5 rechnen sich gleich. Was sich zwischen ihnen geändert hat, sind die Datensätze, die eine Zeile mitführt — aber die Kette hasht diese Nutzlast als Text, ihr innerer Aufbau erreicht die Rechnung nie. Form 2 hat zwei bestehenden Satzarten je ein Feld gegeben (category_source in der Fahrt, full_tank im Kilometerstand-Referenzpunkt), Form 3 hat zwei neue Satzarten hinzugefügt (origin und restore, unten erklärt), Form 4 hat der Fahrt ein weiteres Feld gegeben (distance_source, unten erklärt), Form 5 dem Kilometerstand-Referenzpunkt (reading_source, unten erklärt). Eine wirklich neue Zeilenform entstünde erst, wenn sich die chg-Zeile selbst ändert, und das ist bisher einmal passiert. Auch ein neues Wort für die Ereignisart (RECONCILE, unten erklärt) ist keine neue Form: Die Werkzeuge prüfen Zeilen, kein Vokabular.
Ein Unterschied, der nur beim Wiederaufbau zählt: Drei Formen haben einen bestehenden Datensatz geändert, statt ein Feld danebenzustellen — Form 2, Form 4 und Form 5. Form 3 ist die einzige, die nur neue Satzarten daneben gestellt hat. Für die Datei, die du in der Hand hältst, ändert das nichts — sie trägt ihren Text mit sich. Für eine Kette, die aus den Datensätzen neu aufgebaut wird, schon: Dabei entsteht ein anderer Text und damit ein anderer Hash. Das gilt für alle drei gleich: unter keiner von ihnen reproduziert ein Hash den Wert der Vorgängerform. Was dann passiert, steht unten unter „Wenn der Bestand aus einem Abzug stammt".
Und die beiden Werkzeuge lesen jede Zeilenform, die es je gab. Eine aktuelle Fassung prüft also eine Datei jeden Alters; niemand muss sich ein altes Werkzeug besorgen. Die Reihenfolge, in der sie entscheiden, trennt dabei Prüfen von Raten:
- Die Datei nennt eine Form, die das Werkzeug kennt → diese Form gilt. Scheitert die Kette gegen sie, ist die Kette gebrochen. Es wird dann nicht weiterprobiert, bis irgendeine Form passt — das würde die Nummer zur Zierde machen.
- Die Datei nennt keine Form → die bekannten werden durchprobiert, und das Ergebnis sagt, welche gepasst hat.
- Die Datei nennt eine neuere Form, als das Werkzeug kennt → es sagt das und prüft trotzdem. Meist gelingt es, weil geänderte Datensätze die Zeile ja nicht berühren; das Ergebnis weist dann darauf hin, dass die Nutzlasten anders aufgebaut sein können, als das Werkzeug sie liest.
Was in keinem dieser Fälle passiert: „Kette gebrochen" als Auskunft über ein Problem des Werkzeugs. Genau dafür gibt es die Nummer. Ohne sie würde ein Werkzeug, das eine unbekannte Zeilenform einfach durchrechnet, bei jeder Zeile einen anderen Hash bekommen und einen Befund über deine Daten melden — ausgerechnet in dem Moment, in dem du die Datei jemandem vorlegst.
So ist eine Zeile aufgebaut
Damit eine Prüfsumme reproduzierbar ist, muss jede Zeile genau eine Textform haben, die gehasht wird. Zuerst ein Kürzel, dann die Felder in fester Reihenfolge, verbunden durch |:
chg|1|1755691500000|6:CREATE|4:TRIP|17|∅|10:trip|17|42|∅
Die Feldreihenfolge ist Teil des Formats. Ein Feld hinzuzufügen, zu entfernen oder zu verschieben ändert jede jemals geschriebene Prüfsumme.
Wie die einzelnen Werte geschrieben werden
| Art | Schreibweise | Beispiel |
|---|---|---|
| Ganzzahl | dezimal, ohne Zusatz | 17 |
| Zeitpunkt | Millisekunden seit 1970, dezimal | 1755691500000 |
| Wahrheitswert | 1 / 0 | 0 |
| Aufzählungswert | über den Namen, mit Längenangabe | 8:BUSINESS |
| Text | mit Längenangabe in UTF-8-Bytes | 9:Meier AG |
| Datei | SHA-256 der Bytes in Kleinbuchstaben, mit Längenangabe | 64:0390…fb81 |
| kein Wert | ∅ (U+2205, UTF-8 E2 88 85) | ∅ |
Vier dieser Festlegungen sind Entscheidungen und keine Zufälle:
- Längenangaben statt Maskierung.
9:Meier AGheißt „nimm neun Bytes" — ein|mitten in einem Freitext kann also keine Feldgrenze vortäuschen. Maskierung müsste jeden Sonderfall treffen, und ein übersehener Fall fällt erst auf, wenn Jahre später eine Prüfsumme nicht mehr stimmt. - Eine leere Zeichenkette ist
0:, nicht∅. Das sind verschiedene Werte und müssen verschieden hashen. - Millisekunden statt ISO-Zeitstempel.
…T14:05:00Zund…T14:05:00.000+00:00meinen denselben Augenblick und würden verschieden hashen. Eine Zahl hat nur eine Schreibweise. - Aufzählungswerte über den Namen, nie über eine Nummer. Eine Nummer ist eine Position in einer Quelldatei; ein Name übersteht es, wenn jemand einen Wert dazwischenschiebt.
In einer geketteten Zeile kommt keine Kommazahl vor. Fließkommazahlen haben keine Schreibweise, die einen Bibliothekswechsel unbeschadet übersteht. Deshalb ist jede Größe eine Ganzzahl einer kleinen Einheit: Strecken in Millimetern, Koordinaten in Zehnmillionstel Grad, Kraftstoff in Millilitern, Beträge in Cent plus Währungskürzel.
Der Kettenschritt
Gehasht werden ausschließlich die Zeilen des Änderungsprotokolls — für die Prüfung wird keine weitere Tabelle gebraucht. Jede dieser Zeilen hat die Form:
chg | lfd. Nummer | Zeitpunkt | Ereignisart | Objektart | Objekt-Nummer
| Stand vorher | Stand nachher | Grund
„Stand vorher" und „Stand nachher" tragen die Textform des Datensatzes selbst, mit Längenangabe wie jeder andere Text. Beim Anlegen ist „vorher" leer, beim Verwerfen „nachher". Der Grund ist meist leer und beim Verwerfen einer Fahrt Pflicht.
Die Prüfsumme der Vorgängerzeile ist kein Feld dieser Zeile. Sie geht über den Hash-Schritt ein — stünde sie zusätzlich im Text, wäre der Vorgänger doppelt gebunden und die beiden Kopien könnten auseinanderlaufen:
Prüfsumme(Zeile) = SHA-256( Prüfsumme_Vorgänger + "|" + Textform(Zeile) )
Ergebnis als Hexadezimalzahl in Kleinbuchstaben. Für die erste Zeile ist die Prüfsumme des Vorgängers die leere Zeichenkette.
Nachrechnen in drei Schritten
Lies die Zeilen nach laufender Nummer sortiert und prüfe je Zeile:
- Die laufende Nummer ist Vorgänger + 1, beginnend bei 1 — keine Sprünge. Ein Sprung heißt, dass eine Zeile entfernt oder eingefügt wurde.
- Die hinterlegte Vorgänger-Prüfsumme stimmt mit der Prüfsumme der vorigen Zeile überein.
- Neu berechnet ergibt sich genau die gespeicherte Prüfsumme.
Melde den ersten Fehler, nicht alle. Nach einer geänderten Zeile scheitert jede folgende zwangsläufig — eine Zählung würde „alles ab Zeile 12 ist falsch" sagen, während die ehrliche Aussage „Zeile 12 ist falsch" lautet.
Ereignis- und Objektarten
Ereignisarten sind CREATE · CLASSIFY · CORRECT · MERGE_REFERENCE · RECONCILE · SPLIT · EXPORT · RESTORE · RECOMPUTED · HANDOVER · VOID.
RECONCILE (seit 13.09.2026) ist das eine Ereignis, bei dem „Stand vorher" und „Stand nachher" denselben Kilometerstand-Referenzpunkt zeigen: Der abgelesene Stand wurde gegen die Summe des Buches gehalten, und die beiden Felder computed_reading_mm und delta_mm sind von ∅ zu einem Wert geworden. Sonst ändert sich in der Zeile nichts, und es passiert je Referenzpunkt höchstens einmal. Ein Export von vor diesem Datum trägt das Paar schon auf der CREATE-Zeile und kennt kein RECONCILE — beide Lesarten sind richtig.
Objektarten sind TRIP (Fahrt) · STAY (Aufenthalt) · VEHICLE (Fahrzeug) · ODOMETER_REFERENCE (Kilometerstand-Referenzpunkt) · GAP (Lücke).
Die Felder der einzelnen Datensätze
Diese Reihenfolgen brauchst du nur, um „Stand vorher" und „Stand nachher" zu lesen; die Unversehrtheit der Kette hängt nicht an ihnen. Schreibweisen wie oben.
trip id · started_at · ended_at · start_lat_e7 · start_lon_e7 · end_lat_e7
· end_lon_e7 · start_place_label · end_place_label · destination_source
· odometer_start_mm · odometer_end_mm · measured_distance_mm
· distance_source · category · category_source · transport_mode
· business_partner · purpose
· detour_reason · vehicle_id · not_my_vehicle · polyline · receipt_hash
· superseded_by · derived_from · chain_seq
stay id · arrived_at · departed_at · lat_e7 · lon_e7 · place_label · customer
· chain_seq
vehicle id · display_name · opened_at · opening_reading_mm
· opening_reading_source · closed_at · closing_reading_mm
· is_premium_parallel
vehid id · vehicle_id · type · value · display_name · confirmed_at
· first_seen_at · chain_seq
odoref id · vehicle_id · recorded_at · kind · entered_reading_mm
· reading_source · computed_reading_mm · delta_mm · fuel_millilitres
· full_tank · amount_minor · amount_currency · superseded_by · note
· chain_seq
gap id · started_at · ended_at · reason · distance_mm · resolved_as · note
· chain_seq
export id · created_at · period_start · period_end · app_version · algo_version
· lines · chain_seq
origin previous_head · previous_length · previous_format
restore previous_head · previous_length · previous_format · signature_state
· key_fingerprint
Die letzten beiden sind keine Datensätze, sondern die Eröffnungszeile einer Kette, die eine andere abgelöst hat — mehr dazu im nächsten Abschnitt.
Sieben Stellen lohnen den zweiten Blick, weil dort die Ehrlichkeit des Buches sitzt:
- Ein Aufenthalt darf eine unbekannte Ankunft haben.
arrived_atist∅, wenn der Aufenthalt schon lief, bevor die Aufzeichnung begann — beim allerersten im Buch oder nach einer Lücke. Die App schreibt dann keinen Wert statt des Zeitpunkts, an dem die Daten zufällig anfangen; eine daraus gerechnete Dauer wäre erfunden, und „Zeit vor Ort" bleibt für so eine Zeile leer.departed_atdagegen ist nie leer: Ein Aufenthalt ohne Abfahrt ist noch offen, und ein offener steht gar nicht erst in der Kette. Er kommt hinein, wenn er geschlossen wird. - Ein Kilometerstand sagt, woher seine Zahl kommt.
reading_source(seit 26.09.2026) istENTERED, wenn ein Mensch den Tacho abgelesen und die Zahl getippt hat,DOCUMENT, wenn sie von einem Beleg oder anderem Papier abgeschrieben ist,DERIVED, wenn sie zurückgerechnet wurde, und∅für eine Zeile, die geschrieben wurde, bevor es das Feld gab. Woran das hängt, ist ein Haken im Ablesedialog („Vom Beleg abgeschrieben"): ohne HakenENTERED, mit HakenDOCUMENT.DERIVEDschreibt die App bis heute nicht — der Wert steht im Format für den Tag, an dem eine Zahl aus einer anderen Zahl entsteht, und ein Werkzeug muss ihn deshalb schon jetzt lesen können. Der Unterschied ist keine Buchhaltung, sondern der Unterschied zwischen prüfbar und nicht prüfbar: Eine Zahl vom Beleg kannst du später gegen diesen Beleg halten, eine an der Zapfsäule getippte nicht.∅heißt deshalb „nicht erfasst" und nie „vom Tacho abgelesen" — ein Wiederaufbau aus einem älteren Abzug lässt das Feld leer, statt eine Herkunft zu erfinden. Dieselbe Regel wie beidistance_source. - Ein Kilometerstand-Referenzpunkt speichert zwei Werte.
entered_reading_mmist, was du am Tacho abgelesen hast,computed_reading_mmist, was die laufende Summe der App zu diesem Zeitpunkt sagte, unddelta_mmist die Differenz. Beide zu behalten ist der Grund, warum die Abweichung überhaupt nachprüfbar bleibt — mit nur einem Wert wäre sie in dem Moment verschwunden, in dem die Fortschreibung neu aufsetzt. Beide dürfen∅sein (seit 13.09.2026): Dann ist der Stand getippt, aber noch nicht gegen die Summe gehalten, weil die Fahrten davor noch nicht gebucht sind. Das Paar kommt mit einerRECONCILE-Zeile nach; bis dahin behauptet die Ablesung keine Abweichung, und keine Lückenzeile gehört zu ihr. - Die beiden Kilometerstände einer Fahrt sind abgeleitet, nie getippt. Von Hand eingegeben werden ausschließlich Referenzpunkte. Ein Fahrt-Kilometerstand kann deshalb gar nicht nachträglich geändert worden sein — es gibt keinen Eingabeweg dafür.
distance_sourcesagt, woher die Kilometer einer Fahrt kommen —MEASUREDentlang der aufgezeichneten Punkte,ODOMETER_GAPaus der Differenz zwischen einem abgelesenen Kilometerstand und der laufenden Summe,ENTEREDvom Fahrer getippt,∅für eine Zeile, die es nie gesagt hat. Die mittlere ist die genaueste der drei: Sie kommt vom Zähler des Fahrzeugs, während unsere Messung Kurven abschneidet und Empfang verliert. Das Feld gibt es, weil ein Fahrtenbuch aufzeichnen darf und nicht rekonstruieren soll: Sobald für eine nie aufgezeichnete Strecke eine Zahl eingetragen werden kann, würdemeasured_distance_mmallein eine Messung behaupten, die nicht stattgefunden hat — und ein Leser könnte die beiden Fälle nicht auseinanderhalten.ENTEREDundODOMETER_GAPkönnen überhaupt erst entstehen, wenn sich eine Fahrt nachtragen lässt; im Format stehen sie schon, weil ein später eingefügtes Feld jede vorhandene Prüfsumme ungültig machen würde.∅findest du an Zeilen, die aus einem älteren Abzug wieder aufgebaut wurden — sie haben nie gesagt, woher ihre Kilometer kamen, und der Wiederaufbau dichtet ihnen nichts an.category_sourcesagt, wer die Kategorie entschieden hat —UNSET,INHERITED,RULEoderUSER_CONFIRMED. Die App darf eine Kategorie vorschlagen, und nach Ablauf der Nachtragsfrist bleibt ein unbeantworteter Vorschlag stehen. Festzuhalten, woher er kam, ist das, was eine Vermutung davon abhält, wie eine Bestätigung auszusehen: NurUSER_CONFIRMEDist die Aussage eines Menschen.full_tankhat drei Werte, und der dritte trägt. Liter zwischen zwei Tankstopps ergeben nur dann einen Verbrauch, wenn beide Male voll getankt wurde.∅heißt „niemand hat es gesagt" — und das darf weder gleich hashen noch gleich gelesen werden wie ein ausdrückliches „nicht voll", sonst rechnet eine Auswertung eine Zahl aus einem Paar, das sie überspringen müsste.
Wenn der Bestand aus einem Abzug stammt
Eine Kette lässt sich nicht über eine Änderung der kanonischen Form hinweg fortführen — die alten Prüfsummen reproduzieren sich dann nicht mehr. Sie wird stattdessen aus den exportierten Datensätzen neu aufgebaut, und die erste Zeile der neuen Kette sagt, was passiert ist. Dafür gibt es die zwei Zeilen von oben:
origin— die Datensätze sind die des Geräts selbst, nach einer Formatänderung neu geschrieben. Das Gerät hat sie erfasst und die Kette geprüft, aus der sie stammen.restore— die Datensätze kamen aus einem Export, den dieses Gerät nicht geschrieben hat: Gerätewechsel oder Wiederherstellung.
Welcher Fall vorliegt, sagt der Mensch, der die Datei einspielt — die App rät es nicht.
Warum die Unterscheidung überhaupt nötig ist, ist der wichtigste Satz auf dieser Seite: Wer eine Exportdatei vor dem Einspielen bearbeitet, bekommt anschließend trotzdem eine fehlerfreie Kette. Eine Kette belegt innere Stimmigkeit, nie Herkunft. Deshalb behauptet die restore-Zeile nichts, sondern hält einen Befund fest: was von der Gerätesignatur des abgebenden Geräts übrig ist.
Der Satz gilt nicht nur für den Fall, an den man zuerst denkt. Eine unvollständige Datei hat dieselbe Wirkung wie eine bearbeitete — und sie entsteht ohne böse Absicht: Wenn ein Feld beim Ausgeben fehlt, wird die Kette beim Einspielen sauber über die Daten aufgebaut, die da sind. Sie ist dann fehlerfrei und beschreibt trotzdem etwas anderes als das ursprüngliche Buch. Deshalb prüft verify_chain die Verkettung der Zeilen, die vorliegen — nicht, ob alle Zeilen und alle Felder vorliegen, die es einmal gab. Für den Vergleich zweier Bestände ist der Kopf-Hash das Mittel, nicht das Prüfergebnis: Zwei Bestände beschreiben dieselben Daten, wenn ihre Ketten auf denselben Kopf laufen.
Der Umkehrschluss gilt nicht — und ausgerechnet hier nicht. Ein Wiederaufbau eröffnet eine neue Kette, und ihre erste Zeile ist die origin- oder restore-Zeile. Damit rückt jede Position um eins, und kein Hash stimmt mehr mit dem alten Bestand überein, obwohl dieselben Fahrten darin stehen. Über einen Wiederaufbau hinweg sagen zwei verschiedene Köpfe also nichts über die Daten; das Mittel taugt zwischen zwei Ausgaben derselben Kette.
Und die neue Kette darf kürzer sein als die alte. Ein Wiederaufbau spielt die Geschichte nicht nach, er schreibt jeden Datensatz einmal — in dem Zustand, in dem er geendet hat. Zeilen, die nur eine Zwischenstufe festhalten, etwa ein nachgereichter Abgleich (RECONCILE), entstehen dabei nicht noch einmal: In einem gemessenen Fall wurden aus 129 Gliedern 124, ohne dass ein einziger Datensatz fehlte. Weniger Glieder sind also kein Verlust — sie sind der sichtbarste Beleg dafür, dass ein Wiederaufbau die Datensätze mitnimmt und die Geschichte nicht.
Was stattdessen trägt: die kanonischen Zeilen ohne ihre Position. Nimm aus jeder der beiden Dateien den payload_after aller Zeilen des Änderungsprotokolls und dann je Zeile:
- alles nach dem letzten
|streichen — das ist die Kettenposition und sonst nichts; - die Ergebnisse als Menge vergleichen, nicht als Liste.
Gleiche Mengen heißt: dieselben Datensätze. Drei Dinge, an denen dieser Weg hängt:
- Am letzten
|schneiden, nicht an einem gezählten. Das letzte Feld ist eine blanke Zahl; so kann kein Freitext dafür gehalten und kein Längenpräfix zerschnitten werden. - Bei
vehiclenichts streichen — diese Satzart trägt gar keine Kettenposition. Wer hier trotzdem schneidet, verliertis_premium_parallelund übersieht einen echten Unterschied. - Menge, nicht Liste, weil die Reihenfolge eine Eigenschaft des Wiederaufbaus ist und nicht des Buchs. Die
origin-Zeile fällt dabei von selbst heraus: Ihr Inhalt steht inpayload_before, ihrpayload_afterist leer.
Die Grenze: Der Vergleich gilt innerhalb einer Kettenform. Über einen Formwechsel hinweg unterscheiden sich die kanonischen Zeilen zwangsläufig — das ist ja der Grund, aus dem neu aufgebaut wird. Er zerfällt dabei aber nicht, sondern wird genauer: In einem gemessenen Fall (36 Datensätze, Wechsel von Form 3 auf 4) blieben 27 identisch, und die 9 Unterschiede waren genau die 9 Fahrten — unterschieden um genau das eine Feld, das die neue Form eingefügt hatte. Beim Wechsel von Form 4 auf 5 dasselbe Bild an einem größeren Buch: von 113 Satzzeilen blieben 100 identisch, die 13 abweichenden waren alle Kilometerstände, und sie stimmten wieder überein, sobald man das neue Feld herausnahm. In beiden gemessenen Fällen zeigte sich die Formänderung also genau dort, wo das neue Feld sitzt — und sonst nirgends. Auf eine Satzart darfst du das aber nicht verallgemeinern: Form 2 hat gleich zwei berührt. Welche Felder eine Form angefasst hat, sagt die Formhistorie in FORMAT.md.
Und was er nicht kann: Er vergleicht Datensätze, nicht Geschichte. Ein Wiederaufbau nimmt das alte Änderungsprotokoll nicht mit — seine Zeitpunkte und seine Zeilen beschreiben eine kanonische Form, die es nicht mehr gibt. Zwei Dateien können also dasselbe Buch enthalten und verschiedene Vorgeschichten dokumentieren; für die ältere Geschichte bleibt die ältere Datei die Quelle.
Eine Ausnahme, und sie ist wichtig: Ein Verwerfen überlebt den Umzug. Der Aufbau gibt jede verworfene Zeile neu aus, mit ihrem ursprünglichen Grund. Andernfalls wäre eine Fahrt, die jemand als „hat nie stattgefunden" zurückgenommen hat, nach dem nächsten Formwechsel still wieder mitgezählt worden — der Umzug hätte eine Korrektur rückgängig gemacht, ohne dass es irgendwo steht. Was verloren geht, ist der Zeitpunkt des Verwerfens, nicht seine Wirkung. Genau deshalb nennt die origin-Zeile Kopf und Länge dessen, was sie ersetzt — sie ist das Bindeglied, das der Kopf-Hash nicht mehr sein kann.
signature_state | heißt |
|---|---|
VALID | Der Abzug trug eine Gerätesignatur über genau den Kopf, den er nennt, und sie ging auf |
INVALID | Eine Signatur war da und ging nicht auf — der eine Wert, bei dem du anhalten solltest |
NONE | Keine Signatur. Nach einem Gerätewechsel der Normalfall, weil der Signierschlüssel sein Gerät nie verlässt: eine schwächere Aussage, keine verdächtige |
UNCHECKED | Vorhanden, hier nicht geprüft — „wir haben nicht nachgesehen" ist etwas anderes als „da war nichts" |
Zwei Festlegungen dahinter, die du kennen solltest:
- Nur eine Signatur auf genau diesen Kopf zählt. Eine ältere deckt eine frühere Position; sie als Deckung zu nehmen wäre das abgeschnittene Kettenende von oben mit einem Zwischenschritt, denn eine gekürzte Kette lässt alle früheren Signaturen gültig.
- Eine ungültige Signatur verhindert die Wiederherstellung nicht, sie wird vermerkt. Wer keine zweite Kopie hat, würde sonst genau zu der Zeile gedrängt, die das Problem dokumentiert.
Ein Export nach einer Wiederherstellung zieht das alles nach oben in einen provenance-Block neben chain — einschließlich der gehashten Zeile im Wortlaut. Damit ist die Zusammenfassung gegen die Kette nachprüfbar und nicht bloß zu glauben. Ein Buch, das von der ersten Zeile an auf einem Gerät geführt wurde, hat den Block nicht; er entsteht nur, wenn es etwas zu sagen gibt.
Womit du prüfst
Du brauchst kein Werkzeug von uns. Das oben Beschriebene reicht aus, um die Prüfung mit jedem SHA-256-Programm nachzubauen — das ist der Zweck dieser Seite und der Grund, warum sie so ausführlich ist.
Bequemer geht es mit den beiden fertigen Fassungen:
- Im Browser. Eine einzelne Datei, die du öffnest und auf die du deinen Export ziehst. Keine Installation, kein Hochladen, nichts wird nachgeladen — sie funktioniert vom USB-Stick auf einem Rechner ohne Internet. Sie bringt ihre eigene SHA-256-Berechnung mit, statt sich auf die des Browsers zu verlassen, deren Verfügbarkeit ausgerechnet bei lokal geöffneten Dateien unsicher ist. Sie prüft die Kette und nicht die Signatur — Signaturprüfung braucht im Browser Bausteine, die nicht verlässlich vorhanden sind, und ein Werkzeug, das stillschweigend weniger prüft als es verspricht, ist schlechter als eines, das seine Grenze nennt.
- Auf der Kommandozeile. Ein Python-Skript, das entweder die vom Telefon gezogene Datenbank oder den exportierten Bestand liest. Für die Kettenprüfung braucht es nichts außer der Standardbibliothek — die Signaturprüfung braucht zusätzlich das Paket
cryptography. Fehlt es, sagt das Skript genau das und rechnet die Kette trotzdem durch; es tut nicht so, als hätte es die Signatur geprüft. Rückgabewert 0 für eine unversehrte Kette, 1 für eine gebrochene und 2 für „diese Kettenform kenne ich nicht" — drei Werte, weil „das ist falsch" und „das kann ich nicht beurteilen" verschiedene Antworten sind. Ein eingebauter Selbsttest prüft das Werkzeug gegen von Hand gebaute Testfälle.
Beide Werkzeuge stehen zum Herunterladen bereit: verify-chain.html für den Browser und verify_chain.py für die Kommandozeile. Dazu die maßgebliche Formatbeschreibung FORMAT.md — englisch, ausführlicher als diese Seite, und das Dokument, auf das der Prüfsummen-Abschnitt deines Exports verweist.
Die Browserfassung ist eine einzige Datei: herunterladen, mitnehmen, auf einem Rechner ohne Internet öffnen — sie lädt nichts nach und schickt nichts weg.
Diese Testfälle sind der Grund, warum wir dem Ergebnis selbst trauen: Sie wurden nach den Regeln dieser Seite von Hand gebaut und mit einem Standard-Kommandozeilenwerkzeug gehasht, nicht aus einer Umsetzung übernommen. Dieselben Testfälle liegen in der App, im Python-Skript und hinter dem Selbsttest der Browserseite. Stimmen alle überein, sind vier voneinander unabhängige Umsetzungen einer Meinung.
Was ein „Kette intakt" wert ist
Genau das, was oben steht, und keinen Satz mehr: Das Vorgelegte stimmt in sich. Es heißt nicht, dass es alles ist — dafür brauchst du die Kettenposition auf einem Bericht, den jemand anderes aufbewahrt.
Uns ist diese Unterscheidung wichtiger als ein starkes Wort auf der Produktseite. Ein Fahrtenbuch wird in dem Moment gebraucht, in dem jemand es anzweifelt; wer dann feststellt, dass die Zusage weiter reichte als das Verfahren, steht schlechter da als jemand, der die Grenze von Anfang an kannte.
Weiterlesen: Wozu ein Fahrtenbuch — und was es überall leisten muss erklärt die drei Prinzipien, an denen ein Fahrtenbuch gemessen wird. Fahrtenbuch-App und Datenschutz stellt dieselbe Frage für deine Standortdaten.
Xorino Log schreibt jede Änderung als eigene Zeile in diese Kette, auf dem Gerät und ohne Konto. Der Export deines Bestands samt Änderungsprotokoll ist kostenlos, auch ohne Premium, und die Datei benennt selbst, was sie nicht enthält.
Kein Steuerrat. Dieser Artikel erklärt allgemeine Regeln und ersetzt keine steuerliche Beratung im Einzelfall. Ob und wie sie auf deine Situation zutreffen, klärst du mit deiner Steuerberaterin oder deinem Steuerberater. Stand: 05.09.2026 — Steuerrecht ändert sich; prüfe bei älteren Artikeln die Quellen.
Xorino Log ist in Entwicklung; der Release ist für Ende 2026 geplant. Was auf dieser Seite über die App steht, gehört zum geplanten Funktionsumfang.