Zugriffsmodell — Multi-User + Kundenzugriff

Zwei Haupt-Szenarien im Bild

Fuer das Verstaendnis reichen zwei Diagramme: wie das Team intern arbeitet, und wie ein Kunde von aussen mit Auth auf seinen Bereich kommt.

Diagramm A — Intern: Team arbeitet am gleichen Repo

Alle Teammitglieder haben vollen Zugriff auf den gesamten Vault. Sync laeuft ueber Git — keine App-Layer dazwischen.

flowchart TB
  classDef human fill:#fef3c7,stroke:#f59e0b,stroke-width:2px,color:#78350f
  classDef local fill:#dbeafe,stroke:#3b82f6,stroke-width:2px,color:#1e3a8a
  classDef server fill:#d1fae5,stroke:#10b981,stroke-width:2px,color:#064e3b

  M["👤 Marvin<br>Laptop"]:::human
  C["👤 Canay<br>Laptop"]:::human
  T["👤 weitere Teammitglieder"]:::human

  subgraph Local["💻 Lokale Obsidian-Vaults"]
    direction LR
    MV["Vault Marvin"]:::local
    CV["Vault Canay"]:::local
    TV["Vault ..."]:::local
  end

  GR["🗄️ Zentrales Git-Repo<br>GitHub Private<br>oder Gitea self-hosted"]:::server

  M ==> MV
  C ==> CV
  T ==> TV
  MV <==>|git pull / push<br>SSH-Key + 2FA| GR
  CV <==>|git pull / push<br>SSH-Key + 2FA| GR
  TV <==>|git pull / push<br>SSH-Key + 2FA| GR

Merkmale:

  • Jeder arbeitet lokal in seinem eigenen Obsidian-Vault-Klon — offline-faehig, schnell.
  • Git ist der Synchronisations-Mechanismus. Konflikte → Merge.
  • Auth passiert beim Repo (SSH-Keys + 2FA auf GitHub / Gitea). Keine App-Layer.
  • Alle Rollen (Marvin, Canay, spaeter weitere) sehen alles.
  • Obsidian-Git-Plugin kann Auto-Commit + Auto-Pull machen (optional).

Diagramm B — Kunde mit Auth auf seinen Bereich

Der Kunde kommt nie direkt ans Git-Repo. Zwischen Repo und Kunde sitzen zwei Dinge: ein Export-Filter (entfernt interne Notizen, trennt Kunden voneinander) und ein Kunden-Portal mit eigener Auth.

flowchart LR
  classDef team fill:#fef3c7,stroke:#f59e0b,stroke-width:2px,color:#78350f
  classDef server fill:#d1fae5,stroke:#10b981,stroke-width:2px,color:#064e3b
  classDef filter fill:#fee2e2,stroke:#ef4444,stroke-width:2px,color:#7f1d1d
  classDef portal fill:#ede9fe,stroke:#8b5cf6,stroke-width:2px,color:#4c1d95
  classDef kunde fill:#fce7f3,stroke:#ec4899,stroke-width:2px,color:#831843

  subgraph Team["Team"]
    direction TB
    M["👤 Marvin"]:::team
    C["👤 Canay"]:::team
  end

  Repo["🗄️ Zentrales Repo<br>alle Kunden + _internal/"]:::server

  Filter["🔒 Export-Filter<br>_internal/ raus<br>visibility:internal raus<br>pro Kunde getrennt"]:::filter

  subgraph Portal["🌐 Kunden-Portal · HTTPS + Auth"]
    direction TB
    Auth["🔑 Auth-Layer<br>Clerk · Supabase · Custom"]:::portal
    TA["Tenant A<br>nur Kunde-A-Inhalte"]:::portal
    TB["Tenant B<br>nur Kunde-B-Inhalte"]:::portal
  end

  KA["👤 Kunde A"]:::kunde
  KB["👤 Kunde B"]:::kunde

  M ==>|voller Zugriff| Repo
  C ==>|voller Zugriff| Repo
  Repo ==> Filter
  Filter ==> Auth
  Auth --> TA
  Auth --> TB
  KA -->|Login + 2FA| Auth
  KB -->|Login + 2FA| Auth
  TA -.Freigabe · Kommentar.-> Repo
  TB -.Freigabe · Kommentar.-> Repo

Merkmale:

  • Vier Rollen, drei Grenzen. Team ↔ Repo ist die erste Grenze (Git-Auth). Repo ↔ Portal ist die zweite (Filter). Portal ↔ Kunde ist die dritte (Auth + Tenant).
  • Kunde A darf nie Kunde B sehen. Der Filter erzeugt pro Kunde einen separaten Export. Das Portal rendert nur, was zum eingeloggten Tenant gehoert. Zwei Verteidigungslinien.
  • Feedback-Loop zurueck ins Repo. Kommentare und Freigaben fliessen zurueck als feedback.md im passenden runs/-Ordner — der Kunde schreibt aber nie direkt ins Repo, das Portal vermittelt.
  • Auth-Anbieter: Clerk, Supabase, Auth0 — alle mit EU-Hosting fuer DSGVO. Eigene Lösung ginge auch, ist aber mehr Aufwand.
  • Phase-0/1: Dieses Diagramm zeigt den Zielzustand. Aktuell (kein Kunde im System) laeuft nur Diagramm A.

Drei Nutzungs-Szenarien (textlich)

Das System hat drei voellig unterschiedliche User-Typen mit ganz unterschiedlichen Rechten und Beduerfnissen. Eine Loesung fuer alle funktioniert nicht — wir brauchen zwei getrennte Oberflaechen (wie oben gezeigt).

Szenario 1 — Team intern (Marvin + Canay + spaeter weitere)

  • Was sie brauchen: Voller Zugriff. Gleichzeitiges Arbeiten am selben Vault. Versionierung und History.
  • Loesung: Privates Git-Repo (empfohlen: GitHub Private, alternative: Gitea self-hosted auf EU-Server).
  • UI: Obsidian-Vault lokal. Sync via git pull/git push. Optional: Obsidian Git Plugin fuer Auto-Sync alle paar Minuten.
  • Konflikt-Handling: Git-Merge. Bei gleichzeitigem Edit derselben Datei: manuelle Konfliktaufloesung.

Szenario 2 — Kunde sieht seinen Bereich

  • Was der Kunde braucht: Seine Posts zur Freigabe sehen, seinen Content-Kalender, seinen Monatsreport, kommentieren koennen, freigeben koennen. Mehr nicht.
  • Was der Kunde NICHT sehen darf: Interne Notizen (unsere Strategie, Preise, interne Kommunikation, andere Kunden).
  • Loesung: Separates Kunden-Portal (Web-App). Zieht aus dem Vault nur die fuer diesen Kunden freigegebenen Files.
  • UI: Web-Oberflaeche mit Login. Kunde sieht: sein Kanban (Posts in Draft / Zur Freigabe / Veroeffentlicht), seine Reports, Kommentar-Funktion.

Szenario 3 — Kunde ↔ Kunde Isolation

  • Hard Rule: Kunde A darf NIE Kunde B’s Daten sehen. Auch nicht versehentlich, nicht als Dateiliste, nicht als Link-Vorschau.
  • Loesung: Auth-Ebene auf dem Portal trennt komplett. Jeder Kunde hat eine eigene Session/Tenant. Im Portal wird nur das gerendert, was zum eingeloggten User gehoert.

Der Daten-Fluss

sequenceDiagram
    participant Marvin
    participant Repo as Git-Repo<br/>(privat)
    participant Agent as Claude-Agent
    participant Portal as Kunden-Portal
    participant Kunde

    Marvin->>Repo: Scoping in runs/ eintragen
    Marvin->>Agent: "Bereite Sprint 1 fuer Koehnemann vor, Anfrage-zu-Rechnung"
    Agent->>Repo: Pipeline ausfuehren, Stage-Outputs schreiben
    Agent->>Portal: Sprint-Plan + Architektur zur Freigabe pushen
    Kunde->>Portal: Login + Plan ansehen
    Kunde->>Portal: Kommentar oder "Freigegeben"
    Portal->>Repo: Feedback zurueckschreiben (runs/.../feedback.md)
    Agent->>Repo: Bei Freigabe → Implementation starten, Go-Live in performance.md vermerken

Wichtig: Der Kunde schreibt nie direkt ins Repo. Das Portal vermittelt. So bleibt die Source-of-Truth (das Repo) kontrollierbar.


Rollen-Modell

RolleKannKann NICHT
Team-Lead (Marvin, Canay)alles: Repo-Zugriff, Portal-Admin, Kunden anlegen, Skills/Pipelines aendern
Team-MemberRepo-Zugriff (lesend + schreibend in runs/ und intern/kunden/), keine Skill-/Pipeline-AenderungenRollen verwalten, Kunden loeschen
KundePortal-Zugriff auf seinen Tenant, Drafts ansehen, kommentieren, freigeben, Reports ansehenRepo, andere Kunden, interne Notizen
Gast (zeitlich befristet)nur lesen auf definierte Files im Portalalles andere

Rollen-Durchsetzung:

  • Repo-Ebene: GitHub Teams / Branch Protection / Deploy Keys.
  • Portal-Ebene: eigene Auth (Clerk / Auth0 / Supabase Auth) mit Row-Level-Security auf Tenant-Basis.

Sicherheits-Grundsaetze

  1. Secrets nie im Repo. API-Keys, Tokens, Passwoerter: .env.local (in .gitignore) + Secret-Manager (GitHub Secrets, Doppler, 1Password Secrets Automation).
  2. Interne-Only Dateien markieren. Dateien/Ordner mit _internal/ Prefix oder Frontmatter-Flag visibility: internal werden vom Portal-Export automatisch ausgeschlossen.
  3. Audit-Log. Jede kundenrelevante Aktion (Freigabe, Kommentar, Login) wird geloggt. Wiederherstellbar.
  4. DSGVO. Kunden-Daten auf EU-Servern. Data-Processing-Agreement im Template-Vertrag mit Kunden. Recht auf Loeschung umsetzbar.
  5. 2FA Pflicht fuer Team. Auf Repo + Portal-Admin.
  6. Rate-Limits + Input-Validation im Portal, Standard-Sicherheits-Hygiene.
  7. Regular Backups. Repo = Git-History reicht. Portal-DB = taegliches Backup.

Self-hosted Server? Reichen dann SSH + OS-Rechte?

Gute Intuition. Kurzantwort: Fuer die Team-Schicht ja, fuer die Kunden-Schicht nicht. Wir brauchen beides.

Warum OS-Rechte fuer Team reichen

SSH + Unix-User + chmod/chown/ACLs sind battle-tested und viel einfacher als eine Auth-Loesung nachzubauen. Fuer Marvin + Canay (+ ggf. spaeter ein:e Angestellte:r) ist das solide:

  • Authentifizierung: SSH-Keys. 2FA via hardware tokens (YubiKey) moeglich.
  • Autorisierung: Unix-Groups (agentur-team) + ACLs auf Ordner-Ebene.
  • Git-Hosting: eigener Gitea/Forgejo auf dem Server, Git-over-SSH.
  • Vorteil: keine GitHub-Abhaengigkeit, Daten liegen bei uns, DSGVO automatisch einfacher.

Warum Kunden das NICHT benutzen koennen

Fuenf Probleme:

  1. UX: Kunde ist nicht-technisch. SSH-Client, Keys generieren, cat post.md — keine Chance.
  2. Granularitaet: OS-Rechte sind File/Ordner-level. Wir wollen aber “Kunde darf kommentieren aber nicht editieren”, “Kunde sieht Draft aber nicht die internen Kommentare im gleichen Verzeichnis”. Das ist App-Logik, nicht Dateisystem.
  3. Collaboration-Features: Kommentar-Threads, Freigabe-Button, @-Mentions, Notifications — das bauen wir uns mit chmod nicht.
  4. Audit/Business-Logik: “Post freigegeben am X durch Kunde Y” ist ein Geschaeftsereignis, kein OS-Log-Eintrag.
  5. Isolation-Risiko: Alle Kunden auf demselben FS mit chmod-Trennung — ein falsches chmod 777, ein Pfad-Traversal-Bug im Skript, ein Symlink zu weit → Datenleck zwischen Kunden. Das ist eine Klasse von Fehlern, die eine Applikations-Layer mit sauberer Tenant-ID viel besser absichert.

Die Loesung: zwei Schichten (Defense-in-Depth)

flowchart TB
    subgraph OS["Schicht 1 — Server + OS"]
        SSH[SSH + Keys]
        Unix[Unix-User/Groups<br/>chmod/ACLs]
        Git[Gitea/Forgejo<br/>Git-over-SSH]
    end
    subgraph App["Schicht 2 — Applikations-Layer"]
        Auth[Web-Auth<br/>pro Kunde eigener Tenant]
        API[Portal-API<br/>Row-Level-Security]
        UI[Kunden-Web-UI]
    end
    Team[Marvin + Canay] -->|SSH direkt| OS
    OS -->|liest Files mit eingeschraenkten Rechten| App
    Kunde -->|nur via HTTPS + Login| App
    App -.-|Portal darf NUR sein Tenant-Verzeichnis<br/>lesen — erzwungen durch Unix-User| OS

Das Portal laeuft auf demselben Server als eigener Unix-User (portal-service) mit Zugriff nur auf eine definierte Datei-Wurzel. Falls die App einen Auth-Bug hat, sichert das Dateisystem als zweite Verteidigungslinie ab — Portal kann physisch nicht auf /team-internal/ zugreifen, weil chmod 750 team-internal/ + falscher Unix-User.

Empfehlung fuer den Stack

Phase 0/1 (Dogfooding): Wir fangen mit GitHub Private an — zero setup, sofort produktiv. Wenn wir spaeter auf eigenen Server wollen, migriert man Git-Repos in 10 Minuten zu Gitea.

Phase 2 (Kunden): Eigener Server bei Hetzner EU (DSGVO einfach, guenstig, solide). Darauf:

  • Gitea/Forgejo fuer Team
  • Next.js-Portal als systemd-Service unter eigenem Unix-User
  • Nginx als Reverse-Proxy mit Let’s Encrypt
  • Backups auf Hetzner Storage-Box oder offsite

Noch nicht jetzt entscheiden — das kommt mit Phase 2. Wichtig ist nur: wir verbauen uns den Weg nicht. Deshalb: clean Git-History von Tag 1, keine Secrets im Repo, Architektur-Trennung Team ↔ Kunde.


Stack-Entscheidungen (Vorschlag)

Phase 0/1 — noch ohne externe Kunden

Fuer Dogfooding reicht nur das Repo. Portal kommt spaeter.

  • Repo: GitHub Private (simpel, Marvin hat da Erfahrung, Canay kann leicht eingeladen werden).
  • Sync: git pull/git push manuell oder Obsidian-Git-Plugin fuer Auto-Commits.
  • Secrets: .env.local + GitHub Secrets fuer CI.

Phase 2 — Kunden-Portal

Hier wird’s interessant. Zwei Varianten:

Variante A — Custom Portal (Next.js o.ae.)

  • Komplette Kontrolle, maximale Flexibilitaet.
  • Aufwand: 2-4 Wochen fuer MVP.
  • Stack-Vorschlag: Next.js + Supabase (Auth + DB + Row-Level-Security eingebaut, EU-Region waehlbar).

Variante B — Notion oder Trello als Kunden-Bruecke

  • Kunde bekommt Einladung zu einem dedizierten Notion/Trello-Workspace.
  • Wir syncen Files/Statuses zwischen Repo und Notion via API.
  • Schneller zu launchen, aber abhaengig vom Drittanbieter + Branding leidet.

Variante C — Kunde bekommt eigenes Git-Repo (interessante Abkuerzung)

  • Pro Kunde ein privates Repo (auf GitHub oder unserem Gitea). Kunde wird als Collaborator hinzugefuegt mit Read-Rechten + Kommentar-Funktion ueber GitHub Issues/PRs.
  • Wir pushen nur sein gefiltertes Subset dorthin (Script macht das automatisch aus dem Haupt-Repo).
  • Wann geht das gut: Kunde ist selbst tech-affin (andere Agentur, IT-naher KMU, Developer-Shop, Marketing-Profi mit GitHub-Erfahrung).
  • Wann NICHT: Traditionelle KMU (Baeckerei, Autohaus, Handwerker). Die wollen klicken, nicht pullen.
  • Vorteile wenn’s passt: Minimaler Bau-Aufwand. Git-History als Audit-Log gratis. Pull-Requests als Freigabe-Mechanismus. Kommentare als Review. Kunde kann Markdown selbst editieren wenn er will.
  • Nachteile: Kein schickes UI. GitHub-Branding ueberall. Kunde muss Account haben und Basics kennen.

Vorschlag fuer Phase 2: Wir segmentieren nach Kundentyp:

  • Tech-affine Kunden → Variante C (eigenes Repo). Startet sofort, null Portal-Aufwand.
  • Traditionelle Kunden → Variante B (Notion). Als Bruecke, bis wir genug haben um C als Option abzuloesen oder ein eigenes Portal (A) zu bauen.
  • Variante A (Custom Portal) erst wenn 5+ traditionelle Kunden, die uns das rechtfertigen.

Das ist Pragmatismus: wir bauen kein Produkt-Fundament vor dem ersten Euro. Jede Variante hat einen sauberen Migrationspfad.


Entscheidungen die JETZT anstehen

  • Git-Host waehlen: GitHub Private (empfohlen) vs Gitea self-hosted.
  • Repo privat einrichten: Branch-Protection, Canay als Collaborator einladen.
  • .gitignore + Secret-Pattern: .env.local, *.key, secrets/ — in Grundstruktur als Datei anlegen.
  • _internal/ Konvention in conventions.md aufnehmen.

Entscheidungen die in Phase 2 anstehen

  • Variante A vs B fuers Portal
  • Auth-Anbieter (Clerk / Auth0 / Supabase / Custom)
  • Datenschutz: DPA-Vorlage, Auftragsverarbeitungsvertrag
  • Feedback/Comment-Mechanismus im Portal (Thread? Inline? Separate Tabelle?)