ED02 - The SME Organizational Model: Tenants, Spheres, Roles
Problem
An entrepreneur founds an organization — and faces the question: How do I structure my organization so that compliance obligations are cleanly separated, traceable, and represented in an audit-proof manner?
Current practice in the SME sector does not provide clean separation:
- Everything in one pot — private and business documents mix in one mailbox, one drive, one cloud account.
- No tenant separation — a holding with several companies keeps all books in one instance, without clarity about which record belongs to which company.
- No sphere separation — in nonprofit status, ideell, vermögensverwaltend, zweckbetrieblich, and wirtschaftlich transactions mix in one account, one folder, one repo.
- No role separation — the entrepreneur does everything himself (GF, accountant, payroll, R&D), but the repo does not know in which role he is currently acting.
- No clear identity — records have no unique identifier that assigns them to an organization, a sphere, a role.
Key Statement
Structuring an organization as a GitCover association means: Tenant (organization), sphere (activity area in nonprofit status), and role (function of the actor) are defined as mandatory fields of every artifact — not as optional metadata. Git hooks check on every commit that the separation is complete. This creates Compliance by Design: The structure of the organization is anchored in the structure of the repo.
Compliance by Design: Tenant, sphere, and role are not subsequent labels — they are structural mandatory fields without which an artifact is not admitted into the repo.
The Three-Level Model: Tenant, Sphere, Role
(Beleg, Tagebucheintrag, Grundbuch-Eintrag)"] A --> T["Tenant
Welche Organisation?"] A --> S["Sphäre
Welcher Tätigkeitsbereich?
(nur gemeinnützig)"] A --> R["Rolle
In welcher Funktion handelt der Akteur?"] T --> T1["ORG-1 (KMU)"] T --> T2["ORG-1a (Tochter)"] T --> T3["ORG-1b (Tochter)"] S --> S1["ideell"] S --> S2["vermögensverwaltend"] S --> S3["zweckbetrieblich"] S --> S4["wirtschaftlich"] R --> R1["GF (Geschäftsführer)"] R --> R2["Buchhalter"] R --> R3["Lohnverantwortlicher"] R --> R4["F&E-Leiter"] R --> R5["Administrator"] style A fill:#0F1B33,stroke:#0F1B33,color:#FBFAF7 style T fill:#DBEAFE,stroke:#1D4ED8,color:#0F1B33 style S fill:#D1FAE5,stroke:#0A7F5C,color:#0F1B33 style R fill:#FDBA74,stroke:#C2410C,color:#0F1B33 style T1 fill:#DBEAFE,stroke:#1D4ED8,color:#0F1B33 style T2 fill:#DBEAFE,stroke:#1D4ED8,color:#0F1B33 style T3 fill:#DBEAFE,stroke:#1D4ED8,color:#0F1B33 style S1 fill:#D1FAE5,stroke:#0A7F5C,color:#0F1B33 style S2 fill:#D1FAE5,stroke:#0A7F5C,color:#0F1B33 style S3 fill:#D1FAE5,stroke:#0A7F5C,color:#0F1B33 style S4 fill:#D1FAE5,stroke:#0A7F5C,color:#0F1B33 style R1 fill:#FDBA74,stroke:#C2410C,color:#0F1B33 style R2 fill:#FDBA74,stroke:#C2410C,color:#0F1B33 style R3 fill:#FDBA74,stroke:#C2410C,color:#0F1B33 style R4 fill:#FDBA74,stroke:#C2410C,color:#0F1B33 style R5 fill:#FDBA74,stroke:#C2410C,color:#0F1B33
Level 1 - Tenant (Organization)
A tenant is a legally independent organizational unit for which its own record-keeping obligations exist. In the GitCover association, each tenant is maintained as its own Organization (Git User) with associated Git repos (or its own branch namespace).
| Tenant Type | Example (placeholder) | Own obligations |
|---|---|---|
| Sole proprietorship | ORG-1 (SME) |
GoBD, AO, e-invoicing |
| Holding with subsidiaries | ORG-1 (holding), ORG-1a/ORG-1b (subsidiaries) |
Separate bookkeeping and separate annual financial statements per company |
| Nonprofit organization | ORG-1 (nonprofit) |
Additionally: sphere separation, § 52 AO, VBG exemption |
| Consortium / association | ORG-1, ORG-2, ORG-3 |
Own tenant per organization, cross-references via V7GUID |
Practical example: An entrepreneur (
E1) runs a holding (ORG-1) with two subsidiaries (ORG-1a,ORG-1b). Each subsidiary is its own tenant with its own repo. The holding has a meta-repo that links the subsidiaries via V7GUID references — but the subsidiaries' books remain separate.
Level 2 - Sphere (activity area, only for nonprofit status)
The sphere is relevant only for nonprofit organizations — but then mandatory. Tax law (§ 51–68 AO) requires separation of the four spheres so as not to jeopardize nonprofit status.
| Sphere | Meaning | Example |
|---|---|---|
| ideell | Charitable purpose operation within the meaning of the articles of association, tax-exempt | Workshop series, educational offering |
| vermögensverwaltend | Management of foundation assets, tax-exempt | Interest from investments, rental income |
| zweckbetrieblich | Commercial business operation that fulfills the purpose, tax-exempt (§ 65 AO) | Membership fees, admission fees |
| wirtschaftlich | Commercial business operation that does not fulfill the purpose, taxable | Merchandising, advertising revenues |
steuerfrei
§ 52 AO"] V --> VV["vermögensverwaltend
steuerfrei"] V --> Z["zweckbetrieblich
steuerfrei
§ 65 AO"] V --> W["wirtschaftlich
steuerpflichtig
§ 64 AO"] I --> F["Freibetrag
§ 3 Nr. 26/26a EStG"] Z --> F W --> ST["USt/KSt
pflichtig"] style V fill:#0F1B33,stroke:#0F1B33,color:#FBFAF7 style I fill:#D1FAE5,stroke:#0A7F5C,color:#0F1B33 style VV fill:#D1FAE5,stroke:#0A7F5C,color:#0F1B33 style Z fill:#D1FAE5,stroke:#0A7F5C,color:#0F1B33 style W fill:#FDBA74,stroke:#C2410C,color:#0F1B33 style F fill:#DBEAFE,stroke:#1D4ED8,color:#0F1B33 style ST fill:#FDBA74,stroke:#C2410C,color:#0F1B33
Risk of missing sphere separation: Commingling of assets (§ 55 Abs. 1 Nr. 5 AO) is grounds for revocation of nonprofit status. A nonprofit organization that does not separate commercial and ideell transactions risks revocation of nonprofit status — and thus retroactive taxation of reserves (up to 15(!) years retroactively).
Level 3 - Role (function of the actor)
The role documents in which function an actor acted. This is important because in many organizations one person often holds several roles (especially in SMEs) — and because the role determines which obligations apply.
| Role | Function | Typical obligations |
|---|---|---|
| GF (Managing Director) | External representation, overall responsibility | GoBD, AO, deadlines, authority to represent |
| Accountant | Bookkeeping, document archive | GoBD, e-invoicing, retention |
| Payroll Officer | Payroll accounting, social insurance notifications | SGB IV, DEÜV, LStDV |
| R&D Lead | Research, FZul hourly records | FZulG, BSFZ, R&D vs. administration |
| Administrator | Repo management, hooks, accesses | Git, GPG, access rights |
Practical example: The entrepreneur
E1is simultaneously GF, accountant, researcher, developer, and R&D lead. In the repo, every artifact documents in which role he acted — e.g.role: "GF"for a resolution,role: "F&E-Leiter"for an FZul hourly record. This keeps it traceable whether a record originated from the GF perspective (administration) or the R&D perspective (research).
Role is not person: The role describes the function (and, in the case of declarations, the represented party), not the identity. The same person can act or sign a document in several roles; the technical execution identity (account, key) is again a separate level. The five levels person · function · represented party · execution identity · signature act are described in ED03.
Implementation in the Git Repo
Directory Structure
ORG-1/ # Tenant: KMU
├── .gitcover/ # GitCover-Konfiguration
│ ├── LEGAL_ENTITY.gc.json # Tenant-Identität (V7GUID)
│ ├── dictionaries/ # Sphären, Rollen, Beleg-Typen
│ └── schemas/ # JSON-Schemata für Artefakte
├── diary/ # Tagebuch (SSoT)
│ ├── entries/ # Tages-Einträge
│ └── tenants/ # Tenant-Sichten (Symlinks)
├── registry/ # Behörden-Identifikatoren
├── sources/ # Belegarchiv (PDF, XML, EML)
├── sidecars/ # .gc.md Sidecars
├── checks/ # Fristen-Check, Sphären-Check
└── {weitere bei Bedarf}/
JSON Artifact with Tenant, Sphere, Role
Every artifact (diary entry, document, land register entry) contains tenant, sphere, and role as mandatory fields. Central core principle of temporal traceability: The capture time is not a separate field, but anchored in the uuidV7 itself — as a 48-bit timestamp according to RFC 9562 §5.7. The uuidV7 can be generated from a predefined timestamp (not only now()) via the GitCover helper (gc-v7guid), with the rest filled with randomness. Thus the capture time is cryptographically linked to the artifact's identity and cannot be changed subsequently.
Important - What ends up in the Git repo: Only these structured artifacts (JSON entries, sidecars, documents) are admitted into the Git repo. Private documents are never admitted into the repo unless they have been explicitly introduced and classified as an artifact. Once it is in, it is in.
{
"$schema": "https://gitcover.org/schemas/diary-entry-1.0.schema.json",
"V7GUID": "<V7GUID-Class-aus-Registry>",
"docUid": "<uuidV7-Object-mit-vorgegebener-Zeitmarke>",
"author": "E1",
"role": "GF",
"tenant": "ORG-1",
"sphere": "ideell",
"source": "E1",
"source_sha256": "<SHA-256-...>",
"tags": ["lohnabrechnung", "sv-meldung"]
}
Explanation Composite Key:
V7GUID(Class Identifier) classifies the document/the action based on the.gitcoverregistry (what/which type).uuidV7(Object ID) is the concrete object identifier with the predefined timestamp (RFC 9562 §5.7, 48-bit Unix ms). Together they form the GCPN Sidecar Composite KeyV7GUID:uuidV7. A separatedatetimeordatestring representation is redundant and is not kept in the facts — the 48-bit timestamp is already contained in theuuidV7. String representations for DTO/HTMX transfer are the harness's responsibility, not the facts level.
Git Hooks Check the Separation
vorhanden?"} P1 -->|nein| R1["Commit abgelehnt
Tenant fehlt"] P1 -->|ja| P2{"Sphäre
vorhanden?
(nur gemeinnützig)"} P2 -->|nein| R2["Commit abgelehnt
Sphäre fehlt"] P2 -->|ja| P3{"Rolle
vorhanden?"} P3 -->|nein| R3["Commit abgelehnt
Rolle fehlt"] P3 -->|ja| P4{"Sphäre
gültig?"} P4 -->|nein| R4["Commit abgelehnt
ungültige Sphäre"] P4 -->|ja| OK["Commit akzeptiert"] style C fill:#0F1B33,stroke:#0F1B33,color:#FBFAF7 style H1 fill:#DBEAFE,stroke:#1D4ED8,color:#0F1B33 style P1 fill:#DBEAFE,stroke:#1D4ED8,color:#0F1B33 style P2 fill:#DBEAFE,stroke:#1D4ED8,color:#0F1B33 style P3 fill:#DBEAFE,stroke:#1D4ED8,color:#0F1B33 style P4 fill:#DBEAFE,stroke:#1D4ED8,color:#0F1B33 style R1 fill:#FDBA74,stroke:#C2410C,color:#0F1B33 style R2 fill:#FDBA74,stroke:#C2410C,color:#0F1B33 style R3 fill:#FDBA74,stroke:#C2410C,color:#0F1B33 style R4 fill:#FDBA74,stroke:#C2410C,color:#0F1B33 style OK fill:#D1FAE5,stroke:#0A7F5C,color:#0F1B33
| Hook | Check | Error if |
|---|---|---|
| Pre-commit | tenant field present and valid |
Missing or unknown tenant |
| Pre-commit | sphere field present (nonprofit only) |
Missing sphere for nonprofit tenant |
| Pre-commit | sphere value valid (ideell/vermögensverwaltend/zweckbetrieblich/wirtschaftlich) |
Invalid sphere value |
| Pre-commit | role field present and valid |
Missing or unknown role |
| Post-commit | Auto-index generation per tenant and sphere | - |
Tenant Association: Multiple Organizations
Meta-Repo"] H --> R1["ORG-1a (Tochter 1)
eigenes Repo"] H --> R2["ORG-1b (Tochter 2)
eigenes Repo"] H --> R3["ORG-1c (Tochter 3)
eigenes Repo"] R1 --> V1["V7GUID-Referenz
auf ORG-1"] R2 --> V2["V7GUID-Referenz
auf ORG-1"] R3 --> V3["V7GUID-Referenz
auf ORG-1"] H --> M["Meta-Repo
Querverweise über V7GUID
keine Buchungen"] style H fill:#0F1B33,stroke:#0F1B33,color:#FBFAF7 style R1 fill:#DBEAFE,stroke:#1D4ED8,color:#0F1B33 style R2 fill:#DBEAFE,stroke:#1D4ED8,color:#0F1B33 style R3 fill:#DBEAFE,stroke:#1D4ED8,color:#0F1B33 style V1 fill:#D1FAE5,stroke:#0A7F5C,color:#0F1B33 style V2 fill:#D1FAE5,stroke:#0A7F5C,color:#0F1B33 style V3 fill:#D1FAE5,stroke:#0A7F5C,color:#0F1B33 style M fill:#10A987,stroke:#0A7F5C,color:#FBFAF7
A tenant association (holding with subsidiaries, consortium) is linked via V7GUID references — not via shared repos. Each tenant retains its own bookkeeping, but the holding's meta-repo can reference the subsidiaries' documents and entries without copying them.
Important: The meta-repo contains no postings — only cross-references. Bookkeeping remains with each tenant. The meta-repo is an index level, not an accounting level.
Risk Leverage
| Today (cheap) | Tomorrow (audit-proof) | Risk reduced |
|---|---|---|
tenant field per artifact |
Clear assignment during holding audit | Commingling of assets between companies |
sphere field per entry |
Nonprofit status defended | Revocation § 55 AO (commingling of assets) |
role field per entry |
Traceable in which function was acted | Role conflict, unauthorized representation |
| Pre-commit hook checks sphere | Sphere separation without gaps | manual sphere errors |
| V7GUID reference instead of copy | Unique assignment without duplicates | Inconsistency with copies |
Harness Requirement (Preview)
Derivable from ED02:
| ID | Requirement | Priority |
|---|---|---|
| FA-1.4 | Tenant assignment (tenant: "ORG-1") per entry |
MUST |
| FA-3.1 | Sphere tags: ideell/vermögensverwaltend/zweckbetrieblich/wirtschaftlich |
MUST (nonprofit) |
| FA-3.2 | Sphere tag mandatory per land register entry and diary entry | MUST (nonprofit) |
| FA-3.3 | Pre-commit hook checks sphere tag completeness | MUST (nonprofit) |
| TA-2.3 | Pre-commit hook: sphere tag check (nonprofit only) | MUST (nonprofit) |
| FA-1.3 | V7GUID per entry (cross-tenant uniqueness) | MUST |
The complete requirements list in Harness-Anforderungen.md.
Sources
- AO (§§ 51–68 - nonprofit status, sphere separation, § 55 Abs. 1 Nr. 5 - commingling of assets)
- EStG (§ 3 Nr. 26/26a - flat-rate allowances, sphere reference)
- GoBD (BMF letter, procedural documentation, tenant separation)
E1/agents/(anonymized) - SSoT concept with tenant, sphere, role
Source Topology and CDN Reference Links
| Role | Location | Purpose |
|---|---|---|
| Primary / SSoT | git.gitcover.org/GCC | Canonical storage (GPG-signed, versioned) |
| Public OSS Mirror / CDN | codeberg.org/gitcover-commons | Read-only mirror; FLOSS discovery |
| Community Hub | github.com/gitcover-commons | Issues & Discussions; source code reference on Codeberg |
Note: This assignment of sources, mirror, and community hub reflects the current state and may change. Please check the respective canonical source on gitcover.org for the current state.