# Repomap Übersicht aller Dateien und Ordner im Projekt. ``` go-wiki/ ├── main.go ← Einstiegspunkt. Lädt .env, prüft Pflicht-Secrets │ (SESSION_SECRET, ADMIN_TOKEN), lädt teams.yaml und │ baut die TeamRegistry auf, richtet alle Routen ein │ und verwaltet den Graceful Shutdown (wartet auf │ laufende Requests bevor der Server sich bei │ CTRL+C / docker stop beendet). │ ├── go.mod ← Go-Moduldefinition (Name, Go-Version, direkte Dependencies). │ Entspricht in etwa einer package.json in Node. │ ├── go.sum ← Automatisch generierte Checksummen aller Dependencies. │ Nicht manuell bearbeiten. │ ├── Dockerfile ← Multi-Stage Build: Stage 1 kompiliert das Binary mit dem │ Go-Compiler, Stage 2 packt nur das fertige Binary in ein │ minimales Alpine-Linux-Image (mit git, für den Webhook). │ ├── deploy.sh ← Shell-Skript zum Hochladen einer lokalen Markdown-Datei │ auf den Wiki-Server via REST-API (curl + Team-Key oder │ Admin-Token). │ ├── .gitignore ← Schließt Binary, data/-Ordner, .env, teams.yaml und │ .DS_Store aus Git aus. │ ├── .dockerignore ← Schließt data/, Binary und .git aus dem Docker-Build- │ Kontext aus — macht den Build schneller. │ ├── .env.example ← Vorlage für die .env-Datei. Zeigt alle verfügbaren │ Umgebungsvariablen mit Erklärungen (u.a. SESSION_SECRET │ und ADMIN_TOKEN — beides Pflichtfelder ohne unsicheren │ Default). Kopieren nach .env und mit echten Werten befüllen. │ ├── teams.yaml.example ← Vorlage für die Team-Konfiguration. Jedes Team bekommt │ eine ID (a-z, 0-9, -, _), einen Anzeigenamen und einen │ Zugriffskey. Kopieren nach teams.yaml. │ ├── golang-wiki-spec.md ← Ursprüngliche Projektspezifikation (Planungsdokument). │ ├── README.md ← Dokumentation: Server starten, Teams anlegen, Seiten │ anlegen, Deployment, MCP-Integration, API-Übersicht. │ ├── REPOMAP.md ← Diese Datei. │ ├── handler/ │ ├── registry.go ← TeamRegistry: zentrale Stelle die weiß welche Teams es │ │ gibt, wo ihre Markdown-Dateien liegen (data//content) │ │ und welcher Zugriffskey zu welchem Team gehört. Löst Keys │ │ timing-sicher auf (subtle.ConstantTimeCompare) und │ │ unterscheidet Team-Key vom globalen Admin-Token. │ │ Unterstützt Hot-Reload (Reload()) für teams.yaml-Änderungen │ │ ohne Serverneustart. Stellt außerdem die Request-Context- │ │ Helfer bereit (WithTeam/TeamFromContext/IsAdminFromContext). │ │ │ ├── teams_config.go ← Lädt und validiert teams.yaml (LoadTeamsConfig) und │ │ überwacht die Datei per mtime-Polling auf Änderungen │ │ (StartTeamsConfigWatcher) — lädt neue/geänderte/entfernte │ │ Teams automatisch in die Registry. │ │ │ ├── cache.go ← In-Memory-Cache für Wiki-Seiten, ein PageCache pro Team. │ │ Beim Serverstart werden alle .md-Dateien eines Teams │ │ einmal geladen und im Arbeitsspeicher gehalten — Metadaten │ │ (Titel, Tags, Hidden) sind danach sofort verfügbar ohne │ │ Dateisystem-Zugriff. Thread-sicher via sync.RWMutex. │ │ Hält außerdem den BM25-Suchindex des Teams aktuell. │ │ Wird nach jedem Upload/Delete sofort aktualisiert. │ │ │ ├── search.go ← In-Memory-Volltextindex mit BM25-Ranking (invertierter │ │ Index: Begriff → Dokumente). Titel-Treffer werden 3x, │ │ Tag-Treffer 2x höher gewichtet als Body-Treffer. Genutzt │ │ von /search (Web) und dem search_pages-MCP-Tool. │ │ │ ├── session.go ← Signierte Web-Session fürs Team-Login. SignSession/ │ │ VerifySession erzeugen bzw. prüfen einen HMAC-SHA256- │ │ signierten, ablaufenden Cookie-Wert ("team:", "admin" │ │ oder "admin:") — der Server signiert nur eine │ │ Behauptung, nie den Zugriffskey selbst. │ │ │ ├── auth.go ← Team-Login fürs Web: Formular (GET/POST /login), Logout │ │ (/logout) und das Umschalten des Admins in ein Team │ │ (POST /switch-team, ausgelöst von den "Öffnen"-Buttons in │ │ templates/teams-admin.html — GET /switch-team ist nur ein │ │ Redirect-Alias auf /admin/teams). │ │ Nutzt den LoginRateLimiter (ratelimit.go) gegen Brute-Force │ │ und schreibt Login-Versuche ins Audit-Log. clientIP() │ │ liest bewusst nur r.RemoteAddr, keinen X-Forwarded-For- │ │ Header (sonst wäre das Rate-Limiting ohne bekannte, │ │ vertrauenswürdige Proxy-Kette leicht auszuhebeln). │ │ │ ├── ratelimit.go ← LoginRateLimiter: Fixed-Window-Zähler pro Client-IP │ │ (5 Fehlversuche/Minute). Wird sowohl vom Team-Login │ │ (auth.go) als auch vom Seiten-Passwort (wiki.go, │ │ HandlePasswordSubmit) genutzt, damit keiner der beiden │ │ Auth-Wege unbegrenzt durchprobiert werden kann. │ │ │ ├── wiki.go ← Liefert Wiki-Seiten des aktiven Teams aus (GET /:pfad, │ │ Team kommt aus dem Request-Context). Liest Markdown- │ │ Datei, prüft Seiten-Passwortschutz via Cookie │ │ (konstantzeitiger Vergleich + Rate-Limiting), baut die │ │ Sidebar-Navigation rekursiv aus der Ordnerstruktur und │ │ rendert das HTML-Template. Verarbeitet auch │ │ POST /auth/:pfad (Passwort-Formular) und GET /search │ │ (BM25-Trefferliste im normalen Wiki-Layout). │ │ │ ├── api.go ← REST-API für das Deployment. POST /api/upload lädt eine │ │ Markdown-Datei hoch, DELETE /api/delete löscht eine Datei. │ │ Team-Key wirkt nur aufs eigene Team; der globale Admin-Key │ │ muss das Zielteam explizit per Parameter "team" angeben. │ │ Prüft Pfade gegen Path-Traversal ("..") und aktualisiert │ │ nach jeder Änderung den PageCache sowie das Audit-Log. │ │ │ ├── webhook.go ← Git-Webhook-Handler (POST /api/webhook, nur aktiv wenn │ │ WEBHOOK_SECRET gesetzt ist). Erkennt den Anbieter anhand │ │ der Request-Headers und prüft die Signatur: GitHub │ │ (X-Hub-Signature-256, HMAC-SHA256), GitLab │ │ (X-Gitlab-Token, direkter Vergleich), Bitbucket │ │ (X-Hub-Signature, HMAC-SHA256). Nach erfolgreicher Prüfung: │ │ git pull im gemeinsamen data-Wurzelverzeichnis, danach │ │ Cache aller Teams komplett neu aufbauen (RebuildAll). │ │ │ ├── mcp.go ← MCP-Server (Model Context Protocol) über Streamable HTTP │ │ (POST /mcp, ein MCP-Server pro Team, lazy gebaut). Ermöglicht │ │ KI-Tools (Claude Desktop, Cline, Opencode) direkten Zugriff │ │ auf das Wiki des aufgelösten Teams. │ │ Resources (Daten): │ │ wiki://pages — alle Seiten als JSON │ │ wiki://page/{+path} — eine Seite als rohes Markdown │ │ Tools (Aktionen): │ │ search_pages — BM25-Volltextsuche mit Snippet │ │ upload_page — Seite erstellen/überschreiben │ │ delete_page — Seite löschen │ │ rebuild_index — generiert _index.md (Karpathy-Wiki-Index) │ │ Admin-Zugriff braucht den Header "X-Wiki-Team". Alle │ │ lesenden Operationen nutzen den PageCache. │ │ │ ├── audit.go ← AuditLogger: schreibt sicherheits-/nachvollziehbarkeits- │ │ relevante Ereignisse (Upload, Delete, Login-Versuche, │ │ Webhook-Sync) als JSON Lines in data/audit.log — eine │ │ gemeinsame Datei für alle Teams. Tail(n) liest die letzten │ │ n Ereignisse für die Admin-Ansicht, neueste zuerst. │ │ │ └── audit_page.go ← Admin-Ansicht des Audit-Logs (GET /admin/audit). Nur mit │ dem globalen Admin-Zugang erreichbar, zeigt die letzten │ 200 Ereignisse als Tabelle. │ ├── middleware/ │ ├── session.go ← RequireTeamSession: schützt die Web-Ansicht ("/", │ │ "/search", "/auth/", "/switch-team", "/admin/audit", │ │ "/admin/teams" und dessen Aktions-Routen). │ │ Prüft den Session-Cookie (handler.VerifySession) und legt │ │ das aufgelöste Team bzw. den Admin-Status im Request- │ │ Context ab. Ohne gültige Session: Redirect zu /login. │ │ │ └── auth.go ← RequireTeamOrAdminKey: schützt /api/upload, /api/delete │ und /mcp. Liest den Bearer-Token aus dem Authorization- │ Header und löst ihn timing-sicher gegen die TeamRegistry │ auf (handler.TeamRegistry.Resolve) — legt das Team bzw. │ den Admin-Status im Request-Context ab. │ ├── render/ │ └── markdown.go ← Kernlogik: Liest eine .md-Datei, trennt den YAML- │ Frontmatter-Block (title, password, tags, hidden) vom │ Markdown-Body und wandelt den Body mit goldmark (Default- │ Konfiguration, kein html.WithUnsafe — eingebettetes │ Roh-HTML wird sicher escaped) in HTML um. Gibt ein │ Page-Struct zurück, inkl. RawBody für die Volltextsuche. │ ├── templates/ │ ├── page.html ← HTML-Template für eine Wiki-Seite. Zweispaltiges Layout │ │ mit Sidebar (rekursive Navigation, Team-Name, "Team │ │ wechseln"-Link für Admins) und Hauptinhalt bzw. │ │ BM25-Suchergebnissen. Kein CSS inline — lädt │ │ /static/style.css und /static/sidebar.js. │ │ │ ├── password.html ← HTML-Template für das Seiten-Passwort-Formular. Wird │ │ angezeigt wenn eine Seite passwortgeschützt ist und kein │ │ gültiger Auth-Cookie vorhanden ist. │ │ │ ├── login.html ← HTML-Template für den Team-Login (GET/POST /login) — │ │ fragt den Zugriffskey des Teams oder den Admin-Token ab. │ │ │ ├── teams-admin.html ← HTML-Template für die Team-Verwaltung (GET /admin/teams) — │ │ Tabelle aller Teams mit Umbenennen/Key-neu-erzeugen/Löschen/ │ │ Öffnen (schaltet den Admin per POST /switch-team in dieses │ │ Team) sowie das Formular zum Anlegen eines neuen Teams. Landet │ │ der Admin nach dem Login oder ohne aktives Team hier. │ │ │ └── audit.html ← HTML-Template für die Audit-Log-Ansicht │ (GET /admin/audit) — Tabelle mit Zeit, Team, Akteur, │ Aktion, Pfad, Quelle und Details je Ereignis. │ ├── static/ │ ├── style.css ← Gesamtes CSS des Wikis an einem Ort: Layout, Sidebar, │ │ Navigation, Markdown-Styling, Login-/Passwort-Formulare, │ │ Audit-Tabelle. Wird unter /static/ ausgeliefert. │ │ │ └── sidebar.js ← Steuert das Ein-/Ausklappen der Sidebar, getrennt für │ Desktop (Zustand in localStorage gemerkt) und Mobile │ (Overlay, startet nach jedem Seitenaufruf zu). │ └── data/ ← Wurzelverzeichnis aller Teams (DATA_ROOT). Nicht im ├── audit.log Git-Repository — wird als Docker-Volume eingehängt. ├── / │ └── content/ ← Markdown-Dateien dieses Teams, z.B. data/alpha/content/. │ └── index.md Wird beim Serverstart automatisch angelegt, pro Team │ per deploy.sh, REST-API, MCP oder Git-Webhook befüllt. └── audit.log ← JSON-Lines-Protokoll aller Uploads/Deletes/Logins/ Webhook-Syncs (teamübergreifend, eine Datei für alle). ``` ## Datenfluss: Browser ruft eine Seite auf ``` Browser GET /projekte/notizen → middleware/session.go (RequireTeamSession) Session-Cookie prüfen, Team auflösen → handler/wiki.go (ServeHTTP) → render/markdown.go (LoadPage) Datei lesen, Frontmatter + HTML → Seiten-Passwortprüfung via Cookie (konstantzeitig, rate-limited) → buildNavForDir() (rekursiv) Ordnerstruktur → Baumnavigation → templates/page.html Template rendern → HTML an Browser ``` ## Datenfluss: Team-Login und Team-Umschaltung ``` Browser POST /login { key } → handler/auth.go (HandleLogin) → handler/ratelimit.go (LoginRateLimiter) IP-Sperre nach 5 Fehlversuchen/Minute → handler/registry.go (TeamRegistry.Resolve) Key → Team oder Admin, timing-sicher → handler/audit.go (Log) login_success / login_failure → handler/session.go (SignSession) signierten Cookie setzen → Redirect zu "/" (Team) oder "/admin/teams" (Admin — Team-Verwaltung ist zugleich die Team-Übersicht/-Auswahl) Admin klickt "Öffnen" bei einem Team in /admin/teams → POST /switch-team { team } → handler/auth.go (HandleSwitchTeam) nur mit Admin-Session erreichbar → handler/session.go (SignSession) Cookie "admin:" setzen → Redirect zu "/" zeigt das Wiki dieses Teams → Von dort per Sidebar-Link "Teams verwalten" jederzeit zurück zu /admin/teams, dort zeigt "Zurück zu " wieder zum zuletzt aktiven Team. ``` ## Datenfluss: KI-Tool liest/schreibt Seiten via MCP ``` Claude Desktop/Cline/Opencode → POST /mcp (Streamable HTTP, JSON-RPC pro Request) → middleware/auth.go (RequireTeamOrAdminKey) Bearer-Token → Team (oder Admin + Header X-Wiki-Team) → handler/mcp.go (Team-Server, lazy gebaut) Resource: wiki://pages → handler/cache.go (All()) aus dem Cache, kein Disk-Zugriff → JSON-Liste aller Seiten des Teams zurück Resource: wiki://page/projekte/notizen → handler/cache.go (URLPathToFilePath) → os.ReadFile() Markdown direkt von Disk → Frontmatter entfernt, Titel als H1 vorangestellt Tool: search_pages { query: "Docker" } → handler/search.go (BM25 Search) invertierter Index, kein Disk-Zugriff → Treffer mit Snippet zurück Tool: upload_page / delete_page → Datei auf Disk schreiben/löschen → handler/cache.go (Set/Delete) Cache + Suchindex aktualisieren → buildWikiIndex() _index.md automatisch neu generieren → handler/audit.go (Log) Audit-Eintrag schreiben ``` ## Datenfluss: Git-Webhook (automatische Synchronisation) ``` Nutzer git push → GitHub / GitLab / Bitbucket → POST /api/webhook → handler/webhook.go → Anbieter erkennen (Header-Analyse) → Signatur prüfen (HMAC-SHA256 oder Token-Vergleich, konstantzeitig) → git pull im data-Wurzelverzeichnis (alle Teams) → handler/registry.go (RebuildAll) Cache + Suchindex jedes Teams neu aufbauen → handler/audit.go (Log) webhook_sync protokollieren → Seiten aller Teams sofort live ohne Neustart ``` ## Datenfluss: Datei deployen (REST oder MCP) ``` deploy.sh / MCP upload_page → POST /api/upload oder Tool: upload_page → middleware/auth.go (Token prüfen, Team auflösen) → Pfad gegen Path-Traversal prüfen ("..") → Datei auf Disk schreiben → render/markdown.go (LoadPage) neue Seite laden → handler/cache.go (Set()) Cache + Suchindex sofort aktualisieren → handler/audit.go (Log) Audit-Eintrag schreiben → ab sofort in Navigation, Suche und MCP sichtbar ```