ED03 - Formato de diario: artefactos JSON, firma temporal, V7GUID, Sidecars
Problema
Un empresario quiere llevar su diario en Git - pero ¿cómo se ve técnicamente una entrada, exactamente? La práctica actual no conoce ninguna respuesta vinculante:
- Notas de texto libre en Markdown, Word o papel - no legibles por máquina, no validadas contra esquema, no trazables
- Filas de Excel con fecha, actividad, horas - no versionadas, no ancladas criptográficamente, no a prueba de auditoría
- Exportaciones de software de nóminas como PDF - no evaluables mecánicamente (§ 147 Abs. 6 AO), no rastreables de forma retrógrada/progresiva
- Sin identidad unívoca - cada entrada tiene un nombre de archivo, pero el nombre de archivo puede cambiar; la identidad de la entrada no es estable
- Sin anclaje temporal - la fecha figura como string en el documento, pero ¿quién registró realmente qué y cuándo? La hora de registro no está vinculada criptográficamente a la identidad
Idea central
Una entrada de diario de GitCover es un artefacto JSON con:
- JSON-Schema-First - el esquema existe antes de la entrada; la entrada se valida contra el esquema (hook de pre-commit)
- V7GUID (Class Identifier) - clasifica el documento/la acción según el
Registry
.gitcover(qué/qué tipo); sirve para la organización del archivado y las consultas a la BD (p. ej., EF Core en Vertical App) - uuidV7 (Object ID) - ya es por sí sola la identidad unívoca
(DocID) de la entrada, RFC 9562 §5.7, generada a partir de una
marca de tiempo predefinida (o
now()) mediante el Helper de GitCover (UuidV7Gen), el resto se rellena con aleatoriedad - Clave compuesta
V7GUID:uuidV7- la clave compuesta del Sidecar GCPN, sirve para la organización del archivado y las consultas; la hora de registro está contenida en lauuidV7(marca de tiempo de 48 bits), sin campodatetime/dateseparado - Obligación de Sidecar - cada comprobante recibe un Sidecar
.v7g.mdcon su propiauuidV7(acto de clasificación) y lauuidV7del documento - Rastreabilidad retrógrada/progresiva - resoluble desde el asiento
contable → comprobante → entrada de diario →
uuidV7y de vuelta
Compliance by Design: El formato no es algo posterior - es estructural. Esquema, V7GUID (Class), uuidV7 (Object) y Sidecar son campos obligatorios sin los cuales una entrada no se incorpora al repositorio.
Sin
datetime/dateredundante: La hora de registro está anclada en la propiauuidV7(marca de tiempo de 48 bits, RFC 9562 §5.7). Una representación como string separada en esquemas JSON o datos JSON es redundante y no se mantiene en los hechos. La representación como string para la transferencia DTO/HTMX es responsabilidad del Harness, no de la capa de hechos.
El esquema 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
El esquema existe antes de la entrada - se almacena en
.gitcover/schemas/diary-entry-1.0.schema.json y está versionado. Una
entrada sin referencia de esquema ($schema) es rechazada por el hook de
pre-commit.
Esquema diary-entry-1.0.schema.json (simplificado)
{
"$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" } }
}
}
Importante - sin campo
datetime/date: La hora de registro está contenida en lauuidV7(marca de tiempo de 48 bits, RFC 9562 §5.7). Una representación como string separada es redundante y no se mantiene en los hechos. LauuidV7por sí sola es la DocID unívoca; la clave compuestaV7GUID:uuidV7sirve para la organización del archivado y las consultas a la base de datos.
V7GUID - estructura y marca de tiempo de 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
| Rango de bits | Ancho | Contenido | Fijado por RFC |
|---|---|---|---|
| 0–47 | 48 bits | Milisegundos Unix desde la época (1970-01-01T00:00:00Z) | Sí (RFC 9562 §5.7) |
| 48–51 | 4 bits | Version = 0111 (UUIDv7) |
Sí |
| 52–63 | 12 bits | rand_a (aleatorio) | No |
| 64–65 | 2 bits | Variant = 10 |
Sí |
| 66–127 | 62 bits | rand_b (aleatorio) | No |
Principio central: El componente de marca de tiempo de 48 bits (bits 0–47) es autoritativo para la hora de registro. Está anclado en la propia
uuidV7y no puede modificarse posteriormente sin invalidar la GUID. Un campodatetimeseparado no existe - el tiempo está en lauuidV7. La representación como string para DTO/HTMX es responsabilidad del Harness.
Extensión GitCover: jerarquía e IDs de proceso en los bits 66–127
La especificación V7GUID de GitCover (work/OSS/TOP/.gitcover/specs/v7guid/)
amplía el componente aleatorio de 62 bits (bits 66–127) con una
jerarquía de 6 niveles e IDs de proceso - esta es la extensión
protegida por patente (DPMA Az. 10 2025 003 091.6):
| Rango de bits | Ancho | Contenido |
|---|---|---|
| 66–89 | 6×4 bits | Jerarquía de 6 niveles (L1–L6, 4 bits cada uno) |
| 90–97 | 8 bits | ProcessTypeId (tipo de proceso) |
| 98–105 | 8 bits | GatewayId (punto de control) |
| 106–113 | 8 bits | Status (máscara de bits del ciclo de vida) |
| 114–121 | 8 bits | Kind (tipo de actividad) |
| 122–127 | 6 bits | VariantId (clase de variante) |
Nota: Esta extensión no es imprescindible para la serie sobre el diario - cobra relevancia en ED02 (Tenant/Esfera/Rol) y en artículos posteriores (Harness). Aquí basta la base RFC 9562 con marca de tiempo de 48 bits.
Rutinas Helper: creación de V7GUID a partir de claves externas
(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
La herramienta OSS de GitCover UuidV7Gen (work/OSS/TOP/tools/UuidV7Gen/)
proporciona dos operaciones centrales:
gen - generar 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 - extraer la marca de tiempo de una 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
Clave externa → uuidV7
Las rutinas Helper permiten crear una clave uuidV7 válida a partir de
una clave externa (p. ej., una marca de tiempo en el nombre de archivo
como 260815_0900_Lohnabrechnung.pdf) - con marca de tiempo
predefinida, no now():
- Parsear la clave externa -
260815_0900→2026-08-15T09:00:00Z - Calcular la marca de tiempo de 48 bits - milisegundos Unix desde la época
- Generar la
uuidV7-Guid.CreateVersion7()con marca de tiempo explícita, el resto con aleatoriedad - Clave compuesta -
V7GUID(Class del Registry) :uuidV7(Object con marca de tiempo) - Documentación GoBD - registro contemporáneo anclado de forma trazable (sin campo datetime separado)
Referencia GoBD: El registro contemporáneo (GoBD Rz. 146) queda documentado criptográficamente mediante la marca de tiempo de 48 bits en la
uuidV7- no solo afirmado. La marca de tiempo es parte de la identidad, no modificable a posteriori.
Estructura del Sidecar (.v7g.md)
Cada comprobante recibe un Sidecar con dos referencias 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
Ejemplo 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
}
}
Clave compuesta
V7GUID:uuidV7:
V7GUID(campo 1) - Class Identifier del Sidecar (clasificación según el Registry.gitcover: qué/qué tipo)uuidV7(campo 2) - Object ID del propio Sidecar, generada con marca de tiempo predefinida (¿cuándo se clasificó?), marca de tiempo de 48 bits anclada en la GUIDv7g_taxonomy[].v7guid- Object ID del documento clasificado (¿qué documento se clasifica?)sha256- identidad del documento original clasificado — la prueba de originalidad, siempre y en todas partes (incluso si el documento se desplaza: independiente de ruta/nombre, calculada a partir del contenido)Así no solo queda identificado de forma unívoca el documento (mediante su SHA-256), sino también el acto de clasificación en sí - incluido el momento (en la
uuidV7), quién clasificó y en qué rol. Sin campodatetimeseparado - el tiempo está en los valoresuuidV7.
Rastreabilidad retrógrada y progresiva
(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
| Dirección | Inicio | Resolución mediante | Objetivo |
|---|---|---|---|
| Retrógrada | Asiento contable (SKR04) | source_sha256 → comprobante → uuidV7 |
Entrada de diario con marca de tiempo de 48 bits |
| Progresiva | Comprobante (SHA-256) | uuidV7 → entrada de diario → source_sha256 |
Asiento contable → Grundbuch → balance de apertura |
Referencia GoBD: La rastreabilidad retrógrada corresponde a GoBD Rz. 146 (trazabilidad). La rastreabilidad progresiva corresponde a GoBD Rz. 147 (verificabilidad). Ambas están garantizadas by Design mediante
uuidV7+ SHA-256 - no mediante referencias cruzadas manuales.
Registro de tiempo en el diario
Cada entrada de diario contiene una tabla de registro de tiempo:
| Inicio | Fin | Horas | Actividad | Tenant | Esfera | Fuente |
|---|---|---|---|---|---|---|
| 0900 | 1200 | 3,0 | Datos maestros de cuenta de nómina | ORG-1 | ideell | E1 |
| 1200 | 1400 | 2,0 | I+D: especificación V7GUID | ORG-1 | ideell | E1 |
Referencia FZul: El registro de tiempo es la base para los justificantes de horas FZul (ED28). La separación entre I+D y administración se realiza mediante el campo
role(F&E-Leitervs.GF) y lastags(["fzul", "forschung"]vs.["verwaltung"]).
Qué entra en el repositorio Git - y qué no
(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
Importante: Solo los artefactos estructurados (JSON, Sidecars, comprobantes, esquemas, diccionarios) se incorporan al repositorio Git. Los documentos privados nunca se incorporan al repositorio, a menos que hayan sido introducidos y clasificados explícitamente como artefacto. Este es un requisito de protección de datos y de cumplimiento (DSGVO, GoBD).
Apalancamiento de riesgo
| Hoy (barato) | Mañana (a prueba de auditoría) | Riesgo mitigado |
|---|---|---|
Artefacto JSON con V7GUID (Class) + uuidV7 (Object) |
Hora de registro anclada criptográficamente en uuidV7 | Negación de la hora de registro |
| Validación de esquema (Pre-Commit) | Solo entradas válidas entran en el repositorio | Violación del esquema, campos obligatorios incompletos |
Sidecar con clave compuesta V7GUID:uuidV7 |
Acto de clasificación trazable | Negación de la clasificación |
source_sha256 por entrada |
Rastreabilidad retrógrada | Negación de la asignación del comprobante |
Helper UuidV7Gen para claves externas |
Registro contemporáneo documentado (marca de tiempo en uuidV7) | Infracción GoBD "no contemporáneo" |
Requisitos del Harness (vista previa)
Derivable de ED03:
| ID | Requisito | Prioridad |
|---|---|---|
| FA-1.1 | Entradas de diario como artefactos JSON (Schema-First) | MUST |
| FA-1.2 | Clave compuesta V7GUID (Class) : uuidV7 (Object) - sin campo datetime/date separado; el tiempo en uuidV7 (RFC 9562 §5.7) |
MUST |
| FA-1.3 | uuidV7 generada a partir de una marca de tiempo predefinida (no now()) mediante el Helper |
MUST |
| FA-2.1 | SHA-256 como ID de comprobante | MUST |
| FA-2.2 | Obligación de Sidecar .v7g.md |
MUST |
| FA-2.3 | Sidecar con clave compuesta V7GUID:uuidV7 (Class + Object) |
MUST |
| FA-2.12 | Rastreabilidad retrógrada (asiento → comprobante → uuidV7) |
MUST |
| FA-2.13 | Rastreabilidad progresiva (comprobante → asiento → Grundbuch) | MUST |
| TA-1.5 | Rutinas Helper para la creación de V7GUID a partir de claves externas (UuidV7Gen) |
MUST |
| TA-1.6 | Requisitos temporales de GoBD documentados (marca de tiempo de 48 bits en uuidV7) |
MUST |
| TA-2.1 | Pre-Commit: validación de esquema JSON | MUST |
La lista completa de requisitos en Harness-Anforderungen.md.
Fuentes
- RFC 9562 §5.7 (UUIDv7) - marca de tiempo Unix-ms de 48 bits
- Especificación V7GUID -
work/OSS/TOP/.gitcover/specs/v7guid/(normativa) - DPMA Az. 10 2025 003 091.6 - patente V7GUID (reivindicación principal 2, reivindicaciones 5–8)
- Herramienta OSS de GitCover
UuidV7Gen-work/OSS/TOP/tools/UuidV7Gen/(Helpergen/decode) - GoBD (escrito del BMF, Rz. 146 - trazabilidad, Rz. 147 - verificabilidad)
- AO (§ 147 Abs. 2 - "evaluable mecánicamente", § 147 Abs. 6 - acceso a datos)
AFJD/agents/(anonimizado) - concepto SSoT con artefactos JSON, Sidecars
Topología de fuentes y enlaces de referencia CDN
| Rol | Lugar | Propósito |
|---|---|---|
| Primary / SSoT | git.gitcover.org/GCC | Almacén canónico (firmado con GPG, versionado) |
| Public OSS Mirror / CDN | codeberg.org/gitcover-commons | Espejo de solo lectura; descubrimiento FLOSS |
| Community Hub | github.com/gitcover-commons | Issues & Discussions; referencia del código fuente en Codeberg |
Nota: Esta asignación de fuentes, mirror y community hub refleja el estado actual y puede cambiar. Por favor, consulte la fuente canónica respectiva en gitcover.org para conocer el estado actual.