ED03 - Format du journal : artefacts JSON, signature temporelle, V7GUID, sidecars
Problème
Un entrepreneur souhaite tenir son journal dans Git - mais à quoi ressemble techniquement une entrée, exactement ? La pratique actuelle ne connaît aucune réponse contraignante :
- Notes en texte libre en Markdown, Word ou papier - non lisibles par machine, non validées par schéma, non traçables
- Lignes Excel avec date, activité, heures - non versionnées, non ancrées cryptographiquement, non à l'épreuve des audits
- Exports de logiciel de paie en PDF - non exploitables par machine (§ 147 Abs. 6 AO), sans traçabilité rétrograde/progressive
- Aucune identité univoque - chaque entrée a un nom de fichier, mais le nom de fichier peut changer ; l'identité de l'entrée n'est pas stable
- Aucun ancrage temporel - la date figure sous forme de chaîne de caractères dans le document, mais qui a réellement saisi quoi, et quand ? L'heure de saisie n'est pas liée cryptographiquement à l'identité
Message clé
Une entrée de journal GitCover est un artefact JSON avec :
- JSON-Schema-First - le schéma existe avant l'entrée ; l'entrée est validée par rapport au schéma (Pre-Commit-Hook)
- V7GUID (Class Identifier) - classifie le document/l'action à partir du
registre
.gitcover(quoi/quel type) ; sert à l'organisation du classement et aux requêtes DB (p. ex. EF Core dans Vertical App) - uuidV7 (Object ID) - constitue déjà à elle seule l'identité unique
(DocID) de l'entrée, RFC 9562 §5.7, générée à partir d'une estampille
temporelle prédéfinie (ou de
now()) via le helper GitCover (UuidV7Gen), le reste étant complété de manière aléatoire - Clé composite
V7GUID:uuidV7- la clé composite du sidecar GCPN, sert à l'organisation du classement et aux requêtes ; l'heure de saisie est contenue dans l'uuidV7(horodatage 48 bits), pas de champdatetime/dateséparé - Obligation de sidecar - chaque pièce justificative reçoit un sidecar
.v7g.mdavec sa propreuuidV7(acte de classification) et l'uuidV7du document - Traçabilité rétrograde/progressive - résoluble de l'écriture
comptable → pièce justificative → entrée de journal →
uuidV7et en sens inverse
Conformité by Design : le format n'est pas ajouté après coup - il est structurel. Schéma, V7GUID (Class), uuidV7 (Object) et sidecar sont des champs obligatoires sans lesquels une entrée n'est pas intégrée au dépôt.
Pas de
datetime/dateredondant : l'heure de saisie est ancrée dans l'uuidV7elle-même (horodatage 48 bits, RFC 9562 §5.7). Une représentation sous forme de chaîne de caractères distincte dans les schémas JSON ou les données JSON est redondante et n'est pas maintenue dans les faits. La représentation en chaîne pour le transfert DTO/HTMX relève de la responsabilité du Harness, pas de la couche des faits.
Le schéma JSON (Schema-First)
(vor Eintrag)"] S --> V["Schema-Validierung
(Pre-Commit-Hook)"] E["Tagebucheintrag
(JSON)"] E --> V V -->|gültig| C["Commit akzeptiert
+ uuidV7 generiert"] V -->|ungültig| R["Commit abgelehnt
Schema-Verstoß"] style S fill:#DBEAFE,stroke:#1D4ED8,color:#0F1B33 style V fill:#DBEAFE,stroke:#1D4ED8,color:#0F1B33 style E fill:#0F1B33,stroke:#0F1B33,color:#FBFAF7 style C fill:#D1FAE5,stroke:#0A7F5C,color:#0F1B33 style R fill:#FDBA74,stroke:#C2410C,color:#0F1B33
Le schéma existe avant l'entrée - il est déposé dans
.gitcover/schemas/diary-entry-1.0.schema.json et est versionné. Une entrée
sans référence de schéma ($schema) est rejetée par le Pre-Commit-Hook.
Schéma diary-entry-1.0.schema.json (simplifié)
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://gitcover.org/schemas/diary-entry-1.0.schema.json",
"title": "GitCover Diary Entry v1.0",
"type": "object",
"required": [
"$schema", "V7GUID", "docUid",
"author", "role", "tenant", "sphere", "source"
],
"properties": {
"$schema": {
"type": "string",
"format": "uri",
"const": "https://gitcover.org/schemas/diary-entry-1.0.schema.json"
},
"V7GUID": {
"type": "string",
"description": "Class Identifier - klassifiziert das Dokument/die Action aus .gitcover Registry (was/welcher Typ)"
},
"docUid": {
"type": "string",
"pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$",
"description": "Object ID - RFC 9562 §5.7 UUIDv7, generiert aus vorgegebener Zeitmarke (nicht now()), 48-Bit-Zeitstempel in der GUID verankert"
},
"author": { "type": "string", "description": "Akteur-Platzhalter, z. B. E1" },
"role": {
"type": "string",
"enum": ["GF", "Buchhalter", "Lohnverantwortlicher", "F&E-Leiter", "Administrator"]
},
"tenant": { "type": "string", "description": "Tenant-Platzhalter, z. B. ORG-1" },
"sphere": {
"type": "string",
"enum": ["ideell", "vermögensverwaltend", "zweckbetrieblich", "wirtschaftlich", "n/a"]
},
"source": {
"type": "string",
"enum": ["E1", "StB", "Notar", "Bank", "DRV", "FA", "BA", "VBG", "KK", "auto"]
},
"source_sha256": {
"type": "string",
"pattern": "^[0-9a-f]{64}$",
"description": "SHA-256 des referenzierten Belegs (eindeutige Beleg-ID)"
},
"tags": { "type": "array", "items": { "type": "string" } }
}
}
Important - pas de champ
datetime/date: l'heure de saisie est contenue dans l'uuidV7(horodatage 48 bits, RFC 9562 §5.7). Une représentation sous forme de chaîne distincte est redondante et n'est pas maintenue dans les faits. L'uuidV7seule est la DocID unique ; la clé compositeV7GUID:uuidV7sert à l'organisation du classement et aux requêtes DB.
V7GUID - Structure et horodatage 48 bits
Bits 0–47
48 Bit Unix-ms"] V["version
Bits 48–51
0111 (UUIDv7)"] R["rand_a
Bits 52–63
12 Bit"] VA["variant
Bits 64–65
10"] RB["rand_b
Bits 66–127
62 Bit"] end T --> DT["48-Bit-Zeitstempel
in uuidV7 verankert
(kein separates datetime-Feld)"] V7 --> ID["uuidV7-Feld
Object ID
(vorgegebene Zeitmarke)"] style T fill:#DBEAFE,stroke:#1D4ED8,color:#0F1B33 style V fill:#D1FAE5,stroke:#0A7F5C,color:#0F1B33 style R fill:#E5E7EB,stroke:#6B7280,color:#0F1B33 style VA fill:#D1FAE5,stroke:#0A7F5C,color:#0F1B33 style RB fill:#E5E7EB,stroke:#6B7280,color:#0F1B33 style DT fill:#0F1B33,stroke:#0F1B33,color:#FBFAF7 style ID fill:#0F1B33,stroke:#0F1B33,color:#FBFAF7
| Plage de bits | Largeur | Contenu | Fixé par la RFC |
|---|---|---|---|
| 0–47 | 48 bits | Millisecondes Unix depuis l'époque (1970-01-01T00:00:00Z) | Oui (RFC 9562 §5.7) |
| 48–51 | 4 bits | Version = 0111 (UUIDv7) |
Oui |
| 52–63 | 12 bits | rand_a (aléatoire) | Non |
| 64–65 | 2 bits | Variant = 10 |
Oui |
| 66–127 | 62 bits | rand_b (aléatoire) | Non |
Principe central : la composante d'horodatage 48 bits (bits 0–47) fait autorité pour l'heure de saisie. Elle est ancrée dans l'
uuidV7elle-même et ne peut pas être modifiée a posteriori sans invalider le GUID. Un champdatetimeséparé n'existe pas - le temps est contenu dans l'uuidV7. La représentation en chaîne pour DTO/HTMX relève de la responsabilité du Harness.
Extension GitCover : hiérarchie et IDs de processus dans les bits 66–127
La spécification GitCover V7GUID (work/OSS/TOP/.gitcover/specs/v7guid/)
étend la composante aléatoire de 62 bits (bits 66–127) par une hiérarchie
à 6 niveaux et des IDs de processus - il s'agit de l'extension protégée
par brevet (DPMA Az. 10 2025 003 091.6) :
| Plage de bits | Largeur | Contenu |
|---|---|---|
| 66–89 | 6×4 bits | Hiérarchie à 6 niveaux (L1–L6, 4 bits chacun) |
| 90–97 | 8 bits | ProcessTypeId (type de processus) |
| 98–105 | 8 bits | GatewayId (point de contrôle) |
| 106–113 | 8 bits | Status (masque de bits du cycle de vie) |
| 114–121 | 8 bits | Kind (nature de l'activité) |
| 122–127 | 6 bits | VariantId (classe de variante) |
Remarque : cette extension n'est pas strictement nécessaire pour la série consacrée au journal - elle devient pertinente dans ED02 (Tenant/Sphère/Rôle) et dans des articles ultérieurs (Harness). Ici, la base RFC 9562 avec horodatage 48 bits suffit.
Routines helper : création d'un V7GUID à partir de clés externes
(Zeitmarke in Daten
oder Dateiname)"] F --> H["Helper-Routine
UuidV7Gen"] H --> TS["48-Bit-Zeitstempel
extrahiert/konvertiert"] TS --> G["uuidV7 generiert
(Guid.CreateVersion7)"] G --> A["JSON-Artefakt
V7GUID + uuidV7"] A --> D["GoBD-Dokumentation
zeitnahe Erfassung
nachvollziehbar"] style F fill:#FDBA74,stroke:#C2410C,color:#0F1B33 style H fill:#DBEAFE,stroke:#1D4ED8,color:#0F1B33 style TS fill:#DBEAFE,stroke:#1D4ED8,color:#0F1B33 style G fill:#D1FAE5,stroke:#0A7F5C,color:#0F1B33 style A fill:#0F1B33,stroke:#0F1B33,color:#FBFAF7 style D fill:#D1FAE5,stroke:#0A7F5C,color:#0F1B33
L'outil OSS GitCover UuidV7Gen (work/OSS/TOP/tools/UuidV7Gen/) fournit
deux opérations centrales :
gen - générer une uuidV7 (guid)
# Erzeugt eine neue uuidV7 mit vorgegebener Zeitmarke (nicht now())
# Zeitmarke z. B. aus Dateiname "260815_0900" → ISO 2026-08-15T09:00:00Z
uuidv7gen gen "2026-08-15T09:00:00Z" "Tagebucheintrag-260815"
# Ausgabe: Label;UUID;ISO-Zeitstempel
# Tagebucheintrag-260815;019f2c6f-0900-7000-8000-000000000000;2026-08-15T09:00:00.000Z
decode - extraire l'horodatage d'une uuidV7
# Extrahiert den 48-Bit-Zeitstempel aus einer uuidV7
uuidv7gen decode 019f2c6f-0900-7000-8000-000000000000
# Ausgabe: ISO-8601-Zeitstempel
# 2026-08-15T09:00:00.000Z
Clé externe → uuidV7
Les routines helper permettent de créer une clé uuidV7 valide à partir
d'une clé externe (p. ex. une estampille temporelle dans le nom de
fichier comme 260815_0900_Lohnabrechnung.pdf) - avec une estampille
temporelle prédéfinie, pas now() :
- Analyser la clé externe -
260815_0900→2026-08-15T09:00:00Z - Calculer l'horodatage 48 bits - millisecondes Unix depuis l'époque
- Générer l'
uuidV7-Guid.CreateVersion7()avec horodatage explicite, reste complété de manière aléatoire - Clé composite -
V7GUID(Class issue du registre) :uuidV7(Object avec estampille temporelle) - Documentation GoBD - saisie en temps utile ancrée de manière traçable (pas de champ datetime séparé)
Lien avec GoBD : la saisie en temps utile (GoBD Rz. 146) est documentée cryptographiquement par l'horodatage 48 bits dans l'
uuidV7- et pas seulement affirmée. L'horodatage fait partie de l'identité, il n'est pas modifiable a posteriori.
Structure du sidecar (.v7g.md)
Chaque pièce justificative reçoit un sidecar avec deux références uuidV7 :
(PDF, XML, EML)"] D --> S["Sidecar .v7g.md"] S --> U1["uuidV7 (eigen)
Identität des Sidecars
+ 48-Bit-Erfassungszeit"] S --> U2["uuidV7 (fremd)
Identität des Dokuments
in v7g_taxonomy"] S --> SH["sha256
Eindeutige Beleg-ID"] S --> T["v7g_taxonomy
Klassifizierung
(Tenant, Sphäre, Kategorie)"] S --> O["obsolescence
status: active/superseded/obsolete"] style D fill:#0F1B33,stroke:#0F1B33,color:#FBFAF7 style S fill:#DBEAFE,stroke:#1D4ED8,color:#0F1B33 style U1 fill:#D1FAE5,stroke:#0A7F5C,color:#0F1B33 style U2 fill:#D1FAE5,stroke:#0A7F5C,color:#0F1B33 style SH fill:#FDBA74,stroke:#C2410C,color:#0F1B33 style T fill:#D1FAE5,stroke:#0A7F5C,color:#0F1B33 style O fill:#E5E7EB,stroke:#6B7280,color:#0F1B33
Exemple de sidecar
{
"$schema": "https://gitcover.org/schemas/v7g-sidecar-1.0.schema.json",
"V7GUID": "<V7GUID-Class-aus-Registry>",
"docUid": "019f2c6f-0900-7001-8000-000000000001",
"sha256": "ab94670c0dd8499c27ef2feddd1d9c5925977d55a52150a6fca0f6567d2e5e79",
"title": "260815_Lohnabrechnung.pdf",
"original_filename": "260815_Lohnabrechnung.pdf",
"locations": [
{
"unc_path": "./ORG-1/sources/lohn/260815_Lohnabrechnung.pdf",
"from": "260815",
"to": null,
"note": "Primärspeicherort"
}
],
"v7g_taxonomy": [
{
"v7guid": "019f2c6f-0900-7000-8000-000000000000",
"taxonomy": "ORG-1/Lohn",
"valid_from": "260815",
"valid_to": null,
"note": "Tenant: ORG-1, Category: Lohn, Sphäre: ideell"
}
],
"gcpn": {
"prima_nota_ref": null,
"journal_entry_ref": null
},
"obsolescence": {
"status": "active",
"superseded_by": null,
"superseded_at": null
}
}
Clé composite
V7GUID:uuidV7:
V7GUID(champ 1) - Class Identifier du sidecar (classification issue du registre.gitcover: quoi/quel type)uuidV7(champ 2) - Object ID du sidecar lui-même, générée avec une estampille temporelle prédéfinie (quand la classification a-t-elle eu lieu ?), horodatage 48 bits ancré dans le GUIDv7g_taxonomy[].v7guid- Object ID du document classifié (quel document est classifié ?)sha256- identité du document original classifié — la preuve de l'originalité, toujours et partout (même si le document se déplace : indépendante du chemin et du nom, calculée à partir du contenu)Ainsi, non seulement le document est identifié de manière univoque (via son SHA-256), mais aussi l'acte de classification lui-même - y compris le moment (dans l'
uuidV7), qui a classifié et dans quel rôle. Pas de champdatetimeséparé - le temps est contenu dans les valeursuuidV7.
Traçabilité rétrograde et progressive
(SKR04)"] B --> BE["Beleg
(SHA-256)"] BE --> DE["Tagebucheintrag
(uuidV7)"] DE --> V7["uuidV7
(48-Bit-Zeitstempel)"] end subgraph PRO["Progressiv (vom Beleg vorwärts)"] direction LR BE2["Beleg
(SHA-256)"] BE2 --> BS["Buchungssatz
(SKR04)"] BS --> GB["Grundbuch
(JSON)"] GB --> EB["Eröffnungsbilanz"] end style B fill:#DBEAFE,stroke:#1D4ED8,color:#0F1B33 style BE fill:#FDBA74,stroke:#C2410C,color:#0F1B33 style DE fill:#D1FAE5,stroke:#0A7F5C,color:#0F1B33 style V7 fill:#0F1B33,stroke:#0F1B33,color:#FBFAF7 style BE2 fill:#FDBA74,stroke:#C2410C,color:#0F1B33 style BS fill:#DBEAFE,stroke:#1D4ED8,color:#0F1B33 style GB fill:#DBEAFE,stroke:#1D4ED8,color:#0F1B33 style EB fill:#D1FAE5,stroke:#0A7F5C,color:#0F1B33
| Direction | Point de départ | Résolution via | Cible |
|---|---|---|---|
| Rétrograde | Écriture comptable (SKR04) | source_sha256 → pièce justificative → uuidV7 |
Entrée de journal avec horodatage 48 bits |
| Progressive | Pièce justificative (SHA-256) | uuidV7 → entrée de journal → source_sha256 |
Écriture comptable → Grundbuch → bilan d'ouverture |
Lien avec GoBD : la traçabilité rétrograde correspond à GoBD Rz. 146 (traçabilité). La traçabilité progressive correspond à GoBD Rz. 147 (vérifiabilité). Toutes deux sont garanties by Design par
uuidV7+ SHA-256 - et non par des renvois croisés manuels.
Saisie du temps dans le journal
Chaque entrée de journal contient un tableau de saisie du temps :
| Début | Fin | Heures | Activité | Tenant | Sphère | Source |
|---|---|---|---|---|---|---|
| 0900 | 1200 | 3,0 | Données de base du compte de paie | ORG-1 | ideell | E1 |
| 1200 | 1400 | 2,0 | R&D : spécification V7GUID | ORG-1 | ideell | E1 |
Lien avec FZul : la saisie du temps est la base des justificatifs d'heures FZul (ED28). La séparation R&D vs. administration s'effectue via le champ
role(F&E-Leitervs.GF) et lestags(["fzul", "forschung"]vs.["verwaltung"]).
Ce qui est intégré au dépôt Git - et ce qui ne l'est pas
(Tagebucheinträge, Grundbuch)"] IN --> S["Sidecars .v7g.md"] IN --> B["Belege
(PDF, XML, EML)"] IN --> SC["Schemata"] IN --> D["Dictionaries"] OUT["Nicht im Git-Repo"] OUT --> P["Private Dokumente
(sofern nicht ausdrücklich eingeführt)"] OUT --> T["Temporäre Dateien"] OUT --> C["Credentials/Secrets"] style IN fill:#D1FAE5,stroke:#0A7F5C,color:#0F1B33 style J fill:#D1FAE5,stroke:#0A7F5C,color:#0F1B33 style S fill:#D1FAE5,stroke:#0A7F5C,color:#0F1B33 style B fill:#D1FAE5,stroke:#0A7F5C,color:#0F1B33 style SC fill:#D1FAE5,stroke:#0A7F5C,color:#0F1B33 style D fill:#D1FAE5,stroke:#0A7F5C,color:#0F1B33 style OUT fill:#FDBA74,stroke:#C2410C,color:#0F1B33 style P fill:#FDBA74,stroke:#C2410C,color:#0F1B33 style T fill:#FDBA74,stroke:#C2410C,color:#0F1B33 style C fill:#FDBA74,stroke:#C2410C,color:#0F1B33
Important : seuls les artefacts structurés (JSON, sidecars, pièces justificatives, schémas, dictionnaires) sont intégrés au dépôt Git. Les documents privés ne sont jamais intégrés au dépôt, sauf s'ils ont été expressément introduits et classifiés en tant qu'artefact. Il s'agit d'une exigence de protection des données et de conformité (RGPD, GoBD).
Levier de risque
| Aujourd'hui (peu coûteux) | Demain (à l'épreuve des audits) | Risque atténué |
|---|---|---|
Artefact JSON avec V7GUID (Class) + uuidV7 (Object) |
Heure de saisie ancrée cryptographiquement dans uuidV7 | Contestation de l'heure de saisie |
| Validation par schéma (Pre-Commit) | Seules les entrées valides sont intégrées au dépôt | Violation du schéma, champs obligatoires incomplets |
Sidecar avec clé composite V7GUID:uuidV7 |
Acte de classification traçable | Contestation de la classification |
source_sha256 par entrée |
Traçabilité rétrograde | Contestation du rattachement de la pièce justificative |
Helper UuidV7Gen pour les clés externes |
Saisie en temps utile documentée (estampille temporelle dans uuidV7) | Violation GoBD « pas en temps utile » |
Exigences Harness (aperçu)
Déductible de ED03 :
| ID | Exigence | Priorité |
|---|---|---|
| FA-1.1 | Entrées de journal en tant qu'artefacts JSON (Schema-First) | MUST |
| FA-1.2 | Clé composite V7GUID (Class) : uuidV7 (Object) - pas de champ datetime/date séparé ; temps dans l'uuidV7 (RFC 9562 §5.7) |
MUST |
| FA-1.3 | uuidV7 générée à partir d'une estampille temporelle prédéfinie (pas now()) via un helper |
MUST |
| FA-2.1 | SHA-256 comme ID de pièce justificative | MUST |
| FA-2.2 | Obligation de sidecar .v7g.md |
MUST |
| FA-2.3 | Sidecar avec clé composite V7GUID:uuidV7 (Class + Object) |
MUST |
| FA-2.12 | Traçabilité rétrograde (écriture → pièce justificative → uuidV7) |
MUST |
| FA-2.13 | Traçabilité progressive (pièce justificative → écriture → Grundbuch) | MUST |
| TA-1.5 | Routines helper pour la création de V7GUID à partir de clés externes (UuidV7Gen) |
MUST |
| TA-1.6 | Exigences temporelles GoBD documentées (horodatage 48 bits dans l'uuidV7) |
MUST |
| TA-2.1 | Pre-Commit : validation du schéma JSON | MUST |
La liste complète des exigences dans
Harness-Anforderungen.md.
Sources
- RFC 9562 §5.7 (UUIDv7) - horodatage Unix-ms 48 bits
- Spécification V7GUID -
work/OSS/TOP/.gitcover/specs/v7guid/(normatif) - DPMA Az. 10 2025 003 091.6 - brevet V7GUID (revendication principale 2, revendications 5–8)
- Outil OSS GitCover
UuidV7Gen-work/OSS/TOP/tools/UuidV7Gen/(helpersgen/decode) - GoBD (circulaire BMF, Rz. 146 - traçabilité, Rz. 147 - vérifiabilité)
- AO (§ 147 Abs. 2 - « exploitable par machine », § 147 Abs. 6 - accès aux données)
AFJD/agents/(anonymisé) - concept SSoT avec artefacts JSON, sidecars
Topologie des sources et liens de référence CDN
| Rôle | Emplacement | Objectif |
|---|---|---|
| Primary / SSoT | git.gitcover.org/GCC | Emplacement canonique (signé GPG, versionné) |
| Public OSS Mirror / CDN | codeberg.org/gitcover-commons | Miroir en lecture seule ; découverte FLOSS |
| Community Hub | github.com/gitcover-commons | Issues & Discussions ; référence du code source sur Codeberg |
Remarque : cette répartition entre sources, miroir et Community Hub reflète l'état actuel et peut changer. Veuillez vérifier la source canonique respective sur gitcover.org pour connaître l'état actuel.