[Docgen] Obsidian-Graph: Frontmatter + Wikilinks + MATRIX — inkl. Fix: TenantVMRange bei Mandantenzuordnung ignoriert #209

Open
opened 2026-07-20 22:12:38 +00:00 by chinux · 5 comments
Owner

Baut auf dem bestehenden Docgen (#17, server/services/docgen.py) auf. Ziel: die erzeugte Doku so rendern, dass sie in Obsidian einen sinnvollen Graphen ergibt (Mandant ↔ Node ↔ VM/CT) und am Ende eine vollwertige Matrix als Übersicht liefert.

Ausgangslage

docgen.py erzeugt heute mandanten/<mandant>/<node>/node.md, .../vms/<vmid>-<name>.md, backups.csv + backups.md. Verlinkt wird nur per relativem Markdown-Link von _mandant_mdnode.md. VMs verlinken gar nicht zurück. Kein Frontmatter, keine Tags, kein Graph.

Fachlicher Bug (wichtigster Teil dieses Issues)

_fetch() in docgen.py baut die Mandantenzuordnung ausschließlich über TenantNode:

node_to_tenant[str(r.node_id)] = str(r.tenant_id)

TenantVMRange (VMID-Bereich auf einer Node → Mandant, siehe server/models/tenant.py) wird komplett ignoriert. Auf geteilten Nodes ist die Mandantenzuordnung der VMs damit falsch. Das ist die zentrale Achse der Matrix und muss zuerst korrekt werden.

Regel (neu): Ein Guest gehört zum Mandanten des passenden TenantVMRange (node_id + vmid_min ≤ vmid ≤ vmid_max). Gibt es keinen Treffer, gilt der Mandant der Node über TenantNode. Gibt es auch den nicht → _OHNE-MANDANT.

Feld "Funktion"

Existiert nirgends strukturiert. Für v1 abgeleitet aus vorhandenen Daten, in dieser Reihenfolge:

  1. VMCardOverride (falls dort ein Label/Rolle gepflegt ist — Modell prüfen)
  2. Docker-Container-Namen/Images aus vm_scans[vmid].docker.containers
  3. Offene Ports aus VMPortSnapshot (80/443 → web, 5432/3306 → db, 22 → ssh-only, …)
  4. Fallback: unbekannt

Als role: ins Frontmatter + als Tag. Keine KI hier — die kommt separat.

Dateibenennung (Nachtrag — blockiert alles andere)

Im Ziel-Repo chinux/Dokumentation tragen aktuell 23 von 132 Dateien einen von drei Namen: 8× node.md, 8× backups.md, 7× README.md. Obsidian nutzt den Dateinamen als Label im Graphen und als Ziel von Kurz-Wikilinks. Damit waere der Graph unbrauchbar (acht ununterscheidbare "node"-Knoten) und [[node]] mehrdeutig.

Neues Schema — Datei traegt den Namen der Ebene, die sie beschreibt:

Ebene bisher neu
Mandant <m-slug>/README.md <m-slug>/<m-slug>.md
Node <node>/node.md <node>/<node>.md
Backups <node>/backups.{md,csv} <node>/<node>-backups.{md,csv}
Guest vms/<vmid>-<slug>.md vms/<node>-<vmid>-<slug>.md

Begruendung:

  • Datei = Ordnername ist die Folder-Note-Konvention in Obsidian; Ordner und Notiz erscheinen als eine Einheit.
  • Node-Praefix bei Guests ist zwingend: VMID 101 existiert im echten Bestand bereits auf bau-prox01 und bau-prox02. Heute kollidiert es nur zufaellig nicht, weil die Guest-Namen unterschiedlich sind.
  • Mandanten-Slug bleibt der Dateiname (nicht der Klarname), weil Mandantennamen &, + und Kommas enthalten (Trendsetzer Marketing GmbH & Co. KG) und diese in Wikilinks und Git-Pfaden Aerger machen. Der Klarname steht im Frontmatter unter name: und als aliases:.

Migrationsfalle: Backup-Historie

_build_and_push() liest vorhandene CSVs vor dem Wipe von mandanten/ ein, um sie per UPID zu mergen — Lookup-Key ist der relative Pfad <m-slug>/<node>/backups.csv. Nach der Umbenennung greift dieser Lookup ins Leere, _merge_backups() bekommt einen leeren existing_csv und der erste Lauf schreibt eine frische CSV ohne die fortgeschriebene Historie. In der Git-Historie bleibt sie, im Arbeitsstand ist sie weg.

Der Einleseschritt muss beide Pfade akzeptieren (alt und neu), damit der Uebergang verlustfrei ist. Nach einem erfolgreichen Lauf ist nur noch der neue Pfad relevant, der Fallback kann aber dauerhaft drin bleiben — er kostet nichts.

Matrix: Bases statt Dataview

Im Vault ist kein Dataview installiert (.obsidian/community-plugins.json enthaelt nur github-sync). Aktiv ist dagegen der Core-Plugin bases — Obsidians native Tabellen-/Datenbankansicht.

Statt MATRIX-DATAVIEW.md daher eine MATRIX.base erzeugen (YAML), die auf #theprox/vm filtert und die Frontmatter-Properties als Spalten zeigt (tenant, node, vmid, role, status, open_ports, agent_version). Keine Community-Plugin-Abhaengigkeit, und sie liest exakt das Frontmatter, das dieses Issue ohnehin schreibt. Das aktuelle Base-Dateiformat vor der Umsetzung in der Obsidian-Doku pruefen — es hat sich seit Einfuehrung mehrfach geaendert.

Die statische MATRIX.md bleibt zusaetzlich bestehen: sie funktioniert in jedem Markdown-Viewer, auch ohne Obsidian, und ist im Git-Diff lesbar.


Prompt für Claude Code

Erweitere den bestehenden Docgen in theProx (server/services/docgen.py) so, dass
die erzeugte Doku in Obsidian als Graph funktioniert und eine Matrix-Übersicht
enthält. KEIN neues Subsystem — reines Rendering + ein Datenfix.

--- 1. DATENFIX (zuerst, eigener Commit) ---
In _fetch(): TenantVMRange mitladen und die Mandantenzuordnung pro GUEST
(nicht nur pro Node) auflösen.

    from models import TenantVMRange
    ranges = (await db.execute(select(TenantVMRange))).scalars().all()

Baue eine Hilfsfunktion:

    def _tenant_for_guest(node_id, vmid, ranges_by_node, node_to_tenant) -> str|None

Reihenfolge: passender VMID-Range auf dieser Node gewinnt; sonst TenantNode der
Node; sonst None (-> _OHNE-MANDANT).

Konsequenz für die Verzeichnisstruktur: Guests einer Node können jetzt zu
VERSCHIEDENEN Mandanten gehoeren. Die physische Ablage bleibt wie bisher
mandanten/<node-mandant>/<node>/vms/ (Node ist physisch), ABER im Frontmatter
jeder VM steht der TATSAECHLICHE Mandant des Guests, und die Matrix + der Graph
nutzen diesen. Das ist bewusst so: der Graph bildet die logische Zuordnung ab,
das Dateisystem die physische.

--- 2. FRONTMATTER ---
Jede erzeugte .md bekommt YAML-Frontmatter als ersten Block.

Mandant (mandanten/<m>/README.md):
    ---
    type: mandant
    name: "<Mandantenname>"
    customer_number: "<oder leer>"
    nodes: <int>
    guests: <int>
    tags: [theprox/mandant]
    generated: <ISO8601 UTC>
    ---

Node (.../<node>/node.md):
    ---
    type: node
    name: "<nodename>"
    tenant: "<Mandant>"
    status: online|offline|pending
    pve_version: "..."
    ip: "..."
    cpu_cores: <int>
    ram_bytes: <int>
    vms: <int>
    cts: <int>
    updates_available: <int>
    updates_security: <int>
    tags: [theprox/node, status/<status>]
    generated: <ISO8601 UTC>
    ---

Guest (.../vms/<vmid>-<slug>.md):
    ---
    type: vm            # oder: lxc
    vmid: <int>
    name: "<name>"
    node: "<nodename>"
    tenant: "<Mandant des Guests, siehe Datenfix>"
    status: running|stopped|...
    role: "<abgeleitete Funktion, s.u.>"
    os: "..."
    ip: "..."
    vcpus: <int>
    ram_bytes: <int>
    docker: true|false
    open_ports: <int>
    agent: true|false
    agent_version: "…"        # nur wenn vorhanden
    agent_last_check: <ISO>   # nur wenn vorhanden
    tags: [theprox/vm, role/<role>, node/<node-slug>, tenant/<tenant-slug>]
    generated: <ISO8601 UTC>
    ---

Werte NICHT erfinden: fehlt etwas, Feld weglassen statt "—" ins YAML zu
schreiben. Strings, die :, #, [ oder " enthalten, sauber quoten (eigener
kleiner _yaml_str()-Helper, keine neue Dependency).

--- 3. DATEINAMEN + WIKILINKS (der Graph) ---
Benennung (siehe Tabelle im Issue-Text, ist verbindlich):
    mandanten/<m-slug>/<m-slug>.md                     (ersetzt README.md)
    mandanten/<m-slug>/<node>/<node>.md                (ersetzt node.md)
    mandanten/<m-slug>/<node>/<node>-backups.md|.csv   (ersetzt backups.*)
    mandanten/<m-slug>/<node>/vms/<node>-<vmid>-<slug>.md

Alle Dateinamen sind damit vault-weit eindeutig -> Kurz-Wikilinks sind
eindeutig aufloesbar:
    [[<m-slug>]]                  -> Mandant
    [[<node>]]                    -> Node
    [[<node>-<vmid>-<slug>]]      -> Guest

Fuer lesbare Anzeige durchgaengig Pipe-Links nutzen:
    [[<node>-<vmid>-<slug>|<vmid> <name>]]
    [[<m-slug>|<Mandant-Klarname>]]

Zusaetzlich in JEDER Datei "aliases:" im Frontmatter setzen (Klarname des
Mandanten, "<node>/<vmid>" beim Guest), damit der Quick-Switcher auch ueber
die menschliche Schreibweise findet.

BACKUP-HISTORIE (Migrationsfalle, unbedingt umsetzen):
_build_and_push() liest existierende backups.csv vor dem Wipe unter dem Key
"<m-slug>/<node>/backups.csv" ein. Nach der Umbenennung greift das ins Leere
und die gemergte Historie geht im Arbeitsstand verloren. Der Einleseschritt
muss BEIDE Pfade akzeptieren:
    "<m-slug>/<node>/<node>-backups.csv"   (neu, Vorrang)
    "<m-slug>/<node>/backups.csv"          (alt, Fallback)
Ein Test deckt den Uebergang ab: altes Layout im Workdir -> nach dem Lauf
liegt die Historie vollstaendig unter dem neuen Namen.

Verlinkung (beidseitig, das fehlt heute komplett):
  - Mandant-Datei: alle Nodes als [[<node>]] + alle Guests des Mandanten
  - Node-Datei: "Mandant: [[<m-slug>|<Klarname>]]" + Abschnitt "## Guests"
    mit einem Link je Guest, gruppiert nach Mandant wenn mehrere
  - Guest-Datei: Kopfblock direkt nach dem Frontmatter:
    "Node: [[<node>]] · Mandant: [[<m-slug>|<Klarname>]]"
  - Backups-Datei: "Node: [[<node>]]"

--- 4. MOC + MATRIX ---
Neue Datei im Repo-Root: MATRIX.md
  - Kopfzeile + Generierungszeitpunkt
  - Eine Zeile PRO GUEST, Spalten:
    | Mandant | Node | VMID | Name | Typ | Funktion | Status | OS | IP | vCPU | RAM | Docker | Ports | Agent | Backup zuletzt |
  - Mandant/Node/Name als Wikilinks
  - Sortierung: Mandant -> Node -> VMID
  - "Backup zuletzt": juengstes ended_at aus backups_by_node fuer diese vmid,
    sonst "—"

Zusaetzlich MATRIX.base (Obsidian-Bases, Core-Plugin, im Vault aktiv —
Dataview ist NICHT installiert). Filter auf #theprox/vm, Spalten aus dem
Frontmatter (tenant, node, vmid, role, status, open_ports, agent_version).
Aktuelles Base-Dateiformat vorher in der Obsidian-Doku pruefen. Ersetzt den
frueher hier vorgesehenen Dataview-Block:

    ```dataview
    TABLE tenant AS Mandant, node AS Node, vmid AS VMID, role AS Funktion,
          status AS Status, open_ports AS Ports, agent_version AS Agent
    FROM #theprox/vm
    SORT tenant ASC, node ASC, vmid ASC
    ```

Und _MOC.md im Root: Einstiegsseite mit Links auf alle Mandanten, MATRIX.md,
Zaehlern (Mandanten/Nodes/Guests) und einer "Auffaellig"-Sektion
(Guests OHNE Agent, Nodes offline, Guests ohne Backup).

--- 5. FUNKTION ABLEITEN ---
Neue reine Funktion:

    def _derive_role(guest: dict, gscan: dict, ports: list, override) -> str

Reihenfolge: expliziter Override (models/vm_card_override.py pruefen, ob dort
ein passendes Feld existiert — wenn nicht, diesen Schritt weglassen und im
Issue-Kommentar vermerken) > Docker-Images (mapping-Dict fuer gaengige:
nginx/traefik/caddy -> "reverse-proxy", postgres/mysql/mariadb -> "database",
gitea/gitlab -> "git", nextcloud -> "filesharing", *-exporter/grafana/
prometheus -> "monitoring", ...) > Port-Heuristik > "unbekannt".
Das Mapping-Dict oben in der Datei als Konstante, damit erweiterbar.

--- 6. SANITIZING ---
VM-/Node-Notizen (VMNote.content, Node.notes) sind FREITEXT VOM NUTZER und
landen im Markdown. Vor dem Einbetten:
  - "[[" und "]]" escapen (sonst entstehen ungewollte Graph-Kanten)
  - Zeilen, die mit "---" beginnen, einruecken (sonst zerschiesst es das
    Frontmatter nachfolgender Blocks)
  - keine rohen HTML-Tags durchlassen
Eigener Helper _safe_md(text).

--- 7. TESTS ---
server/tests/test_docgen_render.py (pytest, keine DB noetig — die Render-
Funktionen sind rein):
  - _tenant_for_guest: Range gewinnt ueber TenantNode; Fallback-Kette
  - Frontmatter parst als gueltiges YAML (yaml.safe_load ueber den Block)
  - _safe_md escapt [[ ]]
  - _derive_role trifft die erwarteten Faelle
  - MATRIX enthaelt eine Zeile pro Guest

NICHT anfassen: Git-Push-Logik, Scheduler, Backups-CSV-Merge. Reines Rendering
+ der _fetch()-Datenfix.

Definition of Done

  • TenantVMRange wird bei der Mandantenzuordnung berücksichtigt (+ Test)
  • Alle erzeugten .md haben valides YAML-Frontmatter
  • Guest-Seiten verlinken zurück auf Node und Mandant
  • MATRIX.md, MATRIX-DATAVIEW.md, _MOC.md werden erzeugt
  • Ein Testlauf gegen ein echtes Vault zeigt einen zusammenhängenden Graphen
  • Freitext-Notizen können den Graphen nicht manipulieren

Nachtrag: CVE raus, Agent rein

CVEs entfallen vollstaendig aus der Doku. Kein cve_open/cve_critical im Frontmatter, keine ## Offene CVEs-Sektion in _vm_md() (steht aktuell in 33 der 79 generierten Dateien und muss weg), keine CVE-Spalte in der Matrix, kein CVE in der "Auffaellig"-Sektion des MOC. VMCveReport wird in _fetch() nicht mehr geladen — der komplette cves_map-Pfad faellt ersatzlos.

Begruendung: CVE-Daten sind hochfrequent und kurzlebig. In einer taeglich committeten Snapshot-Doku erzeugen sie Diff-Rauschen, das echte Infrastruktur-Aenderungen ueberdeckt, und sind schon beim Lesen veraltet. CVEs gehoeren ins Dashboard, nicht ins Vault.

An ihre Stelle tritt der Agent-Status als Qualitaetsmerkmal der Doku selbst — siehe #212. Ein Guest ohne Agent liefert keine Disk-, Docker-, Update- und Service-Daten; die Doku ist an dieser Stelle also nachweislich unvollstaendig, und genau das muss sichtbar sein. Tag theprox/no-agent, eigene Sektion im _MOC.md.

Reihenfolge: #212 sollte vor diesem Issue umgesetzt werden. Sonst schreibt das neue Frontmatter agent: false fuer alle Guests fest — die Felder waeren dann strukturell korrekt und inhaltlich trotzdem falsch.


Nachtrag: role wird zu roles[]

Die Funktions-Ableitung wandert vollstaendig nach #216 und liefert eine Liste statt eines Einzelwerts. Grund: ein Windows-Server ist real oft gleichzeitig AD-DC, DNS, DHCP und Fileserver — ein einzelner String erzwingt eine willkuerliche Entscheidung.

Frontmatter statt role: "...":

roles: [ad-dc, dns, dhcp, fileserver]
primary_role: ad-dc
role_confidence: high|medium|low
role_source: inferred|manual
tags: [theprox/vm, role/ad-dc, role/dns, role/dhcp, role/fileserver, …]

Ein Tag pro Rolle — dadurch ist im Graphen und in MATRIX.base nach jeder einzelnen Rolle filterbar, statt nur nach einer Hauptrolle.

_derive_role() aus Punkt 5 des CC-Prompts entfaellt hier ersatzlos. Docgen ruft die fertigen Werte aus guest_view ab und rendert zusaetzlich eine Sektion ## Rollen mit der Evidenz (welcher Dienst / welche Windows-Rolle die Zuordnung begruendet hat). Ohne die Evidenz ist eine falsche Rolle nicht widerlegbar.

Matrix-Spalte "Funktion" zeigt primary_role, mit den weiteren Rollen als Zusatz.

Abhaengigkeitskette neu: #214#215#216#209.

Baut auf dem bestehenden Docgen (#17, `server/services/docgen.py`) auf. Ziel: die erzeugte Doku so rendern, dass sie in **Obsidian** einen sinnvollen Graphen ergibt (Mandant ↔ Node ↔ VM/CT) und am Ende eine vollwertige **Matrix** als Übersicht liefert. ## Ausgangslage `docgen.py` erzeugt heute `mandanten/<mandant>/<node>/node.md`, `.../vms/<vmid>-<name>.md`, `backups.csv` + `backups.md`. Verlinkt wird nur per relativem Markdown-Link von `_mandant_md` → `node.md`. VMs verlinken **gar nicht** zurück. Kein Frontmatter, keine Tags, kein Graph. ## Fachlicher Bug (wichtigster Teil dieses Issues) `_fetch()` in `docgen.py` baut die Mandantenzuordnung **ausschließlich** über `TenantNode`: ```python node_to_tenant[str(r.node_id)] = str(r.tenant_id) ``` `TenantVMRange` (VMID-Bereich auf einer Node → Mandant, siehe `server/models/tenant.py`) wird komplett ignoriert. Auf geteilten Nodes ist die Mandantenzuordnung der VMs damit **falsch**. Das ist die zentrale Achse der Matrix und muss zuerst korrekt werden. Regel (neu): Ein Guest gehört zum Mandanten des passenden `TenantVMRange` (node_id + vmid_min ≤ vmid ≤ vmid_max). Gibt es keinen Treffer, gilt der Mandant der Node über `TenantNode`. Gibt es auch den nicht → `_OHNE-MANDANT`. ## Feld "Funktion" Existiert nirgends strukturiert. Für v1 **abgeleitet** aus vorhandenen Daten, in dieser Reihenfolge: 1. `VMCardOverride` (falls dort ein Label/Rolle gepflegt ist — Modell prüfen) 2. Docker-Container-Namen/Images aus `vm_scans[vmid].docker.containers` 3. Offene Ports aus `VMPortSnapshot` (80/443 → web, 5432/3306 → db, 22 → ssh-only, …) 4. Fallback: `unbekannt` Als `role:` ins Frontmatter + als Tag. Keine KI hier — die kommt separat. ## Dateibenennung (Nachtrag — blockiert alles andere) Im Ziel-Repo `chinux/Dokumentation` tragen aktuell **23 von 132 Dateien** einen von drei Namen: 8× `node.md`, 8× `backups.md`, 7× `README.md`. Obsidian nutzt den **Dateinamen als Label im Graphen** und als Ziel von Kurz-Wikilinks. Damit waere der Graph unbrauchbar (acht ununterscheidbare "node"-Knoten) und `[[node]]` mehrdeutig. Neues Schema — Datei traegt den Namen der Ebene, die sie beschreibt: | Ebene | bisher | neu | |---|---|---| | Mandant | `<m-slug>/README.md` | `<m-slug>/<m-slug>.md` | | Node | `<node>/node.md` | `<node>/<node>.md` | | Backups | `<node>/backups.{md,csv}` | `<node>/<node>-backups.{md,csv}` | | Guest | `vms/<vmid>-<slug>.md` | `vms/<node>-<vmid>-<slug>.md` | Begruendung: - **Datei = Ordnername** ist die Folder-Note-Konvention in Obsidian; Ordner und Notiz erscheinen als eine Einheit. - **Node-Praefix bei Guests** ist zwingend: VMID 101 existiert im echten Bestand bereits auf `bau-prox01` *und* `bau-prox02`. Heute kollidiert es nur zufaellig nicht, weil die Guest-Namen unterschiedlich sind. - Mandanten-Slug bleibt der Dateiname (nicht der Klarname), weil Mandantennamen `&`, `+` und Kommas enthalten (`Trendsetzer Marketing GmbH & Co. KG`) und diese in Wikilinks und Git-Pfaden Aerger machen. Der Klarname steht im Frontmatter unter `name:` und als `aliases:`. ## Migrationsfalle: Backup-Historie `_build_and_push()` liest vorhandene CSVs **vor** dem Wipe von `mandanten/` ein, um sie per UPID zu mergen — Lookup-Key ist der relative Pfad `<m-slug>/<node>/backups.csv`. Nach der Umbenennung greift dieser Lookup ins Leere, `_merge_backups()` bekommt einen leeren `existing_csv` und der erste Lauf schreibt eine frische CSV ohne die fortgeschriebene Historie. In der Git-Historie bleibt sie, im Arbeitsstand ist sie weg. **Der Einleseschritt muss beide Pfade akzeptieren** (alt und neu), damit der Uebergang verlustfrei ist. Nach einem erfolgreichen Lauf ist nur noch der neue Pfad relevant, der Fallback kann aber dauerhaft drin bleiben — er kostet nichts. ## Matrix: Bases statt Dataview Im Vault ist **kein Dataview** installiert (`.obsidian/community-plugins.json` enthaelt nur `github-sync`). Aktiv ist dagegen der Core-Plugin **`bases`** — Obsidians native Tabellen-/Datenbankansicht. Statt `MATRIX-DATAVIEW.md` daher eine `MATRIX.base` erzeugen (YAML), die auf `#theprox/vm` filtert und die Frontmatter-Properties als Spalten zeigt (tenant, node, vmid, role, status, open_ports, agent_version). Keine Community-Plugin-Abhaengigkeit, und sie liest exakt das Frontmatter, das dieses Issue ohnehin schreibt. Das aktuelle Base-Dateiformat vor der Umsetzung in der Obsidian-Doku pruefen — es hat sich seit Einfuehrung mehrfach geaendert. Die statische `MATRIX.md` bleibt zusaetzlich bestehen: sie funktioniert in jedem Markdown-Viewer, auch ohne Obsidian, und ist im Git-Diff lesbar. --- ## Prompt für Claude Code ``` Erweitere den bestehenden Docgen in theProx (server/services/docgen.py) so, dass die erzeugte Doku in Obsidian als Graph funktioniert und eine Matrix-Übersicht enthält. KEIN neues Subsystem — reines Rendering + ein Datenfix. --- 1. DATENFIX (zuerst, eigener Commit) --- In _fetch(): TenantVMRange mitladen und die Mandantenzuordnung pro GUEST (nicht nur pro Node) auflösen. from models import TenantVMRange ranges = (await db.execute(select(TenantVMRange))).scalars().all() Baue eine Hilfsfunktion: def _tenant_for_guest(node_id, vmid, ranges_by_node, node_to_tenant) -> str|None Reihenfolge: passender VMID-Range auf dieser Node gewinnt; sonst TenantNode der Node; sonst None (-> _OHNE-MANDANT). Konsequenz für die Verzeichnisstruktur: Guests einer Node können jetzt zu VERSCHIEDENEN Mandanten gehoeren. Die physische Ablage bleibt wie bisher mandanten/<node-mandant>/<node>/vms/ (Node ist physisch), ABER im Frontmatter jeder VM steht der TATSAECHLICHE Mandant des Guests, und die Matrix + der Graph nutzen diesen. Das ist bewusst so: der Graph bildet die logische Zuordnung ab, das Dateisystem die physische. --- 2. FRONTMATTER --- Jede erzeugte .md bekommt YAML-Frontmatter als ersten Block. Mandant (mandanten/<m>/README.md): --- type: mandant name: "<Mandantenname>" customer_number: "<oder leer>" nodes: <int> guests: <int> tags: [theprox/mandant] generated: <ISO8601 UTC> --- Node (.../<node>/node.md): --- type: node name: "<nodename>" tenant: "<Mandant>" status: online|offline|pending pve_version: "..." ip: "..." cpu_cores: <int> ram_bytes: <int> vms: <int> cts: <int> updates_available: <int> updates_security: <int> tags: [theprox/node, status/<status>] generated: <ISO8601 UTC> --- Guest (.../vms/<vmid>-<slug>.md): --- type: vm # oder: lxc vmid: <int> name: "<name>" node: "<nodename>" tenant: "<Mandant des Guests, siehe Datenfix>" status: running|stopped|... role: "<abgeleitete Funktion, s.u.>" os: "..." ip: "..." vcpus: <int> ram_bytes: <int> docker: true|false open_ports: <int> agent: true|false agent_version: "…" # nur wenn vorhanden agent_last_check: <ISO> # nur wenn vorhanden tags: [theprox/vm, role/<role>, node/<node-slug>, tenant/<tenant-slug>] generated: <ISO8601 UTC> --- Werte NICHT erfinden: fehlt etwas, Feld weglassen statt "—" ins YAML zu schreiben. Strings, die :, #, [ oder " enthalten, sauber quoten (eigener kleiner _yaml_str()-Helper, keine neue Dependency). --- 3. DATEINAMEN + WIKILINKS (der Graph) --- Benennung (siehe Tabelle im Issue-Text, ist verbindlich): mandanten/<m-slug>/<m-slug>.md (ersetzt README.md) mandanten/<m-slug>/<node>/<node>.md (ersetzt node.md) mandanten/<m-slug>/<node>/<node>-backups.md|.csv (ersetzt backups.*) mandanten/<m-slug>/<node>/vms/<node>-<vmid>-<slug>.md Alle Dateinamen sind damit vault-weit eindeutig -> Kurz-Wikilinks sind eindeutig aufloesbar: [[<m-slug>]] -> Mandant [[<node>]] -> Node [[<node>-<vmid>-<slug>]] -> Guest Fuer lesbare Anzeige durchgaengig Pipe-Links nutzen: [[<node>-<vmid>-<slug>|<vmid> <name>]] [[<m-slug>|<Mandant-Klarname>]] Zusaetzlich in JEDER Datei "aliases:" im Frontmatter setzen (Klarname des Mandanten, "<node>/<vmid>" beim Guest), damit der Quick-Switcher auch ueber die menschliche Schreibweise findet. BACKUP-HISTORIE (Migrationsfalle, unbedingt umsetzen): _build_and_push() liest existierende backups.csv vor dem Wipe unter dem Key "<m-slug>/<node>/backups.csv" ein. Nach der Umbenennung greift das ins Leere und die gemergte Historie geht im Arbeitsstand verloren. Der Einleseschritt muss BEIDE Pfade akzeptieren: "<m-slug>/<node>/<node>-backups.csv" (neu, Vorrang) "<m-slug>/<node>/backups.csv" (alt, Fallback) Ein Test deckt den Uebergang ab: altes Layout im Workdir -> nach dem Lauf liegt die Historie vollstaendig unter dem neuen Namen. Verlinkung (beidseitig, das fehlt heute komplett): - Mandant-Datei: alle Nodes als [[<node>]] + alle Guests des Mandanten - Node-Datei: "Mandant: [[<m-slug>|<Klarname>]]" + Abschnitt "## Guests" mit einem Link je Guest, gruppiert nach Mandant wenn mehrere - Guest-Datei: Kopfblock direkt nach dem Frontmatter: "Node: [[<node>]] · Mandant: [[<m-slug>|<Klarname>]]" - Backups-Datei: "Node: [[<node>]]" --- 4. MOC + MATRIX --- Neue Datei im Repo-Root: MATRIX.md - Kopfzeile + Generierungszeitpunkt - Eine Zeile PRO GUEST, Spalten: | Mandant | Node | VMID | Name | Typ | Funktion | Status | OS | IP | vCPU | RAM | Docker | Ports | Agent | Backup zuletzt | - Mandant/Node/Name als Wikilinks - Sortierung: Mandant -> Node -> VMID - "Backup zuletzt": juengstes ended_at aus backups_by_node fuer diese vmid, sonst "—" Zusaetzlich MATRIX.base (Obsidian-Bases, Core-Plugin, im Vault aktiv — Dataview ist NICHT installiert). Filter auf #theprox/vm, Spalten aus dem Frontmatter (tenant, node, vmid, role, status, open_ports, agent_version). Aktuelles Base-Dateiformat vorher in der Obsidian-Doku pruefen. Ersetzt den frueher hier vorgesehenen Dataview-Block: ```dataview TABLE tenant AS Mandant, node AS Node, vmid AS VMID, role AS Funktion, status AS Status, open_ports AS Ports, agent_version AS Agent FROM #theprox/vm SORT tenant ASC, node ASC, vmid ASC ``` Und _MOC.md im Root: Einstiegsseite mit Links auf alle Mandanten, MATRIX.md, Zaehlern (Mandanten/Nodes/Guests) und einer "Auffaellig"-Sektion (Guests OHNE Agent, Nodes offline, Guests ohne Backup). --- 5. FUNKTION ABLEITEN --- Neue reine Funktion: def _derive_role(guest: dict, gscan: dict, ports: list, override) -> str Reihenfolge: expliziter Override (models/vm_card_override.py pruefen, ob dort ein passendes Feld existiert — wenn nicht, diesen Schritt weglassen und im Issue-Kommentar vermerken) > Docker-Images (mapping-Dict fuer gaengige: nginx/traefik/caddy -> "reverse-proxy", postgres/mysql/mariadb -> "database", gitea/gitlab -> "git", nextcloud -> "filesharing", *-exporter/grafana/ prometheus -> "monitoring", ...) > Port-Heuristik > "unbekannt". Das Mapping-Dict oben in der Datei als Konstante, damit erweiterbar. --- 6. SANITIZING --- VM-/Node-Notizen (VMNote.content, Node.notes) sind FREITEXT VOM NUTZER und landen im Markdown. Vor dem Einbetten: - "[[" und "]]" escapen (sonst entstehen ungewollte Graph-Kanten) - Zeilen, die mit "---" beginnen, einruecken (sonst zerschiesst es das Frontmatter nachfolgender Blocks) - keine rohen HTML-Tags durchlassen Eigener Helper _safe_md(text). --- 7. TESTS --- server/tests/test_docgen_render.py (pytest, keine DB noetig — die Render- Funktionen sind rein): - _tenant_for_guest: Range gewinnt ueber TenantNode; Fallback-Kette - Frontmatter parst als gueltiges YAML (yaml.safe_load ueber den Block) - _safe_md escapt [[ ]] - _derive_role trifft die erwarteten Faelle - MATRIX enthaelt eine Zeile pro Guest NICHT anfassen: Git-Push-Logik, Scheduler, Backups-CSV-Merge. Reines Rendering + der _fetch()-Datenfix. ``` ## Definition of Done - [ ] `TenantVMRange` wird bei der Mandantenzuordnung berücksichtigt (+ Test) - [ ] Alle erzeugten `.md` haben valides YAML-Frontmatter - [ ] Guest-Seiten verlinken zurück auf Node und Mandant - [ ] `MATRIX.md`, `MATRIX-DATAVIEW.md`, `_MOC.md` werden erzeugt - [ ] Ein Testlauf gegen ein echtes Vault zeigt einen zusammenhängenden Graphen - [ ] Freitext-Notizen können den Graphen nicht manipulieren --- ## Nachtrag: CVE raus, Agent rein **CVEs entfallen vollstaendig aus der Doku.** Kein `cve_open`/`cve_critical` im Frontmatter, keine `## Offene CVEs`-Sektion in `_vm_md()` (steht aktuell in 33 der 79 generierten Dateien und muss weg), keine CVE-Spalte in der Matrix, kein CVE in der "Auffaellig"-Sektion des MOC. `VMCveReport` wird in `_fetch()` nicht mehr geladen — der komplette `cves_map`-Pfad faellt ersatzlos. Begruendung: CVE-Daten sind hochfrequent und kurzlebig. In einer taeglich committeten Snapshot-Doku erzeugen sie Diff-Rauschen, das echte Infrastruktur-Aenderungen ueberdeckt, und sind schon beim Lesen veraltet. CVEs gehoeren ins Dashboard, nicht ins Vault. **An ihre Stelle tritt der Agent-Status** als Qualitaetsmerkmal der Doku selbst — siehe #212. Ein Guest ohne Agent liefert keine Disk-, Docker-, Update- und Service-Daten; die Doku ist an dieser Stelle also nachweislich unvollstaendig, und genau das muss sichtbar sein. Tag `theprox/no-agent`, eigene Sektion im `_MOC.md`. **Reihenfolge:** #212 sollte vor diesem Issue umgesetzt werden. Sonst schreibt das neue Frontmatter `agent: false` fuer alle Guests fest — die Felder waeren dann strukturell korrekt und inhaltlich trotzdem falsch. --- ## Nachtrag: `role` wird zu `roles[]` Die Funktions-Ableitung wandert vollstaendig nach #216 und liefert eine **Liste** statt eines Einzelwerts. Grund: ein Windows-Server ist real oft gleichzeitig AD-DC, DNS, DHCP und Fileserver — ein einzelner String erzwingt eine willkuerliche Entscheidung. Frontmatter statt `role: "..."`: roles: [ad-dc, dns, dhcp, fileserver] primary_role: ad-dc role_confidence: high|medium|low role_source: inferred|manual tags: [theprox/vm, role/ad-dc, role/dns, role/dhcp, role/fileserver, …] Ein Tag **pro Rolle** — dadurch ist im Graphen und in `MATRIX.base` nach jeder einzelnen Rolle filterbar, statt nur nach einer Hauptrolle. `_derive_role()` aus Punkt 5 des CC-Prompts entfaellt hier ersatzlos. Docgen ruft die fertigen Werte aus `guest_view` ab und rendert zusaetzlich eine Sektion `## Rollen` mit der Evidenz (welcher Dienst / welche Windows-Rolle die Zuordnung begruendet hat). Ohne die Evidenz ist eine falsche Rolle nicht widerlegbar. Matrix-Spalte "Funktion" zeigt `primary_role`, mit den weiteren Rollen als Zusatz. **Abhaengigkeitskette neu:** #214 → #215 → #216 → #209.
Author
Owner

Teil 1/3 der Docgen-Ausbaustufe. Folgt: #210 (Pro-Node-Toggle + Konfig/Logging), #211 (KI-Layer).

Teil 1/3 der Docgen-Ausbaustufe. Folgt: #210 (Pro-Node-Toggle + Konfig/Logging), #211 (KI-Layer).
Author
Owner

Nachtrag Dateibenennung. Bestandsanalyse des Ziel-Repos chinux/Dokumentation (132 Dateien): 8× node.md, 8× backups.md, 7× README.md — in Obsidian ununterscheidbar im Graphen und als Wikilink-Ziel mehrdeutig. Benennungsschema im Issue-Text ergänzt und Punkt 3 des CC-Prompts entsprechend neu gefasst.

Zwei Folgepunkte mit aufgenommen:

  • Backup-Historie: der Rename bricht den existing_csv-Lookup in _build_and_push(). Fallback auf den alten Pfad ist Pflicht, sonst ist die fortgeschriebene CSV nach dem ersten Lauf im Arbeitsstand weg.
  • Matrix: Dataview ist im Vault nicht installiert, bases dagegen aktiv. MATRIX-DATAVIEW.md durch MATRIX.base ersetzt; die statische MATRIX.md bleibt als plugin-freier Fallback.
**Nachtrag Dateibenennung.** Bestandsanalyse des Ziel-Repos `chinux/Dokumentation` (132 Dateien): 8× `node.md`, 8× `backups.md`, 7× `README.md` — in Obsidian ununterscheidbar im Graphen und als Wikilink-Ziel mehrdeutig. Benennungsschema im Issue-Text ergänzt und Punkt 3 des CC-Prompts entsprechend neu gefasst. Zwei Folgepunkte mit aufgenommen: - **Backup-Historie:** der Rename bricht den `existing_csv`-Lookup in `_build_and_push()`. Fallback auf den alten Pfad ist Pflicht, sonst ist die fortgeschriebene CSV nach dem ersten Lauf im Arbeitsstand weg. - **Matrix:** Dataview ist im Vault nicht installiert, `bases` dagegen aktiv. `MATRIX-DATAVIEW.md` durch `MATRIX.base` ersetzt; die statische `MATRIX.md` bleibt als plugin-freier Fallback.
Author
Owner

CVE-Anteil entfernt (Frontmatter, Matrix-Spalte, _vm_md-Sektion, _fetch-Query, MOC). Ersetzt durch den Agent-Status — Begruendung im Nachtrag am Issue-Ende.

Abhaengigkeit: #212 zuerst. Die Agent-Daten sind aktuell in 78/78 generierten Dateien leer; ohne den Fix zementiert dieses Issue nur agent: false ueber den gesamten Bestand.

CVE-Anteil entfernt (Frontmatter, Matrix-Spalte, `_vm_md`-Sektion, `_fetch`-Query, MOC). Ersetzt durch den Agent-Status — Begruendung im Nachtrag am Issue-Ende. **Abhaengigkeit:** #212 zuerst. Die Agent-Daten sind aktuell in 78/78 generierten Dateien leer; ohne den Fix zementiert dieses Issue nur `agent: false` ueber den gesamten Bestand.
Author
Owner

Abhaengigkeiten praezisiert: #212 (Agent-Daten korrekt) und #213 (Updates/Dienste/Ports vollstaendig) liefern beide Felder, die hier ins Frontmatter, in die Matrix und in die role-Ableitung einfliessen. Reihenfolge: #212#213#209.

Abhaengigkeiten praezisiert: #212 (Agent-Daten korrekt) und #213 (Updates/Dienste/Ports vollstaendig) liefern beide Felder, die hier ins Frontmatter, in die Matrix und in die `role`-Ableitung einfliessen. Reihenfolge: #212 → #213 → #209.
Author
Owner

roleroles[] (Nachtrag am Issue-Ende). Ableitung liegt jetzt in #216, _derive_role() entfaellt hier. Kette: #214#215#216#209.

`role` → `roles[]` (Nachtrag am Issue-Ende). Ableitung liegt jetzt in #216, `_derive_role()` entfaellt hier. Kette: #214 → #215 → #216 → #209.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
chinux/theProx#209
No description provided.