ED02 - The SME Organization 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 represented in a cleanly separated, traceable, and audit-proof way?
Today's practice in the SME sector knows no clean separation:
- Everything in one pot - private and business documents get mixed in one mailbox, one drive, one cloud account.
- No tenant separation - a holding with multiple companies keeps all books in a single instance, without it being clear which record belongs to which company.
- No sphere separation - for non-profits, ideell, vermögensverwaltend, zweckbetrieblich and wirtschaftlich transactions get mixed in one account, one folder, one repo.
- No role separation - the entrepreneur does everything himself (GF, bookkeeper, payroll, R&D), but the repo doesn't know in which role he is currently acting.
- No clear identity - records have no unambiguous identifier that assigns them to an organization, a sphere, a role.
Core Message
Structuring an organization as a GitCover federation means: Tenant (organization), Sphere (area of activity in the non-profit case) and Role (function of the actor) are defined as mandatory fields of every artifact - not as optional metadata. Git hooks check with every commit that the separation is complete. This results in 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 after-the-fact labels - they are structural mandatory fields, without which an artifact is not admitted into the repo.
The Three-Level Model: Tenant, Sphere, Role
(document, diary entry, land register entry)"] A --> T["Tenant
Which organization?"] A --> S["Sphere
Which area of activity?
(non-profit only)"] A --> R["Role
In which function does the actor act?"] T --> T1["ORG-1 (SME)"] T --> T2["ORG-1a (subsidiary)"] T --> T3["ORG-1b (subsidiary)"] S --> S1["ideell"] S --> S2["vermögensverwaltend"] S --> S3["zweckbetrieblich"] S --> S4["wirtschaftlich"] R --> R1["GF (Managing Director)"] R --> R2["Bookkeeper"] R --> R3["Payroll Officer"] R --> R4["Head of R&D"] 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 federation, 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 for each company |
| Non-profit organization | ORG-1 (non-profit) |
Additionally: sphere separation, § 52 AO, VBG exemption |
| Consortium / federation | 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 (Area of Activity, Only for Non-Profits)
The Sphere is only relevant for non-profit organizations - but then it is mandatory. Tax law (§ 51–68 AO) requires the separation of the four spheres so as not to jeopardize non-profit status.
| Sphere | Meaning | Example |
|---|---|---|
| ideell | Purpose-related business within the meaning of the statutes, tax-exempt | Workshop series, educational program |
| vermögensverwaltend | Management of the foundation's assets, tax-exempt | Interest from investments, rental income |
| zweckbetrieblich | Commercial business that fulfils the purpose, tax-exempt (§ 65 AO) | Membership fees, admission fees |
| wirtschaftlich | Commercial business that does not fulfil the purpose, subject to tax | Merchandising, advertising revenue |
tax-exempt
§ 52 AO"] V --> VV["vermögensverwaltend
tax-exempt"] V --> Z["zweckbetrieblich
tax-exempt
§ 65 AO"] V --> W["wirtschaftlich
taxable
§ 64 AO"] I --> F["Exemption
§ 3 Nr. 26/26a EStG"] Z --> F W --> ST["VAT/corporate tax
liable"] 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 a ground for revocation of non-profit status. A non-profit organization that does not separate wirtschaftlich and ideell transactions risks revocation of its non-profit status - and thus subsequent 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, power of representation |
| Bookkeeper | Bookkeeping, document archive | GoBD, e-invoicing, retention |
| Payroll Officer | Payroll accounting, social insurance notifications | SGB IV, DEÜV, LStDV |
| Head of R&D | Research, FZul hour records | FZulG, BSFZ, R&D vs. administration |
| Administrator | Repo administration, hooks, access | Git, GPG, access rights |
Practical example: The entrepreneur
E1is simultaneously GF, bookkeeper, researcher, developer, and head of R&D. 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 hour record. This keeps it traceable whether a record originated from the GF perspective (administration) or the R&D perspective (research).
Implementation in the Git Repo
Directory Structure
ORG-1/ # Tenant: SME
├── .gitcover/ # GitCover configuration
│ ├── LEGAL_ENTITY.v7g.json # Tenant identity (V7GUID)
│ ├── dictionaries/ # Spheres, roles, document types
│ └── schemas/ # JSON schemas for artifacts
├── diary/ # Diary (SSoT)
│ ├── entries/ # Daily entries
│ └── tenants/ # Tenant views (symlinks)
├── registry/ # Authority identifiers
├── sources/ # Document archive (PDF, XML, EML)
├── sidecars/ # .v7g.md sidecars
├── checks/ # Deadline check, sphere check
└── {more as needed}/
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 is anchored in the uuidV7 itself - as a 48-bit timestamp
according to RFC 9562 §5.7. The uuidV7 can be generated from a predetermined timestamp
(not only now()) via GitCover Helper (UuidV7Gen), with the remainder filled with
randomness. This way, the capture time is cryptographically linked to the
identity of the artifact and cannot be changed retroactively.
Important - What ends up in the Git repo: Only these structured artifacts (JSON entries, sidecars, documents) are added to the Git repo. Private documents are never added to the repo unless they have been explicitly introduced and classified as artifacts. Once it's in, it's in.
{
"$schema": "https://gitcover.org/schemas/diary-entry-1.0.schema.json",
"V7GUID": "<V7GUID-Class-aus-Registry>",
"uuidV7": "<uuidV7-Object-mit-vorgegebener-Zeitmarke>",
"author": "E1",
"role": "GF",
"tenant": "ORG-1",
"sphere": "ideell",
"source": "E1",
"source_sha256": "<SHA-256-...>",
"tags": ["lohnabrechnung", "sv-meldung"]
}
Composite Key explanation:
V7GUID(Class Identifier) classifies the document/action based on the.gitcoverregistry (what/which type).uuidV7(Object ID) is the concrete object identifier with the predetermined timestamp (RFC 9562 §5.7, 48-bit Unix-ms). Together, both form the GCPN Sidecar Composite KeyV7GUID:uuidV7. A separatedatetimeordatestring representation is redundant and is not maintained 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 layer.
Git Hooks Check the Separation
present?"} P1 -->|no| R1["Commit rejected
Tenant missing"] P1 -->|yes| P2{"Sphere
present?
(non-profit only)"} P2 -->|no| R2["Commit rejected
Sphere missing"] P2 -->|yes| P3{"Role
present?"} P3 -->|no| R3["Commit rejected
Role missing"] P3 -->|yes| P4{"Sphere
valid?"} P4 -->|no| R4["Commit rejected
Invalid sphere"] P4 -->|yes| OK["Commit accepted"] 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 | Fails on |
|---|---|---|
| Pre-Commit | tenant field present and valid |
Missing or unknown tenant |
| Pre-Commit | sphere field present (non-profit only) |
Missing sphere for non-profit 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 | Automatic index generation per tenant and sphere | - |
Tenant Federation: Multiple Organizations
meta-repo"] H --> R1["ORG-1a (Subsidiary 1)
own repo"] H --> R2["ORG-1b (Subsidiary 2)
own repo"] H --> R3["ORG-1c (Subsidiary 3)
own repo"] R1 --> V1["V7GUID reference
to ORG-1"] R2 --> V2["V7GUID reference
to ORG-1"] R3 --> V3["V7GUID reference
to ORG-1"] H --> M["meta-repo
cross-references via V7GUID
no postings"] 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 federation (holding with subsidiaries, consortium) is linked via V7GUID references - not via shared repos. Each tenant keeps its own bookkeeping, but the holding's meta-repo can reference documents and entries of the subsidiaries without copying them.
Important: The meta-repo contains no postings - only cross-references. Bookkeeping remains with each tenant. The meta-repo is an index layer, not a bookkeeping layer.
Risk Leverage
| Today (cheap) | Tomorrow (audit-proof) | Risk mitigated |
|---|---|---|
tenant field per artifact |
Clear assignment during holding audits | Commingling of assets between companies |
sphere field per entry |
Non-profit status defended | Revocation § 55 AO (commingling of assets) |
role field per entry |
Traceable in which function the action was taken | Role conflict, unauthorized representation |
| Pre-commit hook checks sphere | Sphere separation without gaps | manual sphere errors |
| V7GUID reference instead of copy | Unambiguous 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 (non-profit) |
| FA-3.2 | Sphere tag mandatory for every land register entry and diary entry | MUST (non-profit) |
| FA-3.3 | Pre-commit hook checks sphere tag completeness | MUST (non-profit) |
| TA-2.3 | Pre-commit hook: sphere tag check (non-profit only) | MUST (non-profit) |
| FA-1.3 | V7GUID per entry (uniqueness across tenants) | MUST |
The complete list of requirements in Harness-Anforderungen.md.
Sources
- AO (§§ 51–68 - non-profit status, sphere separation, § 55 Abs. 1 Nr. 5 - commingling of assets)
- EStG (§ 3 Nr. 26/26a - lump sums, sphere relevance)
- GoBD (BMF letters, process documentation, tenant separation)
AFJD/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 to 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.