ED03 - Formato de diario: artefactos JSON, firma temporal, V7GUID, sidecars
Problema
Un empresario quiere llevar su diario en Git - pero ¿cómo se ve exactamente una entrada a nivel técnico? La práctica actual no conoce una 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ómina como PDF - no evaluables por máquina (§ 147 Abs. 6 AO), no rastreables de forma retrógrada/progresiva
- Sin identidad única - 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 cuándo? El tiempo de registro no está vinculado criptográficamente con 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 (Pre-Commit-Hook)
- V7GUID (Class Identifier) - clasifica el documento/la Action
según la Registry
.gitcover(qué/qué tipo); sirve para la organización de archivado y las consultas a la DB (p. ej., EF Core en Vertical App) - uuidV7 (Object ID) - ya es por sí sola la identidad única
(DocID) de la entrada, RFC 9562 §5.7, generada a partir de una
marca de tiempo predefinida (o
now()) vía helper de GitCover (UuidV7Gen), el resto se rellena con aleatoriedad - Composite Key
V7GUID:uuidV7- la clave compuesta del sidecar GCPN, sirve para la organización de archivado y las consultas; el tiempo de registro está contenido 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 - Trazabilidad retrógrada/progresiva - desde el asiento contable →
comprobante → entrada de diario →
uuidV7y de vuelta, todo resoluble
Compliance by Design: el formato no se añade a posteriori - es estructural. El esquema, el V7GUID (Class), el uuidV7 (Object) y el sidecar son campos obligatorios sin los cuales una entrada no se incorpora al repo.
Sin
datetime/dateredundante: el tiempo de registro está anclado en la propiauuidV7(marca de tiempo de 48 bits, RFC 9562 §5.7). Una representación String separada en esquemas JSON o datos JSON es redundante y no se mantiene en los hechos. La representación 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
Pre-Commit-Hook.
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", "uuidV7",
"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)"
},
"uuidV7": {
"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: el tiempo de registro está contenido en lauuidV7(marca de tiempo de 48 bits, RFC 9562 §5.7). Una representación String separada es redundante y no se mantiene en los hechos. LauuidV7por sí sola es la DocID única; el Composite KeyV7GUID:uuidV7sirve para la organización de archivado y las consultas a la DB.
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 el tiempo de registro. Está anclado en la propia
uuidV7y no puede modificarse a posteriori sin invalidar la GUID. Un campodatetimeseparado no existe - el tiempo está contenido en lauuidV7. La representación String para DTO/HTMX es responsabilidad del Harness.
Extensión de 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 del 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: crear 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 la 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():
- Analizar 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 - Composite Key -
V7GUID(Class de la Registry) :uuidV7(Object con marca de tiempo) - Documentación GoBD - registro inmediato anclado de forma trazable (sin campo datetime separado)
Referencia GoBD: el registro inmediato (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>",
"uuidV7": "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
}
}
Composite Key
V7GUID:uuidV7:
V7GUID(campo 1) - Class Identifier del sidecar (clasificación según la 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?)Así no solo el documento queda identificado de forma única, 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á contenido en los valoresuuidV7.
Trazabilidad 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 trazabilidad retrógrada corresponde a GoBD Rz. 146 (trazabilidad). La trazabilidad progresiva corresponde a GoBD Rz. 147 (verificabilidad). Ambas quedan 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 de los justificantes de horas FZul (ED28). La separación I+D vs. administración se realiza mediante el campo
role(F&E-Leitervs.GF) y lastags(["fzul", "forschung"]vs.["verwaltung"]).
Qué acaba 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 se incorporan nunca al repo, salvo que se hayan introducido y clasificado explícitamente como artefacto. Es un requisito de protección de datos y de cumplimiento (RGPD, GoBD).
Apalancamiento de riesgo
| Hoy (barato) | Mañana (a prueba de auditoría) | Riesgo mitigado |
|---|---|---|
Artefacto JSON con V7GUID (Class) + uuidV7 (Object) |
Tiempo de registro anclado criptográficamente en uuidV7 | Cuestionamiento del tiempo de registro |
| Validación de esquema (Pre-Commit) | Solo entran al repo entradas válidas | Violación de esquema, campos obligatorios incompletos |
Sidecar con Composite Key V7GUID:uuidV7 |
Acto de clasificación trazable | Cuestionamiento de la clasificación |
source_sha256 por entrada |
Trazabilidad retrógrada | Cuestionamiento de la asignación del comprobante |
Helper UuidV7Gen para claves externas |
Registro inmediato documentado (marca de tiempo en uuidV7) | Infracción GoBD "no inmediato" |
Requisitos del Harness (vista previa)
Se puede deducir de ED03:
| ID | Requisito | Prioridad |
|---|---|---|
| FA-1.1 | Entradas de diario como artefactos JSON (Schema-First) | MUST |
| FA-1.2 | Composite Key V7GUID (Class) : uuidV7 (Object) - sin campo datetime/date separado; tiempo en la uuidV7 (RFC 9562 §5.7) |
MUST |
| FA-1.3 | uuidV7 generada a partir de una marca de tiempo predefinida (no now()) vía 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 Composite Key V7GUID:uuidV7 (Class + Object) |
MUST |
| FA-2.12 | Trazabilidad retrógrada (asiento → comprobante → uuidV7) |
MUST |
| FA-2.13 | Trazabilidad 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 la 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/(helpersgen/decode) - GoBD (escrito del BMF, Rz. 146 - trazabilidad, Rz. 147 - verificabilidad)
- AO (§ 147 Abs. 2 - "evaluable por máquina", § 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 | Almacenamiento canónico (firmado con GPG, versionado) |
| Public OSS Mirror / CDN | codeberg.org/gitcover-commons | Réplica 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, compruebe la fuente canónica correspondiente en gitcover.org para conocer el estado actual.