munin/REPOMAP.md
2026-09-15 20:36:26 +02:00

18 KiB

Repomap — Munin

Übersicht aller Dateien und Ordner mit ihrer Aufgabe.

munin/
├── main.go                     ← Einstiegspunkt. Liest die Konfiguration, prüft die
│                                 Pflichtangaben (SESSION_SECRET, ADMIN_TOKEN inkl.
│                                 Mindestlänge), öffnet die Datenbank, setzt die
│                                 Bestandteile zusammen und startet den Server.
│                                 Enthält außerdem die Server-Timeouts (gegen
│                                 Slowloris), die Body-Größenlimits und die
│                                 tägliche Aufräumaufgabe fürs Protokoll.
│
├── go.mod / go.sum             ← Moduldefinition und Prüfsummen der Abhängigkeiten.
│
├── Dockerfile                  ← Zweistufiger Bau: Stufe 1 kompiliert mit dem
│                                 Go-Compiler, Stufe 2 packt nur das fertige Binary
│                                 in ein minimales Alpine-Abbild. Läuft als
│                                 gewöhnlicher Benutzer, mit Healthcheck und Volume
│                                 für die Datenbank.
│
├── .env.example                ← Vorlage für .env, mit Erklärung jeder Variable.
├── .gitignore / .dockerignore  ← Schließen Datenbank, Binary und Geheimnisse aus.
│
├── README.md                   ← Anleitung: Schnellstart, MCP einrichten, Werkzeuge,
│                                 Konfiguration, Sicherheit, Betrieb.
├── AGENTEN-GUIDE.md            ← Wie man KI-Werkzeuge anbindet und anspricht,
│                                 damit sie den Speicher tatsächlich nutzen:
│                                 Projektprompt zum Übernehmen, slug-Regeln,
│                                 Muster für einzelne Aufgaben.
├── AUSBAUSTUFEN.md             ← Was bewusst nicht gebaut wurde: Messwerte,
│                                 Begründung und die Auslöser, an denen man
│                                 merkt, dass es Zeit dafür wird.
├── REPOMAP.md                  ← Diese Datei.
│
├── internal/
│   │
│   ├── store/                  ── Die einzige Stelle, die SQL kennt ──────────────
│   │   ├── store.go            ← Öffnet die Datenbank, setzt die nötigen PRAGMAs
│   │   │                         (WAL, busy_timeout, foreign_keys) und wendet die
│   │   │                         Migrationen an. Erklärt auch, warum der Treiber
│   │   │                         modernc.org/sqlite ist: Er ist reines Go und
│   │   │                         erlaubt damit ein statisches Binary.
│   │   │
│   │   ├── models.go           ← Die Datentypen (Project, APIKey, Entry, …), die
│   │   │                         Fehlerwerte (ErrNotFound, ErrConflict,
│   │   │                         ErrDuplicate) sowie ID- und Zeitbehandlung.
│   │   │                         NewID erzeugt sortierbare IDs in Go statt per
│   │   │                         AUTOINCREMENT — das hält den Umstieg auf eine
│   │   │                         andere Datenbank offen.
│   │   │
│   │   ├── projects.go         ← Workspace und Projekte: anlegen, lesen, ändern,
│   │   │                         löschen. Enthält ValidSlug und Slugify sowie
│   │   │                         SetProjectWriteLimit für die eigene Obergrenze
│   │   │                         eines Projekts.
│   │   │
│   │   ├── keys.go             ← API-Keys. ResolveKeyHash ist der heißeste Pfad im
│   │   │                         Programm (jeder MCP-Request) und deshalb ein
│   │   │                         indizierter Lookup über die Prüfsumme, keine
│   │   │                         Schleife. Zurückgezogene Keys gelten als nicht
│   │   │                         vorhanden, der Datensatz bleibt fürs Protokoll.
│   │   │
│   │   ├── entries.go          ← Einträge. PutEntry legt an ODER aktualisiert —
│   │   │                         der Bezeichner entscheidet, und genau das ist der
│   │   │                         Mechanismus gegen Dubletten. Enthält das
│   │   │                         optimistische Sperren (ExpectedVersion) und das
│   │   │                         Nachladen der Schlagworte in einer Abfrage statt
│   │   │                         einer pro Eintrag.
│   │   │
│   │   ├── links.go            ← Erkennt [[bezeichner]] im Inhalt und ersetzt sie
│   │   │                         beim Rendern. Das ersetzt die frühere
│   │   │                         Ordnerhierarchie als Struktur zwischen Einträgen.
│   │   │
│   │   ├── audit.go            ← Protokoll: schreiben, lesen, aufräumen. Die
│   │   │                         Aufbewahrungsfrist ist nicht nur eine Frage des
│   │   │                         Plattenplatzes — das Protokoll enthält Adressen
│   │   │                         und Zugangsnamen.
│   │   │
│   │   └── migrations/
│   │       ├── 001_init.sql    ← Das Schema. Der Kopfkommentar nennt die Regeln
│   │       │                     für portables SQL, an die sich alle weiteren
│   │       │                     Migrationen halten sollen.
│   │       ├── 002_projekt_in_nebentabellen.sql
│   │                           ← Führt project_id auch in entry_tags und
│   │                             entry_links. Eine bewusste Redundanz gegen zwei
│   │                             gemessene Bremsen: das Schlagwortmenü lief über
│   │                             alle Einträge (6,7 ms bei jedem Seitenaufruf),
│   │                             und bei den Rückverweisen kippte durch ein
│   │                             ORDER BY die Verknüpfungsreihenfolge
│   │                             (3,1 ms statt 9 µs).
│   │       └── 003_schreibbudget_je_projekt.sql
│   │                           ← Spalte write_limit_hourly an projects. Vorgabe 0
│   │                             heißt "keine eigene Angabe" — dann gilt der
│   │                             Standard aus WRITE_LIMIT_HOURLY. So muss die
│   │                             Migration keine Daten anfassen.
│   │
│   ├── auth/                   ── Wer ist das? ────────────────────────────────────
│   │   ├── doc.go              ← Erklärt die beiden Wege hinein (API-Key im Header,
│   │   │                         Sitzungs-Cookie im Browser) und was hier bewusst
│   │   │                         NICHT liegt: die Frage, was jemand darf.
│   │   │
│   │   ├── keys.go             ← Erzeugen, Prüfsumme bilden, Form prüfen. Enthält
│   │   │                         die Rollen (read/write/admin) und die Begründung,
│   │   │                         warum SHA-256 statt bcrypt richtig ist.
│   │   │
│   │   ├── session.go          ← Signierte Sitzungs-Cookies (HMAC-SHA256). Der
│   │   │                         Cookie trägt nur eine Behauptung ("ich bin Key X"),
│   │   │                         nie den Key selbst.
│   │   │
│   │   ├── csrf.go             ← CSRF-Tokens aus Sitzung und Geheimnis, ohne Zustand
│   │   │                         auf dem Server. Der Kopfkommentar erklärt den
│   │   │                         Angriff, gegen den das schützt.
│   │   │
│   │   ├── cookies.go          ← Setzt HttpOnly, SameSite und Secure an einer
│   │   │                         Stelle. Secure ist ohne Konfiguration an, außer
│   │   │                         bei localhost — damit die lokale Entwicklung über
│   │   │                         http weiter funktioniert.
│   │   │
│   │   ├── ratelimit.go        ← Zwei Begrenzungen auf einem gemeinsamen
│   │   │                         Fixed-Window-Zähler: RateLimiter sperrt eine
│   │   │                         Adresse nach zu vielen Fehlversuchen,
│   │   │                         WriteBudget deckelt die schreibenden
│   │   │                         MCP-Aufrufe je Projekt und Stunde. Letzteres
│   │   │                         hängt am Projekt und nicht am Key — ein
│   │   │                         Key-Limit ließe sich mit einem zweiten Key
│   │   │                         umgehen.
│   │   │
│   │   └── trustedproxy.go     ← Entscheidet, wann X-Forwarded-For geglaubt wird:
│   │                             nur bei einer über TRUSTED_PROXY hinterlegten
│   │                             Absenderadresse. Ohne Konfiguration wird der
│   │                             Header ignoriert — er ist frei fälschbar.
│   │
│   ├── entry/                  ── Wie sieht ein Eintrag aus? ─────────────────────
│   │   └── entry.go            ← Markdown nach HTML (goldmark, ohne Roh-HTML),
│   │                             Auflösen der [[…]]-Verweise, Textausschnitte für
│   │                             Trefferlisten samt sicherer Hervorhebung.
│   │
│   ├── export/                 ── Sicherung als Markdown ─────────────────────────
│   │   └── export.go           ← Schreibt jedes Projekt als Ordner voller
│   │                             Markdown-Dateien, mit allen Feldern im Dateikopf.
│   │                             Gedacht als Git-fähige Sicherung: Ein Lauf ohne
│   │                             inhaltliche Änderungen fasst keine Datei an und
│   │                             erzeugt deshalb auch keinen Diff. Verwaiste
│   │                             Dateien gelöschter Einträge werden entfernt,
│   │                             fremde Dateien im Zielordner bleiben unberührt.
│   │
│   ├── search/                 ── Volltextsuche ──────────────────────────────────
│   │   ├── index.go            ← Invertierter Index mit BM25-Bewertung. Titel zählen
│   │   │                         dreifach, Schlagworte zweifach. Enthält die
│   │   │                         Präfix-Rückfallebene: Ohne sie findet "deploy"
│   │   │                         den "Deployment-Prozess" nicht — bei einer
│   │   │                         KI-Anfrage ein stiller Fehltreffer.
│   │   │
│   │   └── registry.go         ← Ein Index je Projekt, verzögert aufgebaut. Getrennte
│   │                             Indizes statt eines gemeinsamen mit Filter: So wird
│   │                             die Abschottung strukturell, und die BM25-Werte
│   │                             werden nicht über Projektgrenzen hinweg verzerrt.
│   │
│   ├── web/                    ── HTTP: Browser und API ──────────────────────────
│   │   ├── context.go          ← Session-Typ (wer greift zu, was darf er) und die
│   │   │                         Ablage im Request-Context. CanAccessProject ist
│   │   │                         die Mandantentrennung der Weboberfläche.
│   │   │
│   │   ├── middleware.go       ← Sitzungsprüfung (inkl. "gilt der Key noch?"),
│   │   │                         CSRF, Projektauflösung aus der URL, Rollenprüfung.
│   │   │                         Dazu die allgemeinen Schutzschichten
│   │   │                         SecurityHeaders und LimitBody sowie APIAuth für
│   │   │                         den MCP-Endpunkt.
│   │   │
│   │   ├── server.go           ← Der Server-Typ, alle Routen und das Rendern der
│   │   │                         Templates (erst in einen Puffer, dann senden —
│   │   │                         sonst gäbe es bei einem Fehler eine halbe Seite
│   │   │                         mit Status 200).
│   │   │
│   │   ├── handlers_auth.go    ← Anmelden, Abmelden, Startseite.
│   │   ├── handlers_entries.go ← Einträge auflisten, suchen, ansehen, bearbeiten.
│   │   └── handlers_admin.go   ← Projekte, Zugänge, Protokollansicht. Die
│   │                             Bearbeitungsseite je Projekt (Name,
│   │                             Beschreibung, Schreibbudget, Löschen) liegt
│   │                             hier — bewusst eine Seite je Projekt statt
│   │                             eines Auswahlfelds über alle: Nur so stehen die
│   │                             tatsächlichen Werte in den Feldern, und ein
│   │                             Speichern ohne Änderung überschreibt nichts.
│   │
│   └── mcpserver/              ── Die Hauptschnittstelle ────────────────────────
│       ├── server.go           ← Handler und Transport (Streamable HTTP), ein
│       │                         MCP-Server je Projekt. Enthält den Text der
│       │                         `instructions` — das wirksamste Stellrad für die
│       │                         Qualität der Antworten, weil er bei jedem Client
│       │                         im Kontext steht.
│       │
│       └── tools.go            ← Die fünf Werkzeuge: recall, get, list, remember,
│                                 forget. Jedes ist eine Closure über genau ein
│                                 Projekt und kann strukturell nicht an ein anderes.
│
├── templates/                  ← HTML der Weboberfläche. partials.html enthält die
│                                 gemeinsamen Blöcke "kopf" und "fuss", die jede
│                                 Seite einbindet — kein verschachteltes Layout.
│
├── static/
│   ├── style.css               ← Das gesamte Aussehen, ohne Framework. Farben als
│   │                             Variablen ganz oben, helle und dunkle Darstellung.
│   └── confirm.js              ← Bestätigungsabfrage vor dem Löschen. Liest das
│                                 Attribut data-confirm, statt onclick="" im HTML zu
│                                 verwenden — Inline-JavaScript verbietet die
│                                 Content-Security-Policy.
│
└── data/                       ← Datenbank (nicht im Repository, als Volume gemountet)
    └── munin.db

Tests

Jedes Package hat seine Tests direkt daneben. Sie laufen mit go test ./... ohne weitere Vorbereitung — die Datenbanktests legen jeweils eine echte SQLite-Datei in einem Testverzeichnis an, damit auch Migrationen und Fremdschlüssel geprüft werden.

Datei Schwerpunkt
internal/auth/auth_test.go Signatur, Manipulation, Ablauf, CSRF, Key-Form, Rollen, Cookie-Flags, Rate-Limiting, X-Forwarded-For
internal/store/store_test.go Migrationen, Slug-Prüfung, Abschottung, Dublettenvermeidung, optimistisches Sperren, Verweise, Protokoll
internal/entry/entry_test.go HTML-Escaping, Verweisauflösung, Textausschnitte, sichere Hervorhebung
internal/search/search_test.go Bewertung, Präfix-Rückfallebene, Aktualisieren und Entfernen
internal/export/export_test.go Dateikopf und YAML-Maskierung, Aufräumen verwaister Dateien, Stabilität über mehrere Läufe
internal/web/web_test.go Anmeldung, zurückgezogene Keys, CSRF, Projektabschottung, Rollen, Header
internal/mcpserver/mcpserver_test.go Werkzeugbestand, instructions, Dublettenvermeidung, Konflikte, Rollen, Projektabschottung

Datenfluss: Agent ruft Wissen ab

KI-Werkzeug  POST /mcp  { "method": "tools/call", "params": { "name": "recall", … } }
    → web/middleware.go (SecurityHeaders, LimitBody)
    → web/middleware.go (APIAuth)          Key → Prüfsumme → Lookup → Projekt + Rolle
                                            in den Context; Fehlversuche in die Sperre
    → mcpserver/server.go (ServeHTTP)      Projekt aus dem Context → dessen MCP-Server
    → mcpserver/tools.go (recall)
        → search/registry.go                Index dieses Projekts (beim ersten Mal
                                            aus der Datenbank aufgebaut)
        → search/index.go (Search)          BM25, bei Bedarf Präfix-Rückfallebene
        → store/entries.go (EntryBySlug)    Treffer nachladen
        → entry/entry.go (Snippet)          Textausschnitt je Treffer
    → JSON-Antwort mit Trefferliste und Hinweis zum weiteren Vorgehen

Datenfluss: Agent legt Wissen ab

KI-Werkzeug  POST /mcp  { … "name": "remember", "arguments": { "slug": …, "content": … } }
    → web/middleware.go (APIAuth)          wie oben
    → mcpserver/tools.go (remember)
        → requireWrite()                    Rolle read wird hier abgewiesen
        → store/entries.go (PutEntry)       eine Transaktion:
                                              · Bezeichner vorhanden? → aktualisieren
                                              · ExpectedVersion prüfen → ggf. Konflikt
                                              · Schlagworte und Verweise ersetzen
                                              · Herkunft (agent) und Zugangsname setzen
        → search/registry.go (Update)       Index nachziehen
        → store/audit.go (LogAudit)         wer, was, wann
    → Antwort: Bezeichner, neue Fassung, angelegt oder aktualisiert

Datenfluss: Mensch bearbeitet im Browser

Browser  POST /p/plattform/e/deployment/save
    → web/middleware.go (SecurityHeaders, LimitBody)
    → web/middleware.go (requireSession)   Cookie prüfen → Key aus der Datenbank
                                            laden → gilt er noch? → CSRF-Token prüfen
    → web/middleware.go (requireProject)   Projekt aus der URL; fremdes Projekt → 404
    → web/middleware.go (requireWrite)     Rollenprüfung
    → web/handlers_entries.go (handleEntrySave)
        → store/entries.go (PutEntry)       Herkunft: human, Autor: Key-Name
        → search/registry.go (Update)
        → store/audit.go (LogAudit)
    → Weiterleitung zur Detailansicht