munin/REPOMAP.md
Thomas Adamek 466f1e40af Workspaces, Änderungshistorie, Papierkorb, Nutzungsübersicht, Hilfe
Umsetzung der Ausbaustufen 2.1, 3.6, 3.8, 3.9, 4.1, 4.2, 4.4, 4.6,
5.1–5.4 (Details in AUSBAUSTUFEN.md).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-25 07:44:52 +02:00

30 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 Kontroll-Datenbank und
│                                 Workspace-Verwaltung (legt beim ersten Start den
│                                 Workspace "Intern" an), setzt die
│                                 Bestandteile zusammen und startet den Server.
│                                 Enthält außerdem die Server-Timeouts (gegen
│                                 Slowloris), die Body-Größenlimits und die
│                                 täglichen Aufgaben (Protokoll aufräumen,
│                                 Export), die über fuerJedenWorkspace alle
│                                 Workspaces erreichen. Tests: main_test.go.
│
├── 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 eine Workspace-Datei (Open), 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.
│   │   │
│   │   ├── control.go          ← Die zentrale Kontroll-Datenbank (OpenControl):
│   │   │                         Workspaces anlegen, umbenennen, löschen, dazu die
│   │   │                         Zuordnung Key-Prüfsumme → Workspace
│   │   │                         (ResolveKeyWorkspace). Das ist der erste Schritt
│   │   │                         jeder Anmeldung: Erst danach ist klar, welche
│   │   │                         Workspace-Datei den Key kennt.
│   │   │
│   │   ├── 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         ← 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. Außerdem:
│   │   │                         · snapshotEntryVersion sichert vor jeder Änderung
│   │   │                           die bisherige Fassung, in derselben Transaktion;
│   │   │                           EntryVersions/EntryVersionByNumber lesen sie
│   │   │                         · BulkUpdateTags ändert Schlagworte vieler
│   │   │                           Einträge auf einmal und überspringt IDs aus
│   │   │                           fremden Projekten
│   │   │                         · LinkGraph liefert Knoten und Kanten für die
│   │   │                           Graph-Ansicht
│   │   │                         Jede lesende Abfrage trägt nichtImPapierkorb
│   │   │                         (deleted_at IS NULL) — außer PutEntry, das
│   │   │                         einen Eintrag im Papierkorb bewusst sieht und
│   │   │                         ihn beim Überschreiben zurückholt.
│   │   │
│   │   ├── nutzung.go          ← Kennzahlen der Nutzungsübersicht: UsageSummaries
│   │   │                         (Schreibvorgänge der letzten 30 Tage aus dem
│   │   │                         Protokoll, Bestand aus entries — für ein oder
│   │   │                         alle Projekte in zwei Abfragen), dazu
│   │   │                         MissingLinks, UntaggedEntries, RecentlyUsedKeys.
│   │   │                         Keine eigene Zähltabelle.
│   │   │
│   │   ├── papierkorb.go       ← Papierkorb: MoveEntryToTrash (statt DELETE),
│   │   │                         ListTrash, RestoreFromTrash, PurgeFromTrash
│   │   │                         (nur was schon im Papierkorb liegt) und
│   │   │                         PurgeTrash für die tägliche Aufgabe. Die Frist
│   │   │                         TrashRetention (30 Tage) steht hier, damit
│   │   │                         Oberfläche und Aufräumen dieselbe Zahl nutzen.
│   │   │
│   │   ├── 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. EntryWriteDetail vermerkt bei
│   │   │                         Schreibvorgängen "angelegt" oder "aktualisiert".
│   │   │
│   │   ├── migrations_control/
│   │   │   └── 001_init.sql    ← Schema der Kontroll-Datenbank: workspaces und
│   │   │                         key_index (Key-Prüfsumme → Workspace). Bewusst
│   │   │                         dünn — Inhalte liegen nie hier.
│   │   │
│   │   └── migrations/         ← Schema jeder Workspace-Datei
│   │       ├── 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.
│   │       ├── 004_aenderungshistorie.sql
│   │                           ← Tabelle entry_versions für abgelöste Fassungen.
│   │                             Wird mit dem Eintrag gelöscht (ON DELETE CASCADE).
│   │       └── 005_papierkorb.sql
│   │                           ← deleted_at/deleted_by an entries und ein
│   │                             Teilindex nur über den Papierkorb. Löschen setzt
│   │                             nur noch die Markierung; CASCADE greift erst
│   │                             beim endgültigen Löschen.
│   │
│   ├── workspace/              ── Welche Datei gehört zu welcher Abteilung? ──────
│   │   └── workspace.go        ← Manager öffnet Workspace-Dateien bei Bedarf und
│   │                             hält sie offen, jeweils mit eigenem Suchindex.
│   │                             Datenbank und Index gehören zusammen, damit kein
│   │                             Index versehentlich an der falschen Datei hängt.
│   │                             Delete schließt eine Datei und entfernt sie.
│   │
│   ├── 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 (über internal/markdown),
│   │   │                         Auflösen der [[…]]-Verweise, Textausschnitte für
│   │   │                         Trefferlisten samt sicherer Hervorhebung.
│   │   │
│   │   └── diff.go             ← Zeilenvergleich zweier Fassungen (DiffLines) als
│   │                             LCS ohne externe Abhängigkeit. Bei sehr langen
│   │                             Einträgen weicht er auf einen groben Vergleich
│   │                             aus (grobDiff), damit Speicher und Rechenzeit
│   │                             begrenzt bleiben.
│   │
│   ├── markdown/               ── Markdown nach HTML, eine Regel für alles ───────
│   │   └── markdown.go         ← Der gemeinsame goldmark-Renderer für Einträge und
│   │                             Hilfe: GFM (Tabellen, Checklisten,
│   │                             Durchstreichen, automatische Links), Roh-HTML
│   │                             gesperrt. Nur die Hilfe bekommt Überschrift-
│   │                             Anker (RenderMitAnkern) — mit gefalteten
│   │                             Umlauten über store.Slugify, damit die
│   │                             "?"-Links der Oberfläche darauf zeigen können.
│   │
│   ├── export/                 ── Sicherung als Markdown ─────────────────────────
│   │   └── export.go           ← Schreibt jeden Workspace als Ordner
│   │                             (<workspace-id>/, mit _workspace.md) und darin
│   │                             jedes Projekt als Ordner voller
│   │                             Markdown-Dateien, mit allen Feldern im Dateikopf.
│   │                             Kennt selbst keine Datenbank: Der Aufrufer gibt
│   │                             die Workspace-Datei je Lauf mit.
│   │                             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.
│   │
│   ├── hilfe/                  ── Eingebaute Onboarding-Hilfe ───────────────────
│   │   ├── hilfe.go            ← Liste der Hilfeseiten (Reihenfolge, Titel) und
│   │   │                         das Rendern mit Überschrift-Ankern. Erklärt,
│   │   │                         warum die Hilfe im Binary steckt statt als
│   │   │                         Einträge in der Datenbank: Sie muss zur
│   │   │                         laufenden Fassung des Programms passen.
│   │   └── seiten/*.md         ← Die Inhalte, je Seite eine Datei. {{SERVER}} und
│   │                             {{PROJEKT}} werden beim Anzeigen ersetzt.
│   │
│   ├── 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.
│   │   │                         Die Sitzung trägt die Workspace-ID signiert mit
│   │   │                         (sessionFromSubject); requireWorkspace hält einen
│   │   │                         Plattform-Admin ohne gewählten Workspace von den
│   │   │                         Projektseiten fern.
│   │   │                         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. Der
│   │   │                         Plattform-Admin ohne gewählten Workspace landet
│   │   │                         auf /admin/workspaces.
│   │   ├── handlers_entries.go ← Einträge auflisten, suchen, ansehen, bearbeiten.
│   │   │                         Dazu Vorschau (preview), Vorschläge für [[…]]
│   │   │                         (suggest), Fassungen und Vergleich, Graph-Daten
│   │   │                         und Schlagworte für viele Einträge (bulk-tags).
│   │   ├── handlers_nutzung.go ← Nutzungsseite je Projekt (/p/{projekt}/usage),
│   │   │                         für jede Rolle. Die Admin-Projektliste nutzt
│   │   │                         dieselben Zahlen (projectListItems).
│   │   ├── handlers_papierkorb.go
│   │   │                       ← Papierkorb je Projekt: ansehen, wiederherstellen,
│   │   │                         endgültig löschen, Filter nach löschendem Zugang.
│   │   │                         Alles nur mit Schreibrecht, und bewusst nur hier
│   │   │                         — über MCP gibt es kein Wiederherstellen.
│   │   ├── handlers_hilfe.go   ← Zeigt die eingebauten Hilfeseiten unter /hilfe
│   │   │                         für jede Sitzung. Setzt vorher Server-Adresse
│   │   │                         und Projekt-Bezeichner in die Beispiele ein
│   │   │                         (serverURL, mit geprüftem Host-Header).
│   │   ├── handlers_workspaces.go
│   │   │                       ← Workspaces auflisten, anlegen, wählen, umbenennen,
│   │   │                         löschen (nur Plattform-Admin). Löscht jemand den
│   │   │                         Workspace der eigenen Sitzung, wird die Auswahl
│   │   │                         zurückgesetzt, statt ihn auszusperren.
│   │   └── handlers_admin.go   ← Projekte, Zugänge, Protokollansicht, Export. 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.
│   ├── preview.js              ← Reiter "Schreiben"/"Vorschau" im Editor. Holt das
│   │                             HTML vom Server, statt Markdown im Browser zu
│   │                             rendern. Ohne JavaScript bleibt das Formular
│   │                             unverändert nutzbar.
│   ├── autocomplete.js         ← Vorschläge nach "[[" im Textfeld, per Tastatur
│   │                             und Maus bedienbar.
│   ├── graph.js                ← Selbst geschriebene Kräftesimulation und
│   │                             SVG-Darstellung für die Graph-Ansicht. Texte nur
│   │                             über textContent, nie innerHTML.
│   └── img/                    ← Logo in mehreren Fassungen (hell, dunkel, ohne
│                                 Hintergrund). Die Anmeldeseite wählt je nach
│                                 Farbschema die passende.
│
├── scripts/
│   └── backup.sh               ← Sichert control.db und jede Workspace-Datei
│                                 einzeln über sqlite3 .backup in einen
│                                 zeitgestempelten Ordner; RETENTION_DAYS räumt
│                                 alte Sicherungen auf.
│
└── data/                       ← Daten (nicht im Repository, als Volume gemountet)
    ├── control.db              ← Kontroll-Datenbank: Workspaces, Key-Zuordnung
    └── workspaces/
        └── <workspace-id>.db   ← Eine Datei je Workspace

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/nutzung_test.go Zählung nach Art und Weg samt Zeitfenster, Bestand ohne Papierkorb, Verweise ins Leere, aktive Zugänge
internal/store/papierkorb_test.go Jeder lesende Weg blendet den Papierkorb aus (Einzelabruf, Liste mit Filtern, Suchindex/Export, Zählen, Schlagworte, Rückverweise, Graph, Sammelbearbeitung); Wiederherstellen, Zurückholen durch Überschreiben, Konflikt, endgültiges Löschen, Frist
internal/store/store_test.go Migrationen, Slug-Prüfung, Abschottung, Dublettenvermeidung, optimistisches Sperren, Verweise, Protokoll, Änderungshistorie
internal/store/control_test.go Kontroll-Datenbank: Standard-Workspace bleibt stabil, Auflistung, Zuordnung Key → Workspace
internal/workspace/workspace_test.go Manager hält Dateien offen, trennt Workspaces, schließt sauber
internal/entry/entry_test.go HTML-Escaping, Verweisauflösung, Textausschnitte, sichere Hervorhebung
internal/markdown/markdown_test.go GFM-Erweiterungen, kein Roh-HTML und keine javascript:-Links, Anker nur in der Hilfe
internal/entry/diff_test.go Zeilenvergleich, Grenzfälle, Ausweichen bei sehr langen Einträgen
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, Workspace-Trennung, Rollen, Header, Hilfeseiten samt eingesetzter Adresse
internal/mcpserver/mcpserver_test.go Werkzeugbestand, instructions, Dublettenvermeidung, Konflikte, Rollen, Projektabschottung
internal/hilfe/hilfe_test.go Seitenliste und Dateien passen zusammen, interne Links führen zu vorhandenen Seiten, kein Roh-HTML

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
        → store/control.go                  (ResolveKeyWorkspace) → Workspace
        → workspace/workspace.go            (Get) → dessen Datei und Suchindex
        → store/keys.go                     (ResolveKeyHash) → 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