Compare commits
118
Commits
@@ -5,6 +5,18 @@
|
||||
# die Retro vom 2026-08-09: sechs solcher Faelle in neun Tagen, keiner davon durch
|
||||
# eine Ueberwachung gefunden.
|
||||
|
||||
# Ohne workflow-Block legt GitLab auch dann eine Pipeline an, wenn KEIN Job auf sie
|
||||
# passt, und fuehrt sie als "failed" - rot ohne Fehler, genau das, wogegen #0104
|
||||
# angeht. Die drei Jobs unten decken schedule, web und push ab; ein API-Trigger
|
||||
# traefe keinen davon. Im gitops-Repo ist dieser Fall am 2026-08-19 real eingetreten
|
||||
# (Pipeline 518), hier wird er vorbeugend ausgeschlossen.
|
||||
workflow:
|
||||
rules:
|
||||
- if: $CI_PIPELINE_SOURCE == "schedule"
|
||||
- if: $CI_PIPELINE_SOURCE == "web"
|
||||
- if: $CI_PIPELINE_SOURCE == "push"
|
||||
- when: never
|
||||
|
||||
stages:
|
||||
- pruefen
|
||||
|
||||
@@ -43,6 +55,7 @@ validate:
|
||||
rules:
|
||||
- if: $CI_PIPELINE_SOURCE == "push"
|
||||
before_script:
|
||||
- apk add --no-cache git >/dev/null # pruefe_prosa.py braucht git cat-file
|
||||
- pip install --quiet pyyaml
|
||||
script:
|
||||
- python3 scripts/validate.py
|
||||
@@ -61,6 +74,12 @@ gruppenpruefung:
|
||||
rules:
|
||||
- if: $CI_PIPELINE_SOURCE == "schedule"
|
||||
- if: $CI_PIPELINE_SOURCE == "web"
|
||||
variables:
|
||||
# gruppenpruefung.py spricht die git.lab-API per urllib an; git.lab läuft
|
||||
# über die private aXionLabs-CA. Python honoriert SSL_CERT_FILE im Default-Context.
|
||||
SSL_CERT_FILE: "$CI_PROJECT_DIR/ci/lab-ca-chain.crt"
|
||||
before_script:
|
||||
- apk add --no-cache git >/dev/null # gruppenpruefung.py ruft 'git log' auf
|
||||
script:
|
||||
- |
|
||||
if [ -z "$GITLAB_TOKEN" ]; then
|
||||
|
||||
@@ -133,6 +133,13 @@ Ohne Lab-Zugang: dieses Repo ist als Push-Mirror unter
|
||||
- Einzige bewusste Ausnahme: der TURN-Rotations-CronJob schreibt nach
|
||||
Gitea; der tägliche CI-Job `canonize_rotation` holt es zurück. Seine
|
||||
rote Pipeline **ist** der Alarm — es gibt bewusst keinen zweiten Meldeweg.
|
||||
Das trägt nur, solange **Grün der Normalzustand** ist: Am 2026-08-18 stand
|
||||
dieser Job neun Tage rot, und genau deshalb fiel es niemandem auf. Siehe
|
||||
Prüfungen unten.
|
||||
- **Wiki:** Das frühere Test-Wiki (Docusaurus-Lesefläche auf `wiki.lab`) wurde als
|
||||
ungenügend bewertet und auf Basis von **Wiki.js in den Stack integriert**
|
||||
([ADR-0014](docs/adr/0014-wikijs-loest-docusaurus-ab.md), `wiki.axion1337.chat`).
|
||||
`wiki.lab` existiert nicht mehr — nicht danach suchen.
|
||||
|
||||
### Issues & Board
|
||||
|
||||
@@ -149,6 +156,26 @@ Ohne Lab-Zugang: dieses Repo ist als Push-Mirror unter
|
||||
dauerhaften Ausnahme von einer Regel** — eine Ausnahme nur zu
|
||||
dokumentieren statt sie zu entscheiden, ist ein Fehler.
|
||||
|
||||
### Prüfungen
|
||||
|
||||
Die Repo-Map oben nennt nur `validate.py` und `gen_status.py` — sie ist
|
||||
neckbeard-Baseline und bleibt byte-treu. Tatsächlich stehen in `scripts/`:
|
||||
|
||||
| Skript | Wann | Wofür |
|
||||
|---|---|---|
|
||||
| `validate.py`, `gen_status.py` | jeder Push | Artefakte gegen `schema.yaml`, STATUS aktuell |
|
||||
| `pruefe_prosa.py`, `pruefe_upstream_drift.py` | jeder Push | tote Verweise/SHA-Zitate; Baseline unverändert |
|
||||
| `gruppenpruefung.py`, `stillstandspruefung.py` | täglich (Zeitplan) | Verbund- und Stillstandsbefunde über die Gruppe |
|
||||
| `spiegel_issues.py` | manuell (sorb) | `docs/issues/` → GitLab, Standard Dry-Run |
|
||||
| `quittungen.py` | von beiden Prüfungen genutzt | Bekanntes quittieren |
|
||||
|
||||
**Bekanntes wird quittiert, nicht toleriert** (`scripts/befund_quittungen.tsv`,
|
||||
[ADR-0020](docs/adr/0020-bekannte-befunde-quittieren.md)): Jede Zeile trägt eine
|
||||
Frist, „dauerhaft" geht nur als ADR-Verweis, und wirkungslose Zeilen melden sich.
|
||||
Quittiertes bleibt in der Ausgabe sichtbar. Eine Prüfung, die dauerhaft rot steht,
|
||||
meldet nichts mehr — deshalb ist Rot-Stehenlassen kein neutraler Zustand, sondern
|
||||
ein Befund für sich.
|
||||
|
||||
### Secrets & Credentials
|
||||
|
||||
- Token-/Secret-Werte **niemals anzeigen, loggen oder in Dateien
|
||||
|
||||
@@ -30,17 +30,28 @@ GitLab-Issue-Template. **Alle Issues leben auf git.lab.**
|
||||
|
||||
| Pfad | Artefakt |
|
||||
|---|---|
|
||||
| [`CLAUDE.md`](CLAUDE.md) | **Kanonische Arbeitskonventionen für alle Agenten-Sessions** (Topologie, Framework, Secrets, Karpathy-Guidelines) |
|
||||
| `vision/` | Eine Vision je Linie: Community (axion1337.chat), Tool (ThreadNet), Plattform (Homelab) |
|
||||
| `roadmap.md` | Linien, Meilenstein-Kandidaten, Kadenz — GitLab-Milestones halten den Stand |
|
||||
| `decisions/` | ADRs — Pflicht bei Architekturentscheidungen **und dauerhaften Ausnahmen** |
|
||||
| `verfahren/` | Wie wir arbeiten: [Deploy-Übergabe/DoD](docs/wiki/deployment/deploy-uebergabe.md), [Refinement & Retro](docs/wiki/admin/refinement.md), AARs (`docs/aar/`), Werkzeuge |
|
||||
| `hosts/`, `shared/` | **Bestand + Historie** je Host/Thema — u. a. [Branding](docs/wiki/architecture/branding.md) (Marke, Paletten, wo welches Theme eingestellt ist); offene Punkte sind Issues |
|
||||
| [`AGENTS.md`](AGENTS.md) | **Kanonische Arbeitskonventionen für alle Agenten-Sessions** (Topologie, Framework, Secrets, Karpathy-Guidelines). `CLAUDE.md` zeigt nur hierher. |
|
||||
| [`WORKFLOW.md`](WORKFLOW.md) | Gates, Größenklassen, Debugging-Pfad, Refinement-Ritual |
|
||||
| [`schema.yaml`](schema.yaml) | Frontmatter-Schema — einzige Wahrheit über den Aufbau der Artefakte |
|
||||
| [`STATUS.md`](STATUS.md) | Generierte Übersicht; **nicht von Hand ändern** (`scripts/gen_status.py`) |
|
||||
| `roadmap.md` | Linien, Meilenstein-Kandidaten, Kadenz — die Gruppen-Milestones halten den Stand |
|
||||
| `docs/adr/` | ADRs — Pflicht bei Architektur-/Prozessentscheidungen **und dauerhaften Ausnahmen** |
|
||||
| `docs/issues/` | Das kanonische Backlog der ganzen Gruppe ([ADR-0012](docs/adr/0012-issues-im-repo-gitlab-als-spiegel.md), [ADR-0019](docs/adr/0019-komponenten-issues-adoptiert.md)) |
|
||||
| `docs/design/`, `docs/aar/` | Design-Dokumente je Vorhaben; AARs zu Vorfällen und größeren Abweichungen |
|
||||
| `docs/components/` | Eine Datei je Projekt der Gruppe — wer hier fehlt, wird zum Befund |
|
||||
| `docs/wiki/`, `docs/sources/` | Wiki-Flächen (u. a. [Deploy-Übergabe/DoD](docs/wiki/deployment/deploy-uebergabe.md), [Refinement & Retro](docs/wiki/admin/refinement.md), [Branding](docs/wiki/architecture/branding.md)) und unveränderliche Quellen |
|
||||
| `scripts/`, `verfahren/` | Deterministische Werkzeuge — Prüfungen, Spiegel, Migration/Adoption |
|
||||
|
||||
Gelesen wird das alles auch gebündelt unter **[axionwiki.lab](https://axionwiki.lab)** —
|
||||
dort stehen Plattform-Wiki, Homelab-Doku und dieses Repo nebeneinander
|
||||
([ADR-0006](docs/adr/0006-wikis-konsolidieren-docusaurus.md), Konfiguration in
|
||||
[`homelab/wiki`](https://git.lab/homelab/wiki)). **Geändert wird immer hier, nie dort.**
|
||||
Die Prüfungen sind die Alarmanlage: `validate.py` und `gen_status.py` laufen bei jedem
|
||||
Push, `gruppenpruefung.py` und `stillstandspruefung.py` täglich per Zeitplan. Bekanntes
|
||||
wird in `scripts/befund_quittungen.tsv` **quittiert, nicht toleriert**
|
||||
([ADR-0020](docs/adr/0020-bekannte-befunde-quittieren.md)) — eine Prüfung, die dauerhaft
|
||||
rot steht, meldet nichts mehr.
|
||||
|
||||
Gelesen wird die Anwender- und Betriebsdoku unter
|
||||
**[wiki.axion1337.chat](https://wiki.axion1337.chat)** (Wiki.js,
|
||||
[ADR-0014](docs/adr/0014-wikijs-loest-docusaurus-ab.md)). Das frühere Docusaurus-Wiki
|
||||
auf `axionwiki.lab` **existiert nicht mehr** — nicht danach suchen.
|
||||
|
||||
## Das Backlog: Issues + Board
|
||||
|
||||
|
||||
@@ -2,72 +2,103 @@
|
||||
|
||||
<!-- Generated by scripts/gen_status.py — do not edit. -->
|
||||
|
||||
## Issues (36 open, 0 closed)
|
||||
## Issues (58 open, 41 closed)
|
||||
|
||||
Verteilung: M1 9 · M2 25 · M4 2
|
||||
Verteilung: M1 11 · M2 17 · M3 4 · M4 11 · M5 15
|
||||
|
||||
| Issue | Status | Meilenstein | Priorität | Title |
|
||||
|---|---|---|---|---|
|
||||
| [0001](docs/issues/0001-matrix-03-www-matrix-axion1337-de-ist.md) | open | M2 | low | MATRIX-03: www.matrix.axion1337.de ist überflüssig |
|
||||
| [0002](docs/issues/0002-game-01-host-von-cfgmon-aus-nicht-erreichbar-2.md) | waiting | M1 | medium | GAME-01: Host von CFGMON aus nicht erreichbar, 2 Prometheus-Targets down |
|
||||
| [0003](docs/issues/0003-game-02-www-game-axion1337-de-ist-ueberfluessig.md) | open | M2 | low | GAME-02: www.game.axion1337.de ist überflüssig |
|
||||
| [0004](docs/issues/0004-overmind-02-e1000e-nic-hang-beobachtung-nach.md) | waiting | M1 | low | OVERMIND-02: e1000e-NIC-Hang — Beobachtung nach EEE-Fix + Firmware-Update |
|
||||
| [0005](docs/issues/0005-zone-01-ionos-default-records-bereinigen-www.md) | open | M2 | low | ZONE-01: IONOS-Default-Records bereinigen (www-Paare, tote Mail-Sätze) |
|
||||
| [0006](docs/issues/0006-zone-02-apex-dmarc-ist-p-none-und-schuetzt.md) | open | M1 | low | ZONE-02: Apex-DMARC ist p=none und schützt nichts |
|
||||
| [0007](docs/issues/0007-cfgmon-01-zertifikatserneuerung-braucht-offene.md) | next | M1 | high | CFGMON-01: Zertifikatserneuerung braucht offene Ports — zeitkritisch ab 2026-09-28 |
|
||||
| [0008](docs/issues/0008-cfgmon-03-prometheus-remote-write-und-loki.md) | waiting | M1 | medium | CFGMON-03: Prometheus-Remote-Write und Loki öffentlich ohne Auth — Weg A, nachgelagerte Prüfung |
|
||||
| [0009](docs/issues/0009-cfgmon-04-grafana-admin-credentials-aus-env.md) | open | M2 | low | CFGMON-04: Grafana-Admin-Credentials aus .env gelten nicht für die HTTP-API |
|
||||
| [0010](docs/issues/0010-cfgmon-09-gitea-backups-off-host-borg-storage.md) | open | M1 | medium | CFGMON-09: Gitea-Backups off-host (Borg/Storage Box) — Backup-Cron ist DEAKTIVIERT |
|
||||
| [0014](docs/issues/0014-cfgmon-14-root-zugang-ueber-die-docker-gruppe.md) | waiting | M2 | low | CFGMON-14: Root-Zugang über die docker-Gruppe umgeht sudo und hinterlässt keine Spur |
|
||||
| [0015](docs/issues/0015-cfgmon-15-token-hygiene-einmal-tokens-der.md) | next | M2 | medium | CFGMON-15: Token-Hygiene — Einmal-Tokens der LABNET-02-Nacht widerrufen |
|
||||
| [0018](docs/issues/0018-doc-01-wiki-rollout-abschliessen-ci-freigaben.md) | open | M2 | low | DOC-01: Wiki-Rollout abschließen — CI-Freigaben, Zeitplan, Dokploy-Stack, wiki.lab |
|
||||
| [0014](docs/issues/0014-cfgmon-14-root-zugang-ueber-die-docker-gruppe.md) | open | M2 | low | CFGMON-14: Root-Zugang über die docker-Gruppe umgeht sudo und hinterlässt keine Spur |
|
||||
| [0015](docs/issues/0015-cfgmon-15-token-hygiene-einmal-tokens-der.md) | waiting | M2 | medium | CFGMON-15: Token-Hygiene — Einmal-Tokens der LABNET-02-Nacht widerrufen |
|
||||
| [0019](docs/issues/0019-doc-02-veralteten-wiki-branch-im-gitops-repo.md) | open | M2 | low | DOC-02: Veralteten `wiki`-Branch im gitops-Repo entfernen? |
|
||||
| [0020](docs/issues/0020-doc-03-wiki-oberflaeche-entscheiden-docusaurus.md) | next | M2 | medium | DOC-03: Wiki-Oberfläche entscheiden — Docusaurus oder BookStack |
|
||||
| [0021](docs/issues/0021-overmind-03-windows-build-vm-verschwindet-ci.md) | waiting | M2 | medium | OVERMIND-03: Windows-Build-VM verschwindet — CI kann sie nur starten, nicht anlegen |
|
||||
| [0022](docs/issues/0022-build-01-macos-client-reproduzierbar-bauen.md) | open | M4 | low | BUILD-01: macOS-Client reproduzierbar bauen — aktuell nur manuell auf sorbs Mac |
|
||||
| [0023](docs/issues/0023-doc-04-navbar-logo-im-docusaurus-wiki-wird.md) | open | M2 | low | DOC-04: Navbar-Logo im Docusaurus-Wiki wird ausgeliefert, ist aber nicht sichtbar |
|
||||
| [0024](docs/issues/0024-wiki-hostname-klaeren-wiki-lab-oder-axionwiki.md) | open | M2 | low | Wiki-Hostname klären: wiki.lab oder axionwiki.lab? |
|
||||
| [0025](docs/issues/0025-deploy-uebergabe-cve-alarme-aggregiert-receiver.md) | waiting | M1 | medium | Deploy-Übergabe: CVE-Alarme aggregiert + Receiver-Robustheit (gitops#51, ff87cb2) |
|
||||
| [0027](docs/issues/0027-audit-01-acht-widersprueche-aus-dem-labnet-02.md) | waiting | M2 | medium | AUDIT-01: Acht Widersprüche aus dem LABNET-02-Nachlauf (Selbst-Audit CFGMON-Session) |
|
||||
| [0028](docs/issues/0028-mirror-01-ein-ausfall-der-push-mirrors-bleibt.md) | open | M2 | low | MIRROR-01: Ein Ausfall der Push-Mirrors bleibt unbemerkt — Produktion friert still ein |
|
||||
| [0027](docs/issues/0027-audit-01-acht-widersprueche-aus-dem-labnet-02.md) | open | M2 | medium | AUDIT-01: Acht Widersprüche aus dem LABNET-02-Nachlauf (Selbst-Audit CFGMON-Session) |
|
||||
| [0029](docs/issues/0029-ui-harmonisieren-gleiche-farben-und-formen.md) | open | M4 | medium | UI harmonisieren: gleiche Farben und Formen über alle Oberflächen |
|
||||
| [0030](docs/issues/0030-der-restore-ist-nie-geprobt-sicherungen-sind.md) | open | M1 | medium | Der Restore ist nie geprobt — Sicherungen sind bisher eine Vermutung |
|
||||
| [0031](docs/issues/0031-stillstandspruefung-gitea-token-und-authentik.md) | open | M1 | low | Stillstandsprüfung: GITEA_TOKEN und Authentik-Teil nachziehen |
|
||||
| [0030](docs/issues/0030-der-restore-ist-nie-geprobt-sicherungen-sind.md) | in-progress | M1 | medium | Der Restore ist nie geprobt — Sicherungen sind bisher eine Vermutung |
|
||||
| [0032](docs/issues/0032-gameserver-hat-keinen-push-mirror-und-auf-gitea.md) | open | M2 | medium | gameserver hat keinen Push-Mirror — und auf Gitea liegt ein anderer Stand |
|
||||
| [0033](docs/issues/0033-overmind-01-element-desktop-build-lab-registry.md) | open | M2 | low | OVERMIND-01 — element-desktop-build von rohana in die Lab-Registry umziehen |
|
||||
| [0034](docs/issues/0034-cfgmon-11-gitea-ci-rueckbau-abschliessen.md) | open | M2 | medium | CFGMON-11 — Gitea-CI-Rückbau abschließen (sicher rückbaubare Schritte) |
|
||||
| [0035](docs/issues/0035-rollout-agents-pointer-axion1337-chat-gitops.md) | open | M2 | medium | Rollout Gruppenregeln-Pointer: `axion1337.chat-gitops` |
|
||||
| [0036](docs/issues/0036-rollout-agents-pointer-threadnet-web.md) | open | M2 | medium | Rollout Gruppenregeln-Pointer: `ThreadNet-Web` |
|
||||
| [0037](docs/issues/0037-rollout-agents-pointer-threadnet-call.md) | open | M2 | medium | Rollout Gruppenregeln-Pointer: `threadnet-call` |
|
||||
| [0038](docs/issues/0038-rollout-agents-pointer-thread-net-git.md) | open | M2 | medium | Rollout Gruppenregeln-Pointer: `thread-net-git` |
|
||||
| [0039](docs/issues/0039-rollout-agents-pointer-threadnet-operating.md) | open | M2 | medium | Rollout Gruppenregeln-Pointer: `threadnet-operating` |
|
||||
| [0040](docs/issues/0040-neckbeard-rueckmeldungen-einreichen.md) | open | M2 | low | neckbeard-Rückmeldungen aus dem Feldtest einreichen |
|
||||
| [0041](docs/issues/0041-wartegrund-der-importierten-waiting-issues.md) | open | M2 | low | wartegrund der 7 importierten waiting-Issues präzisieren |
|
||||
| [0042](docs/issues/0042-migration-in-betrieb-nehmen-push-spiegel-schedule.md) | open | M2 | high | Migration in Betrieb nehmen: Push, erster Spiegel-Lauf, CI-Schedule |
|
||||
| [0051](docs/issues/0051-cve-remediation-pass.md) | open | M5 | high | CVE-Remediation-Pass: Schwachstellen-Report abarbeiten |
|
||||
| [0053](docs/issues/0053-historien-durchgang-nicht-kanonische-commits.md) | open | M2 | low | Historien-Durchgang: acht nicht-kanonische Commits mitziehen |
|
||||
| [0056](docs/issues/0056-gitops-9-external-postgresql-migration-cloudnativepg-o.md) | open | M1 | high | External PostgreSQL Migration: CloudNativePG or Hetzner |
|
||||
| [0057](docs/issues/0057-gitops-11-element-call-vp9-codec-retry.md) | open | M4 | medium | Element Call: VP9 codec retry |
|
||||
| [0058](docs/issues/0058-gitops-14-web-application-firewall-waf.md) | open | M5 | medium | Web Application Firewall (WAF) |
|
||||
| [0059](docs/issues/0059-gitops-16-pod-security-admission-restricted.md) | open | M5 | medium | Pod Security Admission (Restricted) |
|
||||
| [0061](docs/issues/0061-gitops-20-external-secrets-operator-vs-current-sops-set.md) | open | M2 | low | External-Secrets Operator vs. current SOPS setup |
|
||||
| [0062](docs/issues/0062-gitops-21-renovate-dependabot-for-chart-and-image-updat.md) | open | M5 | medium | Renovate/Dependabot for chart and image updates |
|
||||
| [0063](docs/issues/0063-gitops-22-security-advisory-monitoring-ess-element.md) | open | M5 | medium | Security advisory monitoring (ESS/Element) |
|
||||
| [0064](docs/issues/0064-gitops-23-disable-automountserviceaccounttoken-where-no.md) | open | M5 | medium | Disable automountServiceAccountToken where not needed |
|
||||
| [0065](docs/issues/0065-gitops-25-k3s-api-security-hardening.md) | open | M5 | high | K3s API security hardening |
|
||||
| [0066](docs/issues/0066-gitops-26-auditd-for-file-integrity-syscall-audit.md) | open | M5 | medium | auditd for file integrity & syscall audit |
|
||||
| [0067](docs/issues/0067-gitops-27-kernel-hardening-sysctl.md) | open | M5 | medium | Kernel hardening (sysctl) |
|
||||
| [0068](docs/issues/0068-gitops-28-lynis-security-baseline.md) | open | M5 | medium | Lynis security baseline |
|
||||
| [0069](docs/issues/0069-gitops-29-crowdsec-integration.md) | open | M5 | medium | CrowdSec integration |
|
||||
| [0070](docs/issues/0070-gitops-30-falco-runtime-monitoring.md) | open | M5 | medium | Falco runtime monitoring |
|
||||
| [0071](docs/issues/0071-gitops-31-trivy-image-scanning-for-cves.md) | open | M5 | low | Trivy image scanning for CVEs |
|
||||
| [0072](docs/issues/0072-gitops-34-dsgvo-datenschutz-compliance-konkretisieren.md) | open | M1 | low | DSGVO/Datenschutz-Compliance konkretisieren |
|
||||
| [0073](docs/issues/0073-gitops-35-architektur-monorepo-umbau-mit-generalisierte.md) | open | M2 | medium | Architektur: Monorepo-Umbau mit generalisiertem Config-Overlay |
|
||||
| [0074](docs/issues/0074-gitops-39-cleanup-checkliste-laufend.md) | open | M2 | low | Cleanup-Checkliste (laufend) |
|
||||
| [0076](docs/issues/0076-gitops-41-registry-git-traffic-zum-gitea-host-ueber-pri.md) | open | M2 | low | Registry-/Git-Traffic zum Gitea-Host ueber privates Hetzner-Netzwerk statt oeffentlichem Internet routen |
|
||||
| [0077](docs/issues/0077-gitops-42-grafana-dashboard-fuer-clamav-scan-ergebnisse.md) | open | M1 | low | Grafana-Dashboard für ClamAV-Scan-Ergebnisse (Issue #19) |
|
||||
| [0078](docs/issues/0078-gitops-45-cve-meldeweg-v2-metriken-grafana-dashboard-al.md) | open | M1 | medium | CVE-Meldeweg v2: Metriken, Grafana-Dashboard, Alerts in eigenen Matrix-Raum |
|
||||
| [0080](docs/issues/0080-gitops-47-raidplaner-mit-sozialer-komponente-verfuegbar.md) | open | M3 | medium | Raidplaner mit sozialer Komponente (Verfügbarkeiten, Aufgaben, Roadmap, Fotoalbum) |
|
||||
| [0081](docs/issues/0081-gitops-48-gaeste-invite-workflow-per-bot-3-tage-account.md) | open | M3 | medium | Gäste-Invite-Workflow per Bot (3-Tage-Accounts, Admin-Freischaltung, begrenzte Reaktivierung) |
|
||||
| [0083](docs/issues/0083-gitops-50-monitoring-deploy-geaenderte-configs-greifen.md) | open | M1 | medium | Monitoring-Deploy: geaenderte Configs greifen nicht ohne --force-recreate (Inode-Falle bei Einzeldatei-Mounts) |
|
||||
| [0085](docs/issues/0085-gitops-52-docs-traegt-zwei-altbestaende-abgeschlossener.md) | open | M2 | low | docs/ trägt zwei Altbestände abgeschlossener Umzüge: TASKS.md und oldwiki/ |
|
||||
| [0086](docs/issues/0086-gitops-53-k8s-ressourcen-heissen-noch-element-web-docs.md) | open | M4 | low | k8s-Ressourcen heißen noch element-web-docs (Rest des ThreadNet-Rebrands) |
|
||||
| [0087](docs/issues/0087-gitops-55-logo-fuer-die-authentik-anmeldemaske-entwerfe.md) | waiting | M4 | low | Logo für die Authentik-Anmeldemaske entwerfen (Querformat/SVG) |
|
||||
| [0088](docs/issues/0088-gitops-56-networkpolicy-ausgehender-verkehr-ist-unbesch.md) | open | M1 | medium | NetworkPolicy: ausgehender Verkehr ist unbeschränkt (13 Ingress-Regeln, 1 Egress) |
|
||||
| [0089](docs/issues/0089-gitops-58-eigene-images-sind-unsigniert-beim-deploy-pru.md) | open | M5 | low | Eigene Images sind unsigniert — beim Deploy prüft nichts die Herkunft |
|
||||
| [0090](docs/issues/0090-gitops-59-kein-kubernetes-audit-log-zugriffe-an-der-api.md) | open | M5 | low | Kein Kubernetes-Audit-Log — Zugriffe an der API werden nicht protokolliert |
|
||||
| [0092](docs/issues/0092-threadnet-web-1-default-client-einstellungen-theme-features-f.md) | waiting | M3 | low | Default-Client-Einstellungen (Theme, Features) für Neuinstallationen provisionieren |
|
||||
| [0093](docs/issues/0093-threadnet-web-3-settings-normalisieren-video-audio-einstellun.md) | open | M4 | medium | Settings normalisieren: Video/Audio-Einstellungen nur im Call-Widget, nicht im Haupt-Client |
|
||||
| [0094](docs/issues/0094-threadnet-web-4-direkter-link-zur-2fa-passkey-einrichtung-in.md) | open | M3 | medium | Direkter Link zur 2FA/Passkey-Einrichtung in den Account-Settings |
|
||||
| [0095](docs/issues/0095-threadnet-web-6-windows-desktop-code-signing-installer-brandi.md) | open | M4 | low | Windows-Desktop: Code-Signing (+ Installer-Branding) |
|
||||
| [0096](docs/issues/0096-threadnet-web-7-rebranding-element-axion1337-web-desktop-gesa.md) | waiting | M4 | medium | Rebranding: Element → aXion1337 (Web + Desktop, Gesamtklammer) |
|
||||
| [0097](docs/issues/0097-threadnet-web-9-feedback-bugreport-weg-eigener-rageshake-oder.md) | open | M4 | low | Feedback-/Bugreport-Weg: eigener Rageshake oder Alternative (Zammad nachhalten) |
|
||||
| [0100](docs/issues/0100-threadnet-web-13-asset-pfade-tragen-weiterhin-element-themes-e.md) | open | M4 | low | Asset-Pfade tragen weiterhin "element" (themes/element/…) |
|
||||
| [0101](docs/issues/0101-threadnet-call-4-kaputtes-paket-0-19-2-threadnet-6-in-der-regi.md) | open | M4 | low | Kaputtes Paket 0.19.2-threadnet.6 in der Registry — Herkunft ungeklärt |
|
||||
| [0102](docs/issues/0102-dmarc-und-mail-haertung-der-zone-axion1337-chat.md) | open | M1 | medium | DMARC der Plattform-Zone `axion1337.chat` steht auf p=none — und gehört IONOS, nicht uns |
|
||||
| [0103](docs/issues/0103-wiki-zugang-ohne-gruppe-endet-stumm.md) | open | M1 | medium | Wiki: Anmeldung ohne Gruppe endet stumm — `wiki-anwender` hat null Mitglieder |
|
||||
| [0105](docs/issues/0105-projekt-wiki-vor-projektabschluss-klaeren.md) | waiting | M2 | low | Projekt `wiki`: Verbleib klären — erst unmittelbar vor Projektabschluss |
|
||||
|
||||
## Active design docs (0)
|
||||
|
||||
_none active_
|
||||
|
||||
## ADRs (13)
|
||||
## ADRs (23)
|
||||
|
||||
| ADR | Status | Title |
|
||||
|---|---|---|
|
||||
| [0001](docs/adr/0001-gitlab-kanonisch-push-mirror.md) | accepted | 0001 — git.lab ist kanonisch, Gitea wird per Push-Mirror beliefert |
|
||||
| [0002](docs/adr/0002-issues-und-management-ins-lab.md) | accepted | 0002 — Issues und Management-Repo ziehen ins Lab („das Lab ist die Quelle der Wahrheit") |
|
||||
| [0002](docs/adr/0002-issues-und-management-ins-lab.md) | superseded | 0002 — Issues und Management-Repo ziehen ins Lab („das Lab ist die Quelle der Wahrheit") |
|
||||
| [0003](docs/adr/0003-cve-meldeweg-aggregiert.md) | accepted | 0003 — CVE-Meldeweg: aggregierte Alarme, eigener Security-Raum, gleicher Bot |
|
||||
| [0004](docs/adr/0004-site-to-site-vpn-hetzner-lab.md) | accepted | 0004 — Site-to-Site-VPN Hetzner-Projektnetz ↔ Lab, schaltbar über die UDM |
|
||||
| [0005](docs/adr/0005-pm-framework-kanban.md) | accepted | 0005 — Projektmanagement: Kanban-Rückgrat mit leichten Scrum-Elementen |
|
||||
| [0006](docs/adr/0006-wikis-konsolidieren-docusaurus.md) | accepted | 0006 — Wikis ins Lab konsolidieren, Docusaurus als gemeinsame Lesefläche |
|
||||
| [0007](docs/adr/0007-wiki-oberflaeche-docusaurus-vs-bookstack.md) | proposed | 0007 — Wiki-Oberfläche: Docusaurus läuft, BookStack als Gegenentwurf |
|
||||
| [0006](docs/adr/0006-wikis-konsolidieren-docusaurus.md) | superseded | 0006 — Wikis ins Lab konsolidieren, Docusaurus als gemeinsame Lesefläche |
|
||||
| [0007](docs/adr/0007-wiki-oberflaeche-docusaurus-vs-bookstack.md) | superseded | 0007 — Wiki-Oberfläche: Docusaurus läuft, BookStack als Gegenentwurf |
|
||||
| [0008](docs/adr/0008-agenten-sessions-root-aequivalent.md) | accepted | 0008 — Agenten-Sessions auf CFGMON laufen root-äquivalent über die docker-Gruppe |
|
||||
| [0009](docs/adr/0009-commit-konventionen-und-historien-anonymisierung.md) | accepted | 0009 — Commit-Konventionen und rückwirkende Anonymisierung der Historie |
|
||||
| [0010](docs/adr/0010-haertung-eigener-meilenstein.md) | accepted | 0010 — Härtung ist ein eigener Meilenstein (M5); M1 misst nur Kaputtes |
|
||||
| [0011](docs/adr/0011-enrollment-localpart-kollision-verweigern.md) | accepted | 0011 — Provisionierung verweigert Localpart-Kollisionen, statt an bestehende Konten zu verknüpfen |
|
||||
| [0012](docs/adr/0012-issues-im-repo-gitlab-als-spiegel.md) | accepted | ADR-0012: Issues leben im Repo; GitLab wird deterministisch bespiegelt |
|
||||
| [0013](docs/adr/0013-gruppenregeln-kanonisch-mit-pruefung.md) | accepted | ADR-0013: Gruppenregeln kanonisch im management-Repo, Komponenten zeigen und werden geprüft |
|
||||
| [0014](docs/adr/0014-wikijs-loest-docusaurus-ab.md) | accepted | 0014 — Wiki.js löst Docusaurus ab: abgeschottete Betriebs-/Anwenderdoku, docs-as-code |
|
||||
| [0015](docs/adr/0015-wiki-git-storage-ueber-gitea-kanonisieren.md) | accepted | 0015 — Wiki.js Git-Storage: Inhalt fließt Cluster→Gitea→kanonisiert nach git.lab |
|
||||
| [0016](docs/adr/0016-notfallhandbuch-nicht-spiegeln.md) | accepted | 0016 — Das Notfallhandbuch bleibt lab-intern und wird nicht gespiegelt |
|
||||
| [0017](docs/adr/0017-split-dns-cfgmon-vier-zonen.md) | accepted | 0017 — Split-DNS auf CFGMON: vier Zonen statt einer, je Zone begründet |
|
||||
| [0018](docs/adr/0018-ki-geraeuschunterdrueckung-clientseitig-opt-in.md) | accepted | 0018 — KI-Geräuschunterdrückung in threadnet-call: client-seitig, opt-in, selbst ausgeliefert |
|
||||
| [0019](docs/adr/0019-komponenten-issues-adoptiert.md) | accepted | ADR-0019: Komponenten-Issues in docs/issues/ adoptiert — eine Nummernwelt für die Gruppe |
|
||||
| [0020](docs/adr/0020-bekannte-befunde-quittieren.md) | accepted | ADR-0020: Bekannte Befunde werden quittiert, damit Rot wieder etwas bedeutet |
|
||||
| [0021](docs/adr/0021-foederation-geschlossen.md) | accepted | ADR-0021: Föderation wird geschlossen — leere Whitelist statt offener Tür |
|
||||
| [0022](docs/adr/0022-upstream-anschluss-durch-einmaligen-merge.md) | accepted | ADR-0022: Anschluss an Upstream durch einen einmaligen Merge, nicht durch einen geteilten Graft |
|
||||
| [0023](docs/adr/0023-fremdhistorie-von-der-git-hygiene-ausnehmen.md) | accepted | ADR-0023: Fremde Historie von der Git-Hygiene ausnehmen — erklärt, nicht global |
|
||||
|
||||
## Open AARs (2)
|
||||
## Open AARs (0)
|
||||
|
||||
- [AAR — Refinement, Betrieb voranbringen, Git-Historie anonymisiert](docs/aar/2026-08-09-refinement-und-betrieb.md)
|
||||
- [AAR — `@apo` konnte nicht telefonieren: fehlende Synapse-`profiles`-Zeile](docs/aar/2026-08-11-apo-calls-profile-zeile.md)
|
||||
_none — nothing awaiting harvest_
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
type: aar
|
||||
status: open
|
||||
status: harvested
|
||||
date: 2026-08-09
|
||||
related: []
|
||||
---
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
type: aar
|
||||
status: open
|
||||
status: harvested
|
||||
date: 2026-08-11
|
||||
related: []
|
||||
---
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
---
|
||||
type: aar
|
||||
status: harvested
|
||||
date: 2026-08-13
|
||||
related: [docs/issues/0046-wiki-in-threadnet-server-suite-umziehen.md, docs/issues/0048-wikijs-in-der-suite-deployen.md, docs/issues/0050-wikijs-theming-farben-logo.md, docs/adr/0014-wikijs-loest-docusaurus-ab.md, docs/adr/0015-wiki-git-storage-ueber-gitea-kanonisieren.md]
|
||||
---
|
||||
|
||||
# AAR — Wiki.js-Umzug: Deploy, Theming, Git-Storage, Inhalts-Migration
|
||||
|
||||
**Datum:** 2026-08-13 · **Stack:** `matrix`-Namespace (K3s Hetzner), gitops-Repo + Wiki.js
|
||||
**Auftrag:** Docusaurus durch Wiki.js ablösen (ADR-0014): reproduzierbar/deploybar, mit
|
||||
Authentik-OIDC-Login + Abschottung, Theming, Git-Storage (ADR-0015), Postgres-Backup und
|
||||
Migration der alten Inhalte.
|
||||
|
||||
## 1. Ergebnis
|
||||
|
||||
Alles live und verifiziert: headless Deploy über einen Konfig-Job, Authentik-OIDC-only
|
||||
Login (`hideLocal`), Rollen + Abschottung (403-Nachweis mit echtem Anwender-Konto),
|
||||
Branding, Git-Storage Wiki.js→Gitea→git.lab, nächtliches Postgres-Backup, 10 alte Seiten
|
||||
migriert. #0046/#0048/#0049/#0050 `done`, ADR-0014/0015 `accepted`.
|
||||
|
||||
Der Weg dahin hatte mehrere nicht-offensichtliche Stolpersteine — das ist der eigentliche
|
||||
Wert dieses AAR: Wiki.js- und Flux-Eigenheiten, die ein Forker oder eine spätere Session
|
||||
sonst teuer neu lernt.
|
||||
|
||||
## 2. Befunde / Stolpersteine
|
||||
|
||||
| # | Stolperstein | Klasse | Dokumentiert |
|
||||
|---|---|---|---|
|
||||
| 1 | **Wiki.js-Login-Seite ist nicht per Custom-CSS themebar.** `master.pug` rendert kein `injectCSS`, der Login-Bundle wendet es nicht an → eine dunkle Login-Karte ist config-seitig unmöglich; bleibt hell. | tool | #0050 |
|
||||
| 2 | **Config-Werte brauchen `{"v": value}`-Kodierung.** Auth-Strategy UND Storage-Target lesen jeden Wert via `_.get(JSON.parse(value),'v',null)`. Ohne die Kodierung sind alle Werte `null` („requires an issuer option"). | tool | `wikijs-config.py` |
|
||||
| 3 | **`hideLocal` statt local deaktivieren.** Wiki.js braucht eine Formular-Strategie, sonst rendert die Login-Seite leer. „Nur OIDC" ⇒ local aktiv lassen + `authHideLocal=true`; Break-Glass `/login?all`. | tool | `wikijs-config.py` |
|
||||
| 4 | **Branding als statische Datei, nicht als gated Asset.** Ein per Upload eingespieltes Logo hängt an `read:assets` → 403 auf der unauth. Login-Seite. Lösung: unter `/_assets/img/...` mounten (öffentlich). | tool | `wikijs.yaml` |
|
||||
| 5 | **Flux streift Bilder aus dem Build-Artefakt** (`*.png`/`*.jpg` in der Default-Ignore) → `configMapGenerator` scheitert mit „no such file or directory". Fix: `.sourceignore` mit Negationen. | infra | `gitops/.sourceignore` |
|
||||
| 6 | **Geänderte immutable Job-Spec blockiert den GANZEN Flux-Apply.** Ändert sich die `wikijs-config`-Job-env, scheitert der Apply an der immutable Job — und **auch unabhängige Änderungen (ConfigMaps, Mounts) kommen still nicht durch**, obwohl die Source-Revision schon aktuell ist. Fix: Job löschen, Flux legt ihn neu an. | infra | dieser AAR |
|
||||
| 7 | **1-MiB-ConfigMap-Limit.** Hochskalierte Favicons + 604-KB-Hintergrund sprengten es (kurzzeitig ein Split in zwei ConfigMaps). Fix: Hintergrund runterskalieren (2560→1920 px, 400 KB), alles in EINER ConfigMap. Icons NICHT hochskalieren (Bloat + unscharf). | infra | #0050 |
|
||||
| 8 | **Git-Storage braucht den Remote-Branch vorab.** Wiki.js: „Invalid branch! Make sure it exists on the remote first." Ziel-Repo mit leerem Initial-Commit auf `main` bootstrappen. Nach fehlgeschlagenem Init sitzt der lokale Klon fest → `purge`-Action + re-init. | tool | ADR-0015 |
|
||||
| 9 | **Cluster erreicht git.lab nicht (Absicht).** Wiki.js pusht nach Gitea, ein CI-Job kanonisiert Gitea→git.lab (Muster `canonize_rotation`, umgekehrte Richtung). | infra | ADR-0015 |
|
||||
| 10 | **Nav-Sidebar rendert `target` wortwörtlich als `href`** (Default-Theme: `href: item.target`, keine `targetType`- oder Slash-Behandlung). Page-Targets ohne führenden Slash lösen **relativ** auf → von `/betrieb/x` aus wird `betrieb/y` zu `/betrieb/betrieb/y` → 404 (von `/` aus geht es zufällig, daher lange unbemerkt). `home` mit leerem Target ist ebenfalls tot. Fix: Targets absolut speichern (`/<path>`; `home` → `/`) — Wiki.js' eigener Editor nutzt `/<locale>/<path>`. | tool | `wikijs-config.py` (`set_navigation`) |
|
||||
| 11 | **Locale-Wechsel migriert nur `pages`.** Default en→de via `localization.updateLocale` (lädt live, KEIN Neustart) + `pages.migrateToLocale`; letzteres patcht NUR die `pages`-Tabelle (Kollisions-Guard). `pageTree`/`pageLinks`/`pageHistory`/Suchindex bleiben auf der alten Locale → danach `pages.rebuildTree` + `search.rebuildIndex`, die Restzeilen in `pageLinks`/`pageHistory` einmalig nachziehen. **Nav-Baum muss unter der NEUEN Locale liegen** (getTree nutzt die Seiten-Locale), sonst leere Sidebar. `namespacing:false` → saubere `/<pfad>`-URLs. | tool | `wikijs-config.py` (`ensure_locale`) |
|
||||
| 12 | **New-User-Zeitzone kommt aus dem DB-Spalten-Default** (`users.timezone` = `America/New_York`), NICHT aus Config: SSO-`processProfile` legt Nutzer ohne `timezone` an (`localeCode` dagegen aus `WIKI.config.lang.code`). Bestehende Konten per `users.update` korrigieren (patcht nur übergebene Felder, kein Nulling). Neue Nutzer brauchen den **Fork-Patch** (s. Nachtrag). | tool | `wikijs-config.py` (`ensure_timezones`) |
|
||||
| 13 | **Wiki-Inhalt nur über die Wiki.js-API editieren** — git-storage ist bidirektional, **nie** direkt nach Gitea schreiben (würde beim nächsten Sync kollidieren/überschrieben). Reproduzierbar via kurzlebigem In-Cluster-Job mit gemountetem Admin-Secret (Creds bleiben im Cluster; Pod-Label `app.kubernetes.io/name: wikijs-config` matcht die NetworkPolicy zu Wiki.js). | tool | dieser AAR |
|
||||
|
||||
## 3. Learnings (knapp)
|
||||
|
||||
- **`checkAccess` ist Default-Deny** (`match && !deny`): eine Gruppe mit globalem
|
||||
`read:pages` + Pfad-Regel auf `anwender` sieht `betrieb/*` von allein nicht — Abschottung
|
||||
ohne explizite Deny-Regeln. Aber **immer mit einem echten Anwender-Konto gegentesten**
|
||||
(lokaler Testnutzer in `wiki-anwender`, HTTP-Status je Seite prüfen).
|
||||
- **Wiki.js ist config-seitig mächtig, aber eigenwillig** — vieles ist nur über
|
||||
Quellcode-Lesen im Pod (`/wiki/server/...`) herauszufinden. Der Konfig-Job
|
||||
(`wikijs-config.py`) kapselt das reproduzierbar; er ist die Doku.
|
||||
- **Browser cachen Favicons hartnäckig** — ein „falsches" Tab-Icon ist meist Cache, kein
|
||||
Deploy-Fehler. Erst server-seitig 16/32/`favicon.ico` prüfen, dann hart neu laden.
|
||||
|
||||
## 4. Actions
|
||||
|
||||
- In-place dokumentiert: Kommentare in `wikijs-config.py`, `wikijs.yaml`, `.sourceignore`
|
||||
(gitops); Notizen in #0048/#0050; Architektur in ADR-0015.
|
||||
- **Geerntet 2026-08-14:** Befunde 1–13 + Learnings sind in
|
||||
[`docs/wiki/stolpersteine/wikijs.md`](../wiki/stolpersteine/wikijs.md) zusammengezogen
|
||||
(Wiki.js-Betriebswissen an einem Ort); dieser AAR ist damit `status: harvested`.
|
||||
|
||||
## 5. Nachtrag 2026-08-14 — Deutsch-Locale, Zeitzone & stehende Fork-Abweichung
|
||||
|
||||
Nach dem Umzug fielen drei Dinge auf (sorb): die deutschen Inhalte hingen an der Locale
|
||||
`en`, die Default-Sprache war Englisch, die Systemkonten standen auf `America/New_York`.
|
||||
Alles behoben und reproduzierbar im Konfig-Job verankert:
|
||||
|
||||
- **Default-Sprache `de` + 25 Seiten en→de migriert** (`ensure_locale`) — Mechanik siehe
|
||||
Stolperstein #11.
|
||||
- **Zeitzone `Europe/Berlin`** für die Systemkonten (`ensure_timezones`, #12); der
|
||||
Nav-`href`-Bug (#10) wurde im selben Zug gefixt.
|
||||
|
||||
**⚠️ Stehende Upstream-Abweichung (Fork-Patch) — framework-relevant:**
|
||||
Wiki.js läuft als Upstream-Image (`ghcr.io/requarks/wiki:2.5`), es gibt **keine**
|
||||
Build-Pipeline. Damit **neue** OIDC-Nutzer `Europe/Berlin` statt des im DB-Spalten-Default
|
||||
verankerten `America/New_York` bekommen, patcht der Container beim Start
|
||||
`server/models/users.js` (`processProfile`) per `sed` (idempotent, fail-open). Das ist eine
|
||||
**dauerhafte Abweichung vom Upstream**, hängt am Anker `localeCode: WIKI.config.lang.code,`
|
||||
und **muss bei jedem Wiki.js-Upgrade gegengeprüft** werden.
|
||||
|
||||
- Dokumentiert: ⚠️-Kommentar in `apps/production/wikijs.yaml` **und** in der Upgrade-Doku
|
||||
`/betrieb/upgrades` (Abschnitt „Wiki.js: Startup-Patch"), neben den Element-Fork-Einträgen.
|
||||
- **ADR m.E. nicht nötig** (kleine operative Abweichung, kein Architektur-/Regel-Grundsatz);
|
||||
Nachverfolgung läuft über die Upgrade-Doku + diesen AAR. Wer das anders sieht, hebt es per
|
||||
ADR nach.
|
||||
|
||||
gitops-Commits: Locale/Nav/Zeitzone `2250969`, Nav-Slash-Fix `9f8eed3`, Fork-Patch `5f54fbe`.
|
||||
@@ -0,0 +1,64 @@
|
||||
---
|
||||
type: aar
|
||||
status: harvested
|
||||
date: 2026-08-16
|
||||
related:
|
||||
- "docs/issues/0005-zone-01-ionos-default-records-bereinigen-www.md"
|
||||
---
|
||||
|
||||
# AAR — Gruppen-Calls fünf Tage tot: `mrtc`-A-Record bei der Zonen-Bereinigung gelöscht
|
||||
|
||||
**Datum des Vorfalls:** 2026-08-11 bis 2026-08-16 · **Beteiligt:** sorb + Mac-Session ·
|
||||
**Stack:** MatrixRTC (LiveKit-SFU + Authorisation-Service), IONOS-DNS, cert-manager
|
||||
**Auftrag am 16.08.:** „zwischen frank und sorb kommt in raum test kein call zustande,
|
||||
OPEN_ID_Error" — Grundursache finden.
|
||||
|
||||
## 1. Ergebnis
|
||||
|
||||
**Behoben:** `mrtc.axion1337.chat A 49.13.132.245` von sorb bei IONOS neu gesetzt; danach
|
||||
Token-Tausch am Authorisation-Service nachweislich wieder angekommen, Calls liefen im Test.
|
||||
|
||||
**Grundursache:** Der A-Record wurde bei der IONOS-Zonen-Bereinigung (#0005, 11.08.) mit
|
||||
gelöscht — **auf Empfehlung der Session**, die keine Soll-Liste hatte, gegen die sie hätte
|
||||
prüfen können. Letzter erfolgreicher Call-Beitritt 11.08. 21:48; erster Fehlversuch danach
|
||||
erst am 16.08. — fünf Tage unbemerkt, weil niemand telefonierte.
|
||||
|
||||
## 2. Warum nichts Alarm schlug
|
||||
|
||||
- **Zertifikat blieb grün:** DNS-01-Renewal braucht keinen A-Record.
|
||||
- **Cluster blieb grün:** Ingress, SFU-Pod, Authorisation-Service — alles gesund; der
|
||||
Dienst bekam schlicht keine Anfragen mehr (nur Health-Checks).
|
||||
- **Der Client-Fehler führte in die Irre:** „OPEN_ID_Error" — dabei war das OpenID-Stück
|
||||
das Einzige, was funktionierte (Synapse gab Tokens mit 200 aus). Der Bruch lag eine
|
||||
Stufe später: `https://mrtc…/sfu/get` war nicht auflösbar.
|
||||
|
||||
## 3. Diagnose-Weg (was künftig Zeit spart)
|
||||
|
||||
1. Synapse-Log: `openid/request_token` **200** für beide Nutzer → OpenID entlastet.
|
||||
2. Authorisation-Service-Log: **nur Health-Checks**, keine echte Anfrage → Bruch davor.
|
||||
3. `curl https://mrtc…/healthz` → HTTP 000 → `dig` → **kein Record, autoritativ bestätigt**.
|
||||
4. DB-Abgleich Versuche↔Beitritte (`open_id_tokens` vs. `call.member`-Events) datierte den
|
||||
Bruch exakt: 11.08. 76/61, 16.08. 3/0.
|
||||
|
||||
## 4. Lehren und Maßnahmen
|
||||
|
||||
- **Es gab keine DNS-Soll-Liste.** Die einzige Nennung von `mrtc` als Pflicht-Record steckte
|
||||
in einem alten Fehlerbericht (`gitops:docs/oldwiki/fix report mrtc.md`). → **Umgesetzt:**
|
||||
`notfallhandbuch:dns-soll.md` (Soll-Liste mit „Wenn er fehlt"-Spalte und dem, was es
|
||||
bewusst NICHT gibt) plus `pruefe-dns.sh` (prüft öffentlich UND autoritativ; Positiv- und
|
||||
Negativlauf verifiziert). Vor jeder Zonen-Änderung laufen lassen.
|
||||
- **Aufschreiben allein hätte nicht gereicht** (Wiederholung der Bindmount-Lehre): wirksam
|
||||
ist das ausführbare Skript, nicht die Tabelle daneben.
|
||||
- **„Meldet Erfolg, ist aber blind", DNS-Ausgabe:** Grüne Zertifikate und grüne Pods sagen
|
||||
nichts über die Erreichbarkeit von außen. Der Fehlertext des Clients benennt die Stufe,
|
||||
auf der er scheitert — nicht die Ursache.
|
||||
- **Bereinigungen brauchen eine Soll-Liste vorab.** Die Session hat beim Aufräumen Einträge
|
||||
freigegeben, deren Zweck sie nicht kannte. Erst prüfen, wogegen — dann löschen.
|
||||
|
||||
## 5. Offen
|
||||
|
||||
- DMARC-/Mail-Härtung der Zone `axion1337.chat`: bei der Diagnose als Nebenbefund erhoben,
|
||||
seit 2026-08-18 als [#0102](../issues/0102-dmarc-und-mail-haertung-der-zone-axion1337-chat.md)
|
||||
geführt. Beim Anlegen präzisiert: #0006 hat `axion1337.**de**` gehärtet (dort heute
|
||||
`p=reject`); die Plattform-Zone `.chat` hängt weiterhin als CNAME an IONOS' geteiltem
|
||||
`p=none` — sie war nie Gegenstand von #0006.
|
||||
@@ -1,10 +1,10 @@
|
||||
---
|
||||
type: adr
|
||||
id: "0002"
|
||||
status: accepted
|
||||
status: superseded
|
||||
date: 2026-08-01
|
||||
supersedes: null
|
||||
superseded_by: null
|
||||
superseded_by: docs/adr/0019-komponenten-issues-adoptiert.md
|
||||
related: []
|
||||
---
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
---
|
||||
type: adr
|
||||
id: "0006"
|
||||
status: accepted
|
||||
status: superseded
|
||||
date: 2026-08-02
|
||||
supersedes: null
|
||||
superseded_by: null
|
||||
superseded_by: docs/adr/0014-wikijs-loest-docusaurus-ab.md
|
||||
related: []
|
||||
---
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
---
|
||||
type: adr
|
||||
id: "0007"
|
||||
status: proposed
|
||||
status: superseded
|
||||
date: 2026-08-02
|
||||
supersedes: null
|
||||
superseded_by: null
|
||||
superseded_by: docs/adr/0014-wikijs-loest-docusaurus-ab.md
|
||||
related: []
|
||||
---
|
||||
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
---
|
||||
type: adr
|
||||
id: "0014"
|
||||
status: accepted
|
||||
date: 2026-08-12
|
||||
supersedes: docs/adr/0007-wiki-oberflaeche-docusaurus-vs-bookstack.md
|
||||
superseded_by: null
|
||||
related: [docs/issues/0047-wiki-oberflaeche-ueber-bookstack-hinaus-pruefen.md, docs/issues/0046-wiki-in-threadnet-server-suite-umziehen.md]
|
||||
---
|
||||
|
||||
# 0014 — Wiki.js löst Docusaurus ab: abgeschottete Betriebs-/Anwenderdoku, docs-as-code
|
||||
|
||||
## Kontext
|
||||
|
||||
ADR-0007 hielt „Docusaurus läuft, BookStack als Gegenentwurf" fest (Status
|
||||
`proposed`). Docusaurus ist statisch — kein Nutzermodell, Zugang nur als
|
||||
Alles-oder-nichts-Tor (Authentik-Forward-Auth, gitops-Guide 09). Drei harte
|
||||
Anforderungen von sorb (2026-08-12) sprengen das:
|
||||
|
||||
1. **Abschottung nach Gruppe** — Anwender dürfen die Betriebsdoku nicht sehen.
|
||||
2. **docs-as-code in git** — hartes, nicht verhandelbares Kriterium.
|
||||
3. **Rollen** — Admins editieren Betriebs- und Anwenderhandbücher, normale
|
||||
Nutzer nur lesen.
|
||||
|
||||
Statisches Docusaurus kann (1) und (3) strukturell nicht: es weiß zur Bauzeit
|
||||
nicht, wer guckt.
|
||||
|
||||
## Betrachtete Optionen
|
||||
|
||||
- **Mehrere Docusaurus-Instanzen + Routing** — Silos, geteilte Suche/Navigation,
|
||||
brechende Querverweise. Verworfen.
|
||||
- **Docusaurus + Pfad-ACL im Proxy** (Gruppen-Header) — gibt 403 statt Verstecken,
|
||||
Sidebar und Suche zeigen Verbotenes weiter. Verworfen.
|
||||
- **BookStack** — native Gruppen-Rechte + OIDC, aber Inhalt nur in der DB, **kein
|
||||
git**. Scheitert am harten docs-as-code-Kriterium. Verworfen.
|
||||
- **Wiki.js** — native Pfad-/Seiten-Regeln pro Gruppe **und** Git-Storage-Modul
|
||||
(Inhalt in einem git-Repo). Erfüllt als einziges beide harten Kriterien.
|
||||
|
||||
## Entscheidung
|
||||
|
||||
**Wiki.js löst Docusaurus als Plattform-Wiki ab.**
|
||||
|
||||
- **Deployment** in die ThreadNet Server Suite (k8s), nicht mehr als
|
||||
Overmind-Einzelstack — löst den Forward-Auth-Zwischenstand (Guide 09,
|
||||
`axionwiki.lab`/#0024) ab.
|
||||
- **Inhalt** in einem **dedizierten `wiki`-Repo** (Git-Storage). Kein Branch eines
|
||||
bestehenden Repos, kein Voll-Monorepo (Forkbarkeit der Produkte, Flux/Mirror pro
|
||||
Repo — Monorepo-Vorhaben liegt auf Eis, sorb).
|
||||
- **Rollen über Authentik-Gruppen**: Admins schreiben Betriebs- +
|
||||
Anwenderhandbücher; normale Nutzer nur lesen; Anwender sehen die Betriebsdoku
|
||||
nicht (Abschottung).
|
||||
- **Scope**: nur Betriebs- und Anwenderdoku. **`homelab/docs` bleibt draußen** —
|
||||
sorbs Homelab-Doku ist nicht Teil der Plattform.
|
||||
- Die Neckbeard-Framework-Artefakte (ADRs/AARs/Issues/Vision, von `validate.py`
|
||||
geprüft) **bleiben in `management`**; Wiki.js übernimmt sie nicht.
|
||||
|
||||
## Konsequenzen
|
||||
|
||||
- **docs-as-code bleibt gewahrt** — Wiki.js hält den Inhalt in git; harte
|
||||
Anforderung erfüllt.
|
||||
- **Postgres nötig** fürs Rendern/Auth/Suche. Kein Widerspruch zum git-Kriterium:
|
||||
git ist die Inhalts-Quelle, die DB ist Laufzeit-Cache/Index — braucht aber ein
|
||||
Backup.
|
||||
- **Autorenmodell dreht sich**: editiert wird in Wiki.js, nicht mehr in den
|
||||
Quell-Repos read-only aggregiert (das alte Docusaurus-Prinzip entfällt für die
|
||||
Betriebs-/Anwenderdoku).
|
||||
- **Neues `wiki`-Repo** auf git.lab (gespiegelt wie die übrigen).
|
||||
- **Docusaurus + Guide 09** werden abgelöst; Guide 09 bleibt als Historie bis zur
|
||||
Umstellung.
|
||||
- **ADR-0007** wird `superseded`.
|
||||
- Offen (Folge-Issues): Deployment in der Suite, Git-Storage, OIDC + Rollen/
|
||||
Abschottung, Theming — siehe #0046 und die daraus abgeleiteten Bau-Issues.
|
||||
@@ -0,0 +1,66 @@
|
||||
---
|
||||
type: adr
|
||||
id: "0015"
|
||||
status: accepted
|
||||
date: 2026-08-13
|
||||
supersedes: null
|
||||
superseded_by: null
|
||||
related: [docs/issues/0048-wikijs-in-der-suite-deployen.md, docs/adr/0014-wikijs-loest-docusaurus-ab.md, docs/adr/0001-gitlab-kanonisch-push-mirror.md]
|
||||
---
|
||||
|
||||
# 0015 — Wiki.js Git-Storage: Inhalt fließt Cluster→Gitea→kanonisiert nach git.lab
|
||||
|
||||
## Kontext
|
||||
|
||||
ADR-0014 macht docs-as-code zur harten Anforderung: der Wiki-Inhalt liegt in git.
|
||||
#0048 hielt dafür fest, ein **git.lab**-`wiki`-Repo anzulegen, „gespiegelt wie die
|
||||
übrigen", und es als Wiki.js-Git-Storage mit bidirektionalem Sync einzubinden.
|
||||
|
||||
Beim Bau (2026-08-13) zeigt sich: das ist so **nicht machbar**. Wiki.js läuft im
|
||||
Hetzner-Cluster, und der erreicht git.lab bewusst **nicht** — `git.lab` löst aus dem
|
||||
Pod nicht auf; nur Gitea (`rohana.axion1337.de`) ist per HTTPS erreichbar (verifiziert).
|
||||
Diese Lab-Unabhängigkeit ist Absicht (ADR-0001, ADR-0004): die Produktion darf nicht
|
||||
von einem Host abhängen, der nur im Lab antwortet. Der Schreiber des Inhalts sitzt also
|
||||
im Cluster und kann git.lab nicht beschreiben.
|
||||
|
||||
## Optionen
|
||||
|
||||
- **A — Cluster→git.lab öffnen.** Verworfen: hebelt die bewusste Lab-Unabhängigkeit
|
||||
aus (ADR-0001), koppelt die Produktion ans Lab.
|
||||
- **B — Nur Gitea, kein git.lab-Kanon.** Wiki.js schreibt in ein Gitea-Repo, fertig.
|
||||
Verworfen: der Inhalt hätte keinen kanonischen git.lab-Stand — widerspricht ADR-0001.
|
||||
- **C — Wiki.js→Gitea, CI kanonisiert Gitea→git.lab.** Wiki.js pusht in ein
|
||||
Gitea-Repo; ein geplanter CI-Job auf git.lab holt den Stand und schreibt ihn nach
|
||||
git.lab. Das ist **dasselbe Muster**, das für den TURN-Rotations-CronJob bereits
|
||||
akzeptiert ist (der einzige verbliebene Cluster-Schreibvorgang nach Gitea, siehe
|
||||
gitops-CLAUDE.md / `canonize_rotation`). Gewählt.
|
||||
|
||||
## Entscheidung
|
||||
|
||||
Der Wiki.js-Git-Storage zielt auf ein **Gitea-Repo** (`sorb/wiki`) über HTTPS mit
|
||||
einem **dedizierten Deploy-PAT**. Der Inhalt wird per geplantem CI-Job Gitea→git.lab
|
||||
**kanonisiert**, analog zu `canonize_rotation`. Die Richtung ist gegenüber dem üblichen
|
||||
Push-Mirror (git.lab→Gitea) **umgekehrt**, weil der Schreiber im Cluster sitzt — das ist
|
||||
eine bewusste, dokumentierte Ausnahme zu ADR-0001, kein Regelbruch.
|
||||
|
||||
## Konsequenzen
|
||||
|
||||
- **Leichter:** reproduzierbar und lab-unabhängig; nutzt ein etabliertes Muster statt
|
||||
eines neuen Sonderwegs; kein Netz-Umbau am Cluster.
|
||||
- **Schwerer:** ein zweiter Cluster→Gitea-Schreibpfad, der gepflegt sein will; braucht
|
||||
den Kanonisierungs-Job (Vorlage `canonize_rotation`); ein dedizierter PAT liegt im
|
||||
Cluster-SOPS (eigene Rotation).
|
||||
- **Voraussetzungen (sorb):** Gitea-Repo `sorb/wiki` (privat) anlegen; dedizierten
|
||||
Deploy-PAT (write:repository) bereitstellen. Beides kann der Agent nicht selbst — der
|
||||
vorhandene Push-Token darf keine Repos anlegen.
|
||||
- **Später zu klären:** ob das Content-Repo unter eine Gruppe mit eigenem Mirror
|
||||
gehört; ob echter bidirektionaler Sync (Edits in git) gewollt ist oder push-only genügt.
|
||||
|
||||
## Umsetzungsstand (2026-08-13)
|
||||
|
||||
Wiki.js→Gitea ist **live und End-to-End verifiziert**: Repo `sorb/ThreadNetWiki`
|
||||
(mit `main` initialisiert), dedizierter Deploy-PAT im SOPS-Secret `wikijs-git-secret`,
|
||||
Storage-Target `operational`, Seite anlegen/löschen propagiert nach Gitea. Ausstehend
|
||||
ist nur die zweite Hälfte — der **Kanonisierungs-Job Gitea→git.lab** (Vorlage
|
||||
`canonize_rotation`); dafür fehlt die Entscheidung, in welches git.lab-Repo kanonisiert
|
||||
wird.
|
||||
@@ -0,0 +1,72 @@
|
||||
---
|
||||
type: adr
|
||||
id: "0016"
|
||||
status: accepted
|
||||
date: 2026-08-14
|
||||
supersedes: null
|
||||
superseded_by: null
|
||||
related:
|
||||
- "docs/adr/0001-gitlab-kanonisch-push-mirror.md"
|
||||
- "docs/issues/0030-der-restore-ist-nie-geprobt-sicherungen-sind.md"
|
||||
---
|
||||
|
||||
# 0016 — Das Notfallhandbuch bleibt lab-intern und wird nicht gespiegelt
|
||||
|
||||
**Status:** akzeptiert · **Datum:** 2026-08-14 · **Entscheider:** sorb
|
||||
|
||||
## Kontext
|
||||
|
||||
ADR-0001 legt fest: git.lab ist kanonisch, **alle** Repos werden per Push-Mirror nach
|
||||
Gitea (`rohana.axion1337.de`) beliefert. Das dient der Verfügbarkeit — Gitea ist von
|
||||
überall erreichbar, git.lab nur im Lab bzw. über VPN.
|
||||
|
||||
Am 2026-08-14 entstand mit `axion1337.chat/notfallhandbuch` ein Repo, dessen Inhalt
|
||||
qualitativ anders ist als Code oder Konfiguration: Es beschreibt die Wiederherstellung
|
||||
der Plattform und damit zwangsläufig **den Aufbau der Infrastruktur, den Ablageort der
|
||||
Sicherungen (Storage Box, Borg-Repos) und wo die Schlüssel zu finden sind** (Vault,
|
||||
`sops-age`, die Kette bis zur Borg-Passphrase). Werte enthält es nicht — aber die
|
||||
vollständige Landkarte dorthin.
|
||||
|
||||
Rein aus Verfügbarkeitssicht wäre ein Mirror besonders naheliegend: git.lab ist genau
|
||||
dann nicht erreichbar, wenn man das Handbuch am dringendsten braucht. Diese Empfehlung
|
||||
wurde in #0030 zunächst auch so gegeben.
|
||||
|
||||
## Entscheidung
|
||||
|
||||
**Das Notfallhandbuch wird bewusst NICHT nach Gitea gespiegelt** und bleibt
|
||||
ausschließlich auf git.lab — eine dauerhafte Ausnahme von ADR-0001.
|
||||
|
||||
Begründung: Gitea/rohana ist aus dem Internet erreichbar und Teil des Stacks, den das
|
||||
Handbuch wiederherstellen soll. Wird dieser Stack kompromittiert, wäre ein dort
|
||||
gespiegeltes Notfallhandbuch genau die Aufklärung, die ein Angreifer für den nächsten
|
||||
Schritt braucht — Backup-Ziele, Schlüsselverwahrung, Wiederanlaufpfade. Ein Angreifer
|
||||
mit rohana-Zugriff soll **nicht** zusätzlich erfahren, wo die Sicherungen liegen.
|
||||
|
||||
**Vertraulichkeit geht hier vor Verfügbarkeit.**
|
||||
|
||||
Die Verfügbarkeitslücke wird stattdessen über einen **lokalen Clone** geschlossen
|
||||
(Laptop, verschlüsselter Datenträger), der nach Änderungen aktualisiert wird. Das ist
|
||||
eine Kopie unter eigener Kontrolle, kein öffentlich erreichbarer Spiegel.
|
||||
|
||||
## Konsequenzen
|
||||
|
||||
- **Kein Mirror einrichten** — auch nicht „der Vollständigkeit halber" oder beim
|
||||
Vereinheitlichen der Mirror-Konfiguration. Die Begründung steht zusätzlich im README
|
||||
des Repos, weil dort zuerst hinschaut, wer die Lücke bemerkt.
|
||||
- Ohne Lab-Zugang (unterwegs, Lab offline) ist das Repo nicht abrufbar. **Der lokale
|
||||
Clone ist damit Teil des Notfallkonzepts**, nicht bloß Bequemlichkeit — veraltet er,
|
||||
verliert man im Ernstfall den aktuellen Stand.
|
||||
- Das Handbuch darf weiterhin **keine Secret-Werte** enthalten, nur Fundorte. Die
|
||||
Vertraulichkeit dieses Repos ist eine zusätzliche Schutzschicht, kein Ersatz für die
|
||||
Secrets-Hygiene.
|
||||
- Andere Repos bleiben von dieser Ausnahme unberührt; ADR-0001 gilt für sie weiter.
|
||||
|
||||
## Verworfene Alternativen
|
||||
|
||||
- **Mirror wie bei allen anderen Repos:** verworfen — verlagert das Handbuch auf genau
|
||||
den Host, dessen Kompromittierung einer der abgedeckten Fälle ist.
|
||||
- **Mirror in ein privates Gitea-Repo:** verworfen — der Schutz stünde und fiele mit der
|
||||
Gitea-Rechteverwaltung, also erneut mit der Integrität des kompromittierten Systems.
|
||||
- **Handbuch ohne Fundorte schreiben** (damit es gefahrlos spiegelbar wäre): verworfen —
|
||||
ein Notfallhandbuch, das verschweigt, wo die Sicherungen und Schlüssel liegen, ist im
|
||||
Ernstfall wertlos.
|
||||
@@ -0,0 +1,94 @@
|
||||
---
|
||||
type: adr
|
||||
id: "0017"
|
||||
status: accepted
|
||||
date: 2026-08-15
|
||||
supersedes: null
|
||||
superseded_by: null
|
||||
related:
|
||||
- "docs/adr/0004-site-to-site-vpn-hetzner-lab.md"
|
||||
- "docs/issues/0027-audit-01-acht-widersprueche-aus-dem-labnet-02.md"
|
||||
---
|
||||
|
||||
# 0017 — Split-DNS auf CFGMON: vier Zonen statt einer, je Zone begründet
|
||||
|
||||
**Status:** akzeptiert · **Datum:** 2026-08-15 · **Entscheider:** sorb
|
||||
|
||||
## Kontext
|
||||
|
||||
[ADR-0004](0004-site-to-site-vpn-hetzner-lab.md) beschreibt in der CFGMON-Zeile
|
||||
*„Split-DNS nur `~lab` → `10.58.73.1`"*. Tatsächlich konfiguriert sind seit
|
||||
2026-08-01 (auf sorbs Ansage, `/etc/wireguard/lab.conf`, festgehalten nur im
|
||||
CFGMON-AAR, Nachtrag 2) **vier** Zonen: `~lab`, `~lab.de`, `~axion1337.de`,
|
||||
`~axionlabs.de`. Aufgefallen im Selbst-Audit als W1 von
|
||||
[#0027](../issues/0027-audit-01-acht-widersprueche-aus-dem-labnet-02.md).
|
||||
|
||||
ADRs sind nach Annahme eingefroren; der As-built-Stand lässt sich nicht in
|
||||
ADR-0004 nachtragen. Diese ADR korrigiert **ausschließlich diese eine Zeile** —
|
||||
die VPN-Architektur aus ADR-0004 bleibt unberührt und gültig.
|
||||
|
||||
Befürchtet wurde: Löst der Lab-Resolver `axion1337.de` anders auf als die
|
||||
öffentliche Sicht, ändert sich unbemerkt der Pfad zum Gitea-Mirror
|
||||
(`rohana.axion1337.de`) — also zur Flux-Quelle.
|
||||
|
||||
## Messung (2026-08-15, vom Lab-VLAN gegen `10.58.73.1`, verglichen per DoH)
|
||||
|
||||
**Die Befürchtung trifft nicht zu.** Kein einziger Record weicht ab — geprüft
|
||||
wurden A, MX, TXT (SPF/DMARC), CNAME (DKIM), nur öffentlich existierende
|
||||
Subdomains sowie Records, die **einen Tag zuvor** angelegt (`rohana` MX `0 .`,
|
||||
`_dmarc.rohana`, SPF `-all`) bzw. **gelöscht** wurden (`www.rohana`, `ftp`).
|
||||
Der Lab-Resolver hält für `axion1337.de` **keine eigene Zone**, sondern reicht
|
||||
live nach oben durch. Das von ihm gesetzte `aa`-Flag ist eine Eigenheit des
|
||||
UniFi-Resolvers und war der irreführende Teil, der den Verdacht überhaupt
|
||||
begründet hat.
|
||||
|
||||
Was die vier Zonen tatsächlich leisten:
|
||||
|
||||
| Zone | Nachweis | Urteil |
|
||||
|---|---|---|
|
||||
| `~lab` | `git.lab` → `10.58.73.17`, `wiki.lab` → `10.58.73.17`; öffentlich **NXDOMAIN** | **notwendig** — ohne sie kein git.lab (266 Fundstellen in der Doku) |
|
||||
| `~axionlabs.de` | `ca.axionlabs.de` → **`10.58.73.13`** intern vs. `91.195.241.232` öffentlich | **notwendig** — echtes Split-Horizon auf die interne step-ca |
|
||||
| `~axion1337.de` | `git.axion1337.de` → **`10.58.73.13`** intern, öffentlich NXDOMAIN; alle übrigen Namen identisch zur öffentlichen Sicht | **notwendig** für den internen Namen; für den Rest wirkungslos, aber schadlos |
|
||||
| `~lab.de` | kein interner Name gefunden; `lab.de` löst identisch zur öffentlichen Sicht auf (`52.59.124.117`, **fremde Domain**); keine einzige Fundstelle im Repo | **ohne belegbaren Zweck** |
|
||||
|
||||
## Entscheidung
|
||||
|
||||
1. **`~lab`, `~axionlabs.de` und `~axion1337.de` bleiben** und sind hiermit als
|
||||
As-built dokumentiert. Jede der drei löst mindestens einen Namen auf, den es
|
||||
öffentlich nicht oder anders gibt — sie sind kein Versehen.
|
||||
2. **`~lab.de` wird entfernt.** Es leitet Anfragen für eine **fremde** öffentliche
|
||||
Domain über den Lab-Resolver, ohne dass ein interner Name darunter existiert
|
||||
oder das Repo sie irgendwo verwendet. Heute schadlos (der Resolver reicht
|
||||
durch), aber eine Umleitung ohne Zweck ist eine Angriffs- und Fehlerfläche,
|
||||
die niemand pflegt.
|
||||
3. **Kein Rückbau auf `~lab` allein.** Das hätte `ca.axionlabs.de` und
|
||||
`git.axion1337.de` unauflösbar gemacht — der Rückbau wäre ins Blinde gegangen,
|
||||
weil der Zweck der Zonen nirgends festgehalten war. Genau das ist die Lücke,
|
||||
die diese ADR schließt.
|
||||
|
||||
## Konsequenzen
|
||||
|
||||
- **Offene Handlung:** `~lab.de` aus `/etc/wireguard/lab.conf` auf CFGMON
|
||||
entfernen (`Domains =`-Zeile), Dienst neu laden, mit `resolvectl domain`
|
||||
gegenprüfen. Bis dahin bleibt der Ist-Zustand vierzonig.
|
||||
- **Der Verdacht aus W1 ist ausgeräumt und belegt** — künftige Sessions müssen
|
||||
ihn nicht erneut prüfen. Die Messung steht oben, nicht nur ihr Ergebnis.
|
||||
- **`aa`-Flag ist hier kein Autoritätsbeweis.** Wer künftig auf UniFi-Resolvern
|
||||
misst, darf daraus nicht auf eine lokale Zone schließen; nur der
|
||||
Datenvergleich gegen die öffentliche Sicht entscheidet.
|
||||
- **Die ACME-Resolver-Festnagelung** in Traefik
|
||||
(`dnschallenge.resolvers=1.1.1.1:53,8.8.8.8:53`, `thread-net-git` `8d089e2`)
|
||||
bleibt sinnvoll (deterministisch, umgeht Negativ-Caching), ist aber **kein**
|
||||
Sicherheitsnetz gegen diese Zonen — eine gegenteilige Behauptung in #0027 wurde
|
||||
nach der Messung korrigiert.
|
||||
|
||||
## Verworfene Alternativen
|
||||
|
||||
- **ADR-0004 nachbessern:** nicht zulässig, ADRs sind nach Annahme eingefroren —
|
||||
deshalb diese eigene ADR statt einer stillen Korrektur.
|
||||
- **Alle vier Zonen belassen und nur dokumentieren:** hätte `~lab.de` als
|
||||
„historisch gewachsen" zementiert, ohne dass irgendwer einen Zweck benennen
|
||||
kann. Eine Ausnahme, die niemand begründen kann, ist keine Ausnahme, sondern
|
||||
ein Rest.
|
||||
- **Rückbau auf `~lab`** (die zweite Option aus W1): bricht die interne CA- und
|
||||
git-Auflösung, siehe Messung.
|
||||
@@ -0,0 +1,97 @@
|
||||
---
|
||||
type: adr
|
||||
id: "0018"
|
||||
status: accepted
|
||||
date: 2026-08-15
|
||||
supersedes: null
|
||||
superseded_by: null
|
||||
related:
|
||||
- "docs/issues/0054-ki-geraeuschunterdrueckung-element-call.md"
|
||||
---
|
||||
|
||||
# 0018 — KI-Geräuschunterdrückung in threadnet-call: client-seitig, opt-in, selbst ausgeliefert
|
||||
|
||||
**Status:** akzeptiert · **Datum:** 2026-08-15 · **Entscheider:** sorb
|
||||
|
||||
## Kontext
|
||||
|
||||
Der WebRTC-Standardfilter (`noiseSuppression`) schätzt ein laufendes Rauschprofil und ist
|
||||
damit auf **stationäre** Störungen ausgelegt. **Transiente** Geräusche — Tastaturanschläge —
|
||||
erkennt er nicht als Störung; sie werden mitübertragen. Betroffen sind ausdrücklich auch
|
||||
leise Chiclet-Tastaturen, nicht nur mechanische. Push-to-Talk als Ausweg wurde verworfen
|
||||
(„inakzeptabel", sorb).
|
||||
|
||||
`threadnet-call:docs/axion1337-fork.md` §5 hatte ML-Rauschunterdrückung bereits einmal
|
||||
verworfen — allerdings **server-seitig** (LiveKit Agents), weil es dort keinen unterstützten
|
||||
Weg gibt, bereinigtes Audio an andere Teilnehmer weiterzureichen. Client-seitig greift dieser
|
||||
Einwand nicht; diese ADR widerspricht der damaligen Entscheidung also nicht, sondern setzt sie
|
||||
fort.
|
||||
|
||||
Eine externe Architekturspezifikation empfahl DeepFilterNet3 via WebAssembly. Statt sie zu
|
||||
übernehmen, wurde ein **Wegwerf-Prototyp** gebaut und gemessen (#0054). Das hat drei ihrer
|
||||
Kernannahmen korrigiert und die Entscheidungsgrundlage von Schätzung auf Messung gestellt.
|
||||
|
||||
## Entscheidung
|
||||
|
||||
**DeepFilterNet3 wird client-seitig integriert — opt-in, nachgeladen, selbst ausgeliefert.**
|
||||
|
||||
1. **Modell:** DeepFilterNet3 über `deepfilternet3-noise-filter` (Apache-2.0 ODER MIT) als
|
||||
LiveKit-`TrackProcessor`. Nicht RNNoise: es ist zwar zehnmal kleiner, aber bei Transienten
|
||||
deutlich schwächer — genau dem Fall, um den es hier geht.
|
||||
2. **Opt-in mit Nachladen.** Standard **AUS**. Die Assets (23,3 MB) werden **erst beim
|
||||
Einschalten** geladen. Damit zahlt nur, wer profitiert — das Größenargument entfällt für
|
||||
alle anderen.
|
||||
3. **Bedienung:** Checkbox zum Aktivieren **plus Regler** für die Stärke.
|
||||
4. **Standardwert 35 %**, nicht 100 %. Gemessen reicht gut ein Drittel für „Tastatur weg und
|
||||
Stimme natürlich"; weniger Dämpfung heißt weniger Artefaktrisiko.
|
||||
5. **Regelung über den Modellparameter** (`setSuppressionLevel` / `atten_lim`), **nicht** über
|
||||
einen Dry/Wet-Mix.
|
||||
6. **Assets werden selbst ausgeliefert.** Der Default-Pfad des Pakets lädt sie von
|
||||
`cdn.mezon.ai`; `assetConfig.cdnUrl` wird auf das eigene Deployment gezeigt.
|
||||
7. **`getUserMedia`:** `noiseSuppression: false` (sonst arbeiten Browser-Filter und Modell
|
||||
gegeneinander), `echoCancellation: true`.
|
||||
|
||||
## Gemessene Grundlage (Prototyp 2026-08-15)
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| Wirkung | Tastatur weg, Stimme natürlich — **bei 35 %** |
|
||||
| Download je Client | **23,27 MB** (15,66 MB wasm + 7,61 MB Modell) |
|
||||
| Vergleich RNNoise | 2,0–4,6 MB, also ~ein Zehntel |
|
||||
| Paketpflege | 20 Versionen, 4 Maintainer, ~20k Downloads/Monat |
|
||||
| CDN-freier Betrieb | im Prototyp nachgewiesen |
|
||||
|
||||
## Konsequenzen
|
||||
|
||||
- **Kein Rust/wasm-Build.** Die Spezifikation hielt ihn für nötig; das fertige Paket macht ihn
|
||||
entbehrlich. Die Fork-Anpassung bleibt dadurch klein: Abhängigkeit, `TrackProcessor`,
|
||||
Bedienelement, Assets in der Auslieferung.
|
||||
- **Kein Dry/Wet-Mixer, kein Delay-Node.** Da über den Modellparameter geregelt wird, gibt es
|
||||
keinen zweiten Signalpfad — und damit weder Phasenauslöschung noch Latenzkompensation.
|
||||
- **23,3 MB gehören ins Deployment.** Sie müssen mit ausgeliefert werden (Image/Ingress) und
|
||||
wachsen bei einem Modell-Update mit.
|
||||
- **Dauerhafte Fork-Anpassung.** Sie muss jeden Upstream-Rebase überleben und gehört in
|
||||
`axion1337-fork.md` samt Portier-Hinweis.
|
||||
- ⚠️ **Mobil ist ungeprüft.** Der Telefontest wurde bewusst ausgesetzt. Weil der Filter opt-in
|
||||
ist, ist das vertretbar: Auf schwachen Geräten bleibt er schlicht aus. Zeigt sich später,
|
||||
dass er dort unbrauchbar ist, ist das ein Folge-Issue, kein Widerruf dieser Entscheidung.
|
||||
- **Neue Lieferkette.** Ein npm-Paket eines Drittanbieters liefert Code, der WebAssembly lädt.
|
||||
Die **Assets** kontrollieren wir (selbst gehostet); der 23-KB-Wrapper bleibt Fremdcode.
|
||||
Vendoring wäre möglich und wurde bewusst nicht gewählt — dann müssten wir Updates selbst
|
||||
nachziehen.
|
||||
|
||||
## Verworfene Alternativen
|
||||
|
||||
- **Push-to-Talk / bewusstes Stummschalten dokumentieren:** billigste Lösung, aber ein
|
||||
Rückschritt gegenüber dem, was Konferenzsysteme heute leisten. Ausdrücklich verworfen.
|
||||
- **RNNoise** statt DFN3: ein Zehntel der Größe, aber konzeptionell schwach bei genau den
|
||||
transienten Geräuschen, die das Problem sind.
|
||||
- **Server-seitige ML-Filterung:** bereits in `axion1337-fork.md` §5 verworfen — LiveKit
|
||||
bietet keinen unterstützten Weg, bereinigtes Audio an andere Teilnehmer weiterzugeben.
|
||||
- **Standardmäßig AN:** beste Wirkung ohne Zutun, aber jeder Client lädt 23 MB — auch auf
|
||||
Telefonen, die ungetestet sind.
|
||||
- **Dry/Wet-Mix als Regler** (Vorschlag der Spezifikation): mischt ungefiltertes Signal
|
||||
zurück, **inklusive der Tastaturanschläge**, und braucht eine Latenzkompensation, die im
|
||||
Spec-Entwurf fehlte. Der native Modellparameter leistet dasselbe ohne diese Nachteile.
|
||||
- **Assets vom Anbieter-CDN laden:** würde bei jedem Call-Start die IP jedes Teilnehmers an
|
||||
einen Dritten melden und die Verfügbarkeit an fremde Infrastruktur hängen.
|
||||
@@ -0,0 +1,87 @@
|
||||
---
|
||||
type: adr
|
||||
id: "0019"
|
||||
status: accepted
|
||||
date: 2026-08-18
|
||||
supersedes: docs/adr/0002-issues-und-management-ins-lab.md
|
||||
superseded_by: null
|
||||
related:
|
||||
- "docs/adr/0012-issues-im-repo-gitlab-als-spiegel.md"
|
||||
- "docs/adr/0013-gruppenregeln-kanonisch-mit-pruefung.md"
|
||||
---
|
||||
|
||||
# ADR-0019: Komponenten-Issues in docs/issues/ adoptiert — eine Nummernwelt für die Gruppe
|
||||
|
||||
## Kontext
|
||||
|
||||
ADR-0012 machte `docs/issues/` kanonisch, **beschränkt auf den
|
||||
Management-Scope**, und ließ die Komponenten-Tracker (gitops,
|
||||
ThreadNet-Web, threadnet-call) ausdrücklich auf GitLab als Wahrheit —
|
||||
„bis die jeweilige Komponente selbst adoptiert". Der Zustand seither:
|
||||
zwei Issue-Welten mit getrennten Nummernkreisen (management#20 ≠
|
||||
gitops#20), Drift zwischen Board und Repo an mehreren Stellen
|
||||
(Beispiel: gitops#61 monatelang ohne Meilenstein, geschlossene
|
||||
Board-Issues zu offenen Dateien), und Host-Sessions ohne Lab-Zugang
|
||||
sehen die Komponenten-Backlogs gar nicht. sorb hat am 2026-08-18 die
|
||||
Adoption angeordnet: ein Backlog, eindeutige Bezeichner, Drift beenden.
|
||||
|
||||
## Entscheidung
|
||||
|
||||
**Die offenen Issues der Komponenten-Tracker werden in `docs/issues/`
|
||||
adoptiert; es gibt ab jetzt genau eine kanonische Nummernwelt.**
|
||||
|
||||
- Jedes adoptierte Issue bekommt die nächste freie Datei-Nummer
|
||||
(0056 ff.); die Datei-ID ist der eindeutige Bezeichner der Gruppe.
|
||||
Die Herkunft steht im Frontmatter (`projekt` + `gitlab_iid`) und im
|
||||
Dateinamen (`0091-gitops-61-…`); ADR-0012s Gleichung „GitLab-iid =
|
||||
Datei-id" gilt nur noch für den Management-Altbestand 0001–0032 —
|
||||
über mehrere Projekte ist sie nicht kollisionsfrei zu halten.
|
||||
- Übernommen werden Titel, Beschreibung (wortgleich), Status
|
||||
(Board-Labels `status:*`), Meilenstein, Priorität, Fälligkeit und
|
||||
eine `area`; Kommentare und Verlauf bleiben auf GitLab (wie beim
|
||||
Management-Import). Geschlossene GitLab-Issues bleiben Historie und
|
||||
werden nicht importiert.
|
||||
- Die GitLab-Projekt-Tracker werden zur **bespiegelten Ansicht** wie
|
||||
zuvor schon das management-Projekt: `spiegel_issues.py` routet
|
||||
jede Datei über ihr `projekt`-Feld ins Herkunftsprojekt, kann
|
||||
geschlossene Issues zu offenen Dateien wieder öffnen, und
|
||||
`gruppenpruefung.py` prüft die Drift über alle adoptierten Projekte.
|
||||
Board-, Meilenstein- und Label-Ansichten arbeiten unverändert weiter
|
||||
— der Grund, aus dem ADR-0012 Option C gewählt hat, bleibt erhalten.
|
||||
- Neue Issues entstehen ab jetzt **immer** als Datei; das `projekt`-Feld
|
||||
bestimmt, in welchem Tracker der Spiegel sie anlegt (fehlt es:
|
||||
management). Ein GitLab-seitig neu angelegtes Issue ohne Datei ist
|
||||
ein Befund der Gruppenprüfung („zweites Backlog"), kein stiller
|
||||
Zustand.
|
||||
- Werkzeug: `verfahren/issue-adoption/adoptiere.py` (deterministisch,
|
||||
idempotent, Dry-Run-Default) hat die 46 offenen Komponenten-Issues
|
||||
als 0056–0101 übernommen.
|
||||
|
||||
## Konsequenzen
|
||||
|
||||
- Ein Backlog, ein Nummernkreis, eine Drift-Prüfung; `STATUS.md` zeigt
|
||||
erstmals die ganze Gruppe. Host-Sessions lesen alle Backlogs über den
|
||||
Gitea-Mirror dieses Repos.
|
||||
- Die Schwelle „Komponente adoptiert selbst" aus ADR-0012 ist damit für
|
||||
gitops, ThreadNet-Web und threadnet-call genommen; thread-net-git,
|
||||
threadnet-operating und notfallhandbuch hatten keine offenen Issues
|
||||
und adoptieren bei Bedarf durch Aufnahme in die `projekt`-Enum.
|
||||
- Querbezüge in Prosa nennen künftig die Datei-ID; alte Bezeichner wie
|
||||
„gitops#61" bleiben über Frontmatter und Dateinamen auffindbar.
|
||||
|
||||
## Verhältnis zu ADR-0002 (Ablösung, teilweise)
|
||||
|
||||
ADR-0002 entschied zweierlei: dass **alle Projekt-Issues auf git.lab leben**, und
|
||||
dass das Backlogs-Repo als `axion1337.chat/management` ins Lab zieht, weil das Lab
|
||||
die Quelle der Wahrheit ist. Nur der **erste** Satz fällt.
|
||||
|
||||
- **Abgelöst:** „Alle Projekt-Issues leben auf git.lab." Kanonisch ist seit
|
||||
ADR-0012 `docs/issues/` im Repo, seit diesem ADR für alle Tracker der Gruppe;
|
||||
GitLab ist der generierte Spiegel. ADR-0002 wird deshalb auf `superseded`
|
||||
gesetzt — die Aussage stünde sonst als gültige Regel neben ihrem Gegenteil.
|
||||
- **Gilt weiter:** git.lab bleibt kanonisch für **Code** (ADR-0001), und das
|
||||
management-Repo bleibt dort, wohin ADR-0002 es gebracht hat. Der Umzug selbst
|
||||
wird nicht rückgängig gemacht und nicht neu entschieden.
|
||||
|
||||
Die Statusmarkierung betrifft also die Issue-Frage, nicht die Repo-Topologie. Wer
|
||||
ADR-0002 künftig liest, findet über den `superseded_by`-Zeiger hierher.
|
||||
@@ -0,0 +1,89 @@
|
||||
---
|
||||
type: adr
|
||||
id: "0020"
|
||||
status: accepted
|
||||
date: 2026-08-18
|
||||
supersedes: null
|
||||
superseded_by: null
|
||||
related:
|
||||
- "docs/issues/0104-daueralarme-melden-nichts-mehr.md"
|
||||
- "docs/adr/0013-gruppenregeln-kanonisch-mit-pruefung.md"
|
||||
---
|
||||
|
||||
# ADR-0020: Bekannte Befunde werden quittiert, damit Rot wieder etwas bedeutet
|
||||
|
||||
## Kontext
|
||||
|
||||
Das Alarmprinzip der Gruppe steht in AGENTS.md: *„Seine rote Pipeline **ist** der
|
||||
Alarm — es gibt bewusst keinen zweiten Meldeweg."* Am 2026-08-18 zeigte die
|
||||
Messung, dass **alle drei** geplanten Prüfungen dauerhaft rot standen:
|
||||
`gruppenpruefung` mit 20 Befunden (17 davon von sorb bewusst vertagt, #0053),
|
||||
`stillstandspruefung` mit fehlendem `GITEA_TOKEN`, und `canonize_rotation` seit
|
||||
neun Tagen an einem liegengebliebenen Rotationszweig.
|
||||
|
||||
Der `canonize`-Fall ist der Beleg, nicht die Anekdote: Neun Tage lang scheiterte
|
||||
der Job an einem Konflikt, der `coturn-secret.yaml`, `synapse-turn-secret.yaml`
|
||||
und `element-server-suite.yaml` betraf — und niemand bemerkte es, weil ein rotes
|
||||
Kreuz mehr zwischen roten Kreuzen unsichtbar ist. Gefunden wurde es nur, weil
|
||||
jemand aus anderem Anlass hinsah.
|
||||
|
||||
Damit war die Regel faktisch außer Kraft: Eine Prüfung, die nur noch rot sein
|
||||
kann, meldet nichts. Gleichzeitig ist das Vertagen selbst legitim — #0053 ist eine
|
||||
bewusste Entscheidung, kein Versäumnis, und sie soll die Alarmfähigkeit nicht als
|
||||
Geisel nehmen.
|
||||
|
||||
## Optionen
|
||||
|
||||
**A: Vertagtes abarbeiten, bis alles grün ist.** Ehrlich, aber es macht die
|
||||
Alarmfähigkeit von Aufräumarbeit abhängig, die bewusst niedrige Priorität trägt —
|
||||
und liefert für die Zwischenzeit keinen funktionierenden Meldeweg.
|
||||
|
||||
**B: Rot tolerieren und die Läufe von Hand lesen.** Der Ist-Zustand. Er hat neun
|
||||
Tage lang einen echten Ausfall verdeckt; genau diese Klasse soll die
|
||||
Stillstandsprüfungs-Familie ja finden.
|
||||
|
||||
**C: Bekannte Befunde quittieren.** Eine gepflegte Liste nimmt Bekanntes aus der
|
||||
Rot-Wertung, ohne es zu verstecken. Rot bleibt dem Neuen vorbehalten.
|
||||
Einwand — und er wiegt: Eine Ausnahmeliste ist selbst ein Kandidat für die nächste
|
||||
Blindstelle, dieselbe Klasse wie der `mrtc`-Record.
|
||||
|
||||
## Entscheidung
|
||||
|
||||
**Option C**, mit drei Regeln, die den Einwand konstruktiv beantworten
|
||||
(`scripts/quittungen.py`, `scripts/befund_quittungen.tsv`):
|
||||
|
||||
- **Quittiertes verschwindet nicht.** Es erscheint weiterhin in der Ausgabe, mit
|
||||
Grund und Frist. Quittieren heißt „bekannt", nicht „weg".
|
||||
- **Jede Zeile trägt eine Frist.** Läuft sie ab, quittiert die Zeile nicht mehr
|
||||
und meldet sich selbst; der Befund zählt wieder. Es gibt keine stille Ewigkeit.
|
||||
- **„Dauerhaft" ist ausschließlich als ADR-Verweis formulierbar.** Eine dauerhafte
|
||||
Ausnahme ohne Entscheidungs-Record ist nach AGENTS.md ohnehin ein Fehler — hier
|
||||
lässt sie sich technisch nicht einmal hinschreiben. Wer Dauer will, muss
|
||||
entscheiden.
|
||||
- **Wirkungslose Zeilen melden sich.** Eine Quittung, auf die kein Befund mehr
|
||||
passt, wird ausgegeben, damit die Datei nicht Zeilen für längst gelöste Probleme
|
||||
sammelt.
|
||||
|
||||
Quittiert wird pro Befund, **nicht per Sammelmuster**: Die 17 Commit-Hygiene-Funde
|
||||
aus #0053 stehen einzeln mit ihrer SHA, weil ein Muster wie `: Echtzeit-Stempel`
|
||||
jeden künftigen Verstoß mitverschluckt hätte.
|
||||
|
||||
**Nicht quittiert werden flüchtige Befunde**, deren Meldung anderswo gebraucht
|
||||
wird. Beispiel: „Mirror auseinander" erscheint bei jedem Lauf kurz nach einem Push,
|
||||
ist aber der einzige Hinweis, wenn ein Spiegel wirklich stehenbleibt (#0028). Ein
|
||||
kurzfristig roter Lauf ist der geringere Preis.
|
||||
|
||||
## Konsequenzen
|
||||
|
||||
- Grün ist wieder erreichbar und bedeutet „nichts Neues". Nachgewiesen am
|
||||
2026-08-18: beide Prüfungen von 25 offenen Befunden auf 0, während ein
|
||||
absichtlich eingefügter neuer Befund weiterhin rot färbt.
|
||||
- Die Quittungsdatei wird Teil der Refinement-Pflege: abgelaufene und wirkungslose
|
||||
Zeilen sind Arbeitsvorrat, kein Rauschen.
|
||||
- Quittungen binden an **Teilzeichenketten des Befundtextes**. Wer eine Ursache
|
||||
behebt oder verschiebt, ändert damit unter Umständen den Text und muss die
|
||||
Quittung nachziehen. Das ist bewusst in Kauf genommen: Die Alternative wären
|
||||
stabile Befund-IDs, die die Datei ohne Spezialwissen unlesbar machen würden.
|
||||
- Die Bedingung, unter der „rote Pipeline = Alarm" trägt, gehört neben die Regel
|
||||
selbst in AGENTS.md. Diese Änderung ist mit sorb abzustimmen und daher hier nur
|
||||
vermerkt, nicht vollzogen (#0104).
|
||||
@@ -0,0 +1,81 @@
|
||||
---
|
||||
type: adr
|
||||
id: "0021"
|
||||
status: accepted
|
||||
date: 2026-08-19
|
||||
supersedes: null
|
||||
superseded_by: null
|
||||
related:
|
||||
- "docs/issues/0060-gitops-17-federation-allowlist-or-closed-federation-dec.md"
|
||||
- "docs/issues/0088-gitops-56-networkpolicy-ausgehender-verkehr-ist-unbesch.md"
|
||||
---
|
||||
|
||||
# ADR-0021: Föderation wird geschlossen — leere Whitelist statt offener Tür
|
||||
|
||||
## Kontext
|
||||
|
||||
Die Frage stand seit dem 2026-07-28 offen, weil niemand wusste, was eine Schließung
|
||||
kosten würde. Am 2026-08-19 wurde sie gemessen statt geschätzt.
|
||||
|
||||
**Föderation war offen und öffentlich erreichbar.** In `synapse-values.yaml` stand zu
|
||||
Föderation nichts, es galten die Synapse-Vorgaben; `/_matrix/federation/v1/version` und
|
||||
`/_matrix/key/v2/server` antworteten von außen mit HTTP 200. Dass Port 8448 zu ist,
|
||||
änderte daran nichts: Die Delegation in `/.well-known/matrix/server` führt die
|
||||
Föderation über `matrix.axion1337.chat:443`, denselben Port wie den Client-Verkehr.
|
||||
|
||||
**Benutzt wurde sie nie.** Über die gesamte Betriebszeit seit dem 2026-04-21, aus der
|
||||
Synapse-Datenbank: **0** Einträge in `destinations`, **0** je erfolgreich kontaktierte
|
||||
Server, **0** fremde Nutzer in den 31 eigenen Räumen, **0** Räume mit fremder
|
||||
Beteiligung. Nicht „wenig", sondern null.
|
||||
|
||||
Damit ist der Handel nicht „Reichweite gegen Sicherheit", sondern **eine ungenutzte
|
||||
Fähigkeit gegen die größte fremdzugewandte Angriffsfläche, die Synapse hat** — und
|
||||
historisch die, in der seine CVEs sitzen.
|
||||
|
||||
## Optionen
|
||||
|
||||
**A: offen lassen.** Kein Aufwand; dauerhaft Angriffsfläche für null nachgewiesenen
|
||||
Bedarf.
|
||||
|
||||
**B: `federation_domain_whitelist: []`.** Föderation nur mit ausdrücklich genannten
|
||||
Servern, leer also mit keinem. Eine Zeile, versioniert, in Minuten umkehrbar.
|
||||
Die Endpunkte antworten weiterhin — der Server wird unbeteiligt, nicht unsichtbar.
|
||||
|
||||
**C: Föderations-Endpunkte am Rand sperren.** Zunächst als „kleinste Fläche" gewählt,
|
||||
dann **verworfen** — beim Umsetzen zeigte sich, dass es die Gruppen-Calls zerstört
|
||||
hätte (siehe unten).
|
||||
|
||||
## Entscheidung
|
||||
|
||||
**Option B.** `federation_domain_whitelist: []` in
|
||||
`gitops:apps/production/custom-configs/synapse-values.yaml`.
|
||||
|
||||
## Warum C verworfen wurde — der Teil, der nicht offensichtlich ist
|
||||
|
||||
Der MatrixRTC-Authorisation-Service (`lk-jwt-service`) prüft OpenID-Tokens über
|
||||
**`/_matrix/federation/v1/openid/userinfo`** — einen Föderations-Pfad. Und er ruft ihn
|
||||
über den **öffentlichen** Namen auf: Das Deployment trägt keine `hostAliases` und
|
||||
`dnsPolicy: ClusterFirst`, `matrix.axion1337.chat` löst also auf die öffentliche IP auf
|
||||
und der Verkehr läuft über Traefik.
|
||||
|
||||
Wer `/_matrix/federation/` am Ingress sperrt, kappt damit die Token-Prüfung für
|
||||
Gruppen-Calls — **derselbe Ausfall wie beim gelöschten `mrtc`-Record, nur mit anderer
|
||||
Ursache.** Ein Pfad-Block sieht dabei korrekter aus als die Whitelist, weshalb dieser
|
||||
Zusammenhang im Konfigurations-Kommentar festgehalten ist und nicht nur hier.
|
||||
|
||||
Synapse bedient diesen Endpunkt ohne X-Matrix-Signatur (`REQUIRE_AUTH = False`); die
|
||||
Whitelist greift dort also nicht und darf es auch nicht.
|
||||
|
||||
## Konsequenzen
|
||||
|
||||
- Fremde Server können weder beitreten noch Ereignisse einliefern. Bestehende Räume und
|
||||
Nutzer sind nicht betroffen — es gab keine fremde Beteiligung, die enden könnte.
|
||||
- **Die Fähigkeit ist eine Zeile entfernt**, nicht verloren: Ein Domain-Eintrag öffnet
|
||||
gezielt für einen Partner. Das war der Grund, B gegenüber einem harten Rückbau
|
||||
vorzuziehen.
|
||||
- Die Endpunkte antworten weiterhin von außen. Wer das ändern will, muss **zuerst** den
|
||||
Auth-Service clusterintern an Synapse binden — sonst siehe oben. Das ist ein eigenes
|
||||
Vorhaben mit Call-Abnahme, kein Nebenschritt.
|
||||
- Eine Port-Sperre bei Hetzner kann diese Trennung **nicht** leisten: Föderation und
|
||||
Client-Verkehr teilen sich 443. Die Konfiguration ist die einzige Stelle, an der sie
|
||||
überhaupt trennbar sind.
|
||||
@@ -0,0 +1,75 @@
|
||||
---
|
||||
type: adr
|
||||
id: "0022"
|
||||
status: accepted
|
||||
date: 2026-08-19
|
||||
supersedes: null
|
||||
superseded_by: null
|
||||
related:
|
||||
- "docs/issues/0099-threadnet-web-12-upstream-sicherheitsfixes-lassen-sich-nicht-m.md"
|
||||
---
|
||||
|
||||
# ADR-0022: Anschluss an Upstream durch einen einmaligen Merge, nicht durch einen geteilten Graft
|
||||
|
||||
## Kontext
|
||||
|
||||
`ThreadNet-Web` enthält keine Upstream-Historie: Am 2026-05-10 kam ein kompletter
|
||||
Element-Web-Baum in einem Commit herein. Ohne gemeinsamen Vorfahren ist
|
||||
`git merge upstream/develop` unmöglich, und jedes Update bedeutet, zwölf eigene Patches
|
||||
von Hand auf einen neuen Baum aufzutragen. Das ist nicht nur mühsam, sondern gefährlich:
|
||||
Verschiebt Element eine Datei, verschwinden unsere Zeilen **ohne Konflikt** (#0099).
|
||||
|
||||
Am 2026-08-19 wurde der tatsächliche Ursprung gemessen statt geraten: **`deadd548`
|
||||
vom 2026-05-08** (nicht der Tag `v1.12.17`, wie zuvor angenommen). Der Beleg ist die
|
||||
Baumdistanz — 43 abweichende Dateien, davon 31 reine Modus-Änderungen und der Rest
|
||||
unser eigenes Feature.
|
||||
|
||||
Im Wegwerf-Klon getestet: Mit gesetztem Vorfahren läuft ein Merge von drei Monaten
|
||||
`develop` durch und erzeugt **32 konfliktbehaftete Dateien, davon nur vier Quellcode** —
|
||||
genau unsere Patches.
|
||||
|
||||
## Optionen
|
||||
|
||||
**A: Geteilter Replace-Ref.** `git replace --graft` und `refs/replace/*` mitliefern.
|
||||
Die Historie bleibt formal unverändert, Git *interpretiert* sie nur anders. Nachteil:
|
||||
Jeder Klon braucht einen zusätzlichen Fetch, und wer ihn vergisst, sieht eine andere
|
||||
Historie als alle anderen — ein stiller Unterschied, der sich erst im Konfliktfall
|
||||
zeigt.
|
||||
|
||||
**B: Einmaliger echter Merge**, danach normale Merges.
|
||||
|
||||
⚠️ **Praezisierung nach dem Test:** `--allow-unrelated-histories` allein liefert
|
||||
einen ZWEI-Wege-Vergleich und damit 1757 Konflikte — gemessen. Der Graft ist kein
|
||||
Gegenentwurf zu B, sondern sein Werkzeug: lokal setzen, den Merge damit rechnen
|
||||
lassen (32 Konflikte), committen. Der Merge-Commit traegt danach die echten Eltern,
|
||||
der Graft kann weg, und die Abstammung laeuft ueber den Merge-Commit selbst.
|
||||
Die Nahtstelle wird ein sichtbarer Merge-Commit. Kein Sonderwissen, kein Zusatzschritt,
|
||||
kein Klon kann sie versehentlich übersehen.
|
||||
|
||||
**C: So weiterarbeiten wie bisher** — Patches von Hand auftragen. Verworfen: Das ist der
|
||||
Zustand, der die stille Klasse überhaupt erst erzeugt.
|
||||
|
||||
## Entscheidung
|
||||
|
||||
**Option B** (sorb, 2026-08-19).
|
||||
|
||||
Ausschlaggebend ist nicht der Aufwand — beide Wege sind ähnlich billig — sondern die
|
||||
Sichtbarkeit. A funktioniert nur, solange alle daran denken; B trägt sich selbst. Für
|
||||
ein Repo, dessen Kernproblem „Git meldet nichts" ist, wäre ein Mechanismus, der
|
||||
stillschweigend unterschiedlich wirkt, die falsche Wahl.
|
||||
|
||||
`deadd548` bleibt trotzdem wichtig: Es ist der Stand, gegen den der Merge gefahren wird,
|
||||
und ohne diese Messung wäre der Merge auf eine erfundene Grundlage gelaufen.
|
||||
|
||||
## Konsequenzen
|
||||
|
||||
- Der erste Merge ist ein einmaliger Kraftakt mit vier Quellcode-Konflikten; danach ist
|
||||
ein Upstream-Update ein gewöhnlicher Merge.
|
||||
- **Die stille Klasse verschwindet**: Verschiebt Upstream eine Datei, die wir angefasst
|
||||
haben, meldet Git künftig einen Konflikt, statt unsere Zeilen wortlos fallen zu lassen.
|
||||
- Die Nahtstelle bleibt als Merge-Commit dauerhaft sichtbar — gewollt, nicht geduldet.
|
||||
- **Abnahme ist kein Build, sondern ein Funktionstest**: Die ClamAV-Patches liegen in der
|
||||
Medien-Pipeline. Ohne den Test aus #0099 (verschlüsselte Datei senden, abgelehnte
|
||||
empfangen) ist der Merge nicht abgenommen.
|
||||
- Schritt 4 aus #0099 — ein Verfahren zum Auftragen der Patches — wird damit
|
||||
gegenstandslos.
|
||||
@@ -0,0 +1,81 @@
|
||||
---
|
||||
type: adr
|
||||
id: "0023"
|
||||
status: accepted
|
||||
date: 2026-08-19
|
||||
supersedes: null
|
||||
superseded_by: null
|
||||
related:
|
||||
- "docs/adr/0009-commit-konventionen-und-historien-anonymisierung.md"
|
||||
- "docs/adr/0022-upstream-anschluss-durch-einmaligen-merge.md"
|
||||
- "docs/issues/0104-daueralarme-melden-nichts-mehr.md"
|
||||
---
|
||||
|
||||
# ADR-0023: Fremde Historie von der Git-Hygiene ausnehmen — erklärt, nicht global
|
||||
|
||||
## Kontext
|
||||
|
||||
Der Upstream-Merge aus [ADR-0022](0022-upstream-anschluss-durch-einmaligen-merge.md)
|
||||
hat am 2026-08-19 **70.265 fremde Commits** in `ThreadNet-Web` geholt. Sie stammen von
|
||||
Element und tragen deren Zeitstempel und Identitäten.
|
||||
|
||||
Die Git-Hygiene-Prüfung aus [ADR-0009](0009-commit-konventionen-und-historien-anonymisierung.md)
|
||||
(`gruppenpruefung.py`, Prüfung 5) bemängelte davon sofort **39 Commits** als
|
||||
„Echtzeit-Stempel" — alle im Fenster seit der Regel-Grenze 2026-08-07.
|
||||
|
||||
Das ist kein Fehlalarm im engeren Sinn: Die Commits verletzen die Konvention wirklich.
|
||||
Sie konnten ihr aber nie folgen, weil sie nicht bei uns entstanden sind, und sie werden
|
||||
sich nie ändern lassen. Damit war die Prüfung dauerhaft rot — genau der Zustand, den
|
||||
[#0104](../issues/0104-daueralarme-melden-nichts-mehr.md) am selben Tag beseitigt hatte.
|
||||
Ein Alarm, der immer rot ist, meldet nichts mehr.
|
||||
|
||||
## Optionen
|
||||
|
||||
**A: Quittieren** über den Mechanismus aus ADR-0020. Verworfen: 39 Einträge, und die
|
||||
Quittung verlangt eine Frist — hier gäbe es keine, weil sich nichts ändern wird.
|
||||
ADR-0020 lässt „permanent" ausdrücklich nur als ADR zu, was auf diese ADR hinausläuft,
|
||||
aber mit 39 Zeilen Ballast im Quittungs-Log.
|
||||
|
||||
**B: Regel-Grenze verschieben oder die Prüfung global lockern.** Verworfen: Das nähme
|
||||
allen Repos den Schutz, um einem zu helfen.
|
||||
|
||||
**C: Fremde Absender hart im Code ausnehmen** (`releases@riot.im`,
|
||||
`noreply@github.com`). Verworfen: Das ist eine Liste, die mit jedem neuen fremden
|
||||
Beitragenden wächst — dieselbe Falle, die MASCHINEN bewusst per Adresse und nicht per
|
||||
Name matched, nur eine Ebene höher.
|
||||
|
||||
**D: Erklärte Ausnahme pro Komponente.** Neues Feld `fremdhistorie` in
|
||||
`docs/components/*.md`. Wo es gesetzt ist, prüft die Hygiene nur noch Commits, die von
|
||||
**eigenen Identitäten committet** wurden.
|
||||
|
||||
## Entscheidung
|
||||
|
||||
**Option D** (sorb, 2026-08-19: „gruppenprüfung fixen, upstream-commits ausnehmen").
|
||||
|
||||
Der Trennschnitt ist der **Committer**, nicht der Autor. Gemessen an ThreadNet-Web im
|
||||
Fenster seit 2026-08-07: 18 eigene Commits, alle von `cfx@riot.8shield.net` committet;
|
||||
39 fremde, committet von `GitHub <noreply@github.com>` (35) und `RiotRobot` (4). **Keine
|
||||
Überschneidung.** Der Autor taugt nicht als Kriterium — unsere eigenen Commits können
|
||||
fremde Autoren tragen (Cherry-Picks), und fremde Commits tragen Autoren, die wie
|
||||
Menschen aussehen.
|
||||
|
||||
Der Feldwert ist die **Begründung**, kein Schalter: Wer die Ausnahme erklärt, sagt,
|
||||
woher die fremden Commits stammen. Das folgt dem Muster der Quittungen aus ADR-0020 —
|
||||
eine Ausnahme ohne Begründung gibt es nicht.
|
||||
|
||||
## Konsequenzen
|
||||
|
||||
- Die Alarmanlage ist wieder grün und damit wieder aussagekräftig: 0 offene Befunde,
|
||||
20 quittiert.
|
||||
- Die Prüfung bleibt in ThreadNet-Web **wirksam** — nachgewiesen, nicht angenommen:
|
||||
Nach dem Fix werden dort weiterhin 18 Commits geprüft, alle auf `12:00:00`.
|
||||
- ⚠️ **Der Preis, ehrlich benannt:** In Repos mit erklärter Fremdhistorie fällt ein
|
||||
Commit durchs Raster, den jemand von uns unter einer **völlig unbekannten** Identität
|
||||
erzeugt — also weder eigene Adresse als Autor noch als Committer. Genau diesen Fall
|
||||
fängt die Identitätsprüfung sonst. Deshalb gilt die Ausnahme nur dort, wo sie
|
||||
deklariert ist, und nicht global.
|
||||
- Wer künftig ein Repo mit fremder Historie aufnimmt, muss das Feld setzen — sonst
|
||||
färbt die Prüfung rot, und das ist richtig so: Die Ausnahme soll eine bewusste
|
||||
Erklärung sein, kein stiller Nebeneffekt.
|
||||
- Nicht gelöst: Der Gitea-Spiegel von ThreadNet-Web scheitert seit demselben Merge am
|
||||
Umfang des Pushs (70.269 Commits, ~600 MB, `HTTP 499`). Eigener Vorgang.
|
||||
@@ -5,8 +5,10 @@ anzeigename: "ThreadNet Web"
|
||||
phase: active
|
||||
gitlab: "axion1337.chat/ThreadNet-Web"
|
||||
mirror: "rohana.axion1337.de/sorb/ThreadNet-Web"
|
||||
fremdhistorie: "Element Web, seit dem Upstream-Merge 88c4e15 am 2026-08-19 (ADR-0022): 70.265 fremde Commits"
|
||||
related:
|
||||
- "docs/adr/0013-gruppenregeln-kanonisch-mit-pruefung.md"
|
||||
- "docs/adr/0022-upstream-anschluss-durch-einmaligen-merge.md"
|
||||
---
|
||||
|
||||
# ThreadNet Web
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
---
|
||||
type: component
|
||||
slug: "notfallhandbuch"
|
||||
anzeigename: "Notfallhandbuch"
|
||||
phase: active
|
||||
gitlab: "axion1337.chat/notfallhandbuch"
|
||||
mirror: null
|
||||
related:
|
||||
- "docs/adr/0016-notfallhandbuch-nicht-spiegeln.md"
|
||||
- "docs/issues/0030-der-restore-ist-nie-geprobt-sicherungen-sind.md"
|
||||
---
|
||||
|
||||
# Notfallhandbuch
|
||||
|
||||
Eigenständiges Kit für den Ernstfall: Einstiegs-README (erst Lage klären, dann Restore),
|
||||
Wiederherstellungsverfahren der Matrix-Plattform und `notfall.sh` (Lage prüfen · Backups
|
||||
prüfen · Restore-Probe · Ernstfall).
|
||||
|
||||
**Bewusst ohne Push-Mirror (ADR-0016):** Das Handbuch beschreibt Infrastruktur, Ablageort der
|
||||
Sicherungen und wo die Schlüssel liegen. Auf dem öffentlich erreichbaren Gitea wäre es bei
|
||||
einer Kompromittierung des Stacks die Landkarte für den nächsten Schritt. Vertraulichkeit vor
|
||||
Verfügbarkeit; die Verfügbarkeitslücke deckt ein lokaler Clone, kein Mirror.
|
||||
@@ -0,0 +1,24 @@
|
||||
---
|
||||
type: component
|
||||
slug: "threadnet-wiki"
|
||||
anzeigename: "ThreadNet Wiki (Inhalt)"
|
||||
phase: active
|
||||
gitlab: "axion1337.chat/threadnet-wiki"
|
||||
mirror: null
|
||||
related:
|
||||
- "docs/adr/0015-wiki-git-storage-ueber-gitea-kanonisieren.md"
|
||||
- "docs/adr/0014-wikijs-loest-docusaurus-ab.md"
|
||||
---
|
||||
|
||||
# ThreadNet Wiki (Inhalt)
|
||||
|
||||
Inhalt des Wiki.js unter `wiki.axion1337.chat` als docs-as-code — Betriebs- und
|
||||
Anwenderhandbücher.
|
||||
|
||||
**Kein Push-Mirror, und das ist Absicht:** Der Fluss läuft hier **umgekehrt** zur übrigen
|
||||
Topologie (ADR-0015). Wiki.js im Cluster schreibt per Git-Storage nach Gitea, ein CI-Job
|
||||
kanonisiert von dort nach git.lab. Ein Mirror in Gegenrichtung würde die Kette schließen und
|
||||
Änderungen überschreiben.
|
||||
|
||||
⚠️ **Nicht von Hand hier committen.** Kanonische Bearbeitung ist die Wiki.js-Oberfläche;
|
||||
direkte Commits kollidieren mit dem nächsten Storage-Sync.
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0001"
|
||||
status: open
|
||||
status: done
|
||||
created: 2026-08-01
|
||||
milestone: M2
|
||||
priority: low
|
||||
@@ -22,3 +22,19 @@ Quelle: [hosts/matrix.md](https://git.lab/axion1337.chat/management/-/blob/main/
|
||||
|
||||
---
|
||||
*Übernommen aus dem Backlogs-Markdown beim Framework-Umbau 2026-08-01 (voller Wortlaut: Git-Historie der Datei).*
|
||||
|
||||
## In Arbeit 2026-08-14 — verifiziert
|
||||
|
||||
DNS-Ist (DoH, extern): `A www.matrix.axion1337.de → 49.13.132.245`. Verifiziert, dass **nichts**
|
||||
darauf hört: der Matrix-Cluster-Ingress routet nur `matrix.axion1337.chat` (nicht `.de`), es
|
||||
gibt keine Ingress-/IngressRoute-Regel für `www.matrix`, und der Name liefert nur ein
|
||||
nicht-passendes Default-Cert (kein eigener Router). → **gefahrlos löschbar.**
|
||||
|
||||
**Aktion (IONOS, nur sorb):** `A www.matrix.axion1337.de` löschen. Teil der Zonen-Bereinigung #0005.
|
||||
|
||||
## Erledigt 2026-08-14
|
||||
|
||||
`matrix.axion1337.de` (der Apex selbst) wurde von sorb bereits eigenständig entfernt (die
|
||||
Matrix-Plattform läuft unter `axion1337.chat`) — damit ist `www.matrix.axion1337.de` zwangsläufig
|
||||
mit weg. Extern per DoH (Cloudflare + Google, unabhängig) bestätigt: kein A-Record mehr, Status
|
||||
NXDOMAIN. Nichts weiter zu tun.
|
||||
|
||||
@@ -6,7 +6,7 @@ created: 2026-08-01
|
||||
milestone: M1
|
||||
priority: medium
|
||||
host: game
|
||||
wartegrund: Grund im GitLab-Verlauf benannt (Import 2026-08-11); im nächsten Refinement präzisieren
|
||||
wartegrund: "Wartet auf Aufnahme des GAME-Hosts in den Hetzner-vSwitch (Handgriff sorb in der Cloud-Console); erst danach lassen sich die Scrape-Targets auf die private Adresse umstellen. Frist: der TargetDown-Silence 3c8f1a2e laeuft am 2026-09-14 ab."
|
||||
gitlab_iid: "2"
|
||||
related: []
|
||||
---
|
||||
@@ -39,3 +39,29 @@ Quelle: [hosts/game.md](https://git.lab/axion1337.chat/management/-/blob/main/ho
|
||||
|
||||
---
|
||||
*Übernommen aus dem Backlogs-Markdown beim Framework-Umbau 2026-08-01 (voller Wortlaut: Git-Historie der Datei).*
|
||||
|
||||
## Nachgeprüft 2026-08-15
|
||||
|
||||
Exporter weiterhin **nicht erreichbar** (Messung vom Mac, öffentliche IP):
|
||||
`157.90.155.206:8080` und `:9100` laufen in den Timeout, während `:443` sofort antwortet —
|
||||
die Signatur eines Paketfilters davor, also unverändert das im Issue beschriebene Bild.
|
||||
Nichts hat sich von allein gelöst.
|
||||
|
||||
**Alert-Nebenwirkung — behoben 2026-08-15:** Die ursprünglichen Silences (`abedb8a2…`,
|
||||
`0f64aa3c…`) liefen am **2026-08-04** ab; danach meldete `TargetDown` für beide Targets rund
|
||||
elf Tage lang alle 4 h. Dauerfeuer ohne Adressat stumpft die Alarmwege ab — schlimmer als
|
||||
kein Alarm, zumal die Backup-Alarme aus #0030 über denselben Weg laufen.
|
||||
|
||||
Neuer Silence gesetzt (sorb, 2026-08-15): **ID `3c8f1a2e`**, gültig bis **2026-09-14 12:00 UTC**.
|
||||
|
||||
- **Matcher:** `alertname="TargetDown"` **plus** `job=~"gameserver_cadvisor|pterodactyl_host_node"` —
|
||||
bewusst eng. Ein Silence nur auf `alertname` hätte jeden künftigen `TargetDown` verschluckt,
|
||||
auch für Dienste, die zählen. Genau daran werden Silences gefährlich.
|
||||
- **Ablaufdatum statt Dauerstille:** Läuft der Silence aus, ohne dass der vSwitch-Umzug erfolgt
|
||||
ist, meldet sich der Alarm zurück und erzwingt eine Neubewertung. Erfolgt der Umzug vorher,
|
||||
läuft der Silence einfach ins Leere — die Targets sind dann wieder `up`.
|
||||
- **Kommentar im Silence** nennt Grund, Ausstiegsbedingung und dieses Issue, damit er in drei
|
||||
Monaten bewertbar bleibt statt blind verlängert zu werden.
|
||||
|
||||
⚠️ **Damit ist der 2026-09-14 die faktische Frist dieses Issues** — nicht nur eine
|
||||
Aufräumnotiz.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0003"
|
||||
status: open
|
||||
status: done
|
||||
created: 2026-08-01
|
||||
milestone: M2
|
||||
priority: low
|
||||
@@ -23,3 +23,18 @@ Quelle: [hosts/game.md](https://git.lab/axion1337.chat/management/-/blob/main/ho
|
||||
|
||||
---
|
||||
*Übernommen aus dem Backlogs-Markdown beim Framework-Umbau 2026-08-01 (voller Wortlaut: Git-Historie der Datei).*
|
||||
|
||||
## In Arbeit 2026-08-14 — verifiziert
|
||||
|
||||
DNS-Ist (DoH, extern): `A www.game.axion1337.de → 157.90.155.206` (identisch zum `game`-Apex).
|
||||
Kein MX/TXT. Extern kein passendes Cert; der Host ist von hier nicht erreichbar (GAME-01), also
|
||||
nicht host-seitig gegengeprüft — es ist aber ein reiner IONOS-Default-`www.` einer Subdomain,
|
||||
kein bewusst eingerichteter Dienst. → löschbar.
|
||||
|
||||
**Aktion (IONOS, nur sorb):** `A www.game.axion1337.de` löschen. Teil von #0005.
|
||||
|
||||
## Erledigt 2026-08-14
|
||||
|
||||
`A www.game.axion1337.de` in IONOS gelöscht (kein Mail-Service-Block, da `game` keinen
|
||||
Mail-Satz hatte). Extern per DoH bestätigt: NXDOMAIN. `game.axion1337.de` selbst unverändert
|
||||
(`157.90.155.206`).
|
||||
|
||||
@@ -6,7 +6,7 @@ created: 2026-08-01
|
||||
milestone: M1
|
||||
priority: low
|
||||
host: overmind
|
||||
wartegrund: Grund im GitLab-Verlauf benannt (Import 2026-08-11); im nächsten Refinement präzisieren
|
||||
wartegrund: "Beobachtungsfenster nach EEE-Abschaltung + NIC-Firmware 2.5.2.0: ohne erneuten Hang bis ca. 2026-08-28 (vier Wochen ab Fix) schließen; bei Wiederauftreten gezielter ASPM-Fix."
|
||||
gitlab_iid: "4"
|
||||
related: []
|
||||
---
|
||||
@@ -27,3 +27,10 @@ Volle Zeitleiste: [hosts/overmind.md](https://git.lab/axion1337.chat/management/
|
||||
|
||||
---
|
||||
*Übernommen aus dem Backlogs-Markdown beim Framework-Umbau 2026-08-01 (voller Wortlaut: Git-Historie der Datei).*
|
||||
|
||||
## Zwischenstand 2026-08-15
|
||||
|
||||
Rund zwei Wochen des vierwöchigen Beobachtungsfensters sind um (Fix: 2026-08-01, Vorfall:
|
||||
2026-07-31). Kein Handlungsbedarf bis ca. **2026-08-28** — dann entweder schließen oder,
|
||||
falls der Hang wiederkehrt, auf den gezielten ASPM-Fix gehen. Bewusst kein Aktionismus:
|
||||
Das Issue *ist* das Warten.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0005"
|
||||
status: open
|
||||
status: done
|
||||
created: 2026-08-01
|
||||
milestone: M2
|
||||
priority: low
|
||||
@@ -51,3 +51,62 @@ Detail-Rezepte pro Name (Löschen/Anlegen-Tabellen): Git-Historie von
|
||||
|
||||
### matrix.axion1337.de (entblockt durch MATRIX-01)
|
||||
Gleiches Härtungsmuster wie selendis: kompletten IONOS-Mail-Satz entfernen, Null-MX + `v=spf1 -all` + `_dmarc p=reject`; `autodiscover.matrix` kann weg. (`www.matrix` → #1.)
|
||||
|
||||
## In Arbeit 2026-08-14 — Ist-Stand verifiziert (DoH, extern)
|
||||
|
||||
Die frühere Notiz „rohana + selendis in Arbeit" ist überholt — tatsächlicher Zonenstand heute:
|
||||
|
||||
| Name | MX | SPF (TXT) | _dmarc | www | Bewertung |
|
||||
|---|---|---|---|---|---|
|
||||
| rohana | — | — | — | `www.rohana` **existiert** (188.245.193.243) | Mail-Satz weg, aber **ungehärtet** (kein Null-MX/-all/reject = spoofbar); www offen |
|
||||
| selendis | mx00/mx01.ionos | `~all` (softfail) | — | weg | **IONOS-Mail-Satz noch komplett da** — unangetastet |
|
||||
| matrix | mx00/mx01.ionos | `~all` | — | `www.matrix` + `autodiscover.matrix`→adsredir.ionos | Mail-Satz + autodiscover + www offen (#0001) |
|
||||
| game | — | — | — | `www.game` da (#0003) | Apex sauber (kein Mail-Satz), nur www weg |
|
||||
| ftp | — | — | — | A → 217.160.233.227 (IONOS-Hosting) | Ballast, löschen falls ungenutzt |
|
||||
|
||||
**Konsolidierte Aktionsliste (IONOS-Panel/-API, nur sorb — ich habe keinen Zugang):**
|
||||
- **rohana**: `www.rohana` löschen · Null-MX (`.` Prio 0) + TXT `v=spf1 -all` + `_dmarc p=reject` **anlegen** (aktuell ungeschützt!).
|
||||
- **selendis**: MX mx00/mx01 + DKIM-CNAMEs (`s1-/s2-/s42582890-_domainkey`) + `autodiscover.selendis` löschen · SPF-TXT **editieren** `~all`→`-all` (keinen zweiten anlegen — PermError!) · Null-MX + `_dmarc p=reject` anlegen.
|
||||
- **matrix**: MX + `autodiscover.matrix` löschen · SPF `~all`→`-all` editieren · Null-MX + `_dmarc p=reject` anlegen · `www.matrix` löschen (#0001).
|
||||
- **game**: `www.game` löschen (#0003). Apex ist bereits sauber.
|
||||
- **ftp**: A löschen, falls nichts darauf zeigt (IONOS-Hosting-Default).
|
||||
|
||||
**Nach Umsetzung verifizieren:** www-Namen lösen nicht mehr auf; genau EIN SPF pro Name; A/AAAA
|
||||
von rohana/selendis/matrix unangetastet (Gitea/Grafana/Matrix weiter per HTTPS). Ich checke die
|
||||
Außensicht per DoH gegen, sobald du durch bist.
|
||||
|
||||
## Erledigt 2026-08-14 (Teil) — rohana & selendis
|
||||
|
||||
Beide gemeinsam mit sorb Schritt für Schritt durch IONOS umgesetzt, extern per DoH bestätigt:
|
||||
|
||||
- **rohana**: `www.rohana` gelöscht · Null-MX (`0 .`) · SPF `v=spf1 -all` · `_dmarc.rohana`
|
||||
`p=reject` — alles neu angelegt, vorher komplett ungeschützt. `rohana` A-Record (Gitea)
|
||||
unangetastet.
|
||||
- **selendis**: IONOS-„Mail"-Service (nur für `selendis`, kein Postfach) deaktiviert, dadurch
|
||||
MX-Einträge löschbar geworden · 3 DKIM-CNAMEs + `www.selendis` gelöscht (`autodiscover.selendis`
|
||||
gab es nicht) · bestehenden SPF-TXT editiert (`~all`→`-all`, kein Duplikat) · Null-MX (`0 .`) ·
|
||||
`_dmarc.selendis` `p=reject` neu angelegt. `selendis` A-Record (Grafana) unangetastet.
|
||||
|
||||
**Lehre für die verbleibenden Namen (matrix):** Falls dort ebenfalls ein IONOS-„Mail"-Service
|
||||
aktiv ist, blockiert er die MX-Löschung mit „wird von einem Service verwaltet" — vorher im
|
||||
IONOS-Mail-Bereich prüfen, ob ein Postfach/eine Weiterleitung existiert, und den Service ggf.
|
||||
deaktivieren, bevor die MX-Einträge gelöscht werden.
|
||||
|
||||
**Noch offen:** `matrix` (Mail-Satz + `autodiscover.matrix` + `www.matrix` → #0001), `www.game`
|
||||
(→ #0003), `ftp` (Ballast, falls ungenutzt).
|
||||
|
||||
## Erledigt 2026-08-14 (Teil 2) — matrix & www.game
|
||||
|
||||
`matrix.axion1337.de` war bereits von sorb selbst entfernt (Plattform läuft unter
|
||||
`axion1337.chat`) — `www.matrix` damit automatisch mit weg (#0001 erledigt). `A www.game`
|
||||
gelöscht, extern bestätigt (#0003 erledigt).
|
||||
|
||||
**Einziger Rest von #0005: `ftp.axion1337.de`** (zeigt auf `217.160.233.227`, IONOS-Hosting-Default)
|
||||
— noch nicht geprüft/gelöscht.
|
||||
|
||||
## Erledigt 2026-08-14 (Abschluss) — ftp
|
||||
|
||||
`A ftp.axion1337.de` von sorb gelöscht, extern per DoH bestätigt: NXDOMAIN. Damit ist die
|
||||
gesamte Zonen-Bereinigung abgeschlossen: rohana + selendis gehärtet (Null-MX/SPF -all/DMARC
|
||||
reject), matrix bereits vorher entfernt, www.game und ftp gelöscht. Alle A/AAAA-Records der
|
||||
produktiven Hosts (rohana, selendis) unangetastet verifiziert.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0006"
|
||||
status: open
|
||||
status: done
|
||||
created: 2026-08-01
|
||||
milestone: M1
|
||||
priority: low
|
||||
@@ -28,3 +28,23 @@ Quelle: [shared/zone-axion1337.md](https://git.lab/axion1337.chat/management/-/b
|
||||
|
||||
---
|
||||
*Übernommen aus dem Backlogs-Markdown beim Framework-Umbau 2026-08-01 (voller Wortlaut: Git-Historie der Datei).*
|
||||
|
||||
## Erledigt 2026-08-14 — Ist-Stand geprüft, Anliegen erfüllt
|
||||
|
||||
Extern per DoH verifiziert; alle drei Punkte des Issues sind adressiert:
|
||||
|
||||
- **`_dmarc.axion1337.de` = `v=DMARC1; p=reject;`** (nicht mehr `p=none`) — der Kernpunkt.
|
||||
Von sorb bereits eigenständig umgestellt.
|
||||
- **Subdomain-Vererbung**: Ohne `sp=`-Tag gilt laut RFC 7489 die `p=`-Policy auch für
|
||||
Subdomains — mit `p=reject` erben sie also `reject`. Das vom Issue gewünschte `sp=reject`
|
||||
ist damit gegenstandslos (und die Einzel-`_dmarc`-Records aus ZONE-01 sind Gürtel+Hosenträger,
|
||||
schaden aber nicht).
|
||||
- **Vermeintliche DKIM-Lücke war ein Fehlalarm**: gesucht wurde damals unter `s1._domainkey`,
|
||||
IONOS verwendet aber `s1-ionos._domainkey` / `s2-ionos._domainkey` / `s42582890._domainkey`.
|
||||
Alle drei CNAMEs existieren am Apex, die Schlüssel hinter `s1-ionos`/`s2-ionos` lösen gültig
|
||||
auf (je 408 Zeichen TXT).
|
||||
|
||||
**Bewusst nicht geändert:** Apex-SPF bleibt `v=spf1 include:_spf-eu.ionos.com ~all` (Softfail).
|
||||
Über `axion1337.de` läuft **echter** Mailverkehr (aktive Postfächer, MX auf mx00/mx01.ionos.de);
|
||||
da DMARC bereits `reject` erzwingt, ist der Sicherheitsgewinn von `-all` marginal, das Risiko
|
||||
eines still gebrochenen Versands durch einen vergessenen legitimen Sender aber real.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0007"
|
||||
status: next
|
||||
status: done
|
||||
created: 2026-08-01
|
||||
milestone: M1
|
||||
priority: high
|
||||
@@ -33,3 +33,60 @@ Quelle: [hosts/cfgmon.md](https://git.lab/axion1337.chat/management/-/blob/main/
|
||||
|
||||
---
|
||||
*Übernommen aus dem Backlogs-Markdown beim Framework-Umbau 2026-08-01 (voller Wortlaut: Git-Historie der Datei).*
|
||||
|
||||
## Plan (2026-08-14) — eingeplant
|
||||
|
||||
**Weg B (DNS-01)** umsetzen — im Issue bereits als empfohlen markiert: entfernt die
|
||||
Port-Fenster-Abhängigkeit komplett (kein offener 443, keine Unsicherheit beim IPv6-Erstlauf)
|
||||
und ermöglicht Wildcards; die Voraussetzung (versionierter Traefik-Stack) ist seit CFGMON-02
|
||||
erfüllt. Ausführung terminlich **vor Mitte September** (Puffer vor Ablauf 2026-10-28).
|
||||
|
||||
Schritte (B):
|
||||
1. IONOS-API-Token als Traefik-Secret hinterlegen (SOPS-verschlüsselt).
|
||||
2. Traefik-ACME-Resolver auf **DNS-01 (IONOS-Provider)** umstellen, committen → ausrollen.
|
||||
3. Test-Erneuerung erzwingen; TXT-Record-Setzung + ausgestelltes Cert für `selendis`/`rohana`
|
||||
prüfen (kein Self-Signed-Fallback).
|
||||
4. Erst nach erfolgreichem DNS-01-Cert die verbliebene 443-Ausnahme schließen.
|
||||
|
||||
**Fallback A (Ports öffnen)** — nur falls B verworfen wird: **Kalender-Checkpoint Mitte
|
||||
September** (NICHT aufs Ablaufdatum warten). 443 für `0.0.0.0/0` **und** `::/0` öffnen
|
||||
(IPv6 separat!), Erneuerung + IPv6-Erstlauf beobachten, danach wieder schließen.
|
||||
|
||||
## In Arbeit (2026-08-14) — Weg B vorbereitet
|
||||
|
||||
Der Traefik-Stack liegt in `axion1337.chat/thread-net-git` (Compose, Deploy **manuell** auf
|
||||
CFGMON: `cd /opt/thread-net-git && docker compose up -d`; kein Runner mehr). Der DNS-01-Umbau
|
||||
ist als Diff fertig (lokal geprüft, noch **nicht** gepusht — gekoppelt an Token + Deploy):
|
||||
|
||||
- `reverse-proxy`-Command: `acme.tlschallenge` → `acme.dnschallenge` + `provider=ionos` +
|
||||
`resolvers=1.1.1.1:53,8.8.8.8:53` (CFGMON ist öffentlich, keine Lab-Port-53-Interception).
|
||||
- `environment: IONOS_API_KEY=${IONOS_API_KEY}` — Token aus `/opt/thread-net-git/.env`
|
||||
(git-ignoriert), Format `<prefix>.<secret>` (IONOS Developer DNS-API).
|
||||
- `acme.json` (letsencrypt-Volume) bleibt — bestehende Certs gelten bis 2026-10-28, Erneuerung
|
||||
läuft dann über DNS-01. Volume NICHT löschen.
|
||||
|
||||
**Zwei menschliche Abhängigkeiten (blockieren den Abschluss):**
|
||||
1. **IONOS Developer DNS-API-Key** anlegen (der `<prefix>.<secret>` gehört in die Host-`.env`).
|
||||
2. **Deploy + Verifikation auf CFGMON** (kein SSH-Zugang von hier): `.env` setzen →
|
||||
`docker compose up -d reverse-proxy` → Test-Erneuerung erzwingen → TXT + neues Cert prüfen.
|
||||
|
||||
Danach: 443-Weltöffnung ist für ACME nicht mehr nötig (nur noch für die Dienste selbst).
|
||||
|
||||
## Erledigt 2026-08-14 — Weg B (DNS-01) live & verifiziert
|
||||
|
||||
Umgestellt und end-to-end bestätigt. Der IONOS-DNS-API-Key liegt in der Host-`.env`
|
||||
(`/opt/thread-net-git/.env`, git-ignoriert); die Compose-Umstellung ist git.lab-Commit
|
||||
`8d089e2` (`thread-net-git`), auf CFGMON deployt.
|
||||
|
||||
- **Testausstellung** (Wegwerf-Router `dns01-test.axion1337.de`): DNS-01 gelöst, TXT via
|
||||
IONOS-API gesetzt, `The server validated our request` 15:33:04 → Zertifikat 15:33:07.
|
||||
- **Live geprüft**: `openssl s_client` (SNI `dns01-test`) → Issuer **Let's Encrypt (YR1)**,
|
||||
gültig bis **2026-11-12** — echtes LE-Cert über DNS-01.
|
||||
- **Produktiv-Resolver** `letsencrypt` steht auf `dnschallenge=true, provider=ionos` und
|
||||
erneuert damit auch `rohana`/`selendis` → die September-Erneuerung braucht **kein offenes
|
||||
Port 443** mehr; Zeitkritikalität ab 2026-09-28 entfällt.
|
||||
- Test-Container entfernt; git.lab-Tracker-Issue 7 geschlossen (Parallel-Session).
|
||||
|
||||
**Rest-Hinweis:** Die September-Erneuerung ist der erste *unbeaufsichtigte* DNS-01-Lauf für
|
||||
die Bestandszertifikate. Nach dem heutigen Test spricht nichts dagegen; wer ganz sichergehen
|
||||
will, wirft Mitte September einmal kurz einen Blick ins Traefik-Log.
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0008"
|
||||
status: waiting
|
||||
status: done
|
||||
created: 2026-08-01
|
||||
milestone: M1
|
||||
priority: medium
|
||||
host: cfgmon
|
||||
area: security
|
||||
wartegrund: Grund im GitLab-Verlauf benannt (Import 2026-08-11); im nächsten Refinement präzisieren
|
||||
gitlab_iid: "8"
|
||||
related: []
|
||||
---
|
||||
@@ -32,3 +31,151 @@ Quelle: [hosts/cfgmon.md](https://git.lab/axion1337.chat/management/-/blob/main/
|
||||
|
||||
---
|
||||
*Übernommen aus dem Backlogs-Markdown beim Framework-Umbau 2026-08-01 (voller Wortlaut: Git-Historie der Datei).*
|
||||
|
||||
## Nachgeprüft 2026-08-15 — akute Exposition besteht nicht
|
||||
|
||||
Messung vom Mac gegen die öffentliche CFGMON-IP: **9090 (Prometheus remote-write) und 3100
|
||||
(Loki) antworten nicht**, während `:443` sofort antwortet (Gegenprobe, Messmethode also
|
||||
gültig). Das bestätigt die im Issue vermerkte Beobachtung: die Ports sind zu, **ohne** dass
|
||||
der Console-Klick je gemacht wurde.
|
||||
|
||||
Damit verschiebt sich der Charakter des Issues: Es geht **nicht mehr um akutes Risiko**,
|
||||
sondern darum, dass der Ist-Zustand nicht bewusst hergestellt und nicht dokumentiert ist —
|
||||
was heute zufällig zu ist, kann bei der nächsten Änderung unbemerkt aufgehen. Zu klären
|
||||
bleibt: welche Regeln greifen tatsächlich, und trägt das den GAME-Push (#0002) nach dessen
|
||||
vSwitch-Umzug noch?
|
||||
|
||||
## Wächter gebaut 2026-08-19 — der Ist-Zustand hat jetzt eine Instanz, die ihn hält
|
||||
|
||||
Aus dem Befund vom 15.08. („zu, aber nicht bewusst hergestellt und nicht
|
||||
dokumentiert") folgt genau ein wirksamer Schritt, und er ist getan:
|
||||
`notfallhandbuch:pruefe-ports.sh` plus `ports-soll.md`, nach dem Muster von
|
||||
`pruefe-dns.sh`.
|
||||
|
||||
**Gemessen, nicht abgeleitet** (2026-08-19, von sorbs Mac):
|
||||
|
||||
| Host | offen | zu |
|
||||
|---|---|---|
|
||||
| Matrix `49.13.132.245` | 80, 443, 3478, 5349, 30001, 2248 | 22 |
|
||||
| CFGMON `188.245.193.243` | 80, 443 | **9090, 3100**, 22 |
|
||||
|
||||
Das Skript prüft **beide Richtungen** — eine reine Erreichbarkeitsprüfung würde eine
|
||||
öffentlich stehende Telemetrie nie bemerken. Vor und nach jeder Firewall-Änderung
|
||||
laufen lassen; damit ist auch die Frage aus dem Issue beantwortbar, ob der GAME-Push
|
||||
(#0002) nach dem vSwitch-Umzug noch trägt: einmal vorher, einmal nachher.
|
||||
|
||||
**Die Gegenprobe ist der Kern, nicht die Zierde.** In einem Netz, das ausgehende
|
||||
Verbindungen filtert, meldet jede Portprüfung „alles zu" — was wie ein perfektes
|
||||
Ergebnis aussieht. Beim Bau real passiert: Die erste Fassung nutzte `/dev/tcp`, das
|
||||
die zsh nicht kennt, und meldete **jeden** Port als geschlossen, 443 eingeschlossen.
|
||||
Das Skript bricht deshalb mit Exit 2 ab, wenn ein bekannt offener Port nicht antwortet.
|
||||
|
||||
**UDP bleibt bewusst außen vor** (TURN 3478, SFU 30002): Ohne Antwort sind
|
||||
„gefiltert" und „offen, aber still" nicht zu trennen. Diese Strecken belegt nur ein
|
||||
echter Gruppen-Call.
|
||||
|
||||
**Weiterhin offen — der Teil, der sorb braucht:** der gemeinsame Blick in die
|
||||
Hetzner-Console. Das Skript sagt, *was* von außen erreichbar ist; welche Regeln das
|
||||
bewirken und ob sie bewusst so stehen, sagt nur die Console. Erst danach ist das
|
||||
Issue erledigt.
|
||||
|
||||
## Console-Einblick 2026-08-19 — Matrix-Host gesehen, CFGMON weiterhin nicht
|
||||
|
||||
sorb hat die Hetzner-Console gezeigt: **`fw-matrix-cx42`, 15 Regeln, vollständig
|
||||
angewandt.** Das ist der **Matrix-Host**, nicht CFGMON — die Frage dieses Issues
|
||||
(welche Regel hält 9090/3100 auf CFGMON zu?) ist damit **noch offen**. Gemessen sind
|
||||
beide Ports zu; *warum*, weiß weiterhin niemand.
|
||||
|
||||
Der Einblick war trotzdem wertvoll, in zwei Richtungen.
|
||||
|
||||
**Sauber gelöst:** SSH (2248) und die Kubernetes-API (6443) sind quellbeschränkt auf
|
||||
sorbs Anschluss (`178.25.213.70`, `2a02:8108:0:2f::/64`), nicht für alle offen. Das hat
|
||||
auch einen Fehler in `pruefe-ports.sh` aufgedeckt: Die Liste führte 2248 schlicht als
|
||||
„offen", womit das Skript aus jedem anderen Netz zwei Falschbefunde gemeldet hätte.
|
||||
Behoben — beide gelten jetzt als `quellbeschraenkt` und werden berichtet, aber nicht
|
||||
gewertet.
|
||||
|
||||
**Die Firewall ist deutlich weiter offen als die Plattform.** Von außen gemessen
|
||||
antwortet auf diesen Regeln nichts:
|
||||
|
||||
| Regel | Befund |
|
||||
|---|---|
|
||||
| `smtp` TCP **587 eingehend**, Any | **Vermutlich versehentlich.** Der Host verschickt Mail *ausgehend*; eingehende Regeln helfen dabei nicht (Hetzner filtert nur eingehend). Der Deployment-Guide dokumentiert genau dieses Debugging („465 blockiert, 587 geht") — die Regel sieht nach dessen Rest aus. |
|
||||
| `mRTC SFU muxed` UDP **30000–65535** | ~35.500 Ports für einen benötigten (30002). |
|
||||
| `mRTC SFU TCP` TCP **30000–60000** | ~30.000 Ports für einen benötigten (30001). |
|
||||
| `RTC SFU 1` 6789, `RTC SFU 2` 7880, `mRTC Auth` 8080, je Any | nichts lauscht; die Dienste laufen über den Ingress auf 443. |
|
||||
|
||||
Kein akutes Risiko — aber eine unnötig breite *erklärte* Angriffsfläche: Sobald dort je
|
||||
etwas horcht, ist es sofort weltweit erreichbar, ohne dass jemand eine Regel anfassen
|
||||
müsste. Festgehalten in `notfallhandbuch:ports-soll.md`.
|
||||
|
||||
**Zum Abschluss dieses Issues fehlt weiterhin:** der Blick auf die **CFGMON**-Firewall.
|
||||
|
||||
## Frage beantwortet 2026-08-19 — und ein größerer Fund daneben
|
||||
|
||||
sorb hat `fw-matrix-cx23` gezeigt, die **CFGMON**-Firewall. Die Antwort auf die Frage
|
||||
dieses Issues ist einfach: **Es gibt für 9090 und 3100 gar keine Regel.**
|
||||
Hetzner-Firewalls sind eingehend Default-Deny — die Ports sind also nicht „zugemacht
|
||||
worden", sie waren **nie offen**. Das deckt sich exakt mit der Beobachtung vom
|
||||
2026-08-01, dass sie zu waren, ohne dass jemand den Console-Klick gemacht hatte.
|
||||
|
||||
„Weg A" (Firewall-Regel, die 9090/3100 auf bekannte Absender beschränkt) ist damit
|
||||
**gegenstandslos für den öffentlichen Weg**. Die privaten Absender (k3s/Matrix über das
|
||||
Hetzner-Netz) laufen an der Cloud-Firewall vorbei, die nur öffentlichen Verkehr filtert.
|
||||
|
||||
Sauber gelöst ist der Rest von CFGMON: SSH (2248), Grafana (3000), Coolify (9001,
|
||||
8000–8001) und WireGuard (51841) sind quellbeschränkt auf sorbs Anschluss; cadvisor
|
||||
(8080) und node-exporter (9100) nur für `157.90.155.206`.
|
||||
|
||||
### ~~Offener rekursiver DNS-Resolver auf CFGMON~~ — WIDERRUFEN am selben Tag
|
||||
|
||||
**Der Befund war falsch.** Ich hatte gemeldet, `188.245.193.243:53` löse für jeden im
|
||||
Internet rekursiv auf, samt Amplifikations-Rechnung. sorbs Rückfrage („dir ist schon
|
||||
klar, dass beide Netze gerade per VPN verbunden sind?") hat es aufgeklärt.
|
||||
|
||||
Nachgemessen **aus dem Cluster** (Hetzner-Egress, weder Lab noch Standortkopplung):
|
||||
|
||||
| Port | von sorbs Mac | aus dem Cluster |
|
||||
|---|---|---|
|
||||
| 443 (Gegenprobe) | offen | **offen** — Methode gültig |
|
||||
| 53 | offen, rekursiv | **zu** |
|
||||
| 9001 (nur sorbs Anschluss) | offen | **zu** |
|
||||
|
||||
Öffentlich ist 53 also **geschlossen**. Die Firewall-Regel (Quelle `10.58.73.17` =
|
||||
Dokploy/git) ist in Ordnung; Verkehr über die Standortkopplung filtert die
|
||||
Cloud-Firewall nicht, deshalb antwortet der Dienst aus dem Lab. Genau so gedacht.
|
||||
|
||||
**Die eigentliche Lehre betrifft mein Werkzeug, nicht die Infrastruktur.** Die
|
||||
Gegenprobe in `pruefe-ports.sh` beweist, dass die *Messmethode* funktioniert — sie
|
||||
beweist **nicht**, dass man von *außen* misst. Zwei verschiedene Fragen, und ich hatte
|
||||
nur eine beantwortet. Das Skript führt jetzt vor allem anderen eine **Standort-Probe**
|
||||
aus (ein Port, den die Firewall nur für sorbs Anschluss freigibt): Antwortet er, werden
|
||||
alle „soll zu"-Prüfungen berichtet, aber **nicht gewertet**. Für diese Hälfte muss das
|
||||
Skript aus einem fremden Netz laufen.
|
||||
|
||||
Zweiter Fehlalarm binnen zweier Tage aus derselben Wurzel — erst `/dev/tcp` in der zsh,
|
||||
das jeden Port als geschlossen meldete, jetzt der Standort. Beide Male sah das Ergebnis
|
||||
plausibel aus.
|
||||
|
||||
## Erledigt 2026-08-19 — vorbehaltlich (Entscheidung sorb)
|
||||
|
||||
Beide Fragen des Issues sind beantwortet:
|
||||
|
||||
**„Welche Regeln greifen tatsächlich?"** — Für 9090 und 3100 gibt es auf CFGMON **keine
|
||||
Regel**. Hetzner filtert eingehend Default-Deny; die Ports waren nie offen. „Weg A"
|
||||
(Freigabe auf bekannte Absender beschränken) ist damit gegenstandslos, es gab nie etwas
|
||||
zu beschränken. Der Rest der Firewall ist quellbeschränkt und sauber.
|
||||
|
||||
**„Ist der Zustand dokumentiert und gehalten?"** — Ja: `notfallhandbuch:ports-soll.md`
|
||||
plus `pruefe-ports.sh`, beide Richtungen prüfend, mit Gegenprobe und Standort-Probe.
|
||||
|
||||
**Der Vorbehalt:** Die „muss zu sein"-Hälfte ist aus sorbs Netz **nicht beweisbar** —
|
||||
dort besteht über die Standortkopplung privilegierter Zugang. Das Skript weist das
|
||||
ehrlich aus, statt falsches Grün zu liefern. Ein Lauf aus einem fremden Netz würde die
|
||||
Lücke schließen; sorb hat das am 2026-08-19 als übertrieben verworfen, und das ist
|
||||
vertretbar: Gemessen ist der Zustand gut, die Regeln sind gesehen, und für den einen
|
||||
Fall, der wirklich zählt — eine Firewall-Änderung an einem „muss zu"-Port — steht der
|
||||
Hinweis in `ports-soll.md`.
|
||||
|
||||
**Offen bleibt an anderer Stelle:** Ob der GAME-Push nach dem vSwitch-Umzug noch trägt,
|
||||
entscheidet sich in **#0002**, nicht hier.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0010"
|
||||
status: open
|
||||
status: rejected
|
||||
created: 2026-08-01
|
||||
milestone: M1
|
||||
priority: medium
|
||||
@@ -32,3 +32,62 @@ Quelle: [hosts/cfgmon.md](https://git.lab/axion1337.chat/management/-/blob/main/
|
||||
|
||||
---
|
||||
*Übernommen aus dem Backlogs-Markdown beim Framework-Umbau 2026-08-01 (voller Wortlaut: Git-Historie der Datei).*
|
||||
|
||||
## In Arbeit 2026-08-14 — Muster existiert bereits (Aufwand deutlich kleiner als geplant)
|
||||
|
||||
Bei der Bestandsaufnahme für #0030 zeigte sich: **Borg auf Hetzner Storage Box läuft im
|
||||
Matrix-Cluster bereits produktiv** — der für dieses Issue geplante Aufbau muss also nicht
|
||||
erfunden, sondern nur auf Gitea übertragen werden.
|
||||
|
||||
**Vorhandenes Muster (erprobt, nächtlich, verifiziert):**
|
||||
- Storage Box `u641795@u641795.your-storagebox.de:23`, drei getrennte Repos
|
||||
(`/./synapse-backup`, `/./authentik-backup`, `/./wikijs-backup`).
|
||||
- Image `rohana.axion1337.de/sorb/axion-backup:v2`; Retention `borg prune` 7d/4w/6m.
|
||||
- Credentials (Borg-Passphrase + SSH-Key) als SOPS-Secret, per Env in den Job.
|
||||
|
||||
**Konsequenz für Gitea:** kein neuer Storage-Box-Vertrag nötig — ein zusätzliches Repo
|
||||
`/./gitea-backup` auf derselben Box (oder ein Sub-Account) genügt. Der Host braucht weiterhin
|
||||
einen eigenen SSH-Key (CFGMON ist **nicht** der Cluster), dessen Public Key in der Storage Box
|
||||
hinterlegt wird. Wie im Issue vorgesehen: Dump **unkomprimiert** an Borg geben (`gzip` aus
|
||||
`backup/gitea-backup.sh` entfernen), sonst greift die Deduplizierung nicht.
|
||||
|
||||
⚠️ **Unverändert akut:** Der nächtliche Cron auf CFGMON ist seit 2026-07-30 auskommentiert —
|
||||
seit über zwei Wochen läuft **kein** Gitea-Backup, der letzte Stand ist
|
||||
`/opt/backup/gitea-dump-2026-07-30.tar.gz`. Das ist unabhängig von der Borg-Umstellung sofort
|
||||
behebbar (Cron wieder einkommentieren) und sollte **nicht** auf das Off-host-Projekt warten.
|
||||
|
||||
⚠️ **Vorrang laut #0030:** Bevor Backups erweitert werden, muss die kalte Kopie des
|
||||
age-Schlüssels stehen (dort dokumentierte Zirkelabhängigkeit) — sonst wächst nur die Menge
|
||||
potenziell unlesbarer Sicherungen.
|
||||
|
||||
**Offen (nur mit Host-Zugang, kein SSH zu CFGMON von hier):** Cron reaktivieren; SSH-Key auf
|
||||
CFGMON erzeugen + in der Storage Box hinterlegen; `gitea-backup.sh` auf Borg umstellen.
|
||||
|
||||
## Verworfen 2026-08-14 — Gitea braucht kein eigenes Backup (Entscheidung sorb)
|
||||
|
||||
**Begründung:** Gitea auf rohana ist **Push-Mirror**, nicht Quelle. Kanonisch ist git.lab
|
||||
(ADR-0001); alle gespiegelten Repos lassen sich nach einem Totalverlust schlicht neu
|
||||
befüllen. Die Issues liegen seit der Migration ohnehin auf git.lab, der Gitea-Tracker ist
|
||||
leer. Ein nächtlicher `gitea dump` sichert damit im Wesentlichen eine Kopie — der Aufwand
|
||||
(Borg-Repo, Host-SSH-Key, Script-Umbau) steht in keinem Verhältnis.
|
||||
|
||||
Der auskommentierte Cron bleibt entsprechend aus; die Platte (73 % voll) wird nicht weiter
|
||||
belastet, der Altstand `/opt/backup/gitea-dump-2026-07-30.tar.gz` kann weg.
|
||||
|
||||
### Dokumentierte Nuance: was auf rohana *kein* Spiegel ist
|
||||
|
||||
Nicht aus git.lab wiederherstellbar sind die **Gitea-Packages (Container-Registry)** — der
|
||||
Cluster zieht vier Images ausschließlich von dort:
|
||||
|
||||
| Image | Rolle |
|
||||
|---|---|
|
||||
| `threadnet-web:v0.4.3` | Element-Web-Fork, den die Nutzer im Browser laden |
|
||||
| `axion-backup:v2` | führt die drei nächtlichen Borg-Backups aus (7 Pods) |
|
||||
| `axion-secret-rotation:v1` | TURN-Secret-Rotation |
|
||||
| `clamav-http-scanner:v1.0.0` | client-seitiges Scannen |
|
||||
|
||||
Bei Verlust von rohana laufende Pods weiter, aber **neue Pods können nicht mehr pullen**, bis
|
||||
die Images neu gebaut sind — und Flux verliert zugleich seine Source. Das ist **kein
|
||||
Datenverlust** (alle vier sind aus dem Quellcode reproduzierbar: Dockerfiles in gitops bzw.
|
||||
den Fork-Repos), sondern **Wiederherstellungszeit**. Bewusst akzeptiert; die Reproduzierbarkeit
|
||||
des Build-Wegs ist ohnehin über #0022/#0033 adressiert.
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0014"
|
||||
status: waiting
|
||||
status: open
|
||||
created: 2026-08-01
|
||||
milestone: M2
|
||||
priority: low
|
||||
host: cfgmon
|
||||
area: security
|
||||
wartegrund: Grund im GitLab-Verlauf benannt (Import 2026-08-11); im nächsten Refinement präzisieren
|
||||
gitlab_iid: "14"
|
||||
related: []
|
||||
related: [docs/adr/0008-agenten-sessions-root-aequivalent.md]
|
||||
---
|
||||
# CFGMON-14: Root-Zugang über die docker-Gruppe umgeht sudo und hinterlässt keine Spur
|
||||
|
||||
@@ -27,3 +26,19 @@ Aus dem [CFGMON-AAR](https://git.lab/axion1337.chat/management/-/blob/main/verfa
|
||||
- **C — getrenntes Konto** für Agenten-Sessions mit definierter, protokollierter Rechteerhöhung
|
||||
|
||||
Vor einer Entscheidung zu klären: Welche anderen Konten sind in der docker-Gruppe, und gilt dasselbe auf MATRIX?
|
||||
|
||||
## Entscheidung liegt vor (ADR-0008) — Restaufgabe 2026-08-15
|
||||
|
||||
Die im Issue offene Wahl zwischen A/B/C ist **entschieden**: **ADR-0008** (2026-08-06,
|
||||
Struktur-Workshop) wählt ausdrücklich **Option A** — Agenten-Sessions laufen auf CFGMON
|
||||
root-äquivalent über die docker-Gruppe, und „sudo mit Passwort" gilt dort ausdrücklich
|
||||
**nicht** als Kontrollmechanismus. Option C bleibt Ziel, aber erst wenn ein zweiter Mensch
|
||||
mitarbeitet. Das Issue wartet also auf nichts mehr → `open` statt `waiting`.
|
||||
|
||||
**Von der Vorfrage ist eine Hälfte beantwortet:** *„Gilt dasselbe auf MATRIX?"* → **Nein.**
|
||||
`getent group docker` auf MATRIX liefert nichts — dort läuft k3s ohne Docker-Daemon, die
|
||||
Root-Äquivalenz über die docker-Gruppe existiert also gar nicht. (Geprüft 2026-08-15 per SSH.)
|
||||
|
||||
**Rest:** Auf **CFGMON** prüfen, welche Konten in der docker-Gruppe sind (`getent group docker`)
|
||||
und ob sie alle dorthin gehören — braucht eine Host-Session, vom Mac nicht einsehbar. Danach
|
||||
kann das Issue geschlossen werden; die Entscheidung selbst steht in ADR-0008.
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0015"
|
||||
status: next
|
||||
status: waiting
|
||||
created: 2026-08-01
|
||||
milestone: M2
|
||||
priority: medium
|
||||
due: 2026-08-31
|
||||
wartegrund: "Entscheidung sorb 2026-08-15: Rotation erfolgt gebuendelt EINMAL bei der Abnahme der Plattform, nicht stueckweise vorher. Die Bestandsaufnahme (24 PATs, 6 Mirrors) liegt vor, die Reihenfolge steht — es fehlt nur der Ausloeser."
|
||||
host: cfgmon
|
||||
area: security
|
||||
gitlab_iid: "15"
|
||||
related: []
|
||||
related: [docs/issues/0027-audit-01-acht-widersprueche-aus-dem-labnet-02.md]
|
||||
---
|
||||
# CFGMON-15: Token-Hygiene — Einmal-Tokens der LABNET-02-Nacht widerrufen
|
||||
|
||||
@@ -22,3 +23,119 @@ Während der Arbeit entstanden **vier Einmal-Tokens** für Issue-Kommentare/Push
|
||||
**Zu tun:** Bestand aufnehmen (Gitea-Access-Tokens + GitLab-PATs), nicht mehr benötigte widerrufen, verbleibende mit Ablaufdatum und sprechendem Namen versehen.
|
||||
|
||||
⚠️ **Randbedingung aus der Mirror-Diskussion:** Welcher Token in den Push-Mirrors der fünf gespiegelten Repos hinterlegt ist, ist derzeit **nicht rekonstruierbar** (die API maskiert ihn, in keiner Session dokumentiert). Ein Widerruf kann deshalb still einen Mirror brechen. Vor der Rotation: entweder die Mirror-Credentials bewusst neu setzen, oder nach dem Widerruf jeden Mirror-Status einmal prüfen (`GET /projects/<id>/remote_mirrors` → `last_error`).
|
||||
|
||||
## Bestandsaufnahme 2026-08-15
|
||||
|
||||
### git.lab-PATs: 24 Stück, davon 13 aktiv
|
||||
|
||||
Abgefragt über `GET /personal_access_tokens` — **nur Metadaten, keine Werte** (die gibt die API
|
||||
ohnehin nur bei der Erzeugung heraus). `last_used_at` ist dabei der eigentliche Hebel: er
|
||||
trennt „wird gebraucht" von „liegt herum".
|
||||
|
||||
**Aktiv und nachweislich in Gebrauch — NICHT anfassen:**
|
||||
|
||||
| id | Name | Scopes | zuletzt genutzt |
|
||||
|---|---|---|---|
|
||||
| 6 | gitlab claude token | api | 2026-08-15 (heute — Session-Zugang) |
|
||||
| 24 | wiki-canonize | write_repository | 2026-08-15 (CI-Kanonisierung) |
|
||||
| 8 | Gitea-push-token | api, write_registry | 2026-08-14 |
|
||||
| 19 | management#31 | read_api | 2026-08-14 |
|
||||
|
||||
**Aktiv, aber NIE benutzt — die eigentliche Hygiene-Baustelle:**
|
||||
|
||||
| id | Name | Scopes | angelegt |
|
||||
|---|---|---|---|
|
||||
| 18 | oskar light | api, read_api, **manage_runner, k8s** | 2026-08-04 |
|
||||
| 9 | registry-cleanup | api, read/write_registry | 2026-08-03 |
|
||||
| 11 | herold-hive | read/write_repository | 2026-08-04 |
|
||||
| 15 | oskar read | read_service_ping, read_user, … | 2026-08-04 |
|
||||
| 23 | wiki-canonize | write_repository | 2026-08-13 — **Dublette zu id=24** |
|
||||
|
||||
**Aktiv, einmal benutzt, seither still:** id=4 `tabby` (27.07.), id=10 `SORB API` (04.08.),
|
||||
id=13 `herold` (04.08.), id=17 `oskar analyse` (09.08.).
|
||||
|
||||
**Bereits inaktiv (nichts zu tun):** ids 1, 2, 3, 5, 7, 12, 14, 16, 20, 21, 22.
|
||||
|
||||
Auffällig ist ein Muster: mehrere Namen existieren **doppelt**, einmal aktiv und einmal
|
||||
inaktiv (`herold`, `oskar read`, `oskar analyse`, `wiki-canonize`) — offenbar wurde jeweils
|
||||
neu erzeugt, ohne das alte zu widerrufen. Genau daraus entsteht der Wildwuchs, den dieses
|
||||
Issue adressiert.
|
||||
|
||||
### Push-Mirrors: alle gesund, Konto aber weiterhin unbekannt
|
||||
|
||||
Sechs Mirrors, **alle `finished`, kein `last_error`** (Stand 2026-08-15): ThreadNet-Web,
|
||||
gitops, thread-net-git, threadnet-call, threadnet-operating, management.
|
||||
|
||||
⚠️ Die Warnung des Issues bestätigt sich: GitLab maskiert in `remote_mirrors` **beide** Teile
|
||||
der URL (`https://*****:*****@rohana…`). Es ist also **weder Konto noch Token rekonstruierbar**.
|
||||
Und: die Mirrors authentifizieren sich gegen **Gitea**, das gesuchte Credential ist damit ein
|
||||
**Gitea**-Token, kein git.lab-PAT — die PAT-Liste oben kann die Frage gar nicht beantworten.
|
||||
|
||||
*(Nebenbefund: `gameserver` hat wirklich keinen Mirror → bestätigt #0032. `notfallhandbuch`
|
||||
bewusst nicht → ADR-0016. `threadnet-wiki` braucht keinen, es läuft in Gegenrichtung.)*
|
||||
|
||||
### Empfehlung — Reihenfolge zählt
|
||||
|
||||
1. **Mirror-Credential bewusst neu setzen, bevor irgendetwas widerrufen wird.** Ein
|
||||
dedizierter Gitea-Token (Name z.B. `gitlab-mirror`, Scope `write:repository`, mit
|
||||
Ablaufdatum), auf allen sechs Mirrors hinterlegt. Danach ist bekannt und dokumentiert,
|
||||
woran sie hängen — die Unklarheit verschwindet, statt umschifft zu werden.
|
||||
2. **Erst dann Gitea-Alttokens widerrufen**, anschließend alle sechs Mirrors einmal prüfen
|
||||
(`GET /projects/<id>/remote_mirrors` → `last_error`).
|
||||
3. **git.lab-PATs aufräumen:** die fünf nie benutzten zuerst (id 18, 9, 11, 15, 23) — id=18
|
||||
ist wegen `manage_runner`+`k8s` der unangenehmste Fund. Danach die vier Einmal-Nutzer
|
||||
(4, 10, 13, 17), sofern die zugehörigen Werkzeuge nicht mehr laufen.
|
||||
4. **Verbleibende** mit sprechendem Namen **und Ablaufdatum** versehen — mehrere laufen Ende
|
||||
August/Anfang September ohnehin aus, das ist der natürliche Zeitpunkt.
|
||||
|
||||
**Die Widerrufe selbst gehören sorb** (Credentials legt/entfernt der Mensch); ich habe
|
||||
bewusst nichts widerrufen — bei Namen wie `herold`, `oskar`, `tabby` ist von hier nicht
|
||||
erkennbar, ob dahinter ein laufendes Werkzeug steht. `last_used_at = None` heißt „nie
|
||||
benutzt", nicht „nicht gebraucht": ein hinterlegter, aber noch nicht ausgelöster Token sieht
|
||||
genauso aus.
|
||||
|
||||
### Bezug zu #0027 (W4/W5)
|
||||
|
||||
- **W5 aufgelöst:** Die Secrets-Regel in `AGENTS.md` hat jetzt eine **Bootstrap-Ausnahme** —
|
||||
auf einem Host, wo sorb die Datei nicht selbst anlegen kann, darf eine Session das
|
||||
Credential schreiben, aber es gilt als exponiert (Rotationspflicht), wird mit Pfad+Zweck
|
||||
festgehalten und bekommt ein fälliges Issue. Damit ist die Regel erfüllbar, ohne sie zu
|
||||
brechen.
|
||||
- **W4 Punkt 5** (Rotation der in der LABNET-02-Nacht exponierten Werte) ist inhaltlich
|
||||
**dieses Issue** — der WG-Private-Key gehört ausdrücklich dazu und ist oben **nicht**
|
||||
abgedeckt (er ist kein API-Token): separat rotieren.
|
||||
- **W4 Punkt 4** (Repo-Zuhause für `lab.conf`, systemd-Drop-in, Root-CA) ist **nachweislich
|
||||
offen**: `gitops:host-config/` existiert als Muster, enthält aber nur `maintenance-notify`.
|
||||
Die Schließung von #16 war für diesen Punkt also verfrüht.
|
||||
|
||||
## Entscheidung 2026-08-15 — gebündelte Rotation bei der Abnahme
|
||||
|
||||
sorb: **Alle Tokens werden einmal gemeinsam rotiert, wenn die Plattform abgenommen ist** —
|
||||
nicht jetzt stückweise.
|
||||
|
||||
Das ist die risikoärmere Reihenfolge: Ein Widerruf mitten im Betrieb kann still einen der
|
||||
sechs Push-Mirrors brechen (das Mirror-Credential ist nach wie vor nicht rekonstruierbar,
|
||||
s.o.), und die Mirrors beliefern unter anderem die **Flux-Quelle**. Einmal koordiniert
|
||||
rotieren, mit allen Beteiligten am Tisch, ist sauberer als vier Einzeleingriffe mit je
|
||||
eigener Bruchstelle.
|
||||
|
||||
**Die Vorarbeit bleibt gültig und macht die spätere Rotation kurz:** Bestand der 24 PATs
|
||||
(inkl. der fünf nie benutzten und der Dubletten), Zustand aller sechs Mirrors, und die
|
||||
festgelegte Reihenfolge — erst dediziertes Mirror-Credential setzen, dann widerrufen, dann
|
||||
Mirror-Status prüfen. Beim Abnahmetermin ist das abzuarbeiten, nicht neu zu erarbeiten.
|
||||
|
||||
⚠️ **Was mit der Vertagung bewusst in Kauf genommen wird** (gehört benannt, nicht
|
||||
stillschweigend):
|
||||
|
||||
- Die in der LABNET-02-Nacht exponierten Werte — **WireGuard-Private-Key** und die beiden
|
||||
git.lab-PATs — bleiben bis zur Abnahme gültig. Nach der Secrets-Regel („anzeigen =
|
||||
Exposure = Rotation") ist das eine **bewusste Ausnahme**, kein Versehen.
|
||||
- Fünf aktive, nie benutzte Tokens bleiben bestehen, darunter id=18 (`oskar light`) mit
|
||||
`manage_runner` + `k8s` — der breiteste Scope im Bestand.
|
||||
- **„Abnahme" ist kein datierter Meilenstein.** Genau so bleibt Sicherheitsarbeit liegen:
|
||||
vertagt auf ein Ereignis ohne Datum. Das `due` dieses Issues (2026-08-31) bleibt deshalb
|
||||
stehen — nicht als Rotationsfrist, sondern als **Wiedervorlage**: Ist die Abnahme dann
|
||||
nicht in Sicht, wird die Vertagung neu bewertet statt weiterzulaufen.
|
||||
|
||||
Wenn die Vertagung dauerhaft gelten soll, gehört sie nach dem Framework in einen ADR
|
||||
(bewusste Ausnahme von der Secrets-Regel) — bislang steht sie nur hier.
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0018"
|
||||
status: open
|
||||
status: rejected
|
||||
created: 2026-08-01
|
||||
milestone: M2
|
||||
priority: low
|
||||
area: infrastructure
|
||||
gitlab_iid: "18"
|
||||
related: []
|
||||
related: [docs/adr/0014-wikijs-loest-docusaurus-ab.md]
|
||||
---
|
||||
# DOC-01: Wiki-Rollout abschließen — CI-Freigaben, Zeitplan, Dokploy-Stack, wiki.lab
|
||||
|
||||
@@ -23,3 +23,25 @@ Das Wiki-Repo [`homelab/wiki`](https://git.lab/homelab/wiki) steht ([ADR-0006](h
|
||||
6. Danach: Link auf wiki.lab in den README der Quell-Repos gegenprüfen (gitops ist erledigt).
|
||||
|
||||
**Verifikation:** `https://wiki.lab` zeigt die drei Bereiche; eine Änderung in einer Quelle ist nach dem nächsten geplanten Lauf sichtbar.
|
||||
|
||||
## Verworfen 2026-08-15 — gegenstandslos
|
||||
|
||||
**`wiki.lab` existiert nicht mehr; die Lesefläche ist in den Stack gewandert** (Bestätigung
|
||||
sorb). Alle sechs Schritte dieses Issues bauen auf dem Docusaurus-Aggregat auf, das
|
||||
**ADR-0014** abgelöst hat: *„Wiki.js löst Docusaurus als Plattform-Wiki ab"* und
|
||||
*„das alte Docusaurus-Prinzip entfällt"* — editiert wird jetzt in Wiki.js unter
|
||||
`wiki.axion1337.chat`, statt Quell-Repos read-only zu aggregieren.
|
||||
|
||||
**Gemessen 2026-08-15:** `wiki.lab` löst zwar noch auf (`10.58.73.17`, Lab-DNS), antwortet
|
||||
aber nicht (HTTP 000) — der Dokploy-Stack aus Schritt 4 wurde nie deployt. Das Issue
|
||||
beschreibt also einen Aufbau, den niemand mehr will und der nie lief.
|
||||
|
||||
**Erledigter Teil davon:** Schritt 6 (Verweise gegenprüfen) war real offen —
|
||||
`gitops:AGENTS.md` behauptete weiterhin, die Doku werde als Docusaurus-Seite auf `wiki.lab`
|
||||
ausgeliefert. Korrigiert in gitops `82412cf`. Doku, die auf etwas Totes zeigt, ist schlimmer
|
||||
als keine: sie schickt die nächste Session auf die Suche nach einem abgeschalteten Dienst.
|
||||
|
||||
⚠️ **Nicht entschieden, gehört sorb:** `homelab/docs` ist laut ADR-0014 **bewusst nicht** Teil
|
||||
der Plattform und hat damit keine Lesefläche mehr. Ob dafür eine gebraucht wird, ist eine
|
||||
Lab-Frage. Ebenso, ob **ADR-0006** (Docusaurus als gemeinsame Lesefläche) auf `superseded`
|
||||
gesetzt werden sollte — ADR-0014 hat nur ADR-0007 abgelöst.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0020"
|
||||
status: next
|
||||
status: done
|
||||
created: 2026-08-02
|
||||
milestone: M2
|
||||
priority: medium
|
||||
@@ -14,6 +14,17 @@ related: []
|
||||
|
||||
> Import aus [management#20](https://git.lab/axion1337.chat/management/-/issues/20) (2026-08-11). Kommentare und Verlauf bleiben dort; kanonisch ist ab jetzt diese Datei (ADR-0012).
|
||||
|
||||
## Entscheidung (2026-08-12, sorb)
|
||||
|
||||
**Docusaurus bleibt** — für jetzt. Zugang wird per Authentik-Forward-Auth
|
||||
eingeschränkt (kein Bereichs-Schutz nötig), Konfiguration vorbereitet in gitops
|
||||
`docs/deployment-guides/09-wiki-forward-auth.md`; Hostname `axionwiki.lab` (#0024).
|
||||
|
||||
Bewusst **kein Schlussstrich unter die Oberflächenfrage**: Die Entscheidung gilt
|
||||
für den Entwicklungs-Zwischenstand. Nach dem Umzug in die ThreadNet Server Suite
|
||||
(#0046) werden Alternativen **über BookStack hinaus** neu geprüft (#0047) — ADR-0007
|
||||
bleibt insofern nicht das letzte Wort, sondern der bisherige Stand.
|
||||
|
||||
Zwei Varianten stehen nebeneinander, damit an echten Inhalten entschieden wird statt am Reißbrett ([ADR-0007](https://git.lab/axion1337.chat/management/-/blob/main/decisions/0007-wiki-oberflaeche-docusaurus-vs-bookstack.md)):
|
||||
|
||||
| Variante | Stand | Repo |
|
||||
|
||||
@@ -6,7 +6,7 @@ created: 2026-08-02
|
||||
milestone: M2
|
||||
priority: medium
|
||||
area: infrastructure
|
||||
wartegrund: Grund im GitLab-Verlauf benannt (Import 2026-08-11); im nächsten Refinement präzisieren
|
||||
wartegrund: "Wartet auf Go von sorb für Variante C (Fehlermeldung des CI-Jobs um den Hinweis 'Stack in Dokploy neu deployen' ergänzen); Variante A erst, wenn der Fall ein drittes Mal auftritt."
|
||||
gitlab_iid: "21"
|
||||
related: []
|
||||
---
|
||||
@@ -24,3 +24,10 @@ sorb hat ihn manuell neu gestartet, danach lief der Build. Der Fall wiederholt s
|
||||
- **C** — so lassen, aber die Fehlermeldung im Job um den Hinweis „Stack in Dokploy neu deployen" ergänzen (billigste Variante).
|
||||
|
||||
Empfehlung: **C jetzt, A wenn es ein drittes Mal passiert.**
|
||||
|
||||
## Präzisierung 2026-08-15
|
||||
|
||||
Kein technischer Blocker — das Issue enthält bereits die Empfehlung (**C jetzt, A beim dritten
|
||||
Auftreten**) und wartet nur auf das Go. C ist ein Einzeiler in der Job-Definition und macht den
|
||||
Fall selbsterklärend, statt die nächste Session wieder raten zu lassen. Bislang ist der Fall
|
||||
**einmal** aufgetreten (2026-08-02, Job 498).
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0023"
|
||||
status: open
|
||||
status: rejected
|
||||
created: 2026-08-02
|
||||
milestone: M2
|
||||
priority: low
|
||||
area: infrastructure
|
||||
gitlab_iid: "23"
|
||||
related: []
|
||||
related: [docs/adr/0014-wikijs-loest-docusaurus-ab.md]
|
||||
---
|
||||
# DOC-04: Navbar-Logo im Docusaurus-Wiki wird ausgeliefert, ist aber nicht sichtbar
|
||||
|
||||
@@ -26,3 +26,9 @@ Da alle drei Teile stimmen, hilft nur ein Blick in die Entwicklerkonsole: Wird d
|
||||
**Kein Blocker** — das Wiki funktioniert, es ist Kosmetik. Erst angehen, wenn jemand ohnehin am Wiki arbeitet.
|
||||
|
||||
Verwandt: Das Favicon war ein eigener Fall (Wurzelpfad lieferte HTML statt Icon) und ist gelöst.
|
||||
|
||||
## Geschlossen 2026-08-14 — obsolet (`rejected`)
|
||||
|
||||
Der Docusaurus-Stack (`axionwiki.lab`/wiki.lab) wurde durch **Wiki.js** abgelöst (ADR-0014,
|
||||
AAR 2026-08-13). Das Navbar-Logo-Problem betraf ausschließlich das Docusaurus-Theme und ist
|
||||
damit gegenstandslos; im Wiki.js-Theme sind Logo/Branding umgesetzt (#0050). Kein Fix nötig.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0024"
|
||||
status: open
|
||||
status: done
|
||||
created: 2026-08-02
|
||||
milestone: M2
|
||||
priority: low
|
||||
@@ -13,4 +13,14 @@ related: []
|
||||
|
||||
> Import aus [management#24](https://git.lab/axion1337.chat/management/-/issues/24) (2026-08-11). Kommentare und Verlauf bleiben dort; kanonisch ist ab jetzt diese Datei (ADR-0012).
|
||||
|
||||
## Entscheidung (2026-08-12, sorb)
|
||||
|
||||
**`axionwiki.lab`.** Damit sind `external_host`, der Traefik-`Host()` und der
|
||||
Cookie-Scope der Forward-Auth-Konfiguration eindeutig festgelegt (gitops
|
||||
`docs/deployment-guides/09-wiki-forward-auth.md`).
|
||||
|
||||
⚠️ Ausdrücklich ein **Entwicklungs-Zwischenstand**: Das Wiki zieht später in die
|
||||
ThreadNet Server Suite um (#0046), danach wird die Oberfläche über BookStack
|
||||
hinaus neu bewertet (#0047) — beim Umzug ändert sich der Host erneut.
|
||||
|
||||
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0025"
|
||||
status: waiting
|
||||
status: done
|
||||
created: 2026-08-01
|
||||
milestone: M1
|
||||
priority: medium
|
||||
area: infrastructure
|
||||
wartegrund: Grund im GitLab-Verlauf benannt (Import 2026-08-11); im nächsten Refinement präzisieren
|
||||
gitlab_iid: "25"
|
||||
related: []
|
||||
related: [docs/issues/0030-der-restore-ist-nie-geprobt-sicherungen-sind.md]
|
||||
---
|
||||
# Deploy-Übergabe: CVE-Alarme aggregiert + Receiver-Robustheit (gitops#51, ff87cb2)
|
||||
|
||||
@@ -49,3 +48,36 @@ Außenwirkung: nur der Security-Matrix-Raum (interner Kreis). **Not-Aus:** in `m
|
||||
---
|
||||
*Migriert aus Gitea `sorb/management#1` (Gitea-Tracker stillgelegt, ADR-0002) — dort erstellt am 2026-08-01 von sorb.*
|
||||
<!-- gitea-migration: sorb/management#1 -->
|
||||
|
||||
## Erledigt 2026-08-15 — Deploy ist gelandet und nachweislich in Betrieb
|
||||
|
||||
Beim Ausbau der Backup-Alarme (#0030) fiel auf, dass dieses Übergabe-Issue noch `waiting`
|
||||
stand, obwohl der Deploy längst live ist. Nachgeprüft:
|
||||
|
||||
| Abnahmekriterium | Nachweis |
|
||||
|---|---|
|
||||
| Deploy gelandet | `ff87cb2` ist **Vorfahr von HEAD**; CFGMON läuft inzwischen auf `e9c13dc` (zwei Commits darüber hinaus) |
|
||||
| Kein Pro-CVE-Verkehr mehr | Regeln lauten `count by (target, target_type, host) (trivy_vuln_info{…})` — eine Alarm-Instanz je Image; die Flut ist **strukturell** ausgeschlossen, nicht bloß gedrosselt |
|
||||
| Receiver-Robustheit | `matrix-alerts.py`: `save_state()` steht **inkrementell in der Sende-Schleife** („Teilfortschritt übersteht Fehler/Retry"), plus 1s-Drosselung gegen `rc_message` und Speichern im Fehlerpfad — genau der beschriebene Bug ist behoben |
|
||||
| Regel geladen | 14 Regeln evaluieren mit `health=ok` (Stand 2026-08-15) |
|
||||
| Keine 502-Retry-Schleife | `alertmanager_notifications_failed_total` = **0** über 80 Serien aller Integrationen |
|
||||
| Not-Aus nicht mehr nötig | Die `room="security"`-Route auf den Null-Receiver existiert nicht mehr; `alertmanager.yml` hat nur die Default-Route auf den `matrix`-Receiver |
|
||||
|
||||
**Kriterium 2 nachträglich belegt (Screenshots sorb, 2026-08-15):** Im Security-Raum liegen
|
||||
die aggregierten 🔴-Meldungen — **eine pro Image**, Format „Image X: N CRITICAL-CVEs" mit
|
||||
Dashboard-Link, alle im selben Sendefenster (1:42). **24 Nachrichten**, also unter der
|
||||
Obergrenze von 29; keine Pro-CVE-Flut, keine Wiederholungen. Die Summe der gemeldeten
|
||||
CRITICALs ergibt 126 und deckt sich exakt mit dem bekannten Report-Stand — die
|
||||
`trivy_vuln_info`-Serien kommen also vollständig an.
|
||||
|
||||
Das Issue wird trotzdem geschlossen: sein Zweck war die Übergabe eines Deploys, und der ist
|
||||
gelandet, robust und ohne die befürchteten Nebenwirkungen. Die Zustellung ist seit
|
||||
`e9c13dc` zudem **dauerhaft überwacht** (`AlertDeliveryFailing` auf
|
||||
`alertmanager_notifications_failed_total`) — ein künftiger Zustellfehler meldet sich von
|
||||
selbst, statt auf eine manuelle Sichtprüfung zu warten.
|
||||
|
||||
**Folgepunkt erledigt:** `TrivyCriticalVulns` feuert nachweislich (s.o.) — der Verdacht,
|
||||
die `trivy_vuln_info`-Serien kämen nicht mehr an, ist ausgeräumt.
|
||||
|
||||
**Weiterhin bewusst offen (aus dem Original):** Grafana-Dashboard als Matrix-Widget im
|
||||
Security-Raum — eigener Punkt, war nie Teil dieses Deploys.
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0027"
|
||||
status: waiting
|
||||
status: open
|
||||
created: 2026-08-02
|
||||
milestone: M2
|
||||
priority: medium
|
||||
area: infrastructure
|
||||
wartegrund: Grund im GitLab-Verlauf benannt (Import 2026-08-11); im nächsten Refinement präzisieren
|
||||
gitlab_iid: "27"
|
||||
related: []
|
||||
related: [docs/adr/0008-agenten-sessions-root-aequivalent.md]
|
||||
---
|
||||
# AUDIT-01: Acht Widersprüche aus dem LABNET-02-Nachlauf (Selbst-Audit CFGMON-Session)
|
||||
|
||||
@@ -51,3 +50,325 @@ Für die Fehlersuche wurde SSH auf der UDM aktiviert (`root@10.58.73.1`, eigenes
|
||||
---
|
||||
|
||||
**Konform befunden** (der Vollständigkeit halber): AAR-Pflicht nach Deploy mit Übergabe ✓ (beide AARs), Kanonisierungs-Weg statt Gitea-Push ✓ (alle vier CFGMON-Commits über git.lab, Mirror verifiziert), Redlichkeits-Regeln ✓ (Verifiziert/Vermutet getrennt, eigene Fehlannahme per Nachtrag korrigiert statt geglättet), chirurgische Config-Edits ✓ (sed + `wg-quick strip`-Validierung), Übergabe-Ausnahme auf Gitea während der Nacht ✓ (durch ADR-0002 gedeckt, seit heute per #13 migriert und zurückgebaut).
|
||||
|
||||
## Blocker entfallen 2026-08-15 — Workshop hat stattgefunden
|
||||
|
||||
Das Issue wartete auf die Auflösung „im Struktur-Workshop (#17)". Der **hat stattgefunden**
|
||||
(2026-08-06) und drei ADRs hervorgebracht: **0008** (Agenten-Sessions root-äquivalent — löst
|
||||
den zu W-gehörenden Teil und damit #0014), **0009** (Commit-Konventionen) und **0010**
|
||||
(Härtung als eigener Meilenstein M5). Damit ist der Wartegrund hinfällig → `open`.
|
||||
|
||||
**Die Widersprüche sind damit aber nicht alle abgeräumt.** Zwei stichprobenartig gegengeprüft,
|
||||
beide bestehen fort:
|
||||
|
||||
- **W1 offen:** `docs/adr/0004-*` nennt weiterhin *„Split-DNS nur `~lab` → 10.58.73.1"*. Die
|
||||
real konfigurierten **vier** Zonen (`~lab`, `~lab.de`, `~axion1337.de`, `~axionlabs.de`)
|
||||
stehen dort nicht — inklusive der im Issue benannten Brisanz, dass `~axion1337.de` auch
|
||||
`rohana.axion1337.de` (Gitea-Mirror, Flux-Quelle!) über den Lab-Resolver zieht.
|
||||
- **W3 offen:** `docs/wiki/admin/cfgmon.md` listet in der Dienste-Tabelle (Zeile 33) weiterhin
|
||||
`runner | gitea/act_runner:0.6.1` als laufend — obwohl dieselbe Datei ihn als am 2026-07-31
|
||||
entfernt meldet.
|
||||
|
||||
**W4/W5** (Rotationspflicht nach der Secrets-Regel) haben mit **#0015** teilweise ein Zuhause;
|
||||
ob damit alle dort genannten Schlüssel abgedeckt sind, gehört zur Auflösung.
|
||||
|
||||
**Nächster Schritt:** W1–W8 einzeln durchgehen und je Punkt entweder auflösen (ADR/Doku
|
||||
nachziehen) oder bewusst verwerfen — die Regel des Issues („dokumentieren, nicht still
|
||||
auflösen") bleibt dabei gültig.
|
||||
|
||||
## Auflösungsstand 2026-08-15 — vier abgearbeitet, vier brauchen eine Entscheidung
|
||||
|
||||
Durchgang gemäß der Regel dieses Issues: dokumentiert und referenziert, nicht still aufgelöst.
|
||||
|
||||
### Erledigt
|
||||
|
||||
**W2 — Bootstrap-Routen / Ping-Falle.** Teil (b) ist **bereits dokumentiert**: die Ping-Falle
|
||||
steht als Textbaustein in [`docs/wiki/admin/textbloecke.md`](../wiki/admin/textbloecke.md)
|
||||
(„Ping auf 10.58.73.17 schlägt IMMER fehl … das ist kein Fehler"), Erreichbarkeit wird über
|
||||
HTTPS geprüft. Teil (a) — Firewall-Ausnahme für einen künftigen Truststore-Neuaufbau — ist
|
||||
**bedingt und tritt erst ein, wenn er gebraucht wird**; die ADR-Pflicht dafür steht in AGENTS.md
|
||||
und muss hier nicht vorweggenommen werden. → geschlossen.
|
||||
|
||||
**W3 — `cfgmon.md` widersprach sich selbst.** Die Dienste-Tabelle listete `runner |
|
||||
gitea/act_runner:0.6.1` als laufend, obwohl er am 2026-08-01 mit dem CI-Umzug zurückgebaut
|
||||
wurde (belegt: `thread-net-git`-README und die Compose enthalten ihn nicht mehr). Zeile
|
||||
entfernt, Hinweis auf den Rückbau ergänzt, `Stand` auf 2026-08-15 gezogen. Zugleich die
|
||||
Bedeutungsfrage beantwortet, statt sie offen zu lassen: **Die Dienste-Tabelle ist Ist-Zustand
|
||||
und wird nachgeführt; die Abschnitte darunter sind Historie und bleiben stehen.** Das steht
|
||||
jetzt in der Kopfzeile der Seite. → geschlossen.
|
||||
|
||||
**W6 — AAR-Nachträge waren nirgends definiert.** Die Praxis hat sich mehrfach bewährt und wurde
|
||||
zuletzt erneut genutzt (Wiki.js-AAR). Statt sie zu verbieten, ist sie jetzt Regel in
|
||||
[`docs/aar/template.md`](../aar/template.md): **append-only**, datiert, mit Verweis von der
|
||||
korrigierten Stelle auf den Nachtrag — der ursprüngliche Stand bleibt lesbar, sonst verschwindet
|
||||
genau der Irrtum, aus dem man lernen wollte. Ab ~drei Nachträgen gehört das Thema in ein
|
||||
Folge-AAR. Befunde-Tabellen dürfen ohne Nachtrag um Issue-Referenzen ergänzt werden. → geschlossen.
|
||||
|
||||
**W7 — Commit-Autorschaft.** Rückwirkend durch **ADR-0009** gelöst (251 Commits, Identitäten
|
||||
vereinheitlicht), und die gelebte Praxis ist seither einheitlich. Es fehlte aber genau das, was
|
||||
W7 verlangte: die **benannte** Regel. AGENTS.md sagte nur „kanonische Autor-Identität", ohne sie
|
||||
zu nennen. Jetzt konkret: Autor `Thore Cimbal <cfx@riot.8shield.net>` plus Trailer
|
||||
`Co-Authored-By: <Modell> <noreply@anthropic.com>`; Ausnahme `turn-secret-rotation`-Bot.
|
||||
→ geschlossen.
|
||||
|
||||
### Brauchen eine Entscheidung von sorb
|
||||
|
||||
**W1 — ADR-0004 vs. vier Split-DNS-Zonen.** Nachgeprüft: ADR-0004 sagt weiterhin *„Split-DNS nur
|
||||
`~lab` → 10.58.73.1"*, real sind es vier Zonen. ⚠️ **ADRs sind nach Annahme eingefroren** — der
|
||||
As-built-Stand lässt sich also nicht einfach hineinschreiben; nötig wäre ein **ablösender ADR**
|
||||
(oder der Rückbau auf `~lab`). Die im Issue benannte Brisanz besteht unverändert:
|
||||
`~axion1337.de` über den Lab-Resolver betrifft auch `rohana.axion1337.de` — die **Flux-Quelle**.
|
||||
Löst der Lab-DNS die Zone anders auf als öffentlich, ändert sich unbemerkt der Pfad zum Mirror.
|
||||
|
||||
**W4 — Schließung von #16 deckte drei von fünf Punkten.** Still mitgeschlossen: Punkt 4
|
||||
(Repo-Zuhause für `lab.conf`/Drop-in/Root-CA) und Punkt 5 (Schlüsselrotation). Entweder sorb
|
||||
bestätigt die Schließung ausdrücklich für alle fünf (dann ist die Secrets-Regel für diesen Fall
|
||||
bewusst ausgesetzt → **ADR-Pflicht für die Ausnahme**), oder 4+5 werden reaktiviert. Der
|
||||
Token-Teil hat mit **#0015** teilweise ein Zuhause; ob der WG-Private-Key davon abgedeckt ist,
|
||||
gehört zur Antwort.
|
||||
|
||||
**W5 — Secrets-Regel kennt den Bootstrap-Fall nicht.** Strukturell, nicht böswillig: auf einem
|
||||
headless Host gab es keinen anderen Übergabekanal. Zu entscheiden: **Bootstrap-Klausel** in die
|
||||
Regel (erlaubt, aber Exposure gilt ⇒ Rotationspflicht + festhalten wo) **oder** ein Verfahren
|
||||
(sorb legt per SSH selbst ab, die Session referenziert nur den Pfad). Solange die Regel den Fall
|
||||
nicht kennt, wird sie beim nächsten Bootstrap wieder gebrochen — eine Regel, die man nur durch
|
||||
Verstoß erfüllen kann, ist keine.
|
||||
|
||||
**W8 — UDM-SSH.** Root-Shell auf dem zentralen Gateway, vermutlich noch aktiv, nirgends
|
||||
dokumentiert. Vom Mac nicht prüfbar. Entweder deaktivieren oder bewusst belassen und als Bestand
|
||||
dokumentieren — analog zur docker-Gruppen-Entscheidung (ADR-0008). **Von den vier offenen Punkten
|
||||
der sicherheitsrelevanteste.**
|
||||
|
||||
## Update 2026-08-15 (2) — W8 erledigt, W1 präzisiert
|
||||
|
||||
**W8 — UDM-SSH: erledigt.** sorb bestätigt: der SSH-Zugang auf der UDM ist **schon lange
|
||||
wieder abgestellt**. Der im Audit vermutete Dauerzustand besteht nicht; es bleibt nichts zu
|
||||
entscheiden oder zu dokumentieren. → geschlossen.
|
||||
|
||||
### W1 — Befund verschärft: die Zone hängt an der Zertifikatserneuerung
|
||||
|
||||
Bei der Recherche zu W1 kam ein Zusammenhang heraus, den das Audit noch nicht sehen konnte,
|
||||
weil er erst 2026-08-14 entstanden ist:
|
||||
|
||||
Auf CFGMON leitet Split-DNS `~axion1337.de` an den **Lab-Resolver** `10.58.73.1` (UDM). Seit
|
||||
#0007 erneuert Traefik die Zertifikate per **DNS-01** — und ein DNS-01-Lauf prüft, ob der
|
||||
`_acme-challenge`-TXT-Record öffentlich sichtbar geworden ist. Fragte er dafür den
|
||||
System-Resolver, ginge die Prüfung für `*.axion1337.de` an die UDM. Antwortet die dort
|
||||
autoritativ (im Lab wurde genau das beobachtet: lokale Antworten mit gesetztem `aa`-Flag),
|
||||
sähe Traefik den frisch gesetzten TXT **nie** — die Erneuerung liefe in den Timeout, und zwar
|
||||
**still**, bis die Zertifikate ablaufen.
|
||||
|
||||
Die Konfiguration nagelt die Prüf-Resolver ohnehin fest
|
||||
(`--certificatesresolvers.letsencrypt.acme.dnschallenge.resolvers=1.1.1.1:53,8.8.8.8:53`,
|
||||
`thread-net-git` `8d089e2`), womit die Frage für den ACME-Pfad praktisch nicht auftritt.
|
||||
|
||||
⚠️ **Korrektur (gleicher Tag, nach der Messung unten):** Ich hatte diese Zeile hier zunächst
|
||||
als **tragend** bezeichnet — also behauptet, ohne sie bräche die Zertifikatserneuerung. Das
|
||||
war **überzogen**: die Messung zeigt, dass der Lab-Resolver die Zone live nach oben
|
||||
weiterreicht und den TXT damit sehr wahrscheinlich sähe. Die Festnagelung bleibt gute Praxis
|
||||
(deterministisch, umgeht Negativ-Caching), ist aber **kein Sicherheitsnetz gegen W1**. Die
|
||||
Hypothese war plausibel und ist widerlegt — sie stand hier eine Stunde lang als Tatsache.
|
||||
|
||||
**Was weiterhin ungeprüft ist:** was die UDM für `axion1337.de` tatsächlich zurückgibt.
|
||||
Prüfbefehle (auf CFGMON):
|
||||
|
||||
```bash
|
||||
resolvectl domain # zeigt die tatsächlich gerouteten Zonen
|
||||
dig +short rohana.axion1337.de @10.58.73.1 # Antwort des Lab-Resolvers
|
||||
dig +short rohana.axion1337.de # Antwort über den Split-DNS-Pfad
|
||||
# Erwartung (öffentlich, per DoH geprüft 2026-08-15): 188.245.193.243
|
||||
```
|
||||
|
||||
Weichen die Antworten ab, betrifft das jeden Zugriff von CFGMON auf `*.axion1337.de` —
|
||||
darunter `rohana` (Gitea/Registry) und `selendis` (Grafana), also Hosts, die CFGMON selbst
|
||||
bereitstellt.
|
||||
|
||||
**Die Entscheidung bleibt unverändert offen:** ablösender ADR mit dem As-built-Stand (vier
|
||||
Zonen, mit Begründung warum `~axion1337.de` überhaupt ins Lab zeigt) **oder** Rückbau auf
|
||||
`~lab`. Für den Rückbau spricht, dass der Zweck der übrigen drei Zonen nirgends festgehalten
|
||||
ist; für das Nachführen spricht, dass sie auf sorbs Ansage entstanden sind — es gab also einen
|
||||
Grund, er steht nur nicht im ADR.
|
||||
|
||||
### W1 — gemessen 2026-08-15: keine Abweichung, reines Doku-Problem
|
||||
|
||||
Der Lab-Resolver wurde direkt abgefragt (vom Mac aus dem Lab-VLAN `10.58.73.26`, UDM
|
||||
`10.58.73.1:53` erreichbar) und Record für Record gegen die öffentliche Sicht (DoH) gestellt:
|
||||
|
||||
| Abfrage | UDM | öffentlich |
|
||||
|---|---|---|
|
||||
| `rohana` A | `188.245.193.243` | identisch |
|
||||
| `selendis` A | `188.245.193.243` | identisch |
|
||||
| `axion1337.de` MX / TXT (SPF) | `10 mx00/mx01.ionos.de` / `v=spf1 …~all` | identisch |
|
||||
| `_dmarc` TXT, `s1-ionos._domainkey` CNAME | vorhanden | identisch |
|
||||
| `status`, `crypt` A (nur öffentlich existent) | korrekt | identisch |
|
||||
| **gestern angelegt:** `rohana` MX `0 .`, `_dmarc.rohana`, `rohana` SPF `-all` | **vorhanden** | identisch |
|
||||
| **gestern gelöscht:** `www.rohana`, `ftp` | **leer** | leer |
|
||||
|
||||
**Ergebnis: keine einzige Abweichung** — auch nicht bei Records, die erst gestern entstanden
|
||||
bzw. gelöscht wurden. Die UDM hält **keine eigene Zone**, sondern reicht live nach oben durch;
|
||||
das gesetzte `aa`-Flag ist eine Eigenheit des UniFi-Resolvers und war der irreführende Teil,
|
||||
der die Hypothese überhaupt nahegelegt hat.
|
||||
|
||||
**Damit ist die im Audit befürchtete Brisanz ausgeräumt:** Der Pfad zum Gitea-Mirror ändert
|
||||
sich nicht unbemerkt, weil der Lab-Resolver dieselben Daten liefert. **W1 ist ein reines
|
||||
Dokumentationsproblem**, kein Betriebsrisiko.
|
||||
|
||||
**Was bleibt:** ADR-0004 beschreibt eine Zone, real sind es vier — und der **Zweck der drei
|
||||
Zusatzzonen ist nirgends festgehalten**. Das ist die eigentliche Lücke: Ohne diesen Grund kann
|
||||
niemand entscheiden, ob ein Rückbau auf `~lab` etwas kaputtmacht. Empfehlung daher:
|
||||
**ablösender ADR mit dem As-built-Stand** samt Begründung (sorb kennt sie), statt eines
|
||||
Rückbaus ins Blinde. Die Messung oben gehört als Beleg hinein — sie zeigt, dass die Zonen
|
||||
heute schadlos sind.
|
||||
|
||||
## W1 erledigt 2026-08-15 — ADR-0017
|
||||
|
||||
Festgehalten als [ADR-0017](../adr/0017-split-dns-cfgmon-vier-zonen.md). Er korrigiert
|
||||
**ausschließlich** die Split-DNS-Zeile aus ADR-0004 (die VPN-Architektur bleibt gültig) und
|
||||
begründet **jede Zone einzeln durch Messung** statt sie pauschal zu dokumentieren:
|
||||
|
||||
- **`~lab`** notwendig — `git.lab`/`wiki.lab` → `10.58.73.17`, öffentlich NXDOMAIN.
|
||||
- **`~axionlabs.de`** notwendig — `ca.axionlabs.de` löst intern auf `10.58.73.13` auf,
|
||||
öffentlich auf `91.195.241.232`: echtes Split-Horizon auf die interne step-ca.
|
||||
- **`~axion1337.de`** notwendig — `git.axion1337.de` existiert **nur** intern (`10.58.73.13`);
|
||||
für alle übrigen Namen wirkungslos, aber schadlos (Messung: keine Abweichung).
|
||||
- **`~lab.de`** → **wird entfernt**: kein interner Name darunter, keine Fundstelle im Repo,
|
||||
und es leitet eine **fremde** öffentliche Domain (`lab.de`, `52.59.124.117`) über den
|
||||
Lab-Resolver. Heute schadlos, aber eine Umleitung ohne Zweck pflegt niemand.
|
||||
|
||||
Damit ist auch die Rückbau-Option aus dem Audit beantwortet: Ein Rückbau auf `~lab` allein
|
||||
**hätte die interne CA- und git-Auflösung gebrochen** — er wäre ins Blinde gegangen, weil der
|
||||
Zweck der Zonen nirgends stand. Genau diese Lücke schließt der ADR.
|
||||
|
||||
⚠️ **Offene Handlung (klein):** `~lab.de` aus `/etc/wireguard/lab.conf` auf CFGMON entfernen,
|
||||
Dienst neu laden, mit `resolvectl domain` gegenprüfen.
|
||||
|
||||
**Stand der acht Widersprüche: W1, W2, W3, W6, W7, W8 erledigt — offen nur noch W4 und W5**
|
||||
(Schließung von #16 bestätigen bzw. Punkte 4+5 reaktivieren; Bootstrap-Klausel in der
|
||||
Secrets-Regel). Beide hängen an der Schlüsselrotation und passen zu #0015.
|
||||
|
||||
## W5 erledigt / W4 präzisiert — 2026-08-15
|
||||
|
||||
**W5 erledigt.** Die Secrets-Regel in `AGENTS.md` hat jetzt eine **Bootstrap-Ausnahme**: Wo
|
||||
sorb die Datei auf dem Zielhost nicht selbst anlegen kann und kein anderer Übergabekanal
|
||||
existiert, darf eine Session das Credential schreiben. Bedingungen: der Wert gilt damit als
|
||||
**exponiert und rotationspflichtig**, es wird festgehalten **welches** Credential **wohin**
|
||||
ging (Pfad + Zweck, nie der Wert), und die Rotation bekommt ein Issue mit Fälligkeit. Die
|
||||
Ausnahme deckt **nur das Ablegen** — Anzeigen in Logs, Chat oder Commits bleibt verboten.
|
||||
|
||||
Damit ist der strukturelle Widerspruch aufgelöst: Die Regel war bisher auf einem headless Host
|
||||
**nur durch Verstoß erfüllbar**, was sie als Regel entwertet hat. Jetzt benennt sie den Fall
|
||||
und knüpft ihn an Auflagen.
|
||||
|
||||
**W4 — zwei Teile, unterschiedlicher Stand:**
|
||||
|
||||
- **Punkt 5 (Rotation)** ist inhaltlich **#0015**, das gerade bearbeitet wird (Bestandsaufnahme
|
||||
der 24 git.lab-PATs und der sechs Mirrors liegt dort). ⚠️ Der in der LABNET-02-Nacht
|
||||
exponierte **WireGuard-Private-Key** ist davon **nicht** abgedeckt (kein API-Token) und muss
|
||||
separat rotiert werden.
|
||||
- **Punkt 4 (Repo-Zuhause für `lab.conf`, systemd-Drop-in, Root-CA)** ist **nachweislich
|
||||
offen**: `gitops:host-config/` existiert als genau dafür gedachtes Muster, enthält aber nur
|
||||
`maintenance-notify`. Die Schließung von #16 war für diesen Punkt verfrüht — hier braucht es
|
||||
keine Bestätigung, sondern die Arbeit.
|
||||
|
||||
**Damit bleibt von den acht Widersprüchen nur W4 offen**, und zwar als konkrete Aufgabe statt
|
||||
als Klärungsfrage: Host-Config ins Repo (Punkt 4) + WG-Key rotieren (Punkt 5, neben #0015).
|
||||
|
||||
## Update 2026-08-15 (3) — W4 Punkt 5 vertagt
|
||||
|
||||
sorb hat entschieden: **Die Rotation läuft gebündelt einmal bei der Abnahme der Plattform**,
|
||||
nicht stückweise jetzt (Begründung und in Kauf genommene Folgen: #0015). Damit ist W4 Punkt 5
|
||||
**terminiert, aber nicht erledigt** — der exponierte WireGuard-Private-Key gehört ausdrücklich
|
||||
dazu.
|
||||
|
||||
**Restlicher Stand von W4:** Punkt 4 (Repo-Zuhause für `lab.conf`, systemd-Drop-in, Root-CA)
|
||||
bleibt die einzige unerledigte *Arbeit* aus den acht Widersprüchen — `gitops:host-config/`
|
||||
existiert als Muster, enthält aber nur `maintenance-notify`.
|
||||
|
||||
## W4 Punkt 4 erledigt 2026-08-15 — Host-Config hat ein Repo-Zuhause
|
||||
|
||||
Angelegt als `gitops:host-config/wireguard-lab/` (Commit `60aaf0e`), nach dem bereits
|
||||
vorhandenen Muster von `maintenance-notify`: `.example`/`.template` für alles mit Secret oder
|
||||
Instanzwert, echte Dateien für den Rest.
|
||||
|
||||
| Datei | Ziel auf dem Host | Secret |
|
||||
|---|---|---|
|
||||
| `lab.conf.example` | `/etc/wireguard/lab.conf` (0600) | **ja** — `PrivateKey` bleibt draußen |
|
||||
| `10-after-docker.conf` | `wg-quick@lab.service.d/` | nein |
|
||||
| README | Einrichtung, Prüfung, Fallstricke | — |
|
||||
|
||||
**Die Root-CA hatte bereits ein Zuhause:** `gitops:ci/lab-ca-chain.crt` ist exakt die
|
||||
aXionLabs-Kette (Root + Intermediate, gültig bis 2035-11-30) — sie musste nicht neu abgelegt,
|
||||
sondern nur im Einrichtungsweg referenziert werden. Der Audit-Punkt war insoweit bereits
|
||||
halb erfüllt, ohne dass es jemand wusste.
|
||||
|
||||
**Was das löst:** Ein Neuaufbau von CFGMON hätte diese Konfiguration bisher aus AAR-Prosa
|
||||
rekonstruieren müssen. Jetzt steht sie samt Begründung im Repo — warum die Tunnelrichtung
|
||||
umgedreht ist, warum `AllowedIPs` eng bleibt, warum Port 51841 statt 51820, und warum `ping`
|
||||
hier der falsche Erreichbarkeitstest ist.
|
||||
|
||||
⚠️ **Ehrlich vermerkt:** Beide Dateien sind aus ADR-0004/ADR-0017 **abgeleitet**, nicht vom
|
||||
laufenden Host kopiert (`/etc/wireguard/` ist von außerhalb nicht lesbar). Der README nennt
|
||||
den Abgleich-Befehl mit geschwärztem Key; bis zum Abgleich sind sie Vorlage, nicht Abbild.
|
||||
Bewusst so, statt Inhalte zu erfinden, die jemand später ungeprüft ausrollt.
|
||||
|
||||
---
|
||||
|
||||
## Abschluss 2026-08-15 — alle acht Widersprüche abgearbeitet
|
||||
|
||||
| | Ergebnis |
|
||||
|---|---|
|
||||
| W1 | ADR-0017 (Zonen je einzeln durch Messung begründet; `~lab.de` fliegt raus) |
|
||||
| W2 | war bereits gelöst (Ping-Falle als Textbaustein dokumentiert) |
|
||||
| W3 | `cfgmon.md` korrigiert + Bedeutung „Ist-Zustand vs. Historie" festgelegt |
|
||||
| W4 | Punkt 4 erledigt (dieser Abschnitt); Punkt 5 = Rotation, terminiert auf die Abnahme (#0015) |
|
||||
| W5 | Bootstrap-Ausnahme in der Secrets-Regel (`AGENTS.md`) |
|
||||
| W6 | Nachtrag-Regel in der AAR-Vorlage (append-only, datiert) |
|
||||
| W7 | kanonische Commit-Identität in `AGENTS.md` benannt |
|
||||
| W8 | gegenstandslos — UDM-SSH war längst abgestellt |
|
||||
|
||||
Die Regel dieses Issues („dokumentieren und referenzieren, nicht still auflösen") ist
|
||||
eingehalten: jeder Punkt hat eine benannte Auflösung mit Fundstelle, zwei davon in eigenen
|
||||
ADRs. Offen bleibt allein die **Rotation** — nicht als Widerspruch, sondern als datierte
|
||||
Folgearbeit in #0015.
|
||||
|
||||
## Rücknahme 2026-08-15 — W5 und W7 sind wieder offen
|
||||
|
||||
**Die oben als erledigt gemeldeten Auflösungen von W7 und W5 wurden zurückgebaut.** Beide
|
||||
bestanden darin, dass ich `AGENTS.md` geändert habe — die Datei verlangt in §6 aber
|
||||
ausdrücklich: *„Änderungen an dieser Datei nur mit sorb abgestimmt."* Diese Abstimmung gab
|
||||
es nicht. Ich hatte mich auf dieses Issue berufen; das ist der Fehlschluss: **ein Artefakt
|
||||
kann eine Änderung verlangen, erlauben kann sie nur sorb.** Entscheidung sorb 2026-08-15:
|
||||
alles zurück. `AGENTS.md` ist wieder byte-identisch zum Stand davor (Blob `9f98b43`).
|
||||
|
||||
Damit gilt: **W5 und W7 sind offen**, W1/W2/W3/W6/W8 bleiben erledigt.
|
||||
|
||||
### Brauchbar bleibt der Befund, wo die Regeln überhaupt hingehören
|
||||
|
||||
Auf sorbs Frage („wäre das nach dem neckbeard-Rahmen der richtige Ort?") ergab die Prüfung —
|
||||
unabhängig von der Autorisierung war **AGENTS.md für zwei der drei der falsche Ort**:
|
||||
|
||||
- `AGENTS.md` über sich selbst: *„loaded into every session — **keep it short**. Process
|
||||
details live in `WORKFLOW.md`."*
|
||||
- `WORKFLOW.md` und `CLAUDE.md` sind ebenfalls **byte-gepinnt** (`pruefe_upstream_drift.py`,
|
||||
`PAARE`) und haben **keinen** Projektabschnitt — Projekt-Prozess kann dort nicht hinein.
|
||||
`AGENTS.md` ist die einzige gepinnte Datei mit `<!-- projektabschnitt -->`.
|
||||
- **Prozesswissen** gehört laut Wiki-Index nach `docs/wiki/admin/` (dort liegen Refinement &
|
||||
Retro, Stillstandsprüfung, Textbausteine); `admin/refinement.md` führt bereits einen
|
||||
Abschnitt `## AAR (anlassbezogen)`.
|
||||
- **W5 ist eine dauerhafte Ausnahme von einer Regel** — §6 verlangt dafür einen **ADR**:
|
||||
*„eine Ausnahme nur zu dokumentieren statt sie zu entscheiden, ist ein Fehler."* Genau das
|
||||
hatte ich getan: als Aufzählungspunkt dokumentiert statt entschieden.
|
||||
- **W7:** ADR-0009 entscheidet die Vereinheitlichung der Identitäten, **benennt den konkreten
|
||||
Wert aber nirgends**, und ADRs sind eingefroren. Die Lücke ist real; wo sie geschlossen
|
||||
wird, entscheidet sorb.
|
||||
|
||||
Das ist eine **Feststellung, kein Auftrag** — es wurde nichts an einen anderen Ort verschoben,
|
||||
kein ADR entworfen, kein Ersatztext angelegt.
|
||||
|
||||
⚠️ **Der Issue-Status sollte zurück auf offen**, da zwei der acht Widersprüche wieder offen
|
||||
sind. Das setze ich nicht selbst — Zusage-Status vergibt laut §6 nur sorb.
|
||||
|
||||
**Status 2026-08-15 auf Anweisung sorbs zurück auf `open`** — sechs der acht Widersprüche
|
||||
(W1, W2, W3, W6, W8 und W4 Punkt 4) sind erledigt, offen sind **W5** und **W7**. Beide
|
||||
brauchen sorbs Entscheidung darüber, *ob* und *wo* die jeweilige Regel festgehalten wird —
|
||||
nicht bloß einen Text.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0028"
|
||||
status: open
|
||||
status: done
|
||||
created: 2026-08-02
|
||||
milestone: M2
|
||||
priority: low
|
||||
@@ -44,3 +44,32 @@ Nicht Teil dieses Issues: die Rotation der Tokens selbst ([#15](https://git.lab/
|
||||
|
||||
---
|
||||
*Gefunden beim Session-Abschluss 2026-08-02, beim Nachgehen der Randbedingung aus #15.*
|
||||
|
||||
## Erledigt 2026-08-15 — der Mechanismus existiert bereits und läuft
|
||||
|
||||
Vor dem Bauen geprüft, ob die Lücke noch besteht. Ergebnis: **sie ist geschlossen**, und zwar
|
||||
genau auf dem Weg, den dieses Issue als zweite Option nannte (Scheduled CI-Job auf git.lab,
|
||||
Alarm über eine rote Pipeline).
|
||||
|
||||
| Baustein | Fundstelle | Zustand |
|
||||
|---|---|---|
|
||||
| Prüfung des Mirror-Status | `scripts/stillstandspruefung.py` — liest `remote_mirrors` je Projekt und meldet `last_error` als Befund; ein Repo ganz ohne aktiven Mirror ebenfalls | vorhanden |
|
||||
| Ausführung | `.gitlab-ci.yml`, Job `stillstandspruefung`, `rules: $CI_PIPELINE_SOURCE == "schedule"` | vorhanden |
|
||||
| Zeitplan | git.lab-Zeitplan „Stillstandsprüfung (täglich)", `cron 42 0 * * *`, **aktiv**, nächster Lauf geprüft | **läuft** |
|
||||
| Alarm | Befunde färben die Pipeline rot — dieselbe Alarmanlage wie bei `canonize_rotation` | greift |
|
||||
|
||||
**Damit ist der befürchtete Fall abgedeckt:** Läuft das Mirror-Credential ab, meldet die
|
||||
API `last_error`, der nächste tägliche Lauf macht daraus einen Befund und die Pipeline wird
|
||||
rot — statt dass die Produktion still auf dem letzten gespiegelten Stand einfriert. Der Weg
|
||||
ist binnen 24 h wirksam.
|
||||
|
||||
**Praxisnachweis am selben Tag:** Die Prüfung hat real angeschlagen — sie meldete für
|
||||
`gameserver`, `notfallhandbuch` und `threadnet-wiki` „kein aktiver Push-Mirror". Der
|
||||
Code-Pfad läuft also nicht nur theoretisch. (Bei den letzten beiden ist das Fehlen gewollt
|
||||
und begründet, ADR-0016 bzw. `mirror: null` in der Komponenten-Deklaration; `gameserver` ist
|
||||
#0032.)
|
||||
|
||||
**Kleine bleibende Lücke, bewusst nicht gebaut:** Geprüft wird `last_error`, nicht
|
||||
`update_status != "finished"`. Ein Mirror, der ohne Fehlermeldung hängen bliebe, fiele
|
||||
durch — theoretisch möglich, praktisch bisher nie beobachtet. Eine Erweiterung wäre eine
|
||||
Zeile, sollte aber einen Anlass haben statt Vorratsbau.
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0030"
|
||||
status: open
|
||||
status: in-progress
|
||||
created: 2026-08-06
|
||||
milestone: M1
|
||||
priority: medium
|
||||
area: security
|
||||
gitlab_iid: "30"
|
||||
related: []
|
||||
related: [docs/adr/0016-notfallhandbuch-nicht-spiegeln.md]
|
||||
---
|
||||
# Der Restore ist nie geprobt — Sicherungen sind bisher eine Vermutung
|
||||
|
||||
@@ -42,3 +42,266 @@ Der letzte Punkt ist hier besonders relevant: **Der SOPS-age-Schlüssel entschl
|
||||
Ich kenne den Sicherungsaufbau nur aus deiner Beschreibung und einem Screenshot, nicht aus eigener Anschauung. Der erste Schritt ist deshalb Bestandsaufnahme, nicht Bewertung.
|
||||
|
||||
*Aufgenommen am 2026-08-06 bei einer Bestandsaufnahme der Sicherheitslage.*
|
||||
|
||||
## Schritt 1 erledigt 2026-08-14 — Bestandsaufnahme (Cluster-Seite)
|
||||
|
||||
Wie im Issue gefordert zuerst „das Billigste": in die vorhandenen Sicherungen hineingeschaut,
|
||||
nichts erweitert. Geprüft wurde die **Matrix-Cluster-Seite** (Hetzner/K3s); die Homelab-Seite
|
||||
(GitLab auf Overmind → MinIO/DSM) ist von hier nicht erreichbar und weiterhin ungeprüft.
|
||||
|
||||
**Befund: die Sicherungen selbst sind besser als vermutet.** Drei nächtliche CronJobs, alle
|
||||
`Complete`, keiner suspendiert, alle **off-host** per Borg auf eine Hetzner Storage Box
|
||||
(`u641795@…your-storagebox.de:23`) — also genau das Muster, das #0010 für Gitea erst plant:
|
||||
|
||||
| Job | Zeit | Ziel-Repo | Volumen (letzter Lauf) | Bewertung |
|
||||
|---|---|---|---|---|
|
||||
| `synapse-backup` (matrix) | 03:00 | `/./synapse-backup` | 199,5 MB, 247 Dateien, Dedup-Delta 3,7 MB | plausibel ✅ |
|
||||
| `authentik-backup` (authentik) | 03:15 | `/./authentik-backup` | ~150 MB gesamt, Delta ~15 MB | plausibel ✅ |
|
||||
| `wikijs-backup` (matrix) | 03:30 | `/./wikijs-backup` | 223 kB | plausibel ✅ (nur Postgres; die Wiki-**Inhalte** liegen per git-storage in Gitea/git.lab) |
|
||||
|
||||
Retention greift nachweislich (`borg prune`, 7 daily / 4 weekly / 6 monthly, „Deleted data"
|
||||
in den Logs). Kein stiller Ausfall, keine leeren Archive — die Sicherung ist **keine bloße
|
||||
Vermutung mehr**, zumindest was Existenz und Inhalt angeht.
|
||||
|
||||
### ⚠️ Kritischer Befund: Zirkelabhängigkeit beim age-Schlüssel
|
||||
|
||||
Genau das vom Issue vorhergesagte Muster („der Dump ist da, aber der Schlüssel lag nur auf dem
|
||||
Host, der weg ist") liegt real vor:
|
||||
|
||||
1. Zum **Lesen** der Borg-Repos braucht man Borg-Passphrase **und** SSH-Key.
|
||||
2. Beide liegen in `synapse-backup-secret.yaml` / `authentik-backup-secret.yaml` — **SOPS-verschlüsselt**.
|
||||
3. Entschlüsselbar ist das nur mit **einem einzigen** age-Schlüssel
|
||||
(Empfänger `age14l0hw…`, siehe `.sops.yaml`).
|
||||
4. Dieser private Schlüssel existiert an **genau zwei Orten**, und beide sind „heiß":
|
||||
- `sops-age`-Secret in `flux-system` — **im Cluster, den das Backup schützen soll**
|
||||
- `~/.age/keys.txt` auf sorbs Mac — **ein einzelnes Gerät**
|
||||
|
||||
**Folge:** Gehen Cluster und Mac zusammen verloren (Totalschaden Hetzner + Laptop weg/defekt),
|
||||
sind **alle drei Borg-Repos dauerhaft unlesbar**. Die Sicherungen wären technisch einwandfrei
|
||||
und trotzdem wertlos. Eine Suche über `docs/` und `hosts/` findet **keine** dokumentierte
|
||||
Auslagerung (Escrow, Offline-Kopie, Passwort-Manager) des Schlüssels.
|
||||
|
||||
**Billigste wirksame Gegenmaßnahme (vor jedem Restore-Test):** eine **kalte Kopie** des
|
||||
age-Schlüssels außerhalb von Cluster und Mac anlegen — Passwort-Manager und/oder Ausdruck an
|
||||
sicherem Ort — und die Fundstelle in `hosts/` dokumentieren (nur *wo*, nie der Wert). Erst danach
|
||||
lohnt der eigentliche Restore-Durchlauf, sonst probt man einen Ablauf, dessen Voraussetzung
|
||||
selbst ungesichert ist.
|
||||
|
||||
### Nächste Schritte (unverändert nach Issue-Plan)
|
||||
|
||||
2. Restore-Verfahren schreiben (Reihenfolge, Herkunft von age-Key und kubeconfig).
|
||||
3. Einmal gegen eine Wegwerf-Umgebung durchspielen.
|
||||
4. Ergebnis nach `verfahren/` und Wiederholungsrhythmus festlegen.
|
||||
|
||||
## Update 2026-08-14 — age-Schlüssel ist ausgelagert (Befund entschärft)
|
||||
|
||||
sorb bestätigt: der private age-Schlüssel liegt **zusätzlich im Passwort-Vault**, also außerhalb
|
||||
von Cluster und Mac. Damit ist die oben beschriebene Zirkelabhängigkeit **aufgelöst** — ein
|
||||
gleichzeitiger Verlust von Hetzner-Host und Laptop macht die Borg-Repos nicht mehr unlesbar.
|
||||
Der Befund bleibt hier stehen, weil die Kette (Backup lesen → Borg-Passphrase → SOPS → **ein**
|
||||
age-Schlüssel) beim Schreiben des Restore-Verfahrens explizit auftauchen muss: der Vault ist
|
||||
Teil des Wiederherstellungswegs, nicht nur Nebensache.
|
||||
|
||||
**Zu ergänzen beim Verfahren (Schritt 2):** Fundort des Schlüssels benennen (nur *wo*, nie der
|
||||
Wert) und im Restore-Ablauf als ersten Schritt führen — ohne ihn ist kein weiterer Schritt
|
||||
möglich.
|
||||
|
||||
## Restaufwand nach heutiger Bestandsaufnahme
|
||||
|
||||
Schritt 1 (Bestandsaufnahme) ist erledigt und positiv ausgefallen; Schritt 2–4 stehen aus:
|
||||
Restore-Verfahren schreiben, einmal gegen eine Wegwerf-Umgebung durchspielen, Ergebnis nach
|
||||
`verfahren/` und Wiederholungsrhythmus. Die Homelab-Seite (GitLab auf Overmind → MinIO/DSM)
|
||||
ist weiterhin ungeprüft — von der Hetzner-Seite aus nicht erreichbar.
|
||||
|
||||
## Schritt 2 erledigt 2026-08-14 — Restore-Verfahren geschrieben
|
||||
|
||||
[`docs/wiki/deployment/restore.md`](../wiki/deployment/restore.md) — als Ablauf, nicht als Prosa:
|
||||
Voraussetzungen (age-Key aus dem Vault zuerst), Fundort und Layout der drei Borg-Repos,
|
||||
Bootstrap-Reihenfolge (Host/K3s → die zwei manuellen Secrets `sops-age` + `flux-system` → Flux →
|
||||
`infra-apps` → production/authentik/monitoring), Daten-Rückspielung und Verifikation.
|
||||
|
||||
**Abweichung vom Issue-Wortlaut:** abgelegt unter `docs/wiki/deployment/` statt `verfahren/` —
|
||||
dort liegt bereits die Deploy-Übergabe, nur `docs/**` wird von `validate.py` geprüft, und die
|
||||
Seite ist über den Wiki-Index auffindbar. `verfahren/` enthält bislang ausschließlich Skripte.
|
||||
|
||||
**Zwei Erkenntnisse beim Schreiben, die vorher nicht dokumentiert waren:**
|
||||
|
||||
1. **Flux zieht aus Gitea (rohana), nicht aus git.lab.** Ist rohana beim Ausfall ebenfalls weg,
|
||||
hängt der Wiederanlauf an einem Host, der gar nicht Teil des Backup-Konzepts ist — dann muss
|
||||
zuerst eine erreichbare Git-Quelle hergestellt und `gotk-sync.yaml` umgebogen werden. Im
|
||||
Verfahren als Warnung vermerkt.
|
||||
2. **Konsumenten müssen vor dem `pg_restore` heruntergefahren werden.** Synapse/MAS/Authentik/
|
||||
Wiki.js legen beim Start ein leeres Schema an, gegen das ein Restore kollidiert.
|
||||
|
||||
Ebenfalls festgehalten: Borg nutzt `repokey-blake2`, die Passphrase allein genügt (keine separate
|
||||
Schlüsseldatei), und Passphrase/SSH-Key sind **ohne Cluster** per `sops -d` aus einem lokalen
|
||||
Clone lesbar — der age-Key aus dem Vault ist damit tatsächlich der einzige harte Startpunkt.
|
||||
|
||||
**Offen: Schritt 3** (einmal gegen eine Wegwerf-Umgebung durchspielen) und **Schritt 4**
|
||||
(Wiederholungsrhythmus). Das Verfahren ist bis dahin abgeleitet, aber unerprobt.
|
||||
|
||||
## Schritt 3 (teilweise) erledigt 2026-08-14 — Restore-Probe bestanden
|
||||
|
||||
Eigenes Repo **`git.lab/axion1337.chat/notfallhandbuch`** angelegt (Wunsch sorb): Einstiegs-
|
||||
README, das Verfahren (`restore-matrix.md`) und ein menügeführtes Werkzeug (`notfall.sh`).
|
||||
Das Verfahren wurde aus `docs/wiki/deployment/` dorthin verschoben — hier steht nur noch ein
|
||||
Zeiger. Begründung: im Ernstfall will man **einen** Clone, nicht drei Repos absuchen.
|
||||
|
||||
**Der Beweis, den dieses Issue verlangt, ist für die Datenbanken erbracht.** `notfall.sh`
|
||||
Stufe 3 spielt die Sicherungen in eine **Wegwerf-Postgres im Pod** zurück — isoliert, die
|
||||
Produktion bleibt unberührt, beliebig wiederholbar:
|
||||
|
||||
| Repo | Datenbank | zurückgespielte Zeilen |
|
||||
|---|---|---|
|
||||
| synapse-backup | `synapse` | **31.908** |
|
||||
| synapse-backup | `matrixauthenticationservice` | **16.085** |
|
||||
| authentik-backup | `authentik` | **325.149** |
|
||||
| wikijs-backup | `wiki` | **251** |
|
||||
|
||||
Die Sicherungen sind damit **keine Vermutung mehr** — sie wurden tatsächlich zurückgespielt.
|
||||
|
||||
**Entwurfsentscheidungen des Werkzeugs (bewusst, nicht beiläufig):**
|
||||
- Arbeit läuft **im Cluster**, nicht auf dem Laptop: `axion-backup:v2` bringt borg + pg-Tools
|
||||
mit, die Backup-Secrets verlassen den Cluster nicht, lokal genügt `kubectl`.
|
||||
- `whiptail` wird genutzt **falls vorhanden**, sonst Textmenü — ein Notfallwerkzeug darf nicht
|
||||
mit „installier erst ein Paket" beginnen (auf dem Mac fehlt whiptail).
|
||||
- **Bestehenskriterium ist die Zeilenzahl, nicht der Exitcode.** Die erste Fassung meldete bei
|
||||
fehlgeschlagenem `pg_restore` fälschlich „OK" (Pipe verschluckte den Code) — genau der
|
||||
stille Ausfall, den dieses Issue beschreibt, nur im Prüfwerkzeug selbst. Behoben und
|
||||
gegengetestet.
|
||||
|
||||
**Weiterhin offen (Rest von Schritt 3 + Schritt 4):**
|
||||
- **Phase A/B nie durchgespielt:** Wiederanlauf auf leerem Host (K3s, die zwei Bootstrap-
|
||||
Secrets, Flux) ist abgeleitet, nicht getestet — das braucht eine Wegwerf-Umgebung.
|
||||
- **Synapse-Medien** (`media_store` → PVC) nicht erprobt; ohne sie sind Bilder in Räumen tote Links.
|
||||
- **Wiederholungsrhythmus** festlegen (Stufe 3 ist gefahrlos → z.B. monatlich).
|
||||
- Homelab-Seite (GitLab/Overmind → MinIO/DSM) weiterhin ungeprüft.
|
||||
|
||||
**Keine Spiegelung — bewusste Ausnahme (Entscheidung sorb 2026-08-14):** Anders als alle
|
||||
übrigen Repos wird das Notfallhandbuch **nicht** nach Gitea gespiegelt. Es beschreibt die
|
||||
Infrastruktur, den Ablageort der Sicherungen und wo die Schlüssel liegen; auf dem öffentlich
|
||||
erreichbaren Gitea wäre es bei einer Kompromittierung des Stacks genau die Landkarte, die ein
|
||||
Angreifer braucht. Vertraulichkeit vor Verfügbarkeit. (Meine ursprüngliche Empfehlung, zu
|
||||
spiegeln, war damit falsch — sie hatte nur die Verfügbarkeit im Blick.) Die Verfügbarkeitslücke
|
||||
— git.lab ist nur im Lab erreichbar — wird über einen **lokalen Clone** abgedeckt, nicht über
|
||||
einen Mirror. Begründung steht im README des Notfallhandbuchs, damit sie nicht erneut
|
||||
"wegoptimiert" wird.
|
||||
|
||||
Das ist eine **dauerhafte Ausnahme von der Spiegel-Topologie** (ADR-0001) und daher als
|
||||
**ADR-0016** festgehalten.
|
||||
|
||||
## Schritt 4 erledigt 2026-08-14 — Rhythmus festgelegt (automatisiert + überwacht)
|
||||
|
||||
**Monatlich, 4. um 04:20**, als CronJob `restore-drill`
|
||||
(`gitops:apps/production/restore-drill.yaml`, Commit `b61dfd9`) — nach den nächtlichen
|
||||
Backups, damit er den frischen Stand zieht. Er spielt die Sicherungen in eine
|
||||
Wegwerf-Postgres **im Pod** zurück und besteht nur, wenn Zeilen ankommen. Vor dem Commit
|
||||
manuell ausgelöst und bestanden (synapse 31.908, MAS 16.085, wiki 251).
|
||||
|
||||
**Automatisiert statt dokumentiert:** ein Prüfrhythmus, den niemand ausführt, ist derselbe
|
||||
Fehler wie ein ungetestetes Backup — nur eine Ebene höher.
|
||||
|
||||
**Abdeckung:** synapse + matrixauthenticationservice + wiki (die unersetzlichen Daten).
|
||||
Authentik ist bewusst **nicht** im automatischen Lauf: Flows/Provider liegen als Blueprints
|
||||
deklarativ im Repo, die DB ist also weitgehend reproduzierbar — und der Job müsste sonst
|
||||
wegen der namespace-gebundenen Credentials dupliziert werden. Auf Zuruf über
|
||||
`notfall.sh` Stufe 3 (deckt alle drei Repos ab) jederzeit prüfbar.
|
||||
|
||||
### Nebenbefund, der wichtiger war als der Rhythmus selbst
|
||||
|
||||
**Es gab überhaupt keine Alarmregel zu Backups.** Ein fehlgeschlagenes nächtliches Backup
|
||||
wäre unbemerkt geblieben — exakt der stille Ausfall, den dieses Issue beschreibt, nur an
|
||||
der Stelle, die ihn hätte melden sollen. Behoben in
|
||||
`threadnet-operating:monitoring/prometheus/alerts.yml` (Commit `1bbff5e`), neue Gruppe
|
||||
`axion-backup`:
|
||||
|
||||
| Alarm | feuert wenn |
|
||||
|---|---|
|
||||
| `BackupJobFailed` | ein Backup- oder Probe-Job fehlschlägt |
|
||||
| `BackupNotRunning` | ein CronJob seit >26h nicht mehr geplant hat |
|
||||
| `RestoreDrillStale` | die Probe seit >40 Tagen nicht lief |
|
||||
|
||||
Der letzte ist Absicht: **auch das Ausbleiben der Prüfung ist ein Alarm.** Metrikweg
|
||||
verifiziert (kube-state-metrics → Alloy → remote_write; der Filter verwirft nur
|
||||
`go_.*|process_.*`, `kube_job_*`/`kube_cronjob_*` kommen an).
|
||||
|
||||
⚠️ **Deploy offen:** Die Alarmregeln liegen im Repo, sind aber noch **nicht auf CFGMON
|
||||
ausgerollt** (Prometheus dort, kein Zugang von hier). Bis zum Reload greifen sie nicht.
|
||||
|
||||
### Restaufwand
|
||||
|
||||
Nur noch **Phase A/B**: Wiederanlauf auf einem leeren Host (K3s, die zwei Bootstrap-Secrets,
|
||||
Flux) und die Rückspielung der Synapse-**Medien** sind weiterhin abgeleitet, nicht erprobt —
|
||||
dafür braucht es eine Wegwerf-Umgebung. Ebenso ungeprüft: die Homelab-Seite
|
||||
(GitLab/Overmind → MinIO/DSM).
|
||||
|
||||
## Nachtrag 2026-08-15 — Alarme ausgerollt, zwei Fehlalarme derselben Klasse gefunden
|
||||
|
||||
Der Rollout auf CFGMON ist durch und **verifiziert**: alle vier Regeln der Gruppe
|
||||
`axion-backup` sind geladen, evaluieren mit `health=ok` und stehen auf `inactive`. Die
|
||||
Fallback-Ausdrücke liefern echte Serien — die drei Backup-CronJobs mit
|
||||
`last_schedule_time` von heute Nacht, `restore-drill` greift wie vorgesehen auf
|
||||
`kube_cronjob_created` zurück, bis der erste geplante Lauf am 4.9. eine Schedule-Zeit setzt.
|
||||
|
||||
Beim Verifizieren kamen **zwei Befunde derselben Fehlerklasse** heraus — beide sind
|
||||
Varianten von „meldet Erfolg, ist aber blind":
|
||||
|
||||
**1. Fehlende Serie = Stille statt Alarm.** `RestoreDrillStale` hätte nie feuern können:
|
||||
ein manuell ausgelöster Job setzt keine `last_schedule_time`, und ein Ausdruck über eine
|
||||
nicht existierende Serie liefert nichts. Dieselbe Lücke in `BackupNotRunning`: wird ein
|
||||
CronJob *gelöscht* — exakt der Fall, den der Alarm abdecken soll — verschwindet die Serie,
|
||||
und der Alarm verstummt. Behoben (`threadnet-operating` `e0808ba`) durch Fallback auf
|
||||
`kube_cronjob_created`, **zusammengefasst per `max by (namespace, cronjob)`**: `or` matcht
|
||||
inklusive `__name__`, ein blanker Fallback hätte beide Serien zurückgegeben und die nie
|
||||
aktualisierte `created`-Zeit hätte nach Fristablauf dauerhaft falsch gefeuert. Ergänzt um
|
||||
`BackupCronJobMissing` (`absent()`), damit ein verschwundener CronJob **selbst** der Alarm ist.
|
||||
|
||||
**2. Reload meldet Erfolg und lädt den alten Stand.** `prometheus_config_last_reload_successful=1`
|
||||
nach SIGHUP — geladen waren trotzdem die alten Regeln. Ursache: Docker hängt
|
||||
Einzeldatei-Bindmounts am Inode auf, `git pull` ersetzt Dateien per Rename. Host-Datei 13
|
||||
Regeln, Container-Datei 12, unterschiedliche md5-Summen. Sichtbar wurde das **nur** durch
|
||||
den Vergleich Host↔Container; ein Neustart löste es.
|
||||
|
||||
⚠️ **Die eigentliche Lehre aus (2):** Diese Falle war in `monitoring/README.md` bereits
|
||||
ausführlich dokumentiert — mit Mechanik, richtigem Kommando (`--force-recreate`) und sogar
|
||||
dem Prüfbefehl — und hat trotzdem zugeschlagen. **Dokumentation hat den Fehler nicht
|
||||
verhindert.** Deshalb strukturell beseitigt statt besser beschrieben
|
||||
(`threadnet-operating` `4cb9bfb`): Prometheus und Alertmanager mounten jetzt ihr
|
||||
Config-**Verzeichnis**; Verzeichnis-Mounts lösen bei jedem Zugriff über den Pfad auf.
|
||||
Config-Pfade unverändert. Die verbliebenen Einzeldatei-Mounts (loki, alloy, die Skripte)
|
||||
sind in der README benannt.
|
||||
|
||||
⚠️ **Deploy offen:** `4cb9bfb` ist gepusht, aber noch nicht auf CFGMON aktiv — die
|
||||
Mount-Änderung greift erst, wenn die Container neu erstellt werden (`docker compose up -d`
|
||||
genügt hier, da sich die Service-Definition ändert).
|
||||
|
||||
## Endstand Überwachung 2026-08-15 — Kette durchgängig, verifiziert
|
||||
|
||||
Drei Commits in `threadnet-operating`, alle live und im Betrieb gegengeprüft:
|
||||
|
||||
| Commit | Was | Nachweis |
|
||||
|---|---|---|
|
||||
| `e0808ba` | Regel-Ausdrücke mit Fallback (`max by` über `… or kube_cronjob_created`) + `BackupCronJobMissing` | 4 Regeln `health=ok`, Fallback liefert Serien statt Leere |
|
||||
| `4cb9bfb` | Prometheus/Alertmanager mounten Config-**Verzeichnis** statt Einzeldateien | md5 Host↔Container identisch; beim **nächsten** Rollout genügte erstmals ein echter SIGHUP-Reload — der Fix hat sich sofort bewährt |
|
||||
| `e9c13dc` | `operating_alertmanager`-Scrape + `AlertDeliveryFailing`; veraltete README-Passage korrigiert | `up{job="operating_alertmanager"}=1`, 14 Regeln `health=ok`, 80 Serien `alertmanager_notifications_failed_total` (alle 0) |
|
||||
|
||||
**Damit ist die Alarmkette lückenlos beobachtet:** Metrik vorhanden (Fallback) → Regel
|
||||
evaluiert (`health=ok`) → Zustellung sichtbar (`AlertDeliveryFailing`) → Scrape-Ausfall
|
||||
gedeckt (`TargetDown`). `AlertDeliveryFailing` kann selbst nicht an fehlender Serie
|
||||
scheitern: der Zähler existiert ab dem ersten Scrape.
|
||||
|
||||
**Nebenkorrektur:** Die README behauptete weiterhin, die Alarm-Zustellung sei per
|
||||
`room="security"`-Null-Receiver stummgeschaltet. Diese Route existiert seit gitops#51
|
||||
nicht mehr — Alarme **werden** zugestellt, auch die neuen Backup-Regeln (kein `room`-Label
|
||||
→ Default-Route auf den `matrix`-Receiver). Eine Doku, die fälschlich „ist stummgeschaltet"
|
||||
sagt, hätte ein ausbleibendes Signal als bekannt-und-erwartet erscheinen lassen.
|
||||
|
||||
### Offen (bewusst nicht reflexhaft gelöst)
|
||||
|
||||
- **`TrivyScanStale`** hat dieselbe Lücke wie ursprünglich `RestoreDrillStale`: ein Image,
|
||||
das **nie** erfolgreich gescannt wurde, hat keine Serie, an der `time() - …` hängen
|
||||
könnte, und bleibt still. Anders als dort gibt es **kein natürliches Pendant zu
|
||||
`kube_cronjob_created`** — es bräuchte eine Soll-Liste der erwarteten Images. Das ist eine
|
||||
Entscheidung, kein Handgriff; gehört fachlich zu #0025/CVE-Pipeline.
|
||||
- **Phase A/B des Restore-Verfahrens** (Wiederanlauf auf leerem Host, Synapse-Medien) —
|
||||
unverändert offen, braucht eine Wegwerf-Umgebung.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0031"
|
||||
status: open
|
||||
status: done
|
||||
created: 2026-08-09
|
||||
milestone: M1
|
||||
priority: low
|
||||
@@ -51,3 +51,20 @@ Mehr ist nicht zu tun — der Code steht, er wartet nur auf die Zugänge.
|
||||
- ✅ `GITLAB_TOKEN` hinterlegt, in der CI verifiziert
|
||||
- ✅ Zeitplan `Stillstandsprüfung (täglich)` angelegt, 6:17 Europe/Berlin
|
||||
- ✅ Erster Lauf über den Zeitplan durchgeführt: fand in der CI **dieselben 7 Befunde** wie lokal — kein Unterschied zwischen den Umgebungen
|
||||
|
||||
## Erledigt 2026-08-19 — Authentik-Teil läuft
|
||||
|
||||
sorb hat `AUTHENTIK_URL` und `AUTHENTIK_TOKEN` als maskierte, geschützte CI-Variablen
|
||||
im management-Projekt hinterlegt (dazu `GITEA_TOKEN`, der die privaten Spiegel
|
||||
gegenlesbar macht). Der Token trägt genau eine Berechtigung —
|
||||
`authentik_blueprints.view_blueprintinstance` —, vergeben an ein eigenes Dienstkonto;
|
||||
kein Admin.
|
||||
|
||||
**Belegt am Lauf, nicht an der Konfiguration** (Pipeline 540): Der Vermerk
|
||||
„Uebersprungen: Authentik-Blueprints … fehlen" ist verschwunden, stattdessen meldet die
|
||||
Prüfung `Geprueft: 11 Projekte der Gruppe axion1337.chat` und **Keine offenen Befunde**.
|
||||
|
||||
Damit ist genau der blinde Fleck geschlossen, den dieses Issue als den ärgerlichsten
|
||||
benannt hat: Der `matrix-recovery-flow`-Blueprint wurde tagelang bei jedem Durchlauf
|
||||
verworfen, während Flux grün meldete — gefunden nur, weil jemand aus anderem Anlass in
|
||||
die Datenbank sah. Dieser Fall würde jetzt auffallen.
|
||||
|
||||
@@ -7,6 +7,7 @@ milestone: M2
|
||||
priority: low
|
||||
host: overmind
|
||||
related: []
|
||||
gitlab_iid: "33"
|
||||
---
|
||||
|
||||
# OVERMIND-01 — element-desktop-build von rohana in die Lab-Registry umziehen
|
||||
|
||||
@@ -7,6 +7,7 @@ milestone: M2
|
||||
priority: medium
|
||||
host: cfgmon
|
||||
related: []
|
||||
gitlab_iid: "34"
|
||||
---
|
||||
|
||||
# CFGMON-11 — Gitea-CI-Rückbau abschließen (sicher rückbaubare Schritte)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0035"
|
||||
status: open
|
||||
status: done
|
||||
created: 2026-08-11
|
||||
milestone: M2
|
||||
priority: medium
|
||||
@@ -21,3 +21,15 @@ Gruppenregeln (management-Repo, git.lab + rohana-Mirror-URL). Bestehende
|
||||
projektspezifische CLAUDE.md-Inhalte (gitops) bleiben erhalten und
|
||||
rücken unter den Pointer. Danach meldet `gruppenpruefung.py` die
|
||||
Komponente grün.
|
||||
|
||||
## Erledigt 2026-08-15
|
||||
|
||||
`CLAUDE.md` (Ein-Zeilen-Pointer) und `AGENTS.md` in `axion1337.chat-gitops` angelegt — Commit `23533c7`.
|
||||
|
||||
Bestehende `CLAUDE.md` (Projektdoku) nach `AGENTS.md` verschoben, `CLAUDE.md` ist jetzt der
|
||||
Ein-Zeilen-Pointer. Der Verweis auf die Gruppenregeln zeigt jetzt auf `management/AGENTS.md` —
|
||||
die dortige `CLAUDE.md` war selbst schon zum Pointer geworden, der Verweis lief also ins Leere.
|
||||
|
||||
**Verifiziert mit dem Prüfskript:** `gruppenpruefung.py` meldete vorher
|
||||
„axion1337.chat-gitops: keine CLAUDE.md-Pointer-Datei (F-011)" und meldet es jetzt nicht mehr
|
||||
(27 → 23 Befunde nach dem Rollout, exakt die vier Pointer-Befunde entfallen).
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0036"
|
||||
status: open
|
||||
status: done
|
||||
created: 2026-08-11
|
||||
milestone: M2
|
||||
priority: medium
|
||||
@@ -21,3 +21,15 @@ Gruppenregeln (management-Repo, git.lab + rohana-Mirror-URL). Bestehende
|
||||
projektspezifische CLAUDE.md-Inhalte (gitops) bleiben erhalten und
|
||||
rücken unter den Pointer. Danach meldet `gruppenpruefung.py` die
|
||||
Komponente grün.
|
||||
|
||||
## Erledigt 2026-08-15
|
||||
|
||||
`CLAUDE.md` (Ein-Zeilen-Pointer) und `AGENTS.md` in `ThreadNet-Web` angelegt — Commit `e7b0028`.
|
||||
|
||||
AGENTS.md benennt das Wesentliche: `README.md` und der Großteil von `docs/` sind **unverändertes
|
||||
Upstream-Material** und beschreiben den Fork nicht — `docs/axion1337-fork.md` ist der einzige Ort,
|
||||
der zählt, und zugleich Portier-Checkliste fürs nächste Upgrade.
|
||||
|
||||
**Verifiziert mit dem Prüfskript:** `gruppenpruefung.py` meldete vorher
|
||||
„ThreadNet-Web: keine CLAUDE.md-Pointer-Datei (F-011)" und meldet es jetzt nicht mehr
|
||||
(27 → 23 Befunde nach dem Rollout, exakt die vier Pointer-Befunde entfallen).
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0037"
|
||||
status: open
|
||||
status: done
|
||||
created: 2026-08-11
|
||||
milestone: M2
|
||||
priority: medium
|
||||
@@ -21,3 +21,14 @@ Gruppenregeln (management-Repo, git.lab + rohana-Mirror-URL). Bestehende
|
||||
projektspezifische CLAUDE.md-Inhalte (gitops) bleiben erhalten und
|
||||
rücken unter den Pointer. Danach meldet `gruppenpruefung.py` die
|
||||
Komponente grün.
|
||||
|
||||
## Erledigt 2026-08-15
|
||||
|
||||
`CLAUDE.md` (Ein-Zeilen-Pointer) und `AGENTS.md` in `threadnet-call` angelegt — Commit `c63be9a`.
|
||||
|
||||
Wie ThreadNet-Web, plus der Hinweis auf die doppelte Ableitung (Upstream → `emmick4/livekit` →
|
||||
hier), die Upgrades aufwendiger macht als bei einem geraden Fork.
|
||||
|
||||
**Verifiziert mit dem Prüfskript:** `gruppenpruefung.py` meldete vorher
|
||||
„threadnet-call: keine CLAUDE.md-Pointer-Datei (F-011)" und meldet es jetzt nicht mehr
|
||||
(27 → 23 Befunde nach dem Rollout, exakt die vier Pointer-Befunde entfallen).
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0038"
|
||||
status: open
|
||||
status: done
|
||||
created: 2026-08-11
|
||||
milestone: M2
|
||||
priority: medium
|
||||
@@ -21,3 +21,15 @@ Gruppenregeln (management-Repo, git.lab + rohana-Mirror-URL). Bestehende
|
||||
projektspezifische CLAUDE.md-Inhalte (gitops) bleiben erhalten und
|
||||
rücken unter den Pointer. Danach meldet `gruppenpruefung.py` die
|
||||
Komponente grün.
|
||||
|
||||
## Erledigt 2026-08-15
|
||||
|
||||
`CLAUDE.md` (Ein-Zeilen-Pointer) und `AGENTS.md` in `thread-net-git` angelegt — Commit `fc30ce8`.
|
||||
|
||||
AGENTS.md trägt die vier unumstößlichen Regeln aus dem README (Projektname, Volumes, kein
|
||||
Gitea-Downgrade, nie über Portainer) — plus die Einordnung, dass Gitea hier die **Flux-Quelle**
|
||||
und die Registry ist: ein Fehler wirkt bis in die Produktion, obwohl das Repo klein aussieht.
|
||||
|
||||
**Verifiziert mit dem Prüfskript:** `gruppenpruefung.py` meldete vorher
|
||||
„thread-net-git: keine CLAUDE.md-Pointer-Datei (F-011)" und meldet es jetzt nicht mehr
|
||||
(27 → 23 Befunde nach dem Rollout, exakt die vier Pointer-Befunde entfallen).
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0039"
|
||||
status: open
|
||||
status: done
|
||||
created: 2026-08-11
|
||||
milestone: M2
|
||||
priority: medium
|
||||
@@ -21,3 +21,15 @@ Gruppenregeln (management-Repo, git.lab + rohana-Mirror-URL). Bestehende
|
||||
projektspezifische CLAUDE.md-Inhalte (gitops) bleiben erhalten und
|
||||
rücken unter den Pointer. Danach meldet `gruppenpruefung.py` die
|
||||
Komponente grün.
|
||||
|
||||
## Erledigt 2026-08-15
|
||||
|
||||
`CLAUDE.md` (Ein-Zeilen-Pointer) und `AGENTS.md` in `threadnet-operating` angelegt — Commit `be6b5f0`.
|
||||
|
||||
AGENTS.md nennt die zwei heute teuer gelernten Punkte: fehlende Zeitreihen erzeugen **Stille
|
||||
statt Alarm**, und Config-Änderungen kommen bei Einzeldatei-Mounts nicht automatisch an —
|
||||
sichtbar nur im Container, nie auf der Platte.
|
||||
|
||||
**Verifiziert mit dem Prüfskript:** `gruppenpruefung.py` meldete vorher
|
||||
„threadnet-operating: keine CLAUDE.md-Pointer-Datei (F-011)" und meldet es jetzt nicht mehr
|
||||
(27 → 23 Befunde nach dem Rollout, exakt die vier Pointer-Befunde entfallen).
|
||||
|
||||
@@ -7,6 +7,7 @@ milestone: M2
|
||||
priority: low
|
||||
related:
|
||||
- "docs/design/done/2026-08-11-neckbeard-migration.md"
|
||||
gitlab_iid: "35"
|
||||
---
|
||||
|
||||
# neckbeard-Rückmeldungen aus dem Feldtest einreichen
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0041"
|
||||
status: open
|
||||
status: done
|
||||
created: 2026-08-11
|
||||
milestone: M2
|
||||
priority: low
|
||||
@@ -17,3 +17,24 @@ related: []
|
||||
Import-`wartegrund` „Grund im GitLab-Verlauf". Im nächsten Refinement
|
||||
je Issue den echten Grund eintragen (alte Regel: `wartet` nur mit
|
||||
benanntem Grund).
|
||||
|
||||
## Erledigt 2026-08-15 — alle sieben durchgegangen
|
||||
|
||||
Alle im Issue genannten Import-`wartegrund`e sind ersetzt; die generische Formel
|
||||
„Grund im GitLab-Verlauf" kommt nicht mehr vor. Ergebnis des Durchgangs:
|
||||
|
||||
| Issue | Ergebnis |
|
||||
|---|---|
|
||||
| #0002 GAME-01 | bleibt `waiting` — wartet auf vSwitch-Aufnahme (sorb). Nachgemessen: Exporter weiterhin dicht; ⚠️ Silences seit 2026-08-04 abgelaufen → `TargetDown` feuert seither alle 4 h |
|
||||
| #0004 OVERMIND-02 | bleibt `waiting` — datiertes Beobachtungsfenster bis ca. 2026-08-28 |
|
||||
| #0008 CFGMON-03 | bleibt `waiting` — wartet auf gemeinsamen Console-Blick. Nachgemessen: 9090/3100 von außen **dicht**, akute Exposition besteht nicht |
|
||||
| #0014 CFGMON-14 | → **`open`**: Entscheidung liegt längst als **ADR-0008** vor (Option A). Vorfrage zur Hälfte beantwortet: **MATRIX hat keine docker-Gruppe** |
|
||||
| #0021 OVERMIND-03 | bleibt `waiting` — wartet nur auf das Go für Variante C |
|
||||
| #0025 Deploy-Übergabe | → **`done`** (separat geschlossen, Deploy live und verifiziert) |
|
||||
| #0027 AUDIT-01 | → **`open`**: der blockierende Struktur-Workshop **hat 2026-08-06 stattgefunden**; W1 und W3 aber stichprobenartig als **weiterhin offen** nachgewiesen |
|
||||
|
||||
**Erkenntnis über den Auftrag hinaus:** Bei drei der sieben war der `waiting`-Zustand nicht
|
||||
nur unpräzise, sondern **sachlich falsch** — der Blocker war längst weg (#0014, #0027) oder
|
||||
die Aufgabe erledigt (#0025). Ein pauschal importierter Wartegrund verdeckt genau das. Die
|
||||
verbliebenen vier warten nachweislich auf eine benannte Handlung von sorb, nicht auf
|
||||
Unbekanntes.
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0042"
|
||||
status: open
|
||||
status: done
|
||||
created: 2026-08-11
|
||||
milestone: M2
|
||||
priority: high
|
||||
related:
|
||||
- "docs/design/done/2026-08-11-neckbeard-migration.md"
|
||||
gitlab_iid: "36"
|
||||
---
|
||||
|
||||
# Migration in Betrieb nehmen: Push, erster Spiegel-Lauf, CI-Schedule
|
||||
@@ -27,3 +28,28 @@ related:
|
||||
4. gitops#61 einen Meilenstein geben (M1 oder M5 an der
|
||||
ADR-0010-Trennlinie) — der rote Befund der Gruppenprüfung ist die
|
||||
Erinnerung.
|
||||
|
||||
## Erledigt 2026-08-18 — alle vier Schritte
|
||||
|
||||
**Schritt 2, erster Spiegel-Lauf:** ausgeführt (von sorb freigegeben). 17 Aktionen:
|
||||
sieben Neuanlagen (`management#33`–`#39`), fünf Schließungen, zwei Wiederöffnungen,
|
||||
drei Label-/Meilenstein-Korrekturen. Die vergebenen iids sind zurückgeschrieben —
|
||||
das Skript tut das inzwischen selbst, sonst legte der nächste Lauf Duplikate an.
|
||||
Die Hinweise der Gruppenprüfung sind von 7 auf **0** gefallen.
|
||||
|
||||
**Schritt 4, Meilenstein für gitops#61:** M1 gesetzt (jetzt [#0091](0091-gitops-61-enrollment-kollidierender-localpart-uebernimm.md),
|
||||
inzwischen geschlossen — der Fix war längst ausgerollt).
|
||||
|
||||
**Schritt 3, CI-Schedule: war bereits erfüllt, nur nicht als solcher erkennbar.**
|
||||
Der Job `gruppenpruefung` trägt dieselbe Regel wie `stillstandspruefung`
|
||||
(`CI_PIPELINE_SOURCE == "schedule"`), und es gibt genau einen Zeitplan — `42 0 * * *`.
|
||||
Der fährt damit **beide** Jobs. Nachgeprüft an der letzten Schedule-Pipeline (472,
|
||||
2026-08-17 22:43): `gruppenpruefung` und `stillstandspruefung` sind beide gelaufen.
|
||||
Beide endeten rot, und das ist die Alarmfunktion, nicht ihr Defekt — die Gruppenprüfung
|
||||
verlässt sich mit Exit 1 bei Befunden genau darauf.
|
||||
|
||||
⚠️ **Stolperstein, bewusst benannt statt behoben:** Der Zeitplan heißt
|
||||
„Stillstandsprüfung (täglich)". Wer nach der Gruppenprüfung sucht, findet sie dort nicht
|
||||
— und wer den Zeitplan wegen der Stillstandsprüfung abschaltet, schaltet unbemerkt die
|
||||
Gruppenprüfung mit ab. Umbenennen (etwa „Nächtliche Prüfungen") ist ein Handgriff auf
|
||||
GitLab und liegt bei sorb.
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0043"
|
||||
status: done
|
||||
created: 2026-08-11
|
||||
milestone: M5
|
||||
priority: medium
|
||||
area: security
|
||||
related: [docs/adr/0011-enrollment-localpart-kollision-verweigern.md]
|
||||
---
|
||||
# Invitation-Flow: case-insensitive Eindeutigkeitsprüfung im Prompt-Stage
|
||||
|
||||
> Offener Rest aus [ADR-0011](../adr/0011-enrollment-localpart-kollision-verweigern.md); GitLab-seitig als [gitops#61](https://git.lab/axion1337.chat/axion1337.chat-gitops/-/issues/61) verfolgt.
|
||||
|
||||
## Problem / Motivation
|
||||
|
||||
Die Kontoübernahme über kollidierende Localparts ist geschlossen — MAS steht auf
|
||||
`on_conflict: fail` (ADR-0011, live nach MAS-Neustart). Das ist die harte
|
||||
Sicherheitsgrenze, aber sie greift erst **beim Login**: Ein Nutzer, der bei der
|
||||
Registrierung einen bereits vergebenen Namen (oder eine Schreibweise-Variante wie
|
||||
`boje` neben `Boje`) wählt, bekommt kein Feedback im Flow, sondern läuft später in
|
||||
einen fehlgeschlagenen Login. Authentiks eigene Eindeutigkeit ist case-sensitive
|
||||
und deckt Kollisionen mit bestehenden Matrix-Konten nicht ab.
|
||||
|
||||
## Acceptance
|
||||
|
||||
- Der `matrix-invitation`-Flow prüft im Prompt-Stage den gewünschten
|
||||
Benutzernamen **case-insensitive** gegen bestehende Konten und weist eine
|
||||
Kollision schon bei der Registrierung sichtbar ab.
|
||||
- Als Blueprint reproduzierbar (`apps/authentik/authentik-blueprints.yaml`), nicht
|
||||
nur als Klick im Admin-UI.
|
||||
- Gegenprobe: Registrierung mit einem existierenden Namen in abweichender
|
||||
Schreibweise scheitert im Flow, nicht erst beim Login.
|
||||
|
||||
## Notes
|
||||
|
||||
Rein defensive Ergänzung / UX — der Übernahme-Vektor selbst ist bereits zu.
|
||||
Deshalb M5 (Härtung, nicht Reparatur) und `priority: medium`.
|
||||
|
||||
## Erledigt 2026-08-15
|
||||
|
||||
Expression-Policy **`matrix-username-eindeutig-ci`** im Blueprint ergänzt und an die
|
||||
Prompt-Stage des `matrix-invitation`-Flows gebunden (gitops `afc4ad3`). Sie prüft
|
||||
`prompt_data.username` per `username__iexact` gegen bestehende Konten und weist eine
|
||||
Kollision mit sichtbarer Meldung ab — dort, wo der Fehler entsteht, statt später beim Login.
|
||||
|
||||
⚠️ **Bewusst ohne Zugriff auf `request.user`.** Die Stage läuft im **anonymen**
|
||||
Enrollment-Kontext; genau daran waren die früher hier hängenden 16 System-Policies gescheitert
|
||||
(`'AnonymousUser' object has no attribute 'group_attributes'`). Gelesen wird ausschließlich
|
||||
`prompt_data`. Der Kommentar im Blueprint hält das fest, damit die Falle nicht wiederkehrt.
|
||||
|
||||
**Verifiziert am laufenden System** (nicht nur „Blueprint gepusht"):
|
||||
|
||||
| Prüfung | Ergebnis |
|
||||
|---|---|
|
||||
| ConfigMap im Cluster | Policy enthalten |
|
||||
| Datei im Worker-Pod gemountet | Policy enthalten |
|
||||
| Blueprint von Authentik angewandt | Policy `matrix-username-eindeutig-ci` in der DB vorhanden |
|
||||
| Bindung an die Stage | `matrix-invitation-prompt → matrix-username-eindeutig-ci` |
|
||||
|
||||
Authentik hat den Blueprint **selbstständig** übernommen — kein Neustart nötig.
|
||||
|
||||
**Grenze, die bleibt:** Geprüft wird gegen **Authentik**-Konten. Ein reines Matrix-Konto ohne
|
||||
Authentik-Entsprechung fällt weiterhin erst bei MAS auf (`on_conflict: fail`, ADR-0011) — das
|
||||
bleibt die harte Sicherheitsgrenze, diese Policy ist die freundliche davor. Eine Prüfung gegen
|
||||
Synapse aus einer Policy heraus hieße Netzwerkaufruf plus Credentials im Ausdruck; das wäre
|
||||
schlechter als der bestehende Zweiklang.
|
||||
@@ -0,0 +1,118 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0044"
|
||||
status: done
|
||||
created: 2026-08-11
|
||||
milestone: M5
|
||||
priority: low
|
||||
area: infrastructure
|
||||
related: [docs/adr/0011-enrollment-localpart-kollision-verweigern.md]
|
||||
---
|
||||
# SOPS-Values-Secret-Änderung startet den konsumierenden Dienst nicht neu
|
||||
|
||||
## Problem / Motivation
|
||||
|
||||
Beim Ausrollen des `on_conflict: fail`-Fixes (ADR-0011) trat ein Footgun zutage:
|
||||
Flux aktualisierte das SOPS-verwaltete Secret `ess-mas-values-secret`, aber der
|
||||
laufende MAS-Pod war älter als die Änderung und behielt die alte Config im
|
||||
Speicher — MAS liest seine Config nur beim Start. Der Sicherheits-Fix stand auf
|
||||
der Platte, war im Prozess aber **nicht aktiv**, bis ein manueller
|
||||
`kubectl rollout restart` folgte. „committet ≠ deployed ≠ aktiv."
|
||||
|
||||
Das betrifft nicht nur MAS: jeder Dienst, der ein von Flux/SOPS gepflegtes
|
||||
Values-Secret nur beim Start liest, hat dieselbe stille Lücke. Aktuell ist die
|
||||
einzige Absicherung die in ADR-0011 festgeschriebene **manuelle** Restart-und-
|
||||
Verifikations-Regel — leicht zu vergessen.
|
||||
|
||||
## Acceptance
|
||||
|
||||
- Änderungen an einem konsumierten Values-Secret lösen automatisch einen Rollout
|
||||
des abhängigen Deployments aus (z. B. Checksum-/Hash-Annotation auf dem
|
||||
Pod-Template, ein Reloader wie stakater/Reloader, oder ein
|
||||
`configMapGenerator`/`secretGenerator`-Namenshash in kustomize).
|
||||
- Verifiziert an MAS: Änderung am Secret → neuer Pod ohne Handeingriff, jünger als
|
||||
die Änderung.
|
||||
- Mindestens MAS abgedeckt; idealerweise als wiederverwendbares Muster für weitere
|
||||
von SOPS-Secrets gespeiste Dienste.
|
||||
|
||||
## Notes
|
||||
|
||||
Automatisiert nur den bereits in ADR-0011 vorgeschriebenen manuellen Schritt —
|
||||
daher `priority: low`. M5, weil die Fähigkeit (Auto-Reload) neu ist, nicht kaputt.
|
||||
|
||||
## Untersuchung 2026-08-15 — die Abdeckung ist deutlich besser als angenommen
|
||||
|
||||
Vor dem Einbau eines Reloaders geprüft, wo die Lücke **tatsächlich** klafft. Ergebnis: der im
|
||||
Issue beschriebene Fall (MAS) ist bereits abgedeckt, und zwar von der Chart selbst.
|
||||
|
||||
**MAS ist abgedeckt — verifiziert am laufenden Deployment.** Die ESS-Chart hängt Prüfsummen
|
||||
als **Labels** ans Pod-Template (`templates/matrix-authentication-service/deployment.yaml`):
|
||||
|
||||
```
|
||||
k8s.element.io/matrix-authentication-service-config-hash: sha1sum(configmap-data)
|
||||
k8s.element.io/matrix-authentication-service-secret-hash: sha1sum(secret-data)
|
||||
```
|
||||
|
||||
Ein geändertes Label ändert das Pod-Template — Kubernetes rollt also von selbst aus. Live
|
||||
vorhanden (drei Hash-Labels am MAS-Deployment). Und der HelmRelease steht auf `interval: 1m`,
|
||||
Flux liest `valuesFrom` also jede Minute neu ein und rendert bei Änderung neu.
|
||||
|
||||
**Damit ist die Ursachenannahme des Issues zu korrigieren:** Der Mechanismus fehlte nicht.
|
||||
Wahrscheinlicher ist, dass beim ADR-0011-Vorfall innerhalb des ersten Reconcile-Fensters
|
||||
geprüft wurde — „committet ≠ deployed" stimmt, aber die Lücke war **zeitlich**, nicht
|
||||
strukturell. Das ändert nichts an der Richtigkeit der ADR-0011-Regel (nach einem
|
||||
Sicherheits-Fix aktiv verifizieren), wohl aber an der Diagnose.
|
||||
|
||||
**Abdeckung im Überblick** (Hash-Labels am Pod-Template gezählt):
|
||||
|
||||
| Abgedeckt | wodurch |
|
||||
|---|---|
|
||||
| MAS (3), element-web (2), haproxy (3), matrix-rtc (1–2) | ESS-Chart-Hash-Labels |
|
||||
| coturn + Synapse (TURN-Secret) | eigener Mechanismus: der Rotations-CronJob hebt `rotated-at`/Checksum-Annotationen, der Merge startet beide Verbraucher neu (#38) |
|
||||
|
||||
| Nicht abgedeckt | konsumiertes Secret |
|
||||
|---|---|
|
||||
| `draupnir` | `draupnir-config` |
|
||||
| `wikijs` | `wikijs-postgres-secret` |
|
||||
| `concierge-bot` | `concierge-credentials` |
|
||||
|
||||
**Bewertung.** Übrig bleiben drei Dienste mit Secrets, die sich **selten und stets absichtlich**
|
||||
ändern — anders als das monatlich rotierende TURN-Secret, das genau deshalb bereits einen
|
||||
eigenen Trigger hat. Ein zusätzlicher Controller (stakater/Reloader) bräuchte Rechte, Deployments
|
||||
zu patchen, und würde eine Dauerkomponente für ein Risiko einführen, das bei den wirklich
|
||||
bewegten Secrets bereits gelöst ist.
|
||||
|
||||
**Empfehlung: nicht einbauen**, sondern diese Abdeckungskarte als Ergebnis festhalten und die
|
||||
ADR-0011-Regel (aktiv verifizieren) für die drei Nachzügler gelten lassen. Wer anderer Meinung
|
||||
ist, hat mit Reloader einen sauberen Weg — die Chart erlaubt Annotationen je Komponente
|
||||
(`matrixAuthenticationService.annotations`, schema-geprüft), sie landen am Deployment **und** am
|
||||
Pod-Template.
|
||||
|
||||
## Geschlossen 2026-08-15 — Entscheidung sorb: kein Reloader
|
||||
|
||||
Die Abdeckungskarte oben ist das Ergebnis; ein zusätzlicher Controller wird **bewusst nicht**
|
||||
eingebaut.
|
||||
|
||||
**Begründung.** Das Ziel des Issues — „Änderungen an einem konsumierten Values-Secret lösen
|
||||
automatisch einen Rollout aus" — ist für die Dienste, bei denen sich Secrets real bewegen,
|
||||
**bereits erfüllt**, nur anders als vermutet:
|
||||
|
||||
- **MAS und die übrigen ESS-Komponenten** über die Hash-Labels der Chart am Pod-Template
|
||||
(live verifiziert), zusammen mit `interval: 1m` am HelmRelease.
|
||||
- **coturn und Synapse** über den `rotated-at`-Bump des Rotations-CronJobs — der einzige Fall
|
||||
mit regelmäßiger, unbeaufsichtigter Secret-Änderung, und genau deshalb schon gelöst (#38).
|
||||
|
||||
Übrig bleiben `draupnir`, `wikijs` und `concierge-bot`: Secrets, die sich **selten und stets
|
||||
durch bewusstes Handeln** ändern — also in dem Moment, in dem ohnehin jemand hinschaut und die
|
||||
ADR-0011-Regel (aktiv verifizieren) greift. Dafür einen Dauer-Controller mit Rechten, beliebige
|
||||
Deployments zu patchen, in die Produktion zu stellen, ist der schlechtere Tausch: bleibende
|
||||
Angriffsfläche und Wartungslast gegen ein Restrisiko, das genau dort klein ist, wo es zählt.
|
||||
|
||||
**Das eigentliche Ergebnis dieses Issues ist die Karte, nicht der Einbau.** Vorher wusste
|
||||
niemand, dass MAS abgedeckt ist — die Annahme war das Gegenteil, und ein Reloader wäre auf ein
|
||||
bereits gelöstes Problem gesetzt worden.
|
||||
|
||||
**Falls die Entscheidung später kippt** (etwa wenn ein Dienst dazukommt, dessen Secret
|
||||
automatisiert rotiert): Der Weg ist vorbereitet — die ESS-Chart erlaubt Annotationen je
|
||||
Komponente (`matrixAuthenticationService.annotations`, schema-geprüft), und sie landen sowohl am
|
||||
Deployment als auch am Pod-Template, also genau dort, wo stakater/Reloader sie liest.
|
||||
@@ -0,0 +1,80 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0045"
|
||||
status: done
|
||||
created: 2026-08-11
|
||||
milestone: M1
|
||||
priority: low
|
||||
area: element
|
||||
related: []
|
||||
---
|
||||
# Inhalts-Meldung führt ins Leere: kein Kontaktweg für Melder
|
||||
|
||||
## Problem / Motivation
|
||||
|
||||
Der Web-Client kann Inhalte melden (`report_event`), aber
|
||||
`report_event.admin_message_md` ist in `element-values.yaml` **nicht gesetzt**
|
||||
(Stand 2026-08-11 gegen die Live-Config geprüft). Wer etwas meldet, sieht danach
|
||||
keinen Kontaktweg — keine Angabe, an wen die Meldung geht oder wo man nachfassen
|
||||
kann. Für einen Community-Betrieb mit Moderation (Draupnir) ist das eine offene
|
||||
Kante: Melden funktioniert technisch, führt für den Nutzer aber ins Leere.
|
||||
|
||||
## Acceptance
|
||||
|
||||
- `report_event.admin_message_md` in
|
||||
`apps/production/custom-configs/element-values.yaml` gesetzt, mit einem echten
|
||||
Kontakt-/Meldeweg (Moderationsraum oder Kontaktadresse).
|
||||
- Live sichtbar: nach einer Meldung erscheint der hinterlegte Hinweis.
|
||||
|
||||
## Notes
|
||||
|
||||
Braucht **eine Angabe von sorb**: welcher Raum bzw. Kontakt der Meldeweg sein
|
||||
soll. Danach ist es eine Zeile Config. Bewusst nicht auf `waiting` gesetzt, weil
|
||||
noch nicht begonnen — der fehlende Input ist im Text benannt.
|
||||
|
||||
## Erledigt 2026-08-15
|
||||
|
||||
`report_event.admin_message_md` gesetzt (gitops `83a14e1`). Der Melder sieht nach dem
|
||||
Absenden jetzt, dass die Meldung angekommen ist — **und an wen er sich wenden kann**:
|
||||
|
||||
> Deine Meldung ist bei der Serveradministration eingegangen und wird gesichtet.
|
||||
> Für Rückfragen oder wenn es dringend ist, schreib bitte direkt an `@sorb:axion1337.chat`.
|
||||
|
||||
### Zustellweg: Weg B (Entscheidung sorb)
|
||||
|
||||
Zur Debatte stand, den Weg über **Draupnir** abzubilden (`pollReports`). Geprüft und
|
||||
**verworfen**: Draupnir liest Meldungen über die **Synapse-Admin-API** und müsste dafür
|
||||
**Server-Admin** werden — gemessen ist `@draupnir:axion1337.chat` heute `admin = 0`. sorb:
|
||||
*„ich mache keine Bots zum vollwertigen Admin."* Server-Admin hieße volle Admin-API
|
||||
(Nutzer deaktivieren, Räume löschen); ein kompromittierter Bot wäre ein kompromittierter
|
||||
Homeserver.
|
||||
|
||||
**Stattdessen Weg B:** Meldungen bleiben im `event_reports`-Speicher und werden über das
|
||||
bereits laufende **Element Admin** gesichtet. Deshalb benennt der Text **einen Menschen**
|
||||
statt einen Automatismus zu versprechen — `@sorb` ist der einzige Server-Admin und damit
|
||||
der Einzige, der die Meldungen überhaupt sehen kann.
|
||||
|
||||
### Kontext für die Einordnung
|
||||
|
||||
- **Bislang gab es 0 Meldungen** (`select count(*) from event_reports`). Der Melde-Knopf
|
||||
existierte, wurde nie benutzt, und es wäre niemandem aufgefallen.
|
||||
- Für Missbrauchsmeldungen wäre ein öffentlicher Raum ohnehin falsch gewesen — es gibt
|
||||
außer `#onboarding` (9 Mitglieder) und Testräumen keinen Raum, und eine Meldung gehört
|
||||
nicht vor Publikum.
|
||||
|
||||
### Verifiziert (live, nicht nur committet)
|
||||
|
||||
| Prüfung | Ergebnis |
|
||||
|---|---|
|
||||
| JSON weiterhin gültig | `config.json` parst |
|
||||
| ConfigMap im Cluster | enthält `admin_message_md` |
|
||||
| Rollout ausgelöst | Chart-Hash-Label wechselte nach **~70 s**, neuer Pod |
|
||||
| Öffentlich ausgeliefert | `https://axion1337.chat/config.json` enthält den Text |
|
||||
|
||||
**Nebenbefund:** Der Rollout bestätigt die Analyse aus #0044 empirisch — die
|
||||
Chart-Hash-Labels lösen den Neustart bei Config-Änderung selbstständig aus, innerhalb eines
|
||||
HelmRelease-Intervalls (1 min). Kein Handgriff nötig, kein Reloader.
|
||||
|
||||
⚠️ **Bleibt bewusst offen:** Weg B heißt, dass jemand **aktiv** in Element Admin nachsehen
|
||||
muss — es gibt keinen Alarm bei einer neuen Meldung. Bei 0 Meldungen bisher vertretbar;
|
||||
sollte sich das ändern, ist das der nächste Punkt.
|
||||
@@ -0,0 +1,51 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0046"
|
||||
status: done
|
||||
created: 2026-08-12
|
||||
milestone: M4
|
||||
priority: medium
|
||||
area: infrastructure
|
||||
related: [docs/issues/0024-wiki-hostname-klaeren-wiki-lab-oder-axionwiki.md, docs/adr/0007-wiki-oberflaeche-docusaurus-vs-bookstack.md]
|
||||
---
|
||||
# Wiki in die ThreadNet Server Suite umziehen
|
||||
|
||||
## Problem / Motivation
|
||||
|
||||
Das Wiki läuft aktuell als einzelner Dokploy-Stack auf Overmind
|
||||
(`axionwiki.lab`) mit Forward-Auth über den Prod-Authentik — ein bewusst
|
||||
provisorischer Entwicklungs-Zwischenstand (#0024, #0020). Die Vision „reproduzierbar
|
||||
für Dritte" verlangt aber, dass das Wiki **Teil des reproduzierbaren Stacks** wird,
|
||||
nicht ein Einzelstück auf dem Lab-Host: Eine forkende Community soll es mit dem Rest
|
||||
der ThreadNet Server Suite ausrollen können.
|
||||
|
||||
## Acceptance
|
||||
|
||||
- Das Wiki ist Bestandteil der ThreadNet-Server-Suite-Deployment-Definition
|
||||
(nicht mehr ein losgelöster Overmind-Stack).
|
||||
- Host, Reverse-Proxy und Auth-Anbindung sind für die Suite konsistent gelöst —
|
||||
die Forward-Auth-Verdrahtung aus gitops-Guide 09 wird dabei sauber abgelöst, nicht
|
||||
fortgeschleppt (sie ist genau darauf ausgelegt).
|
||||
- Instanzwerte (Host, Auth-Client) sind von generischer Struktur getrennt, damit
|
||||
ein Fork sie ohne Code-Änderung setzen kann.
|
||||
|
||||
## Notes
|
||||
|
||||
Beim Umzug ändert sich der Host erneut — der `axionwiki.lab`-Stand aus #0024 gilt
|
||||
nur bis dahin. Erst nach diesem Umzug greift #0047 (breitere Oberflächen-Evaluation).
|
||||
|
||||
## Realisierung (2026-08-12)
|
||||
|
||||
Der Umzug wird zugleich der Oberflächen-Wechsel: **Wiki.js statt Docusaurus**
|
||||
(ADR-0014, #0047 entschieden). Dieses Issue ist die Klammer; die konkrete Arbeit
|
||||
liegt in #0048 (Deployment + Git-Storage), #0049 (OIDC + Rollen/Abschottung) und
|
||||
#0050 (Theming). `homelab/docs` bleibt draußen.
|
||||
|
||||
## Erledigt 2026-08-13
|
||||
|
||||
Umzug vollzogen: Wiki.js ist Teil der Suite (#0048 done, inkl. Git-Storage +
|
||||
Kanonisierung Gitea→git.lab), OIDC/Rollen/Abschottung (#0049 done), Theming (#0050 done).
|
||||
Zusätzlich die **alten 15 Docusaurus-/gitops-Wiki-Seiten migriert**: 9 → `betrieb/`,
|
||||
ThreadNet-Desktop-Setup → `anwender/`, verworfen: Home/Old-Home/00-TASKS + 2
|
||||
Archiv-Recherchen. Inhalt liegt reproduzierbar in git. Instanzwerte per Job-Env von der
|
||||
generischen Struktur getrennt (forkbar); `homelab/docs` blieb draußen. → erledigt.
|
||||
@@ -0,0 +1,40 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0047"
|
||||
status: done
|
||||
created: 2026-08-12
|
||||
milestone: M2
|
||||
priority: low
|
||||
area: infrastructure
|
||||
related: [docs/adr/0007-wiki-oberflaeche-docusaurus-vs-bookstack.md, docs/issues/0020-doc-03-wiki-oberflaeche-entscheiden-docusaurus.md]
|
||||
---
|
||||
# Wiki-Oberfläche über BookStack hinaus prüfen
|
||||
|
||||
## Problem / Motivation
|
||||
|
||||
Die Wiki-Oberfläche wurde bisher nur als **Docusaurus vs. BookStack** betrachtet
|
||||
(ADR-0007, #0020). Für den Entwicklungs-Zwischenstand fiel die Entscheidung auf
|
||||
Docusaurus mit Forward-Auth. Nach dem Umzug in die ThreadNet Server Suite (#0046)
|
||||
soll die Frage aber **breiter** neu aufgemacht werden — nicht nur die alte
|
||||
Zwei-Optionen-Wahl, sondern weitere Kandidaten (z. B. Outline, Wiki.js, andere
|
||||
docs-as-code- oder App-basierte Lösungen), gemessen an dem, was die Suite dann
|
||||
tatsächlich braucht (Bereichs-Rechte? Browser-Editing? Git-Quelle?).
|
||||
|
||||
## Acceptance
|
||||
|
||||
- Ein Vergleich mehrerer Kandidaten (nicht nur Docusaurus/BookStack) gegen die
|
||||
dann geklärten Anforderungen der ThreadNet Server Suite.
|
||||
- Entscheidung als ablösendes ADR (ADR-0007 wird dann `superseded`), falls sich
|
||||
etwas anderes als Docusaurus durchsetzt.
|
||||
|
||||
## Notes
|
||||
|
||||
Bewusst `low` und ohne Termin: erst nach #0046 sinnvoll, vorher fehlt der Kontext
|
||||
(die Anforderungen der Suite). Hängt inhaltlich an #0046.
|
||||
|
||||
## Entscheidung (2026-08-12, sorb)
|
||||
|
||||
**Wiki.js.** Es erfüllt als einziges beide harten Kriterien — Abschottung nach
|
||||
Gruppe **und** docs-as-code (git-Storage). BookStack scheidet aus (Inhalt nur in
|
||||
DB, kein git). Festgehalten in ADR-0014 (löst ADR-0007 ab). Umsetzung über #0046
|
||||
und die daraus abgeleiteten Bau-Issues.
|
||||
@@ -0,0 +1,85 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0048"
|
||||
status: done
|
||||
created: 2026-08-12
|
||||
milestone: M4
|
||||
priority: medium
|
||||
area: infrastructure
|
||||
related: [docs/adr/0014-wikijs-loest-docusaurus-ab.md, docs/issues/0046-wiki-in-threadnet-server-suite-umziehen.md]
|
||||
---
|
||||
# Wiki.js in der ThreadNet Server Suite deployen (k8s + Postgres + Git-Storage)
|
||||
|
||||
## Problem / Motivation
|
||||
|
||||
ADR-0014: Wiki.js löst Docusaurus ab und wird Teil der ThreadNet Server Suite
|
||||
(k8s), nicht mehr ein Overmind-Einzelstack. Braucht das Deployment plus den
|
||||
Inhalts-Speicher.
|
||||
|
||||
## Acceptance
|
||||
|
||||
- Wiki.js läuft in der Suite (k8s): Deployment, **Postgres** (Rendern/Auth/Suche),
|
||||
in die GitOps-Definition aufgenommen (Flux).
|
||||
- **Öffentlich erreichbar unter `wiki.axion1337.chat`** (Entscheidung sorb
|
||||
2026-08-12) — wie die übrigen Subdomains: A-Record → `49.13.132.245`,
|
||||
cert-manager `Certificate` via ClusterIssuer `letsencrypt-prod`
|
||||
(Secret `wiki-axion1337-chat-tls`), Traefik-`IngressRoute` `Host(wiki.axion1337.chat)`
|
||||
→ Service `wikijs`:3000. Muster: `apps/authentik/ingress.yaml`. Kein
|
||||
Forward-Auth/Outpost — Wiki.js loggt selbst via OIDC ein (#0049).
|
||||
- **Neues `wiki`-Repo** auf git.lab angelegt (gespiegelt wie die übrigen), als
|
||||
Wiki.js **Git-Storage** eingebunden (bidirektionaler Sync) — Inhalt liegt in git
|
||||
(hartes Kriterium, ADR-0014).
|
||||
- Grundstruktur für **Betrieb** und **Anwender** angelegt (zwei Bereiche);
|
||||
`homelab/docs` ausdrücklich **nicht** eingebunden.
|
||||
- Postgres ist gesichert (Backup), da git nur Inhalt, nicht den Laufzeit-Zustand
|
||||
hält.
|
||||
|
||||
## Notes
|
||||
|
||||
Ersetzt den Forward-Auth-Zwischenstand auf Overmind (Guide 09, #0024). OIDC +
|
||||
Rollen kommen in #0049, Theming in #0050. Umbrella: #0046.
|
||||
|
||||
## Update 2026-08-13 — Git-Storage-Architektur korrigiert (Widerspruch)
|
||||
|
||||
Deployment, Postgres, Ingress/Cert, öffentliche Erreichbarkeit, OIDC/Rollen (#0049)
|
||||
und Theming (#0050) laufen live und reproduzierbar über den Konfig-Job.
|
||||
|
||||
**Offen ist nur der Git-Storage** — und die oben geforderte Fassung ist so **nicht
|
||||
machbar**: „`wiki`-Repo auf git.lab, gespiegelt wie die übrigen, als Storage" setzt
|
||||
voraus, dass Wiki.js git.lab erreicht. Tut es nicht — der Cluster erreicht bewusst
|
||||
nur Gitea (verifiziert 2026-08-13). Auflösung in
|
||||
[ADR-0015](../adr/0015-wiki-git-storage-ueber-gitea-kanonisieren.md): Wiki.js→Gitea
|
||||
(`sorb/wiki`), CI kanonisiert Gitea→git.lab (Muster `canonize_rotation`).
|
||||
|
||||
Voraussetzung (nur sorb): Gitea-Repo `sorb/wiki` (privat) anlegen + dedizierten
|
||||
Deploy-PAT bereitstellen. Danach verdrahtet der Agent Storage + Kanonisierungs-Job.
|
||||
|
||||
**Nachtrag 2026-08-13 (nachmittags):** Erledigt. Repo `sorb/ThreadNetWiki` + Deploy-PAT
|
||||
(`wiki-git-storage-deploy`) angelegt; Git-Storage in Wiki.js verdrahtet (Secret
|
||||
`wikijs-git-secret`), Status `operational`, End-to-End verifiziert (Seite anlegen/löschen
|
||||
propagiert nach Gitea). Damit ist das harte Kriterium „Inhalt in git" erfüllt. Einziger
|
||||
Rest: der **Kanonisierungs-Job Gitea→git.lab** — braucht die Entscheidung, in welches
|
||||
git.lab-Repo kanonisiert wird.
|
||||
|
||||
**Nachtrag 2026-08-13 (abends):** Auch der Kanonisierungs-Job ist erledigt. Ziel-Repo
|
||||
`git.lab/axion1337.chat/threadnet-wiki` (leer angelegt); `canonize_wiki` in der
|
||||
gitops-CI (Tages-Schedule) spiegelt Gitea→git.lab per Fast-Forward (kein Force,
|
||||
Protection bleibt; Token `WIKI_CANONIZE_TOKEN`, nur `write_repository`). Verifiziert:
|
||||
git.lab main === Gitea main mit voller Historie. Damit ist die Git-Storage-Strecke aus
|
||||
ADR-0015 komplett. Offen für #0048 bleibt nur noch die inhaltliche Grundstruktur
|
||||
(Bereiche Betrieb/Anwender) — die entsteht mit dem ersten echten Inhalt.
|
||||
|
||||
**Nachtrag 2026-08-13 (spät):** Grundstruktur angelegt: `home`, `anwender/` (+ Erste
|
||||
Schritte), `betrieb/` (+ Deployment) — liegt in git-storage (Gitea→git.lab). Abschottung
|
||||
verifiziert: `wiki-anwender` liest nur `anwender/*` + `home`; `betrieb/*` fällt unter
|
||||
Default-Deny (checkAccess: `match && !deny`). Damit sind alle Acceptance-Punkte erfüllt
|
||||
**bis auf** das `wikijs-postgres`-Backup — es existiert (noch) keins. Inhaltlich
|
||||
unkritisch (Inhalt liegt in git), aber Kommentare/lokale Konten/Suchindex wären bei
|
||||
Postgres-Verlust weg. Entscheidung sorb offen: dediziertes Backup wie `synapse-backup`
|
||||
anlegen, oder bewusst auf die git-Wiederherstellung setzen.
|
||||
|
||||
**Abschluss 2026-08-13:** `wikijs-backup` angelegt (nächtliches Borg-Backup der Wiki-DB,
|
||||
03:30, DB-only nach dem `authentik-backup`-Muster; wiederverwendet
|
||||
`synapse-backup-credentials`/`-known-hosts`, eigener Repo-Pfad `wikijs-backup`).
|
||||
Testlauf grün: Borg-Repo initialisiert, DB gedumpt und hochgeladen, Retention 7/4/6.
|
||||
Damit sind **alle** Acceptance-Punkte erfüllt → **erledigt**.
|
||||
@@ -0,0 +1,49 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0049"
|
||||
status: done
|
||||
created: 2026-08-12
|
||||
milestone: M4
|
||||
priority: medium
|
||||
area: security
|
||||
related: [docs/adr/0014-wikijs-loest-docusaurus-ab.md, docs/issues/0048-wikijs-in-der-suite-deployen.md]
|
||||
---
|
||||
# Wiki.js: Authentik-OIDC + Rollen und Abschottung
|
||||
|
||||
## Problem / Motivation
|
||||
|
||||
Der Kern der Wiki.js-Entscheidung (ADR-0014): Zugang und Sichtbarkeit nach
|
||||
Authentik-Gruppe, was Docusaurus nicht konnte.
|
||||
|
||||
## Acceptance
|
||||
|
||||
- **Authentik-OIDC** als Login-Provider in Wiki.js (Gruppen-Claim gemappt) —
|
||||
**nativer Wiki.js-Login**, kein Forward-Auth. Authentik-Provider mit Redirect-URI
|
||||
`https://wiki.axion1337.chat/login/<providerKey>/callback`; Anwender und Admin
|
||||
rufen dieselbe URL auf, die Rolle entscheidet über Sicht/Bearbeiten.
|
||||
- **Rollen** über Gruppen:
|
||||
- **Admins**: schreiben (read+write) Betriebs- **und** Anwenderhandbücher.
|
||||
- **Normale Nutzer**: nur lesen — kein Schreiben.
|
||||
- **Abschottung**: Anwender sehen die **Betriebsdoku nicht** (Pfad-/Seiten-Regeln
|
||||
pro Gruppe, `/betrieb/*` nur für die Betriebs-Gruppe). Betrieb darf Anwenderdoku
|
||||
lesen.
|
||||
- Verifiziert: Anwender-Konto sieht `/betrieb` nicht (nicht 403 mit sichtbarem
|
||||
Link, sondern gar nicht in Navigation/Suche); Nicht-Admin kann nichts editieren;
|
||||
Admin kann beides bearbeiten.
|
||||
|
||||
## Notes
|
||||
|
||||
⚠️ Braucht die **konkreten Authentik-Gruppen** von sorb: welche Gruppe = Betrieb,
|
||||
welche = Anwender, welche = Admin (Vorschlag: `authentik Admins` schreibt,
|
||||
`wiki-betrieb` liest Betrieb+Anwender, `wiki-anwender` liest nur Anwender). Zugangs-
|
||||
/Gruppenvergabe macht sorb selbst.
|
||||
|
||||
## Erledigt 2026-08-13
|
||||
|
||||
Nativer Authentik-OIDC-Login live (`hideLocal`, Break-Glass `/login?all`). Rollen
|
||||
umgesetzt — statt der 3-Gruppen-Skizze gilt sorbs Vereinfachung „Betrieb = Admin":
|
||||
`authentik Admins` schreibt alles, `wiki-anwender` liest nur `anwender/*` + Startseite.
|
||||
Abschottung **end-to-end verifiziert**: ein Test-Konto nur in `wiki-anwender` bekommt auf
|
||||
JEDE `betrieb/*`-Seite HTTP 403 (Wiki.js `checkAccess`: `match && !deny` → Default-Deny),
|
||||
`home` + `anwender/*` liefern 200; Navigation/Suche filtern per `checkAccess` mit; Guests
|
||||
komplett gesperrt. → erledigt.
|
||||
@@ -0,0 +1,67 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0050"
|
||||
status: done
|
||||
created: 2026-08-12
|
||||
milestone: M4
|
||||
priority: low
|
||||
area: infrastructure
|
||||
related: [docs/adr/0014-wikijs-loest-docusaurus-ab.md, docs/issues/0048-wikijs-in-der-suite-deployen.md]
|
||||
---
|
||||
# Wiki.js: Theming — Docusaurus-Farben und Logo übernehmen
|
||||
|
||||
## Problem / Motivation
|
||||
|
||||
Das neue Wiki soll aussehen wie das aktuelle Docusaurus (Wiedererkennung). Werte
|
||||
aus `git.lab/homelab/wiki` extrahiert (2026-08-12):
|
||||
|
||||
- **Akzent/primary**: hell `#2b6cb0`, dunkel `#63b3ed` (blau)
|
||||
- **Farbmodus**: dark als Default, `respectPrefersColorScheme`
|
||||
- **Hintergrund/Rest**: `custom.css` überschreibt nichts → Docusaurus-Dark-Defaults
|
||||
- **Logo**: `homelab/wiki:static/img/logo.png` (183×128, rotes Icon), alt „ThreadNet";
|
||||
Wortmarke `threadnet-logo-wortmarke.png` (512×512)
|
||||
- **Favicon**: `favicon.ico` + `favicon-32/96.png`
|
||||
|
||||
## Acceptance
|
||||
|
||||
- Wiki.js-Theme: dunkler Default, blauer Akzent (`#2b6cb0`/`#63b3ed`), Logo und
|
||||
Favicon aus den obigen Assets übernommen (in das `wiki`-Repo kopiert, nicht aus
|
||||
`homelab/wiki` referenziert).
|
||||
- Optischer Abgleich gegen den Docusaurus-Stand (Screenshot 2026-08-12).
|
||||
|
||||
## Notes
|
||||
|
||||
Nice-to-have (`low`), nach #0048/#0049. Assets liegen in `homelab/wiki`; beim Bau
|
||||
in das neue `wiki`-Repo kopieren.
|
||||
|
||||
## Update 2026-08-13 — Umsetzung (Teil)
|
||||
|
||||
Live und reproduzierbar über den Konfig-Job (gitops-Commit `63460e7`):
|
||||
|
||||
- **Dark-Mode als Default.**
|
||||
- **Logo** (ThreadNet `logo.png`) — als statische Datei in Wiki.js gemountet
|
||||
(`platform-branding` ConfigMap → `/_assets/img/branding/`), öffentlich ausgeliefert,
|
||||
auf der Login-Seite und eingeloggt sichtbar (kein `read:assets` für Guests nötig).
|
||||
- **Login-Hintergrund** = `alpenglow.jpg`, dieselbe Datei wie Authentik/Element
|
||||
(Erweiterung ggü. der ursprünglichen Spec, Wunsch sorb 2026-08-13) — ebenfalls lokal
|
||||
gemountet statt per externer URL verlinkt.
|
||||
|
||||
Statt der Spec-Idee „Assets ins `wiki`-Repo kopieren" liegen sie als **eine Quelle** in
|
||||
`gitops:apps/production/branding/` und werden in den Pod gemountet; später auch in
|
||||
Authentik/Element mountbar (eine Datei, keine Mehrfachpflege).
|
||||
|
||||
Noch offen: **Akzentfarbe** (`#2b6cb0`/`#63b3ed`, via injectCSS), **Favicon**, und der
|
||||
**optische Abgleich** durch sorb.
|
||||
|
||||
## Erledigt 2026-08-13 (Rest)
|
||||
|
||||
- **Akzentfarbe** `#2b6cb0` (hell) / `#63b3ed` (dunkel) via `injectCSS` auf dem App-UI.
|
||||
- **Favicon** (ThreadNet) für alle Größen ersetzt (`favicon.ico`, 16/32/150/180/192).
|
||||
- Bonus: **TOC rechts** (`tocPosition: right`, Wunsch sorb).
|
||||
- **Abweichung:** eine dunkle Login-Karte ist in Wiki.js nicht möglich — der Login-Bundle
|
||||
wendet kein Custom-CSS an; bleibt hell mit dem Alpenglow-Hintergrund.
|
||||
- **Größen-Detour (für Forks dokumentiert):** hochskalierte Favicons + der 604-KB-
|
||||
Hintergrund sprengten kurz das 1-MiB-ConfigMap-Limit → Hintergrund auf 1920 px (400 KB)
|
||||
runterskaliert, alles wieder in EINER `platform-branding`-ConfigMap.
|
||||
|
||||
Optischer Abgleich durch sorb via Screenshots (2026-08-13) erfolgt. → erledigt.
|
||||
@@ -0,0 +1,34 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0051"
|
||||
status: open
|
||||
created: 2026-08-14
|
||||
milestone: M5
|
||||
priority: high
|
||||
area: security
|
||||
related: [docs/issues/0025-deploy-uebergabe-cve-alarme-aggregiert-receiver.md, docs/aar/2026-08-01-cve-pipeline-gitops47.md]
|
||||
gitlab_iid: "37"
|
||||
---
|
||||
# CVE-Remediation-Pass: Schwachstellen-Report abarbeiten
|
||||
|
||||
## Problem / Motivation
|
||||
|
||||
Die Trivy-CVE-Pipeline meldet über 29 Images ~5400 CVEs (**126 CRITICAL, 1222 HIGH**, Rest
|
||||
MEDIUM/LOW), Spitzenreiter `goauthentik/server:2026.2.3` mit **369 CRIT+HIGH**. #0025 deckt
|
||||
nur die Alarm-Zustellung ab — die eigentliche **Behebung** fehlte als eigenes Issue.
|
||||
|
||||
## Acceptance
|
||||
|
||||
- Nach **Schwere × Fixbarkeit** priorisiert (CRITICAL zuerst; „fixed available" vor
|
||||
won't-fix; exponiert vor intern) — Methode als Runbook `/betrieb/sicherheit` im Wiki.
|
||||
- Top-Offender mit verfügbarem Fix behoben: v.a. **Authentik-Update** (mit Backup), eigene
|
||||
Images (`axion-backup`, `axion-secret-rotation`, `clamav-http-scanner`) auf aktueller Base
|
||||
neu gebaut, ESS-Bump → Delta im Grafana-CVE-Dashboard sichtbar.
|
||||
- won't-fix / nicht-exponierte CVEs mitigiert oder in **`.trivyignore` mit Begründung +
|
||||
Review-Datum** suppress-t.
|
||||
- Re-Scan zeigt deutlich gesunkene CRITICAL/HIGH.
|
||||
|
||||
## Notes
|
||||
|
||||
Hängt eng an der Update-Kadenz (#0052) — Updaten ist der Haupthebel gegen die CVEs.
|
||||
Methode/Runbook: `/betrieb/sicherheit`; Update-Prozess: `/betrieb/upgrades`.
|
||||
@@ -0,0 +1,75 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0052"
|
||||
status: done
|
||||
created: 2026-08-14
|
||||
milestone: M5
|
||||
priority: medium
|
||||
area: infrastructure
|
||||
related: [docs/issues/0051-cve-remediation-pass.md]
|
||||
---
|
||||
# Update-Kadenz festlegen & `:latest`-Tags beseitigen
|
||||
|
||||
## Problem / Motivation
|
||||
|
||||
Regelmäßige Komponenten-Updates sind der wirksamste CVE-Schutz (#0051), passieren bisher
|
||||
aber nur ad-hoc. Zudem ist `coturn:latest` **unpinned** — nicht reproduzierbar und nicht
|
||||
sauber scanbar — und `busybox:1.28` ist veraltet.
|
||||
|
||||
## Acceptance
|
||||
|
||||
- **`coturn`** auf eine gepinnte, aktuelle Version; **`busybox`** aktualisiert; **kein
|
||||
`:latest`-Tag** mehr im gitops-Repo.
|
||||
- **Update-Kadenz** festgelegt (Richtwert monatlich + ad-hoc bei CRITICAL-CVE), dokumentiert
|
||||
auf `/betrieb/upgrades`.
|
||||
- Fork-Updates (ThreadNet-Web, threadnet-call) folgen der Portier-Checkliste
|
||||
(`axion1337-fork.md`).
|
||||
|
||||
## Notes
|
||||
|
||||
Update-Prozess: `/betrieb/upgrades`. Bei DB-Migrationen (Authentik, Wiki.js) vorher Backup
|
||||
(die Backup-Jobs stehen). Rollback ist trivial (GitOps: Commit zurück).
|
||||
|
||||
## Erledigt 2026-08-15
|
||||
|
||||
**`:latest` ist aus dem Repo verschwunden** (gitops `b4650dc`) — `grep -rE '^\s*image:.*:latest' apps/`
|
||||
findet nichts mehr.
|
||||
|
||||
### Der Befund, der die Dringlichkeit belegt
|
||||
|
||||
Im Cluster lief **coturn 4.10.0**, während `:latest` auf der Registry längst **4.17.2** zeigte.
|
||||
Mit `imagePullPolicy: IfNotPresent` hält der Node das einmal gezogene Image fest — niemand
|
||||
konnte wissen, was tatsächlich lief, und ein Reschedule auf einen frischen Node wäre still über
|
||||
**sieben Minor-Versionen** gesprungen. Nebenwirkung: der Trivy-Report maß gegen ein bewegliches
|
||||
Ziel, seine „12 CRITICAL für coturn:latest" beschrieben nicht zwingend das laufende Image.
|
||||
|
||||
### Umgesetzt
|
||||
|
||||
| Änderung | von | auf |
|
||||
|---|---|---|
|
||||
| coturn | `coturn/coturn:latest` (real 4.10.0) | **`4.17.2`** |
|
||||
| busybox (init-Container) | `1.28` (2018) | **`1.36`** — die im Repo bereits anderswo genutzte Version |
|
||||
|
||||
Vorher geprüft: Der Tag existiert (Registry-Abfrage — `4.10.0` gibt es dort gar nicht mehr, ein
|
||||
Pin darauf wäre fehlgeschlagen), und die Konfiguration nutzt ausschließlich langlebige
|
||||
Kern-Optionen (`realm`, `use-auth-secret`, `relay-ip`, `cert`/`pkey`), von denen keine im
|
||||
Versionsbereich entfallen ist.
|
||||
|
||||
### Verifiziert
|
||||
|
||||
- Pod läuft, Listener auf 3478 und 5349 (TLS/TCP), keine Fehler im Log.
|
||||
- `turnserver --version` im Container: **4.17.2**.
|
||||
- **Funktionstest von außen:** STUN-Binding-Request an `49.13.132.245:3478` → *Binding Success*,
|
||||
und der Server meldet die externe Adresse des Anfragenden korrekt zurück. Der Relay-Pfad für
|
||||
Anrufe funktioniert also nachweislich, nicht nur der Prozess.
|
||||
|
||||
⚠️ Der Wechsel bedeutete einen **coturn-Neustart**; laufende Relay-Verbindungen wurden dabei
|
||||
getrennt. Bei künftigen TURN-Upgrades einplanen.
|
||||
|
||||
### Kadenz
|
||||
|
||||
Der zweite Teil des Issues (Update-Kadenz) steht bereits im Betriebs-Wiki unter
|
||||
`/betrieb/upgrades`, Abschnitt „Kadenz & CVE-Bezug": monatlich als Richtwert, ad-hoc bei
|
||||
CRITICAL-CVE, Fork-Updates über die Portier-Checkliste. **Erwartete Nebenwirkung für #0051:**
|
||||
Der nächste Trivy-Lauf sollte für coturn deutlich weniger CRITICALs zeigen — und erstmals gegen
|
||||
ein festes Ziel messen.
|
||||
@@ -0,0 +1,68 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0053"
|
||||
status: open
|
||||
created: 2026-08-15
|
||||
milestone: M2
|
||||
priority: low
|
||||
area: infrastructure
|
||||
related:
|
||||
- "docs/adr/0009-commit-konventionen-und-historien-anonymisierung.md"
|
||||
gitlab_iid: "38"
|
||||
---
|
||||
# Historien-Durchgang: acht nicht-kanonische Commits mitziehen
|
||||
|
||||
## Problem / Motivation
|
||||
|
||||
Acht Commits vom 14./15.08.2026 tragen `sorb <sorb.business@gmail.com>` statt der kanonischen
|
||||
Identität `Thore Cimbal <cfx@riot.8shield.net>` (ADR-0009). Entstanden in einer Agenten-Session:
|
||||
in frisch geklonten Repos wurde `user.email` von Hand gesetzt — ausgerechnet kurz **nachdem**
|
||||
dieselbe Session die kanonische Identität in `AGENTS.md` ausgeschrieben hatte (#0027/W7).
|
||||
|
||||
Die betroffenen Commits:
|
||||
|
||||
| Repo | Commits |
|
||||
|---|---|
|
||||
| `threadnet-operating` | `1bbff5e7`, `4cb9bfb2`, `e0808bad`, `e9c13dcf` |
|
||||
| `notfallhandbuch` | `5e398e73`, `6ff291d0`, `a55e6ee2` |
|
||||
| `thread-net-git` | `8d089e23` |
|
||||
|
||||
**Entscheidung sorb 2026-08-15:** *nicht* jetzt einzeln umschreiben, sondern **beim nächsten
|
||||
ohnehin anstehenden Historien-Durchgang mitziehen**. Ein Force-Push über zwei Repos für acht
|
||||
Commits steht in keinem Verhältnis — der Inhalt ist korrekt, nur das Namensschild ist falsch.
|
||||
|
||||
## Acceptance
|
||||
|
||||
- Die acht Commits tragen die kanonische Identität (ADR-0009).
|
||||
- Die Auflage aus ADR-0009 ist erfüllt: **alt→neu-Zuordnung** dokumentiert, analog zu
|
||||
`docs/sources/migration/commit-zuordnung-2026-08-07.md`.
|
||||
- `gruppenpruefung.py` meldet keine `nicht-kanonische Identität` mehr.
|
||||
|
||||
## Notes
|
||||
|
||||
**Warum dieses Issue existiert, obwohl die Sache vertagt ist:** `gruppenpruefung.py` meldet die
|
||||
acht Befunde bei *jedem* Lauf. Ohne einen dokumentierten Grund fängt die nächste Session an,
|
||||
sie zu „reparieren" — oder, schlimmer, gewöhnt sich an rote Befunde. Genau das ist am selben Tag
|
||||
beim `TargetDown`-Dauerfeuer aus #0002 passiert. Die Vertagung ist eine Entscheidung, kein
|
||||
Versehen, und gehört deshalb sichtbar abgelegt.
|
||||
|
||||
**Vorbeugung (der eigentliche Punkt):** Die Ursache war ein `git config user.email` von Hand in
|
||||
einem frischen Klon. Dass die Regel eine Stunde vorher aufgeschrieben wurde, hat sie nicht
|
||||
verhindert — dieselbe Lehre wie beim Bindmount (#0027/W6-Klasse). Wirksam wäre etwas
|
||||
Strukturelles statt mehr Sorgfalt, z.B. ein `[includeIf]`-Block in der globalen `.gitconfig`, der
|
||||
die Identität für Klone der Gruppe automatisch setzt, sodass sie gar nicht erst von Hand gesetzt
|
||||
werden muss.
|
||||
|
||||
## Nachtrag 2026-08-18: Echtzeit-Stempel gehören in denselben Durchgang
|
||||
|
||||
`gruppenpruefung.py` meldet zusätzlich neun Commits mit **Echtzeit-Zeitstempeln** statt der
|
||||
normalisierten Stempel — dieselbe Klasse „Namensschild/Stempel falsch, Inhalt korrekt", also
|
||||
derselbe Beschluss: **beim nächsten Historien-Durchgang mitziehen**, nicht einzeln umschreiben.
|
||||
|
||||
| Repo | Commits | Anmerkung |
|
||||
|---|---|---|
|
||||
| `management` | `d47c2c4e`, `2daadb8b`, `6b8869f1`, `bff1ca44`, `0cce6f64` | Author-Datum normalisiert, Committer-Datum real (Rebase vom 11.08.) |
|
||||
| `axion1337.chat-gitops` | `27a5395e`, `e110918d` | wie oben (12.08.) |
|
||||
| `axion1337.chat-gitops` | `065b1308`, `ea01c0bc` | beide Stempel real (12.08.) |
|
||||
|
||||
Acceptance ergänzt: `gruppenpruefung.py` meldet auch keine `Echtzeit-Stempel` mehr.
|
||||
@@ -0,0 +1,372 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0054"
|
||||
status: done
|
||||
created: 2026-08-15
|
||||
milestone: M4
|
||||
priority: medium
|
||||
area: element
|
||||
related:
|
||||
- "docs/issues/0029-ui-harmonisieren-gleiche-farben-und-formen.md"
|
||||
---
|
||||
# Tastaturgeräusche in Calls: quelloffene KI-Geräuschunterdrückung im Client prüfen
|
||||
|
||||
## Problem
|
||||
|
||||
Der WebRTC-Standardfilter (`noiseSuppression`) ist auf **stationäres** Rauschen ausgelegt
|
||||
(Lüfter, Netzbrummen). **Transiente** Geräusche — Tastaturanschläge — rutschen durch: sie
|
||||
haben eine sehr schnelle Anstiegszeit und ein unvorhersehbares Spektrum, sodass die
|
||||
laufende Rauschprofil-Schätzung sie nicht als Störung erkennt. Betroffen sind ausdrücklich
|
||||
**auch leise Chiclet-Tastaturen** (MacBook), nicht nur mechanische.
|
||||
|
||||
Grundlage ist eine externe Architekturspezifikation (Gemini Deep Research, 2026-07-29):
|
||||
client-seitige KI-Filterung via WebAssembly, eingehängt über das LiveKit-`TrackProcessor`-
|
||||
Interface, mit Intensitätsregler in den Audio-Einstellungen.
|
||||
|
||||
## Bewertung der Spezifikation
|
||||
|
||||
### Trägt
|
||||
|
||||
- **Diagnose stimmt.** Stationär vs. transient ist die richtige Erklärung dafür, warum die
|
||||
vorhandenen Toggles nicht helfen.
|
||||
- **Client-seitig ist der richtige Ort — und kein Widerspruch zur bisherigen Linie.**
|
||||
`threadnet-call:docs/axion1337-fork.md` §5 verwirft ML-Rauschunterdrückung **server-seitig**
|
||||
(LiveKit Agents), weil es dort keinen unterstützten Weg gibt, bereinigtes Audio an andere
|
||||
Teilnehmer weiterzureichen. Genau dieser Einwand greift client-seitig **nicht**. Die
|
||||
Spezifikation setzt die alte Entscheidung fort, statt ihr zu widersprechen.
|
||||
- **AudioWorklet statt ScriptProcessor**, eigener hochpriorer Audio-Thread: richtig und
|
||||
nicht verhandelbar.
|
||||
- **Der 128-vs-480-Sample-Mismatch** (Web Audio liefert 128er-Blöcke, die Modelle brauchen
|
||||
480) und der nötige Ringpuffer sind sauber benannt — daran scheitern naive Umsetzungen.
|
||||
- **Chromium-AudioWorklet-Leak** und die Gegenmaßnahme (eigener `AudioContext`, hart
|
||||
schließen) sind real und richtig adressiert.
|
||||
- **Wasm SIMD** ist tatsächlich Voraussetzung, nicht Optimierung.
|
||||
|
||||
### Trägt nicht
|
||||
|
||||
1. ⚠️ **Konkreter Fehler: die Latenzkompensation fehlt.** Der finale Dry/Wet-Code mischt das
|
||||
**unverzögerte** Original mit dem ~40 ms verzögerten KI-Signal. Das erzeugt Kammfilter und
|
||||
Phasenauslöschung — hörbar als blechernes Echo, also genau das Gegenteil des Ziels. Ein
|
||||
früherer Entwurf im selben Gespräch hatte dafür einen `DelayNode`; in der Endfassung ist er
|
||||
verschwunden. Das ist kein Detail.
|
||||
2. **Der Dry/Wet-Ansatz ist konzeptionell fragwürdig.** Die Begründung („neuronale Netze
|
||||
kennen nur An/Aus") ist für DeepFilterNet **falsch** — es hat einen nativen Parameter zur
|
||||
**Begrenzung der Dämpfung**. „Weniger aggressiv" heißt richtig: das Modell weniger dämpfen
|
||||
lassen. Dry/Wet mischt stattdessen ungefiltertes Signal zurück — **inklusive der
|
||||
Tastaturanschläge**, die man loswerden wollte.
|
||||
3. **Die Zahlen taugen nicht als Entscheidungsgrundlage.** PESQ „RNNoise ~3.88" gegen „DFN3
|
||||
3.5–4.34": die untere DFN3-Grenze läge unter RNNoise. Werte aus verschiedenen Testsets,
|
||||
nicht vergleichbar.
|
||||
4. **Bundle-Größe geschätzt, nicht gemessen** („15–25 MB"). Für eine Browser-App, die beim
|
||||
Call-Start lädt, ist das der kritische Wert überhaupt — muss gemessen werden.
|
||||
5. **Die genannten NPM-Pakete sind Experimente** (`deepfilternet3-worker-test`,
|
||||
`…-noise-filter-trong`). Die Spec empfiehlt selbst, aus dem Rust-Quellcode zu bauen — dann
|
||||
gehört ehrlich dazu: wir übernehmen eine **Rust/wasm-Toolchain in die Build-Kette**.
|
||||
6. **Mobil fehlt.** Element Call läuft auf Telefonen; DFN3 auf einem Mittelklasse-Android ist
|
||||
offen und wird mit „läuft auf modernen Prozessoren" abgetan.
|
||||
7. **`getUserMedia`-Constraints fehlen im Code.** Wer selbst filtert, muss die Browser-eigene
|
||||
`noiseSuppression` **abschalten** (sonst arbeiten zwei Filter gegeneinander) und
|
||||
`echoCancellation` erhalten. Im Gespräch erwähnt, im finalen Code verschwunden.
|
||||
8. **Erzwungene 48 kHz** ohne Fallback — Geräte mit 44,1 kHz brauchen einen Pfad.
|
||||
9. ⚠️ **Der größte Posten fehlt ganz: Fork-Wartung.** Das wäre eine erhebliche
|
||||
Eigenentwicklung in `threadnet-call`, die bei **jedem** Upstream-Rebase mitgeschleppt und
|
||||
in `axion1337-fork.md` gepflegt werden muss. Die Spec erwähnt das mit keinem Wort.
|
||||
10. **Lizenz nur behauptet.** DeepFilterNet-Code ist MIT/Apache-2.0 — die **Modellgewichte**
|
||||
sind separat zu prüfen, bevor „null Lizenzkosten" behauptet wird.
|
||||
|
||||
## Vorgeschlagenes Vorgehen (vor jeder Zeile Produktivcode)
|
||||
|
||||
1. **Messen statt annehmen.** Reproduzierbarer A/B-Test mit mechanischer *und* Chiclet-Tastatur
|
||||
gegen die heutigen Toggles — belegt das Problem und liefert die Referenz für „besser".
|
||||
2. **Wegwerf-Prototyp außerhalb des Forks.** DFN3 als Wasm auf einer eigenen Testseite:
|
||||
**Bundle-Größe, CPU und Latenz auf echten Geräten messen** (inkl. Telefon). Erst diese
|
||||
Zahlen entscheiden über Modell und Machbarkeit.
|
||||
3. **Regler über den Modellparameter**, nicht über Dry/Wet. Falls doch Dry/Wet: Delay-Node zur
|
||||
Latenzkompensation ist Pflicht.
|
||||
4. **Dann erst** Integration als `TrackProcessor` und Eintrag in `axion1337-fork.md`.
|
||||
|
||||
## Offen (Entscheidung sorb)
|
||||
|
||||
Ob der Aufwand lohnt. Die Plattform hat derzeit einen sehr kleinen Nutzerkreis; dem steht
|
||||
eine dauerhaft zu pflegende Fork-Anpassung mit Rust/wasm-Build gegenüber. Die Alternative —
|
||||
Tastaturgeräusche als hinnehmbar erklären und stattdessen Push-to-Talk bzw. bewusstes
|
||||
Stummschalten dokumentieren — ist billiger und sollte bewusst verworfen werden, nicht
|
||||
übersehen.
|
||||
|
||||
## Prototyp gebaut und getestet 2026-08-15
|
||||
|
||||
Wegwerf-Aufbau außerhalb des Forks (`deepfilternet3-noise-filter` v1.3.0 + `livekit-client`,
|
||||
lokale Testseite, Assets **selbst ausgeliefert**). Ziel war, die drei ungeklärten Punkte der
|
||||
Spezifikation durch Messung zu ersetzen.
|
||||
|
||||
### Ergebnis: es funktioniert — und zwar besser als nötig
|
||||
|
||||
**Höreindruck sorb:** *„die Tastatur ist weg, Stimme klingt natürlich"* — bei **35 %**
|
||||
Dämpfung.
|
||||
|
||||
Das ist der wichtigste Einzelbefund, und er entscheidet die Reglerfrage:
|
||||
|
||||
- **Der Dry/Wet-Mix der Spezifikation entfällt ersatzlos.** Das Paket bietet
|
||||
`setSuppressionLevel()`, und die wasm-Signatur trägt `atten_lim` — DeepFilterNet begrenzt
|
||||
die Dämpfung **nativ**. Die Prämisse der Spec („neuronale Netze kennen nur An/Aus") ist
|
||||
widerlegt. Damit entfallen zugleich der fehlende Delay-Node und das Phasenproblem —
|
||||
es gibt gar keinen zweiten Signalpfad mehr, der phasenversetzt zurückgemischt werden müsste.
|
||||
- **35 % statt 100 % als Vorgabe.** Die Spec setzt „standardmäßig 100 % Filter-Aktivität";
|
||||
gemessen reicht gut ein Drittel für „Tastatur weg **und** Stimme natürlich". Weniger
|
||||
Dämpfung heißt weniger Artefaktrisiko — der Standardwert sollte bei ~35 % liegen, nicht am
|
||||
Anschlag.
|
||||
|
||||
### Gemessen (ersetzt die Schätzungen der Spec)
|
||||
|
||||
| Größe | Wert | Bemerkung |
|
||||
|---|---|---|
|
||||
| Download je Client | **23,27 MB** | 15,66 MB `df_bg.wasm` + 7,61 MB Modell — Spec-Schätzung („15–25 MB") bestätigt, oberer Rand |
|
||||
| Vergleich RNNoise | 2,0–4,6 MB | rund ein Zehntel (`@jitsi/rnnoise-wasm`, `@shiguredo/rnnoise-wasm`) |
|
||||
| Lizenz Paket | Apache-2.0 **oder** MIT | wie behauptet; Modellgewichte kommen aus dem DeepFilterNet-Projekt |
|
||||
|
||||
### ⚠️ Befund, der die Paketwahl bestimmt: fremdes CDN
|
||||
|
||||
`deepfilternet3-noise-filter` lädt Modell und wasm zur Laufzeit von **`cdn.mezon.ai`**. Für
|
||||
eine selbstgehostete Plattform ist das nicht hinnehmbar: jeder Teilnehmer meldet bei jedem
|
||||
Call-Start seine IP an einen Dritten, und die Verfügbarkeit des Calls hinge an fremder
|
||||
Infrastruktur.
|
||||
|
||||
**Entschärft:** `assetConfig.cdnUrl` ist konfigurierbar. Der Prototyp liefert die Assets
|
||||
bereits **lokal** aus — der CDN-freie Betrieb ist damit nachgewiesen, nicht nur angenommen.
|
||||
Für eine Integration hieße das: die 23 MB gehören mit ausgeliefert (Image/Ingress), nicht
|
||||
nachgeladen.
|
||||
|
||||
### Ebenfalls korrigiert gegenüber der Spec
|
||||
|
||||
Die `getUserMedia`-Constraints, die im finalen Spec-Code fehlten, sind im Prototyp gesetzt:
|
||||
`noiseSuppression: false` (sonst arbeiten Browser-Filter und Modell gegeneinander),
|
||||
`echoCancellation: true`.
|
||||
|
||||
### Weiterhin offen — und entscheidungsrelevant
|
||||
|
||||
1. **CPU-Last** (Jitter mit/ohne Filter) noch nicht abgelesen.
|
||||
2. **Telefon.** Die eigentliche Härteprobe: 23 MB Download und DFN3-Inferenz auf einem
|
||||
Mittelklasse-Gerät. Fällt das durch, braucht es einen Pfad (RNNoise als leichte Variante,
|
||||
oder Filter auf Mobilgeräten aus).
|
||||
3. **Fork-Wartung** bleibt der ungemessene Posten — die Anpassung muss jeden Upstream-Rebase
|
||||
überleben.
|
||||
|
||||
## Entscheidung 2026-08-15 → ADR-0018
|
||||
|
||||
sorb: **integrieren, opt-in mit Nachladen, Checkbox plus Regler, Standard 35 %.**
|
||||
Festgehalten als [ADR-0018](../adr/0018-ki-geraeuschunterdrueckung-clientseitig-opt-in.md).
|
||||
|
||||
Der Größeneinwand entfällt damit für alle, die den Filter nicht nutzen: erst das Einschalten
|
||||
löst den Download aus. Der Telefontest wurde bewusst ausgesetzt — vertretbar, weil der Filter
|
||||
auf schwachen Geräten schlicht aus bleibt.
|
||||
|
||||
### Umsetzung (offen)
|
||||
|
||||
1. `deepfilternet3-noise-filter` als Abhängigkeit in `threadnet-call`.
|
||||
2. `TrackProcessor` an den lokalen Audio-Track hängen; `assetConfig.cdnUrl` auf das eigene
|
||||
Deployment zeigen (**nicht** auf `cdn.mezon.ai`).
|
||||
3. Assets (23,3 MB) in die Auslieferung aufnehmen.
|
||||
4. Audio-Einstellungen: Checkbox + Regler, Standard aus / 35 %.
|
||||
5. `getUserMedia`: `noiseSuppression: false`, `echoCancellation: true`.
|
||||
6. Eintrag in `threadnet-call:docs/axion1337-fork.md` als Fork-Anpassung mit Portier-Hinweis
|
||||
(§5 dort ergänzen — die Absage galt der Server-Seite und bleibt gültig).
|
||||
|
||||
Der Prototyp liegt außerhalb der Repos und ist Wegwerf-Material; er wird nicht eingecheckt.
|
||||
|
||||
## Umgesetzt 2026-08-15 — threadnet-call `3f17001`
|
||||
|
||||
Nach ADR-0018 gebaut. Alle Prüfungen des Repos grün: `tsc` 0, `eslint` 0, `prettier` sauber,
|
||||
`ECConnectionFactory.test.ts` **9/9** (der Test deckt genau die geänderte
|
||||
`noiseSuppression`-Logik ab), `pnpm build` erfolgreich.
|
||||
|
||||
| Datei | Änderung |
|
||||
|---|---|
|
||||
| `src/livekit/aiNoiseSuppression.ts` | **neu** — baut den TrackProcessor, `undefined` wenn ausgeschaltet |
|
||||
| `src/settings/settings.ts` | `ai-noise-suppression` (false), `ai-noise-suppression-level` (35) |
|
||||
| `ConnectionFactory.ts` | `processor:` in `audioCaptureDefaults`; Browser-NS aus bei aktivem Filter |
|
||||
| `SettingsModal.tsx` | Checkbox + Regler im **Audio**-Tab (nicht im Entwickler-Tab) |
|
||||
| `public/assets/dfn3/**` | Modell + wasm, selbst ausgeliefert |
|
||||
| `Dockerfile` | gzip für das Modell-wasm |
|
||||
| `docs/axion1337-fork.md` | §5b mit Portier-Checkliste |
|
||||
|
||||
### Zwei Funde beim Bauen, die die Zahlen verbessern
|
||||
|
||||
1. **Der Dockerfile hätte die 23 MB ungzippt ausgeliefert.** Sein `gzip`-Glob greift nur auf
|
||||
oberster Ebene, das Modell-wasm liegt aber in einem Unterordner. Gemessen: **15,7 → 4,1 MB**
|
||||
(26 %). Zusammen mit dem bereits komprimierten Modell sind es **11,7 statt 23,3 MB** pro
|
||||
Client. Behoben.
|
||||
2. **Die 23 MB landen nicht im JS-Bundle.** Verifiziert: `dist/assets/index-*.js` bleibt bei
|
||||
2,9 MB, die Assets liegen daneben als statische Dateien. Das Opt-in-Nachladen funktioniert
|
||||
also wie entworfen — wer den Filter aus lässt, lädt nichts.
|
||||
|
||||
### Entscheidung sorb: Assets ins Git
|
||||
|
||||
Statt beim Bauen zu holen. Begründung: ein Build-Zeit-Download von `cdn.mezon.ai` hätte genau
|
||||
die Fremdabhängigkeit wieder eingeführt, die wir zur Laufzeit entfernt haben — nur verschoben.
|
||||
So baut das Repo offline und aus sich heraus. Preis: +23 MB dauerhaft (bisher größte Datei:
|
||||
1,4 MB), und dasselbe nochmal bei jedem Modell-Update.
|
||||
|
||||
### Offen
|
||||
|
||||
- **Image bauen und ausrollen** — bis dahin läuft die Änderung nirgends.
|
||||
- **Echter Call zu zweit** als Abnahme: der Prototyp lief gegen die eigenen Kopfhörer, nicht
|
||||
über die Leitung.
|
||||
- **Mobil** weiterhin ungeprüft (ADR-0018, bewusst).
|
||||
|
||||
## Rollout 2026-08-16 — Auslieferungsweg korrigiert, wartet auf Publish
|
||||
|
||||
⚠️ **Beim Ausrollen kam heraus, dass die erste Umsetzung im Produktivpfad nicht funktioniert
|
||||
hätte.** Element Call läuft **nicht** als eigenes Image. Der Weg ist:
|
||||
|
||||
```
|
||||
threadnet-call → npm-Paket @sorb/threadnet-call-embedded → Gitea
|
||||
→ ThreadNet-Web (webpack kopiert nach webapp/widgets/element-call/) → threadnet-web-Image
|
||||
```
|
||||
|
||||
Der Embedded-Build setzt `publicDir: false` — Upstream begründet das damit, `public/` enthalte
|
||||
nur das Favicon. Seit die Modell-Assets dort liegen, stimmt das nicht mehr. **Gemessen, nicht
|
||||
vermutet:** mit dem Upstream-Wert baut alles fehlerfrei, der Standalone-Build funktioniert, und
|
||||
**nur im Widget wäre der Filter tot** (404 auf das Modell). Genau der stille Fehlschlag, der
|
||||
ohne Prüfung des Auslieferungswegs live gegangen wäre.
|
||||
|
||||
Behoben in `threadnet-call` `d270e0c`: `publicDir` aktiviert, Version auf
|
||||
`0.19.2-threadnet.8`, Fork-Doku um den Auslieferungsweg und den Rebase-Hinweis ergänzt
|
||||
(diese eine Zeile ist der wahrscheinlichste stille Rückfall beim nächsten Rebase).
|
||||
|
||||
**Verifiziert:**
|
||||
- URL-Auflösung im Widget: `…/widgets/element-call/assets/dfn3/v3/pkg/df_bg.wasm` ✓
|
||||
- CI-Pipeline #371 grün, `build_embedded` erfolgreich
|
||||
- **Das CI-Artefakt enthält die Assets** (24,3 MB) — nicht nur der lokale Build
|
||||
|
||||
### Korrektur einer Zahl aus der Entscheidungsvorlage
|
||||
|
||||
Ich hatte beim Abfragen der Asset-Entscheidung gesagt, das npm-Paket wachse „von ~2 MB auf
|
||||
~25 MB". **Gemessen: 41 → 66 MB.** Der Aufschlag stimmt (+24 MB), die Ausgangsbasis war
|
||||
falsch — das Paket enthielt bereits 17,6 MB Source-Maps und 16 MB Crypto-/Vision-wasm. Der
|
||||
relative Aufschlag ist also +60 %, nicht das Zwölffache.
|
||||
|
||||
### Offen — beides bewusst nicht von mir ausgelöst
|
||||
|
||||
1. **`publish_npm`** steht auf `manual`. Laut Fork-Doku §4 ist das Absicht: *„bewusst kein
|
||||
Automatismus — Veröffentlichen bleibt ein Akt."* Auslösen gehört sorb.
|
||||
2. **Danach ThreadNet-Web:** Abhängigkeit auf `0.19.2-threadnet.8` anheben, bauen, Image in
|
||||
die Registry, Tag im gitops-Repo anheben.
|
||||
3. **Abnahme im echten Call zu zweit** steht weiterhin aus.
|
||||
|
||||
## Vorfall 2026-08-16: v0.5.0 brach das Entmuten — behoben in v0.5.1, Filter stillgelegt
|
||||
|
||||
⚠️ **Das erste Produktivimage mit dem Filter (v0.5.0, embedded `.8`) hat das Entmuten fuer
|
||||
ALLE gebrochen** — nicht nur fuer Nutzer, die den Filter eingeschaltet hatten. Rueckrollung
|
||||
auf v0.4.3 stellte den Dienst wieder her; die Ursache wurde ueber ein von sorb angefordertes
|
||||
Diagnosefenster (v0.5.0 gezielt wieder ausgerollt, Browser-Konsole gesichert) festgenagelt.
|
||||
|
||||
**Zwei getrennte Fehler derselben Aenderung:**
|
||||
|
||||
1. **Filter an:** `LocalAudioTrack.setProcessor()` verlangt einen AudioContext auf dem Track.
|
||||
Den gibt es nur mit `webAudioMix` beim Raum-Bau — und das steht in Element Call als
|
||||
unerprobtes Upstream-TODO auskommentiert. Konsole: *"Audio context needs to be set on
|
||||
LocalAudioTrack in order to enable processors"*; im SFU-Log erschien nie ein Track-Publish.
|
||||
**ADR-0018 hat die Integrationstiefe unterschaetzt:** der Prototyp verdrahtete seinen
|
||||
Audio-Graphen selbst und hat LiveKits Prozessor-Anbindung nie mitgeprueft.
|
||||
2. **Filter aus:** Der Optionen-Bau setzte `processor: undefined` und schrieb
|
||||
`noiseSuppression` um. LiveKit kopiert **jeden** Schluessel der `audioCaptureDefaults` bis
|
||||
in die getUserMedia-Constraints durch, `undefined` eingeschlossen — in Safari brach damit
|
||||
das Entmuten trotz publiziertem Track. In Chromium unauffaellig; die fruehe Entlastung
|
||||
dieses Verdachts war ein Browser-uebergreifender Fehlschluss aus einem Chromium-Test.
|
||||
|
||||
**Fix (threadnet-call `dcc8643`, embedded `.9`, ThreadNet-Web v0.5.1):**
|
||||
- Aus-Pfad als bedingtes Spread — identisch mit Upstream, kein `processor`-Schluessel.
|
||||
- Feature-Tor `AI_NOISE_SUPPRESSION_AVAILABLE = false`: neutralisiert auch Clients mit noch
|
||||
aktivierter Einstellung im localStorage (real existierender Fall), UI dahinter versteckt.
|
||||
- Zwei Regressionstests, **auf dem alten Stand nachweislich rot** — erst dadurch beweisen sie
|
||||
etwas.
|
||||
|
||||
**Abnahme bestanden 2026-08-16:** Call sorb+frank auf v0.5.1, Muten/Entmuten beidseitig,
|
||||
sorbs Client mit unveraendertem localStorage. Konsolen-Fehlerbild identisch mit dem
|
||||
funktionierenden v0.4.3-Stand (bekanntes Rauschen, als eigene Beobachtung notiert:
|
||||
einmaliger 401 auf ein m.call-State-Event, einmaliger LiveKit-Connect-Fehlversuch mit
|
||||
erfolgreichem Folgeversuch).
|
||||
|
||||
**Lehre (Wiederholung des Sitzungsmusters):** Die Auslieferungskette war penibel verifiziert
|
||||
— npm, node_modules, webpack, CI-Artefakt, Container, ausgelieferte URL. Geprueft wurde, ob
|
||||
die *Dateien ankommen*, nicht, ob die *Funktion im Zielsystem tut*. Der Call-Test zu zweit
|
||||
stand als „danach" auf der Liste statt als Voraussetzung. Fuer Aenderungen im Mikrofon-Pfad
|
||||
gilt ab jetzt: **Abnahme im echten Call ist Rollout-Voraussetzung, nicht Nacharbeit.**
|
||||
|
||||
**Offen (Folge-Entscheidung, nicht Teil dieses Issues):** Der Filter bleibt stillgelegt, bis
|
||||
`webAudioMix` entschieden ist — Upstream-TODO mit Nebenwirkungen auf Echo-Unterdrueckung und
|
||||
Ausgabegeraete-Wahl. Erst diese Entscheidung macht ADR-0018 umsetzbar oder widerlegt ihn.
|
||||
|
||||
## Weg B umgesetzt (2026-08-16/17): v0.5.2 live, Tor zu, Abnahme ausstehend
|
||||
|
||||
**Entscheidung sorb:** Weg B — AudioContext nur auf dem lokalen Mikrofon-Track
|
||||
(`LocalAudioTrack.setAudioContext()` unmittelbar vor `setProcessor()`), statt `webAudioMix`
|
||||
am ganzen Raum. Kleinster Wirkradius: Wiedergabe, Ausgabegeraete-Wahl und Echo-Verhalten
|
||||
bleiben unberuehrt.
|
||||
|
||||
**Umsetzung** (threadnet-call `df4e5ee`, embedded `.10`, ThreadNet-Web v0.5.2):
|
||||
- Anbindung in `Publisher.onLocalTrackPublished`, also erst **nach** der Publikation — ein
|
||||
scheiternder Filter kann das Entmuten per Konstruktion nicht mehr verhindern.
|
||||
- Verschaerfte Invariante: `audioCaptureDefaults` traegt in **keinem** Zustand einen
|
||||
`processor`-Schluessel mehr; Regressionstest deckt auch den Aktiv-Fall ab.
|
||||
- Tor bleibt zu; ein einzelner Test-Client aktiviert ueber zwei localStorage-Schluessel
|
||||
(`ai-noise-suppression-dev` + normale Einstellung). Die normale Einstellung allein bleibt
|
||||
wirkungslos. 77 Tests gruen ueber die beruehrten Suiten.
|
||||
|
||||
**Stolperstein aus dem ersten Testversuch:** sorb suchte die Filter-Einstellung in den
|
||||
Client-Einstellungen — die UI ist aber absichtlich hinter dem Tor versteckt, und sie sass
|
||||
auch vorher nicht in Element Web, sondern in den Einstellungen **im laufenden Call-Widget**.
|
||||
Der Testweg ist in dieser Phase bewusst UI-los (Konsole); ausserdem kein privater Tab
|
||||
(eigener, leerer localStorage) und harter Reload noetig. Fuer die Tor-Oeffnung notiert:
|
||||
die Wiederauffindbarkeit der Einstellung gehoert in die Anwenderdoku.
|
||||
|
||||
**Offen — Abnahme in zwei Stufen (Rollout-Voraussetzung fuer die Tor-Oeffnung):**
|
||||
1. Normaler Call zu zweit ohne Schalter: muss sich exakt wie v0.5.1 verhalten.
|
||||
2. Test-Client mit beiden Schluesseln: Konsole meldet den aktiven Filter, ~23 MB dfn3 laden
|
||||
erst beim Entmuten, Gegenseite hoert keine Tastatur, Entmuten funktioniert weiterhin.
|
||||
|
||||
Erst danach: Tor oeffnen + UI einblenden (v0.5.3). Alternativ bleibt Abbruch (Option C)
|
||||
jederzeit moeglich — v0.5.2 ist fuer alle Nutzer verhaltensgleich mit v0.5.1.
|
||||
|
||||
## Nachtrag 2026-08-17/18: Safari sendete ungefiltert — behoben in v0.5.4
|
||||
|
||||
Nach der Tor-Oeffnung (v0.5.3) meldete sorb: Filter wirkungslos, **Staerke 0-100 ohne jeden
|
||||
Unterschied**, sauberer Alleintest (Hoergeraet gemutet). Auf anderen Rechnern (Chromium-Familie)
|
||||
funktionierte derselbe Stand. Messungen mit einem lokalen Pruefstand (Playwright, echter
|
||||
LiveKit-`setProcessor`-Pfad, Sprachsignal mit Klick-Transienten) ergaben: Prozessor und Modell
|
||||
arbeiten korrekt — Sprache passiert, Klicks verschwinden.
|
||||
|
||||
**Ursache:** LiveKits `setProcessor` tauscht den Sender-Track per
|
||||
`this.sender?.replaceTrack(processedTrack)`. Ist der Sender in dem Moment nicht am Track
|
||||
(Safari-Timing beim LocalTrackPublished-Event), wird der Tausch **stumm uebersprungen** — der
|
||||
Prozessor laedt seine 23 MB, meldet Erfolg, und das rohe Mikrofon bleibt auf der Leitung.
|
||||
Viertes Vorkommen des Sitzungsmusters "meldet Erfolg, ist aber blind", diesmal in Fremdcode
|
||||
(das `?.` verschluckt den Fehlschlag).
|
||||
|
||||
**Zwei Irrwege der Diagnose, festgehalten weil lehrreich:**
|
||||
- Ein synthetischer Sinuston als "Stimme" liess den Filter wie einen Totalausfall aussehen —
|
||||
fuer ein **Sprach**-Modell ist ein Ton Rauschen, die Daempfung bis zum Limit war korrektes
|
||||
Verhalten am falschen Signal. Messsignale muessen dem Modell entsprechen.
|
||||
- Zwei Geraete im selben Raum machen jeden Hoertest wertlos (Tastatur akustisch und ueber das
|
||||
zweite, ungefilterte Mikro hoerbar). Der brauchbare Alleintest: Hoergeraet **gemutet** mit
|
||||
Kopfhoerer.
|
||||
|
||||
**Fix (threadnet-call `fee9866`, embedded `.12`, ThreadNet-Web v0.5.4):** Nach dem Anhaengen
|
||||
wird verifiziert, dass der RTCRtpSender den gefilterten Track traegt — notfalls wird auf den
|
||||
Sender gewartet und der Tausch explizit erzwungen. Die Konsolen-Zeile weist den Zustand aus:
|
||||
`Sendepfad gefiltert: ja/NEIN`. Drei Tests decken die Sender-Faelle ab.
|
||||
|
||||
**Test sorb 2026-08-18 (Safari als sendender Client): bestanden.** Damit ist der Filter auf
|
||||
beiden Engine-Familien nachgewiesen. Weiterhin offen und bewusst vertagt: Telefontest (mobil).
|
||||
|
||||
## Geschlossen 2026-08-18 (Entscheidung sorb)
|
||||
|
||||
Der Filter ist in Produktion (v0.5.4), auf beiden Engine-Familien im echten Call nachgewiesen,
|
||||
mit Feature-Tor als Rueckhebel und Regressionstests auf allen drei stillen Fehlschlaegen des
|
||||
Wegs dorthin. Der Telefontest bleibt bewusst ausgesetzt (ADR-0018-Konsequenz: opt-in macht das
|
||||
vertretbar); sollte er noetig werden, ist das ein neues Issue, kein Wiederoeffnen.
|
||||
|
||||
**Korrektur zur Schliessung (sorb, 2026-08-18):** Der Telefontest ist nicht vertagt, sondern
|
||||
**gestrichen**. Begruendung: Der Filter ist opt-in und hinter dem Feature-Tor — ein mobiler
|
||||
Fehlschlag traefe nur den Nutzer, der ihn einschaltet, und dessen Ausweg ist die Checkbox.
|
||||
Es gibt damit keinen offenen Rest an diesem Vorhaben.
|
||||
@@ -0,0 +1,129 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0055"
|
||||
status: done
|
||||
created: 2026-08-16
|
||||
milestone: M2
|
||||
priority: medium
|
||||
area: security
|
||||
related:
|
||||
- "docs/issues/0054-ki-geraeuschunterdrueckung-element-call.md"
|
||||
- "docs/adr/0018-ki-geraeuschunterdrueckung-clientseitig-opt-in.md"
|
||||
- "docs/adr/0001-gitlab-kanonisch-push-mirror.md"
|
||||
gitlab_iid: "39"
|
||||
---
|
||||
|
||||
# Issue-0055: `@sorb`-Scope in ThreadNet-Web ist nirgends auf rohana festgelegt
|
||||
|
||||
## Problem / Motivation
|
||||
|
||||
`ThreadNet-Web` bezieht `@sorb/threadnet-call-embedded` aus der Gitea-npm-Registry auf rohana,
|
||||
aber **im Repo steht nirgends, dass der Scope `@sorb` dorthin zeigt**. Es gibt weder eine
|
||||
`.npmrc` im Wurzelverzeichnis noch in `apps/web`, und die CI setzt auch keine.
|
||||
|
||||
Dass es trotzdem funktioniert, liegt allein am Lockfile: `pnpm-lock.yaml` pinnt die vollständige
|
||||
Tarball-URL
|
||||
|
||||
```
|
||||
tarball: https://rohana.axion1337.de/api/packages/sorb/npm/%40sorb%2F…tgz
|
||||
```
|
||||
|
||||
und die CI installiert mit `--frozen-lockfile`. Solange niemand die Version anhebt, wird der
|
||||
Scope nie aufgelöst — und der fehlende Eintrag fällt nicht auf.
|
||||
|
||||
Aufgefallen beim Anheben auf `0.19.2-threadnet.8` (#0054): `pnpm install` griff nach
|
||||
`registry.npmjs.org` und brach ab.
|
||||
|
||||
```
|
||||
ERR_PNPM_FETCH_404 GET https://registry.npmjs.org/@sorb%2Fthreadnet-call-embedded: Not Found
|
||||
```
|
||||
|
||||
Die Anhebung ging nur durch, weil der Scope für diesen einen Aufruf von Hand gesetzt wurde:
|
||||
|
||||
```sh
|
||||
env 'npm_config_@sorb:registry=https://rohana.axion1337.de/api/packages/sorb/npm/' pnpm install
|
||||
```
|
||||
|
||||
Das steht in keiner Doku. Der nächste, der die Abhängigkeit anhebt, läuft in dieselbe Wand.
|
||||
|
||||
## Warum das mehr ist als eine Unbequemlichkeit
|
||||
|
||||
Der heutige Fehlschlag ist **laut** — 404, Abbruch, niemand baut versehentlich etwas Falsches.
|
||||
Laut ist gut. Der Punkt ist, dass die Lautstärke von einer Bedingung abhängt, die uns nicht
|
||||
gehört: `@sorb/threadnet-call-embedded` existiert auf `registry.npmjs.org` **derzeit nicht**.
|
||||
|
||||
Registriert dort jemand diesen Namen, löst genau derselbe Befehl nicht mehr mit 404 auf, sondern
|
||||
**erfolgreich — gegen ein fremdes Paket**. Das ist das Muster, das als *dependency confusion*
|
||||
bekannt ist, und die Bedingung dafür ist nicht „ein Angriff auf uns", sondern „jemand legt einen
|
||||
Scope-Namen an". Der Schutz besteht heute ausschließlich darin, dass ein Name auf einer fremden
|
||||
Registry noch frei ist.
|
||||
|
||||
Verschärfend: Der Fehler träfe genau den Moment, in dem jemand eine Version anhebt — also den
|
||||
Moment, in dem eine neue Abhängigkeit ohnehin erwartet wird und ein Download nicht auffällt.
|
||||
|
||||
Das ist dieselbe Klasse wie die Befunde aus #0054 und der Backup-Reihe: **die Absicherung besteht
|
||||
nicht, sie ergibt sich nur zufällig aus dem aktuellen Zustand.** Sie „meldet" auch nichts — sie
|
||||
funktioniert stillschweigend, bis sie es nicht mehr tut.
|
||||
|
||||
## Acceptance
|
||||
|
||||
- Eine **eingecheckte** `.npmrc` in `ThreadNet-Web` bindet den Scope fest:
|
||||
`@sorb:registry=https://rohana.axion1337.de/api/packages/sorb/npm/`
|
||||
- Ein Anheben der Version funktioniert im **frischen Klon ohne Zusatzschritte** — das ist der
|
||||
eigentliche Nachweis, nicht das Vorhandensein der Datei.
|
||||
- Gegenprobe: Ein Lauf mit absichtlich unerreichbarer rohana **scheitert**, statt auf
|
||||
`registry.npmjs.org` auszuweichen.
|
||||
- Die anderen Repos der Gruppe sind auf denselben Befund geprüft: Wer sonst noch `@sorb`-Pakete
|
||||
zieht, hat dieselbe Lücke (`threadnet-call` selbst publisht nur, konsumiert aber ggf. auch).
|
||||
- Der Auslieferungsweg in `ThreadNet-Web:docs/axion1337-fork.md` nennt den Befehl zum Anheben.
|
||||
|
||||
## Notes
|
||||
|
||||
**Kein Token nötig.** Der Lesezugriff auf die Registry ist anonym möglich (nachgeprüft: der
|
||||
Paket-Index antwortet ohne Authentifizierung). Die `.npmrc` enthält damit **nur eine URL und kein
|
||||
Geheimnis** und kann bedenkenlos eingecheckt werden — das ist der Grund, warum hier kein
|
||||
Secrets-Handling dranhängt.
|
||||
|
||||
**Warum nicht einfach dokumentieren:** Ein Satz in der Fork-Doku würde den 404 erklären, aber die
|
||||
`registry.npmjs.org`-Auflösung nicht verhindern. Nach der Lehre aus #0053 und dem Bindmount-Fall
|
||||
ist Aufschreiben hier ausdrücklich **nicht** die Maßnahme: die Regel war beide Male vorhanden und
|
||||
hat den Fehler nicht verhindert. Wirksam ist die eingecheckte Datei, weil sie den falschen Weg
|
||||
gar nicht erst offen lässt.
|
||||
|
||||
**Verwandt, aber getrennt:** Bei der Gelegenheit ist aufgefallen, dass der Job `publish_npm` in
|
||||
`threadnet-call` `allow_failure: true` trägt — ein *scheiternder* Publish färbt die Pipeline
|
||||
nicht rot. Das ist ein eigener Befund und gehört nicht in dieses Issue; hier nur notiert, damit
|
||||
er nicht verloren geht.
|
||||
|
||||
## Erledigt 2026-08-18 — `.npmrc` eingecheckt, Auflösung nachgewiesen
|
||||
|
||||
Umgesetzt in `ThreadNet-Web` (`be323ed`): `.npmrc` im Wurzelverzeichnis bindet
|
||||
`@sorb` an die Registry auf rohana.
|
||||
|
||||
**Ein Fund beim Umsetzen, der den naiven Fix stillschweigend zunichtegemacht hätte:**
|
||||
Upstreams `.gitignore` enthält `/.npmrc` (Zeile 7) — sinnvoll, wo die Datei Tokens trägt.
|
||||
Die Datei wäre also lokal geblieben, ohne dass irgendetwas gemeldet hätte; CI und frischer
|
||||
Klon hätten weiter gegen npmjs aufgelöst. Die Ausnahme steht jetzt ausdrücklich mit
|
||||
Begründung im `.gitignore` (`!/.npmrc`), damit der nächste Upstream-Merge die Lücke nicht
|
||||
wieder aufmacht. Dieselbe Klasse wie die Befunde aus #0054: gemeldeter Erfolg ohne Wirkung.
|
||||
|
||||
**Abnahme — in einem isolierten Baum gemessen, nicht angenommen:**
|
||||
|
||||
| Prüfung | Ergebnis |
|
||||
|---|---|
|
||||
| ohne `.npmrc` (heutiger Zustand) | löst gegen `registry.npmjs.org` auf, bricht ab |
|
||||
| mit `.npmrc`, frische Auflösung ohne Zusatzschritt | Tarball von `rohana.axion1337.de`, Integrity identisch mit dem Repo-Lockfile |
|
||||
| Gegenprobe rohana unerreichbar | `ERR_PNPM_META_FETCH_FAIL`, **kein** Ausweichen auf npmjs, kein Lockfile |
|
||||
| `pnpm install --frozen-lockfile` (CI-Weg) | grün, Lockfile unverändert |
|
||||
|
||||
**Andere Repos geprüft:** `threadnet-call` *publisht* das Paket nur und schreibt die
|
||||
Scope-Zeile in seiner CI bereits selbst (`.gitlab-ci.yml`); es konsumiert keine
|
||||
`@sorb`-Pakete. In `gitops` kommt der Scope nicht vor. `ThreadNet-Web` war der einzige
|
||||
Konsument — die Lücke ist damit vollständig geschlossen, nicht nur an einer Stelle.
|
||||
|
||||
**Dokumentiert:** `ThreadNet-Web:docs/axion1337-fork.md` hat jetzt einen Abschnitt
|
||||
„Element Call anheben" mit dem Weg ohne Umgebungsvariable, dem `--dir`-statt-`--filter`
|
||||
Fallstrick und der Prüfung, dass die Tarball-URL auf rohana zeigt.
|
||||
|
||||
**Nicht angefasst:** `allow_failure: true` auf `publish_npm` — eigener Befund, liegt
|
||||
weiterhin bei sorb.
|
||||
@@ -0,0 +1,21 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0056"
|
||||
status: open
|
||||
created: 2026-05-14
|
||||
milestone: M1
|
||||
priority: high
|
||||
area: database
|
||||
projekt: gitops
|
||||
gitlab_iid: "9"
|
||||
related: []
|
||||
---
|
||||
# External PostgreSQL Migration: CloudNativePG or Hetzner
|
||||
|
||||
> Adoptiert aus [gitops#9](https://git.lab/axion1337.chat/axion1337.chat-gitops/-/issues/9) (2026-08-18, ADR-0019). Kommentare und Verlauf bleiben dort; kanonisch ist ab jetzt diese Datei.
|
||||
|
||||
Migrate from ESS embedded Postgres to external database. Setup HA + Replication. Test all services. Est. Time: 1-2 days
|
||||
|
||||
---
|
||||
*Migriert aus Gitea `sorb/axion1337.chat-gitops#9` — dort erstellt am 2026-05-14 von sorb.*
|
||||
<!-- gitea-migration: sorb/axion1337.chat-gitops#9 -->
|
||||
@@ -0,0 +1,21 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0057"
|
||||
status: open
|
||||
created: 2026-07-28
|
||||
milestone: M4
|
||||
priority: medium
|
||||
area: element
|
||||
projekt: gitops
|
||||
gitlab_iid: "11"
|
||||
related: []
|
||||
---
|
||||
# Element Call: VP9 codec retry
|
||||
|
||||
> Adoptiert aus [gitops#11](https://git.lab/axion1337.chat/axion1337.chat-gitops/-/issues/11) (2026-08-18, ADR-0019). Kommentare und Verlauf bleiben dort; kanonisch ist ab jetzt diese Datei.
|
||||
|
||||
Retry `video_codec: vp9` for better compression efficiency than the current H.264. First attempt (2026-07-28) broke calls entirely (no audio/video transmitted). Likely cause: LiveKit uses SVC for vp9/av1 instead of classic simulcast, but the threadnet-call fork's `buildPublishOptions()` (`src/livekit/options.ts`) always builds simulcast-shaped `videoSimulcastLayers` regardless of codec. Needs a code fix (branch SVC vs simulcast config by codec) before retrying, plus a real browser-console repro if it fails again. H.264 is live and working well in the meantime (7/8 tracks native, 1 clean VP8 fallback).
|
||||
|
||||
---
|
||||
*Migriert aus Gitea `sorb/axion1337.chat-gitops#11` — dort erstellt am 2026-07-28 von sorb.*
|
||||
<!-- gitea-migration: sorb/axion1337.chat-gitops#11 -->
|
||||
@@ -0,0 +1,21 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0058"
|
||||
status: open
|
||||
created: 2026-07-28
|
||||
milestone: M5
|
||||
priority: medium
|
||||
area: security
|
||||
projekt: gitops
|
||||
gitlab_iid: "14"
|
||||
related: []
|
||||
---
|
||||
# Web Application Firewall (WAF)
|
||||
|
||||
> Adoptiert aus [gitops#14](https://git.lab/axion1337.chat/axion1337.chat-gitops/-/issues/14) (2026-08-18, ADR-0019). Kommentare und Verlauf bleiben dort; kanonisch ist ab jetzt diese Datei.
|
||||
|
||||
Application-layer (L7) request filtering in front of Traefik - inspects actual HTTP content for attack patterns (SQLi, XSS, known exploit signatures), separate from and not covered by the Hetzner Cloud Firewall (which is network-layer L3/L4 IP/port filtering only). Was counted in the original security task total but never had its own written-up task. Consider overlap with CrowdSec (separate issue) which can provide some WAF-like bouncer behavior via Traefik integration - evaluate whether a dedicated WAF (e.g. Coraza/ModSecurity-compatible) is still needed on top of that.
|
||||
|
||||
---
|
||||
*Migriert aus Gitea `sorb/axion1337.chat-gitops#14` — dort erstellt am 2026-07-28 von sorb.*
|
||||
<!-- gitea-migration: sorb/axion1337.chat-gitops#14 -->
|
||||
@@ -0,0 +1,21 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0059"
|
||||
status: open
|
||||
created: 2026-07-28
|
||||
milestone: M5
|
||||
priority: medium
|
||||
area: security
|
||||
projekt: gitops
|
||||
gitlab_iid: "16"
|
||||
related: []
|
||||
---
|
||||
# Pod Security Admission (Restricted)
|
||||
|
||||
> Adoptiert aus [gitops#16](https://git.lab/axion1337.chat/axion1337.chat-gitops/-/issues/16) (2026-08-18, ADR-0019). Kommentare und Verlauf bleiben dort; kanonisch ist ab jetzt diese Datei.
|
||||
|
||||
Apply Restricted Pod Security Admission to the `matrix` and `authentik` namespaces: enforce non-root, no privileged containers, read-only root filesystem. Test carefully for chart breakage before enforcing.
|
||||
|
||||
---
|
||||
*Migriert aus Gitea `sorb/axion1337.chat-gitops#16` — dort erstellt am 2026-07-28 von sorb.*
|
||||
<!-- gitea-migration: sorb/axion1337.chat-gitops#16 -->
|
||||
@@ -0,0 +1,144 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0060"
|
||||
status: done
|
||||
created: 2026-07-28
|
||||
milestone: M1
|
||||
priority: medium
|
||||
area: security
|
||||
projekt: gitops
|
||||
gitlab_iid: "17"
|
||||
related: []
|
||||
---
|
||||
# Federation allowlist or closed federation decision
|
||||
|
||||
> Adoptiert aus [gitops#17](https://git.lab/axion1337.chat/axion1337.chat-gitops/-/issues/17) (2026-08-18, ADR-0019). Kommentare und Verlauf bleiben dort; kanonisch ist ab jetzt diese Datei.
|
||||
|
||||
Decide: open federation with all public Matrix servers (current default, larger attack surface) vs. an explicit `federation_domain_whitelist`, vs. fully closed federation (`allow_public_rooms_without_join_rules: false`). Config lives in `apps/production/custom-configs/synapse-values.yaml`.
|
||||
|
||||
---
|
||||
*Migriert aus Gitea `sorb/axion1337.chat-gitops#17` — dort erstellt am 2026-07-28 von sorb.*
|
||||
<!-- gitea-migration: sorb/axion1337.chat-gitops#17 -->
|
||||
|
||||
## Entscheidungsgrundlage 2026-08-19 — gemessen statt geschätzt
|
||||
|
||||
Die Frage war seit dem 2026-07-28 offen, weil niemand wusste, was eine Schließung
|
||||
kosten würde. Jetzt ist es beziffert.
|
||||
|
||||
**Föderation ist offen und öffentlich erreichbar.** In `synapse-values.yaml` steht zu
|
||||
Föderation **nichts** — es gelten die Synapse-Vorgaben. Von außen gemessen:
|
||||
|
||||
```
|
||||
https://matrix.axion1337.chat/_matrix/federation/v1/version -> HTTP 200
|
||||
https://matrix.axion1337.chat/_matrix/key/v2/server -> HTTP 200
|
||||
```
|
||||
|
||||
Port 8448 ist zwar zu, das ändert nichts: Die Delegation
|
||||
(`/.well-known/matrix/server` → `matrix.axion1337.chat:443`) führt die Föderation über
|
||||
den regulären HTTPS-Port, und der ist offen.
|
||||
|
||||
**Benutzt wurde sie noch nie.** Aus der Synapse-Datenbank, über die gesamte Betriebszeit
|
||||
(Erstkonto 2026-04-21):
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| Zielserver in `destinations` | **0** |
|
||||
| davon je erfolgreich kontaktiert | **0** |
|
||||
| fremde Nutzer in unseren Räumen | **0** |
|
||||
| Räume mit fremder Beteiligung | **0** |
|
||||
| eigene Räume | 31 |
|
||||
|
||||
Nicht „wenig genutzt" — **null**, in vier Monaten. Damit kostet eine Schließung heute
|
||||
nichts Messbares; sie nimmt nur eine Möglichkeit weg, die niemand wahrgenommen hat.
|
||||
|
||||
**Was dagegen bezahlt wird:** Die Föderations-Schnittstelle ist die größte
|
||||
fremdzugewandte Angriffsfläche, die Synapse hat, und historisch die, in der seine CVEs
|
||||
sitzen. Jeder Matrix-Server der Welt darf derzeit Beitritts- und Ereignisverkehr
|
||||
versuchen.
|
||||
|
||||
### Optionen mit ihren Folgen
|
||||
|
||||
**A — offen lassen (Ist-Zustand).** Kein Aufwand. Wir bezahlen dauerhaft
|
||||
Angriffsfläche für eine Fähigkeit mit null nachgewiesenem Bedarf.
|
||||
|
||||
**B — `federation_domain_whitelist` mit leerer Liste.** Föderation nur mit
|
||||
ausdrücklich genannten Servern; leer heißt: mit keinem. Eine Zeile Konfiguration,
|
||||
über GitOps versioniert, in Minuten rückgängig durch Eintragen einer Domain.
|
||||
⚠️ **Was es NICHT tut:** Die Endpunkte antworten weiterhin (`key/v2/server`,
|
||||
`federation/v1/version`) — der Server ist also nicht unsichtbar, nur unbeteiligt.
|
||||
|
||||
**C — Föderations-Listener ganz abschalten.** Kleinste Fläche, aber Eingriff in das
|
||||
ESS-Chart und schwerer zurückzudrehen.
|
||||
|
||||
**Empfehlung: B.** Sie kostet heute nichts, ist eine Zeile, ist versioniert und hält die
|
||||
Tür offen, ohne Miete dafür zu zahlen. C lohnt erst, wenn feststeht, dass Föderation
|
||||
dauerhaft unerwünscht ist — dann als eigene Entscheidung mit ADR.
|
||||
|
||||
**Getrennt davon** nennt das Issue `allow_public_rooms_without_join_rules`: Das steuert
|
||||
nur, ob das Raumverzeichnis über Föderation sichtbar ist, und ist unabhängig von der
|
||||
Grundsatzfrage.
|
||||
|
||||
**Entscheidung sorb steht aus.**
|
||||
|
||||
## Entscheidung und Umsetzung 2026-08-19
|
||||
|
||||
**sorb: erst C, dann C verworfen — B umgesetzt.** Beim Bauen von C zeigte sich, dass
|
||||
ein Pfad-Block die Gruppen-Calls zerstört hätte: `lk-jwt-service` prüft OpenID-Tokens
|
||||
über `/_matrix/federation/v1/openid/userinfo` und ruft ihn über den **öffentlichen**
|
||||
Namen auf (keine `hostAliases`, `dnsPolicy: ClusterFirst`), also über Traefik. Ein
|
||||
Block auf `/_matrix/federation/` hätte denselben Ausfall erzeugt wie der gelöschte
|
||||
`mrtc`-Record. Festgehalten als [ADR-0021](../adr/0021-foederation-geschlossen.md),
|
||||
inklusive der Begründung, warum C nicht nachträglich „noch schnell" nachgeholt werden
|
||||
sollte.
|
||||
|
||||
`federation_domain_whitelist: []` steht in
|
||||
`gitops:apps/production/custom-configs/synapse-values.yaml` (Commit `3935f35`).
|
||||
|
||||
**Zwei Fallen beim Umsetzen, beide vor dem Ausrollen bemerkt:**
|
||||
|
||||
1. Der erste Einschub **zerschnitt den `auto_join`-Block** — `auto_join_rooms_for_guests`
|
||||
landete unter `federation`. Nach dem Zusammenführen der Fragmente funktional
|
||||
identisch, zu lesen falsch; korrigiert, der Diff ist jetzt 20 Zeilen Zugewinn und
|
||||
nichts Verschobenes.
|
||||
2. **Flux hat angewandt, Synapse lief weiter mit der alten Konfiguration.** Die
|
||||
ConfigMap trug die Zeile, der laufende Pod nicht — er stammte vom 2026-08-01. Die
|
||||
Konfiguration wird beim Pod-Start gerendert; ohne Neustart ist die Änderung
|
||||
wirkungslos. Dieselbe Klasse wie #0044. Neustart per
|
||||
`rollout restart statefulset/matrix-stack-synapse-main` angestoßen.
|
||||
|
||||
**Zur Hetzner-Port-Sperre:** Sie kann diese Trennung nicht leisten. 8448 ist bereits zu,
|
||||
und die Delegation führt die Föderation über **443** — denselben Port wie alle Clients.
|
||||
Die Synapse-Konfiguration ist die einzige Stelle, an der Föderation und Client-Verkehr
|
||||
überhaupt trennbar sind.
|
||||
|
||||
### Abnahme nach dem Neustart (2026-08-19, Synapse-Start 11:13 UTC)
|
||||
|
||||
| Prüfung | Ergebnis |
|
||||
|---|---|
|
||||
| `federation_domain_whitelist: []` **im laufenden Prozess** (nicht nur in der ConfigMap) | ✅ vorhanden |
|
||||
| `/_matrix/federation/v1/openid/userinfo` — die Element-Call-Abhängigkeit | ✅ HTTP 401 (bedient, verlangt Token) |
|
||||
| `/_matrix/client/versions` — Client-Verkehr | ✅ HTTP 200 |
|
||||
| `/_matrix/federation/v1/version` | HTTP 200 — **erwartet** |
|
||||
|
||||
Der letzte Punkt ist kein Mangel, sondern die bewusste Grenze von Option B: Die
|
||||
Endpunkte antworten weiterhin, der Server ist **unbeteiligt, nicht unsichtbar**. Was
|
||||
sich geändert hat, ist nicht die Sichtbarkeit, sondern dass Synapse mit keinem fremden
|
||||
Server mehr Ereignisse austauscht.
|
||||
|
||||
**Noch offen: die Abnahme im echten Gruppen-Call.** `curl` belegt, dass der
|
||||
OpenID-Endpunkt antwortet — nicht, dass die vollständige Token-Prüfung durchläuft. Für
|
||||
Änderungen im Call-Pfad gilt hier die Regel aus #0054: Abnahme im echten Call ist
|
||||
Rollout-Voraussetzung, nicht Nacharbeit.
|
||||
|
||||
### Call-Abnahme bestanden (sorb, 2026-08-19)
|
||||
|
||||
Gruppen-Call nach dem Synapse-Neustart getestet: **läuft**. Damit ist belegt, was
|
||||
`curl` nicht belegen konnte — die vollständige OpenID-Token-Prüfung über
|
||||
`/_matrix/federation/v1/openid/userinfo` durchläuft mit geschlossener Föderation
|
||||
unverändert. Die Whitelist greift dort tatsächlich nicht.
|
||||
|
||||
Das ist der Punkt, an dem C endgültig gestorben ist: Genau dieser Pfad hätte bei einem
|
||||
Block auf `/_matrix/federation/` gefehlt, und der Fehler wäre erst im Call aufgefallen.
|
||||
|
||||
**Issue erledigt.** Entscheidung, Begründung und die verworfene Alternative stehen in
|
||||
[ADR-0021](../adr/0021-foederation-geschlossen.md).
|
||||
@@ -0,0 +1,21 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0061"
|
||||
status: open
|
||||
created: 2026-07-28
|
||||
milestone: M2
|
||||
priority: low
|
||||
area: security
|
||||
projekt: gitops
|
||||
gitlab_iid: "20"
|
||||
related: []
|
||||
---
|
||||
# External-Secrets Operator vs. current SOPS setup
|
||||
|
||||
> Adoptiert aus [gitops#20](https://git.lab/axion1337.chat/axion1337.chat-gitops/-/issues/20) (2026-08-18, ADR-0019). Kommentare und Verlauf bleiben dort; kanonisch ist ab jetzt diese Datei.
|
||||
|
||||
Current SOPS+age encryption is working fine. Consider whether External-Secrets Operator (cloud-native secret sourcing, e.g. from a proper secrets manager) is worth the migration effort, or whether to just keep/improve the current SOPS rotation strategy.
|
||||
|
||||
---
|
||||
*Migriert aus Gitea `sorb/axion1337.chat-gitops#20` — dort erstellt am 2026-07-28 von sorb.*
|
||||
<!-- gitea-migration: sorb/axion1337.chat-gitops#20 -->
|
||||
@@ -0,0 +1,21 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0062"
|
||||
status: open
|
||||
created: 2026-07-28
|
||||
milestone: M5
|
||||
priority: medium
|
||||
area: infrastructure
|
||||
projekt: gitops
|
||||
gitlab_iid: "21"
|
||||
related: []
|
||||
---
|
||||
# Renovate/Dependabot for chart and image updates
|
||||
|
||||
> Adoptiert aus [gitops#21](https://git.lab/axion1337.chat/axion1337.chat-gitops/-/issues/21) (2026-08-18, ADR-0019). Kommentare und Verlauf bleiben dort; kanonisch ist ab jetzt diese Datei.
|
||||
|
||||
Automate Helm chart version bumps and container image tag updates, with security patch monitoring, instead of manual version tracking.
|
||||
|
||||
---
|
||||
*Migriert aus Gitea `sorb/axion1337.chat-gitops#21` — dort erstellt am 2026-07-28 von sorb.*
|
||||
<!-- gitea-migration: sorb/axion1337.chat-gitops#21 -->
|
||||
@@ -0,0 +1,21 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0063"
|
||||
status: open
|
||||
created: 2026-07-28
|
||||
milestone: M5
|
||||
priority: medium
|
||||
area: security
|
||||
projekt: gitops
|
||||
gitlab_iid: "22"
|
||||
related: []
|
||||
---
|
||||
# Security advisory monitoring (ESS/Element)
|
||||
|
||||
> Adoptiert aus [gitops#22](https://git.lab/axion1337.chat/axion1337.chat-gitops/-/issues/22) (2026-08-18, ADR-0019). Kommentare und Verlauf bleiben dort; kanonisch ist ab jetzt diese Datei.
|
||||
|
||||
Subscribe to element-hq security mailing list / advisories and Matrix community security channels, set up alerts for new CVEs/patches affecting the deployed components.
|
||||
|
||||
---
|
||||
*Migriert aus Gitea `sorb/axion1337.chat-gitops#22` — dort erstellt am 2026-07-28 von sorb.*
|
||||
<!-- gitea-migration: sorb/axion1337.chat-gitops#22 -->
|
||||
@@ -0,0 +1,21 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0064"
|
||||
status: open
|
||||
created: 2026-07-28
|
||||
milestone: M5
|
||||
priority: medium
|
||||
area: security
|
||||
projekt: gitops
|
||||
gitlab_iid: "23"
|
||||
related: []
|
||||
---
|
||||
# Disable automountServiceAccountToken where not needed
|
||||
|
||||
> Adoptiert aus [gitops#23](https://git.lab/axion1337.chat/axion1337.chat-gitops/-/issues/23) (2026-08-18, ADR-0019). Kommentare und Verlauf bleiben dort; kanonisch ist ab jetzt diese Datei.
|
||||
|
||||
Audit all Deployments/StatefulSets in `matrix` and `authentik` namespaces, add `automountServiceAccountToken: false` wherever the pod doesn't actually need Kubernetes API access (Synapse, ElementWeb, MAS, Postgres, Authentik, etc). Test for no breakage.
|
||||
|
||||
---
|
||||
*Migriert aus Gitea `sorb/axion1337.chat-gitops#23` — dort erstellt am 2026-07-28 von sorb.*
|
||||
<!-- gitea-migration: sorb/axion1337.chat-gitops#23 -->
|
||||
@@ -0,0 +1,21 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0065"
|
||||
status: open
|
||||
created: 2026-07-28
|
||||
milestone: M5
|
||||
priority: high
|
||||
area: infrastructure
|
||||
projekt: gitops
|
||||
gitlab_iid: "25"
|
||||
related: []
|
||||
---
|
||||
# K3s API security hardening
|
||||
|
||||
> Adoptiert aus [gitops#25](https://git.lab/axion1337.chat/axion1337.chat-gitops/-/issues/25) (2026-08-18, ADR-0019). Kommentare und Verlauf bleiben dort; kanonisch ist ab jetzt diese Datei.
|
||||
|
||||
K3s API currently listens on :6443 on all interfaces (default). Options: firewall-restrict :6443 to localhost only, bind K3s to a WireGuard/internal IP via `--bind-address`/`--advertise-address`, or require a bastion/jumphost for kubectl access. The API is a high-value target.
|
||||
|
||||
---
|
||||
*Migriert aus Gitea `sorb/axion1337.chat-gitops#25` — dort erstellt am 2026-07-28 von sorb.*
|
||||
<!-- gitea-migration: sorb/axion1337.chat-gitops#25 -->
|
||||
@@ -0,0 +1,21 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0066"
|
||||
status: open
|
||||
created: 2026-07-28
|
||||
milestone: M5
|
||||
priority: medium
|
||||
area: security
|
||||
projekt: gitops
|
||||
gitlab_iid: "26"
|
||||
related: []
|
||||
---
|
||||
# auditd for file integrity & syscall audit
|
||||
|
||||
> Adoptiert aus [gitops#26](https://git.lab/axion1337.chat/axion1337.chat-gitops/-/issues/26) (2026-08-18, ADR-0019). Kommentare und Verlauf bleiben dort; kanonisch ist ab jetzt diese Datei.
|
||||
|
||||
Monitor `/etc`, `~/.kube`, `/var/lib/rancher/k3s` for sensitive file changes via auditd rules, output to syslog/centralized logging. Low overhead, good forensics/compliance signal.
|
||||
|
||||
---
|
||||
*Migriert aus Gitea `sorb/axion1337.chat-gitops#26` — dort erstellt am 2026-07-28 von sorb.*
|
||||
<!-- gitea-migration: sorb/axion1337.chat-gitops#26 -->
|
||||
@@ -0,0 +1,21 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0067"
|
||||
status: open
|
||||
created: 2026-07-28
|
||||
milestone: M5
|
||||
priority: medium
|
||||
area: infrastructure
|
||||
projekt: gitops
|
||||
gitlab_iid: "27"
|
||||
related: []
|
||||
---
|
||||
# Kernel hardening (sysctl)
|
||||
|
||||
> Adoptiert aus [gitops#27](https://git.lab/axion1337.chat/axion1337.chat-gitops/-/issues/27) (2026-08-18, ADR-0019). Kommentare und Verlauf bleiben dort; kanonisch ist ab jetzt diese Datei.
|
||||
|
||||
Apply Lynis-recommended sysctl hardening: `kernel.kptr_restrict=2`, `kernel.dmesg_restrict=1`, `net.ipv4.tcp_syncookies=1`, `net.ipv4.conf.all.rp_filter=1`, disable ICMP redirects, etc. Persist via `/etc/sysctl.d/99-hardening.conf`.
|
||||
|
||||
---
|
||||
*Migriert aus Gitea `sorb/axion1337.chat-gitops#27` — dort erstellt am 2026-07-28 von sorb.*
|
||||
<!-- gitea-migration: sorb/axion1337.chat-gitops#27 -->
|
||||
@@ -0,0 +1,21 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0068"
|
||||
status: open
|
||||
created: 2026-07-28
|
||||
milestone: M5
|
||||
priority: medium
|
||||
area: security
|
||||
projekt: gitops
|
||||
gitlab_iid: "28"
|
||||
related: []
|
||||
---
|
||||
# Lynis security baseline
|
||||
|
||||
> Adoptiert aus [gitops#28](https://git.lab/axion1337.chat/axion1337.chat-gitops/-/issues/28) (2026-08-18, ADR-0019). Kommentare und Verlauf bleiben dort; kanonisch ist ab jetzt diese Datei.
|
||||
|
||||
Run `lynis audit system` on the host, review and implement high-priority recommendations, aim for a score >80. Re-run quarterly as a baseline check.
|
||||
|
||||
---
|
||||
*Migriert aus Gitea `sorb/axion1337.chat-gitops#28` — dort erstellt am 2026-07-28 von sorb.*
|
||||
<!-- gitea-migration: sorb/axion1337.chat-gitops#28 -->
|
||||
@@ -0,0 +1,21 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0069"
|
||||
status: open
|
||||
created: 2026-07-28
|
||||
milestone: M5
|
||||
priority: medium
|
||||
area: security
|
||||
projekt: gitops
|
||||
gitlab_iid: "29"
|
||||
related: []
|
||||
---
|
||||
# CrowdSec integration
|
||||
|
||||
> Adoptiert aus [gitops#29](https://git.lab/axion1337.chat/axion1337.chat-gitops/-/issues/29) (2026-08-18, ADR-0019). Kommentare und Verlauf bleiben dort; kanonisch ist ab jetzt diese Datei.
|
||||
|
||||
Install CrowdSec agent on the host, feed auth.log/syslog for collaborative attack detection, auto-block malicious IPs via the local firewall or Hetzner Firewall API. Also relevant to the WAF discussion (CrowdSec has Traefik bouncer integration).
|
||||
|
||||
---
|
||||
*Migriert aus Gitea `sorb/axion1337.chat-gitops#29` — dort erstellt am 2026-07-28 von sorb.*
|
||||
<!-- gitea-migration: sorb/axion1337.chat-gitops#29 -->
|
||||
@@ -0,0 +1,21 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0070"
|
||||
status: open
|
||||
created: 2026-07-28
|
||||
milestone: M5
|
||||
priority: medium
|
||||
area: security
|
||||
projekt: gitops
|
||||
gitlab_iid: "30"
|
||||
related: []
|
||||
---
|
||||
# Falco runtime monitoring
|
||||
|
||||
> Adoptiert aus [gitops#30](https://git.lab/axion1337.chat/axion1337.chat-gitops/-/issues/30) (2026-08-18, ADR-0019). Kommentare und Verlauf bleiben dort; kanonisch ist ab jetzt diese Datei.
|
||||
|
||||
Deploy Falco as a DaemonSet in K3s to monitor for suspicious runtime behavior (shell spawning in containers, privilege escalation, anomalous syscalls), output to Loki/syslog with alerting.
|
||||
|
||||
---
|
||||
*Migriert aus Gitea `sorb/axion1337.chat-gitops#30` — dort erstellt am 2026-07-28 von sorb.*
|
||||
<!-- gitea-migration: sorb/axion1337.chat-gitops#30 -->
|
||||
@@ -0,0 +1,21 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0071"
|
||||
status: open
|
||||
created: 2026-07-28
|
||||
milestone: M5
|
||||
priority: low
|
||||
area: infrastructure
|
||||
projekt: gitops
|
||||
gitlab_iid: "31"
|
||||
related: []
|
||||
---
|
||||
# Trivy image scanning for CVEs
|
||||
|
||||
> Adoptiert aus [gitops#31](https://git.lab/axion1337.chat/axion1337.chat-gitops/-/issues/31) (2026-08-18, ADR-0019). Kommentare und Verlauf bleiben dort; kanonisch ist ab jetzt diese Datei.
|
||||
|
||||
Scan container images referenced in Flux HelmReleases for known CVEs, block deployment if a critical CVE is found. CI/CD hook in the git workflow (though note: no Gitea Actions runner is currently active in this repo, per the milestone-release.yml findings from an earlier session - would need that resolved first, or run scanning out-of-band).
|
||||
|
||||
---
|
||||
*Migriert aus Gitea `sorb/axion1337.chat-gitops#31` — dort erstellt am 2026-07-28 von sorb.*
|
||||
<!-- gitea-migration: sorb/axion1337.chat-gitops#31 -->
|
||||
@@ -0,0 +1,20 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0072"
|
||||
status: open
|
||||
created: 2026-07-28
|
||||
milestone: M1
|
||||
priority: low
|
||||
projekt: gitops
|
||||
gitlab_iid: "34"
|
||||
related: []
|
||||
---
|
||||
# DSGVO/Datenschutz-Compliance konkretisieren
|
||||
|
||||
> Adoptiert aus [gitops#34](https://git.lab/axion1337.chat/axion1337.chat-gitops/-/issues/34) (2026-08-18, ADR-0019). Kommentare und Verlauf bleiben dort; kanonisch ist ab jetzt diese Datei.
|
||||
|
||||
Bisher nur als vages "M7: Enterprise-Ready - Future"-Ziel in der alten Milestone-Tabelle vermerkt, nie konkretisiert. Relevant, sobald echte Nutzer (nicht nur Testaccounts) und offene Federation im Spiel sind - fremde Server/Nutzer sehen dann ggf. Daten mit. Prio 0 laut User - erst angehen, wenn die anderen Punkte durch sind.
|
||||
|
||||
---
|
||||
*Migriert aus Gitea `sorb/axion1337.chat-gitops#34` — dort erstellt am 2026-07-28 von sorb.*
|
||||
<!-- gitea-migration: sorb/axion1337.chat-gitops#34 -->
|
||||
@@ -0,0 +1,23 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0073"
|
||||
status: open
|
||||
created: 2026-07-28
|
||||
milestone: M2
|
||||
priority: medium
|
||||
area: infrastructure
|
||||
projekt: gitops
|
||||
gitlab_iid: "35"
|
||||
related: []
|
||||
---
|
||||
# Architektur: Monorepo-Umbau mit generalisiertem Config-Overlay
|
||||
|
||||
> Adoptiert aus [gitops#35](https://git.lab/axion1337.chat/axion1337.chat-gitops/-/issues/35) (2026-08-18, ADR-0019). Kommentare und Verlauf bleiben dort; kanonisch ist ab jetzt diese Datei.
|
||||
|
||||
Aktuell drei getrennte Repos (`axion1337.chat-gitops`, `ThreadNet-Web`, `threadnet-call`). Idee: alles in ein Monorepo überführen, mit einem generalisierten Setup und einer Art Config-Overlay (z.B. Kustomize-Overlays oder Helm-Values-Layering pro Deployment-Ziel), damit der gesamte Stack reproduzierbar auch an anderer Stelle/für eine andere Domain deploybar wird - nicht fest auf axion1337.chat verdrahtet.
|
||||
|
||||
**Wichtig**: Das ist ein größeres Architektur-Vorhaben und braucht erst eine gründliche, eigene Planungssession, bevor irgendwas umgesetzt wird. Nicht nebenbei anfassen.
|
||||
|
||||
---
|
||||
*Migriert aus Gitea `sorb/axion1337.chat-gitops#35` — dort erstellt am 2026-07-28 von sorb.*
|
||||
<!-- gitea-migration: sorb/axion1337.chat-gitops#35 -->
|
||||
@@ -0,0 +1,83 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0074"
|
||||
status: open
|
||||
created: 2026-07-28
|
||||
milestone: M2
|
||||
priority: low
|
||||
projekt: gitops
|
||||
gitlab_iid: "39"
|
||||
related: []
|
||||
---
|
||||
# Cleanup-Checkliste (laufend)
|
||||
|
||||
> Adoptiert aus [gitops#39](https://git.lab/axion1337.chat/axion1337.chat-gitops/-/issues/39) (2026-08-18, ADR-0019). Kommentare und Verlauf bleiben dort; kanonisch ist ab jetzt diese Datei.
|
||||
|
||||
Laufende Sammel-Liste kleiner Aufraeumarbeiten, kein einzelnes Projekt - hier landen zukuenftig weitere kleine Punkte.
|
||||
|
||||
- [ ] Verwaistes `@bojeledoggo:axion1337.chat` (leeres Matrix-Konto ohne OIDC-Link) loeschen/deaktivieren
|
||||
- [ ] `Boje`s fehlende E-Mail in Authentik ergaenzen (oder bewusst so lassen?)
|
||||
- [ ] Test-Invitations/-Raeume aus der Session 2026-07-27/28 aufraeumen (`test-fix-2026-07-27*`, Call-Test-Raeume von akadmin/frank/clark/lucky)
|
||||
- [ ] `clark`/`lucky` Testaccounts loeschen, sobald der Stack final abgenommen ist
|
||||
|
||||
---
|
||||
*Migriert aus Gitea `sorb/axion1337.chat-gitops#39` — dort erstellt am 2026-07-28 von sorb.*
|
||||
<!-- gitea-migration: sorb/axion1337.chat-gitops#39 -->
|
||||
|
||||
## Zwischenstand 2026-08-18 — gemessen, nicht abgehakt
|
||||
|
||||
Bei der Wiki-Zugangsdiagnose (#0103) fielen drei der vier Punkte als Messwerte an:
|
||||
|
||||
- **`@bojeledoggo` — erledigt.** Das Konto ist in Synapse `deactivated = 1`
|
||||
(angelegt 2026-07-28). Der Punkt kann gestrichen werden.
|
||||
- **`Boje`s fehlende E-Mail — weiterhin offen**, und die Lage ist eigentümlicher als
|
||||
die Zeile vermuten lässt: `Boje` existiert **nur in Authentik** (angelegt
|
||||
2026-05-15, genau ein Login am selben Tag, seither nie wieder, keine E-Mail, keine
|
||||
Gruppe). Ein Matrix-Konto `@boje`/`@Boje` gibt es **nicht**, und in MAS existiert
|
||||
keine Verknüpfung — die Identität hat nie einen Matrix-Login abgeschlossen. Die
|
||||
Frage ist damit weniger „E-Mail nachtragen" als „gehört die Identität noch
|
||||
irgendwohin".
|
||||
- **`clark`/`lucky` — noch nicht gelöscht**, beide in Synapse aktiv
|
||||
(`deactivated = 0`). Die Bedingung des Punktes („sobald der Stack final abgenommen
|
||||
ist") ist die eigentliche Frage.
|
||||
|
||||
Zur Einordnung, weil die Namensähnlichkeit hier schon einmal in die Irre geführt hat:
|
||||
`@bojeledoggo` (deaktiviert, Matrix), `Boje` (aktiv, nur Authentik) und `elbojoloco`
|
||||
(= `@akadmin`, aktiv, sorbs Admin-Konto) sind **drei verschiedene Konten**.
|
||||
|
||||
### `Boje` gelöscht 2026-08-18 (Entscheidung sorb)
|
||||
|
||||
Der Authentik-Nutzer `Boje` (pk 5, `76893901-…`, ohne Name, ohne E-Mail, ohne
|
||||
Gruppe, angelegt 2026-05-15, letzter und einziger Login am selben Tag) ist
|
||||
gelöscht. Django meldete `(1, {'authentik_core.User': 1})` — **genau ein
|
||||
Datensatz, keine abhängigen Objekte**, weil nie etwas daran hing.
|
||||
|
||||
**Warum hier gelöscht und nicht deaktiviert werden konnte:** Matrix-Konten lassen
|
||||
sich nicht wirklich löschen — Synapse kennt nur Deaktivierung, und der Localpart
|
||||
bleibt anschließend belegt, damit niemand eine fremde Identität samt Historie und
|
||||
Erwähnungen erben kann. Deshalb ist `@bojeledoggo` deaktiviert und nicht entfernt;
|
||||
das **ist** der Endzustand, kein halber Schritt. `Boje` war aber nie ein
|
||||
Matrix-Konto: kein Eintrag in Synapse, keiner in MAS, keine Verknüpfung. Die
|
||||
Einschränkung galt für dieses Konto also gar nicht.
|
||||
|
||||
Damit sind von der Liste zwei Punkte erledigt (`@bojeledoggo` deaktiviert, `Boje`
|
||||
gelöscht). Offen bleiben die Testräume aus der Session 2026-07-27/28 und
|
||||
`clark`/`lucky`, beide in Synapse weiterhin aktiv.
|
||||
|
||||
### `lucky` gesperrt 2026-08-18 (Entscheidung sorb)
|
||||
|
||||
`mas-cli manage lock-user lucky` (MAS `locked_at = 2026-08-18 19:57`) plus
|
||||
`kill-sessions`: eine OAuth-2.0- und zwei Browser-Sessions beendet, Geräte-Sync
|
||||
angestoßen. Anmelden ist damit nicht mehr möglich.
|
||||
|
||||
⚠️ **Gesperrt ist nicht deaktiviert.** `deactivated_at` ist leer — `lucky` steht
|
||||
jetzt wie `scanner-test`, nicht wie `@bojeledoggo` (dort ist `deactivated_at`
|
||||
gesetzt und Synapse führt `deactivated = 1`). Der Unterschied: Sperren ist
|
||||
reversibel (`unlock-user`), Deaktivieren räumt Profil, Geräte und Raum-Mitgliedschaften
|
||||
ab und ist es nicht.
|
||||
|
||||
**Warum nur gesperrt:** Die hier laufende MAS-Version kennt im CLI kein
|
||||
`deactivate-user` (`mas-cli manage` bietet nur `lock-user`/`unlock-user`). Volle
|
||||
Deaktivierung läuft über die Admin-API bzw. Element Admin und damit über ein
|
||||
Admin-Token — das gehört nicht in eine Session. Wenn `lucky` endgültig weg soll,
|
||||
ist das ein Klick in Element Admin; das Sperren nimmt bis dahin die Wirkung vorweg.
|
||||
@@ -0,0 +1,45 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0075"
|
||||
status: rejected
|
||||
created: 2026-07-28
|
||||
milestone: M2
|
||||
priority: low
|
||||
area: infrastructure
|
||||
projekt: gitops
|
||||
gitlab_iid: "40"
|
||||
related: []
|
||||
---
|
||||
# Neue Issues erscheinen nicht automatisch im Gitea-Kanban/Projects-Board
|
||||
|
||||
> Adoptiert aus [gitops#40](https://git.lab/axion1337.chat/axion1337.chat-gitops/-/issues/40) (2026-08-18, ADR-0019). Kommentare und Verlauf bleiben dort; kanonisch ist ab jetzt diese Datei.
|
||||
|
||||
Gitea bietet aktuell keine REST-API fuer Projects/Kanban-Boards (bestaetigt mit 4 verschiedenen Tokens, darunter ein Full-Admin-Token - durchgehend 404). Das ist keine Berechtigungsfrage auf unserer Seite, sondern eine tatsaechlich fehlende Upstream-Funktion (siehe Gitea GitHub Issue #36824, offenes Feature-Request, Stand 2026-07-28 noch nicht implementiert).
|
||||
|
||||
Konkrete Auswirkung: Neu erstellte Issues (#11-#39, per Batch-Script aus dem alten TASKS.md-Backlog migriert) landen nicht automatisch im bestehenden Kanban/Projects-Board der Roadmap - muessen manuell per Drag&Drop/UI hinzugefuegt werden.
|
||||
|
||||
Optionen fuer die Zukunft:
|
||||
- Manuell nachpflegen (aktueller Stand)
|
||||
- Auf ein Gitea-Update warten, falls die Projects-API implementiert wird
|
||||
- Alternatives Board-Tool evaluieren, falls das dauerhaft zu nervig wird
|
||||
|
||||
Niedrige Prioritaet, da rein organisatorisch - kein technisches Risiko.
|
||||
|
||||
---
|
||||
*Migriert aus Gitea `sorb/axion1337.chat-gitops#40` — dort erstellt am 2026-07-28 von sorb.*
|
||||
<!-- gitea-migration: sorb/axion1337.chat-gitops#40 -->
|
||||
|
||||
## Gegenstandslos — Relevanz-Durchgang 2026-08-18
|
||||
|
||||
Die Voraussetzung des Issues gibt es nicht mehr. Es beschreibt, dass neue Issues
|
||||
nicht automatisch im **Gitea**-Kanban landen, weil Gitea keine Projects-API hat.
|
||||
Inzwischen liegen die Issues weder in Gitea noch primär in einem Board: kanonisch
|
||||
ist `docs/issues/` im management-Repo, und die Board-Ansicht auf GitLab wird von
|
||||
`scripts/spiegel_issues.py` deterministisch beschrieben — genau die Automatik, die
|
||||
hier gefehlt hat, nur an einer anderen Stelle
|
||||
([ADR-0012](../adr/0012-issues-im-repo-gitlab-als-spiegel.md),
|
||||
[ADR-0019](../adr/0019-komponenten-issues-adoptiert.md)).
|
||||
|
||||
Die fehlende Gitea-API ist damit kein Mangel mehr, sondern irrelevant. Kein
|
||||
Aufwand offen, nichts zu tun — deshalb `rejected` statt `done`: erledigt wurde
|
||||
hier nichts, die Frage hat sich aufgelöst.
|
||||
@@ -0,0 +1,29 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0076"
|
||||
status: open
|
||||
created: 2026-07-28
|
||||
milestone: M2
|
||||
priority: low
|
||||
area: infrastructure
|
||||
projekt: gitops
|
||||
gitlab_iid: "41"
|
||||
related: []
|
||||
---
|
||||
# Registry-/Git-Traffic zum Gitea-Host ueber privates Hetzner-Netzwerk statt oeffentlichem Internet routen
|
||||
|
||||
> Adoptiert aus [gitops#41](https://git.lab/axion1337.chat/axion1337.chat-gitops/-/issues/41) (2026-08-18, ADR-0019). Kommentare und Verlauf bleiben dort; kanonisch ist ab jetzt diese Datei.
|
||||
|
||||
Waehrend der Backup-Implementierung (#6/#15) stellte sich heraus, dass eine Firewall-Fehlkonfiguration den K3s-Node komplett von `rohana.axion1337.de` (Gitea, Container-Registry) abschnitt - Image-Pulls schlugen mit Timeout fehl. Ursache gefunden: beide Server haengen im selben privaten Hetzner-Netzwerk (Node 10.0.0.2, Gitea-Host 10.0.0.3, <2ms Latenz), aber der Traffic lief bisher ausschliesslich ueber die oeffentliche IP/Internet.
|
||||
|
||||
Als Sofortmassnahme wurde ein statischer Eintrag in `/etc/hosts` auf dem K3s-Node ergaenzt (`10.0.0.3 rohana.axion1337.de`), der Image-Pulls unabhaengig vom Zustand der oeffentlichen Firewall macht. Das ist aber unmanaged Node-Konfiguration (kein GitOps, ueberlebt einen Node-Neuaufbau nicht).
|
||||
|
||||
Sauberer, dauerhafter Fix waere eine cluster-weite Loesung, z.B.:
|
||||
- CoreDNS-Rewrite/Hosts-Plugin im Corefile, damit alle Pods (nicht nur der Node selbst) `rohana.axion1337.de` intern aufloesen
|
||||
- Pruefen, ob auch Flux GitRepository-Sync davon profitieren kann/sollte
|
||||
|
||||
Vorteil: Traffic bleibt intern, unabhaengig von oeffentlicher Firewall/Internet-Erreichbarkeit, kein Punkt mehr, an dem eine Firewall-Anpassung versehentlich Image-Pulls oder Flux-Sync brechen kann.
|
||||
|
||||
---
|
||||
*Migriert aus Gitea `sorb/axion1337.chat-gitops#41` — dort erstellt am 2026-07-28 von sorb.*
|
||||
<!-- gitea-migration: sorb/axion1337.chat-gitops#41 -->
|
||||
@@ -0,0 +1,32 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0077"
|
||||
status: open
|
||||
created: 2026-07-29
|
||||
milestone: M1
|
||||
priority: low
|
||||
area: infrastructure
|
||||
projekt: gitops
|
||||
gitlab_iid: "42"
|
||||
related: []
|
||||
---
|
||||
# Grafana-Dashboard für ClamAV-Scan-Ergebnisse (Issue #19)
|
||||
|
||||
> Adoptiert aus [gitops#42](https://git.lab/axion1337.chat/axion1337.chat-gitops/-/issues/42) (2026-08-18, ADR-0019). Kommentare und Verlauf bleiben dort; kanonisch ist ab jetzt diese Datei.
|
||||
|
||||
Wunsch aus Issue #19-Tests: sichtbar machen, wie oft ClamAV tatsächlich etwas blockiert
|
||||
(bisher nur in Synapse-Logs sichtbar: `clamav_spam_checker - WARNING - ClamAV rejected an
|
||||
upload: <signature>`).
|
||||
|
||||
Da Alloy bereits alle Pod-Logs nach Loki schickt (`10.0.0.3:3100`, siehe
|
||||
`docs/deployment-guides/03-monitoring-integration.md`), braucht es dafür keine neue
|
||||
Instrumentierung - nur ein neues Grafana-Dashboard/Panel mit einer LogQL-Query auf
|
||||
`{app="synapse-main"} |= "ClamAV rejected"` (Anzahl über Zeit, evtl. Tabelle mit erkannten
|
||||
Signaturen). Optional zusätzlich: ein Panel für Scanner-Ausfälle (`ClamAV scan failed` -
|
||||
fail-open-Fälle, die sonst unbemerkt blieben).
|
||||
|
||||
Kein Server-seitiger Code nötig, rein Grafana/Loki-Dashboard-Arbeit.
|
||||
|
||||
---
|
||||
*Migriert aus Gitea `sorb/axion1337.chat-gitops#43` — dort erstellt am 2026-07-29 von sorb.*
|
||||
<!-- gitea-migration: sorb/axion1337.chat-gitops#43 -->
|
||||
@@ -0,0 +1,33 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0078"
|
||||
status: open
|
||||
created: 2026-08-01
|
||||
milestone: M1
|
||||
priority: medium
|
||||
projekt: gitops
|
||||
gitlab_iid: "45"
|
||||
related: []
|
||||
---
|
||||
# CVE-Meldeweg v2: Metriken, Grafana-Dashboard, Alerts in eigenen Matrix-Raum
|
||||
|
||||
> Adoptiert aus [gitops#45](https://git.lab/axion1337.chat/axion1337.chat-gitops/-/issues/45) (2026-08-18, ADR-0019). Kommentare und Verlauf bleiben dort; kanonisch ist ab jetzt diese Datei.
|
||||
|
||||
Entscheidung sorb (2026-08-01): Die reine Artifact-Ablage der Trivy-Funde (#31) ist **ungenügend**. Zielbild:
|
||||
|
||||
1. **Eigener Matrix-Raum für CVE-/Release-Meldungen**: `!YRJvcEbVXtRlUIkNld:axion1337.chat` (angelegt), Absender bleibt der bestehende `@alerts`-Bot — kein zweiter Bot (beantwortet CFGMON-13). ⬜ **Bot einladen** (Raum ist restricted, Join wurde abgelehnt) — sorb.
|
||||
2. **CVEs als Metriken** → **Grafana-Dashboard** + **Prometheus-Alertregeln** → Alertmanager → Matrix. Damit laufen CVE-Warnungen über denselben Alarmweg wie alles andere (Historie, Silences, Dashboards inklusive).
|
||||
|
||||
**Architektur-Vorschlag (zur Diskussion):**
|
||||
- **Scan-Ort wandert von der Lab-CI nach CFGMON**: Trivy als Compose-Service/Cron im monitoring-Stack (`--format json` → kleiner Stdlib-Konverter → Prometheus-Textfile/Pushgateway-los via Remote-Write auf localhost:9090). Begründung: Metriken, Prometheus und Grafana wohnen dort; die Lab-CI bleibt fürs schnelle „Report als Artifact" beim Release-Build. Alternativ: Lab-CI pusht Metriken — scheitert aber an CFGMON-03 (9090 wird gerade zugezogen) und koppelt Prod-Monitoring an Lab-Verfügbarkeit.
|
||||
- **Metrik-Schema**: `trivy_image_vulnerabilities{image,severity}` (Gauge) + `trivy_scan_timestamp{image}`; Alertregel z. B. `trivy_image_vulnerabilities{severity="CRITICAL"} > 0` → severity=critical, `HIGH > 0` → warning mit `for: 24h` (Rauschdämpfung).
|
||||
- **Raum-Routing**: matrix-alerts-Receiver bekommt Label-basiertes Routing (`room`-Label im Alert → Ziel-Raum, Default = Alerts-Raum); Alertmanager-Route setzt `room: cve` für Trivy-Alerts. Kleiner, sauberer Eingriff im bestehenden Stdlib-Receiver.
|
||||
- **release-watch** (Advisory-Notizen, #22) zieht in denselben CVE-Raum um — Env dafür ist vorbereitet (`MATRIX_RELEASE_ROOM_ID`, Fallback Alerts-Raum).
|
||||
|
||||
**Abgrenzung SBOM** (Frage sorb, dokumentiert auch im Script): `release-watch` lebt von einer **handgepflegten Repo-Liste** — de facto ein Mini-SBOM auf Repo-Granularität, ohne Versions-/Dependency-Wissen. **Trivy dagegen erzeugt sein SBOM selbst aus den Images** (OS-Pakete + Sprach-Dependencies) — dort ist nichts zu pflegen. Beide ergänzen sich: Trivy = „was IST verwundbar in dem, was wir ausliefern", release-watch = „Upstream hat etwas veröffentlicht, das uns betreffen könnte".
|
||||
|
||||
Verweise: #31 (Scan existiert), #22 (release-watch), CFGMON-13 im Backlog (Absender-Design — durch Punkt 1 entschieden).
|
||||
|
||||
---
|
||||
*Migriert aus Gitea `sorb/axion1337.chat-gitops#47` — dort erstellt am 2026-08-01 von sorb.*
|
||||
<!-- gitea-migration: sorb/axion1337.chat-gitops#47 -->
|
||||
@@ -0,0 +1,56 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0079"
|
||||
status: done
|
||||
created: 2026-08-01
|
||||
milestone: M2
|
||||
priority: high
|
||||
projekt: gitops
|
||||
gitlab_iid: "46"
|
||||
related: []
|
||||
---
|
||||
# Issue-Migration nach GitLab + zentrale Projekt-Roadmap (Harmonisierung)
|
||||
|
||||
> Adoptiert aus [gitops#46](https://git.lab/axion1337.chat/axion1337.chat-gitops/-/issues/46) (2026-08-18, ADR-0019). Kommentare und Verlauf bleiben dort; kanonisch ist ab jetzt diese Datei.
|
||||
|
||||
**Auftrag sorb (2026-08-01, HOHE PRIORITÄT):** Es wird zunehmend unübersichtlich — Issues liegen in vier Gitea-Repos, Host-Backlogs im Backlogs-Repo, Code seit der Migration auf git.lab. Ziel: **eine zentrale Sicht in GitLab.**
|
||||
|
||||
## Umfang
|
||||
1. **Issue-Migration (zwingend):** alle bestehenden Gitea-Issues (offen UND geschlossen — die Abschlussdokumentation ist wertvoll) nach GitLab überführen: gitops (47+), ThreadNet-Web (8), threadnet-call (2), thread-net-git (1).
|
||||
2. **Harmonisierung/Roadmap:** zentrale Projekt-Roadmap auf Gruppen-Ebene (`axion1337.chat`), die Issues + Host-Backlogs sinnvoll zusammenführt — GitLab-Bordmittel: Gruppen-**Epics/Roadmap-Ansicht**, Milestones, Labels-Taxonomie (prio/*, area/*, host/*), Gruppen-Boards.
|
||||
|
||||
## Plan-Skizze (zur Abstimmung vor Umsetzung)
|
||||
- **Werkzeug:** GitLabs eingebauter Gitea-Importer übernimmt Issues+Kommentare (Autorenschaft läuft auf den Import-User — bekannter, akzeptierbarer Verlust). Da die Projekte in GitLab schon existieren, ist ggf. stattdessen ein API-Skript nötig (Issues in BESTEHENDE Projekte importieren kann der Importer nicht) — Skript-Weg: Gitea-API lesen → GitLab-API schreiben, `[Gitea #N]`-Präfix im Titel oder Migrations-Fußzeile pro Issue für die Nummern-Zuordnung (Commit-Messages referenzieren alte Nummern!).
|
||||
- **Host-Backlogs:** `Backlogs`-Repo-Einträge (CFGMON-xx, MATRIX-xx, …) als GitLab-Issues in einem neuen Projekt `infrastruktur` (oder Labels `host::cfgmon` etc.) — die Markdown-Historie bleibt als Archiv erhalten.
|
||||
- **Folgeänderungen (nicht vergessen):** Repo-Topologie-Doku (CLAUDE.md/README „Issues bleiben Gitea" wird obsolet), TURN-Rotations-CronJob-PR-Hinweis, alle `rohana…/issues`-Links in Doku, meine Sessions-Werkzeuge (claude-issues-Token → GitLab-Token). ⚠️ **Erreichbarkeits-Trade-off bewusst machen:** git.lab ist nur im Lab auflösbar — Issues wären unterwegs nicht mehr erreichbar. Optionen: (a) akzeptieren, (b) GitLab extern erreichbar machen (eigenes Projekt!), (c) Hybrid vermeiden — genau der räumt ja nicht auf. **Entscheidung sorb nötig, bevor migriert wird.**
|
||||
- **Reihenfolge:** Labels/Epics-Gerüst zuerst, dann Testmigration EIN Repo (thread-net-git, 1 Issue), Review, dann Rest.
|
||||
|
||||
Verwandt: Backlogs CFGMON-12 (wird hiervon abgelöst/erweitert).
|
||||
|
||||
---
|
||||
*Migriert aus Gitea `sorb/axion1337.chat-gitops#48` — dort erstellt am 2026-08-01 von sorb.*
|
||||
<!-- gitea-migration: sorb/axion1337.chat-gitops#48 -->
|
||||
|
||||
## Erledigt — Relevanz-Durchgang 2026-08-18
|
||||
|
||||
Beide Teile des Auftrags sind eingelöst, der zweite anders als hier skizziert.
|
||||
|
||||
**Teil 1, Issue-Migration: durchgeführt.** Der Skript-Weg wurde gebaut
|
||||
(`verfahren/issue-migration/migrate.py`, Gitea-API lesen → GitLab-API schreiben,
|
||||
idempotent über einen Migrations-Marker) und ist gelaufen — die Migrations-Fußzeilen
|
||||
in den adoptierten Issues sind sein Ergebnis. Der hier vermutete Verlust der
|
||||
Autorenschaft trat wie erwartet ein und wurde akzeptiert.
|
||||
|
||||
**Teil 2, zentrale Sicht: entschieden, aber gegen die Skizze.** Der Plan wollte die
|
||||
Zentrale *in GitLab* bauen (Epics, Gruppen-Boards). Entschieden wurde das Gegenteil:
|
||||
`docs/issues/` im Repo ist kanonisch, GitLab ist der generierte Spiegel
|
||||
([ADR-0012](../adr/0012-issues-im-repo-gitlab-als-spiegel.md)), seit
|
||||
[ADR-0019](../adr/0019-komponenten-issues-adoptiert.md) für alle Tracker der Gruppe.
|
||||
Damit ist auch der hier als offen markierte **Erreichbarkeits-Trade-off beantwortet**,
|
||||
und zwar besser als mit den drei Optionen: die Issues liegen im Repo und sind über
|
||||
dessen Gitea-Spiegel von überall lesbar — es braucht weder Lab-Zugang noch ein extern
|
||||
erreichbares GitLab.
|
||||
|
||||
Die genannten Folgeänderungen sind mitgezogen (Repo-Topologie-Doku, Token-Weg der
|
||||
Sessions). Was von der Host-Backlog-Zusammenführung übrig war, steckt in den
|
||||
Issues 0001–0034.
|
||||
@@ -0,0 +1,34 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0080"
|
||||
status: open
|
||||
created: 2026-08-01
|
||||
milestone: M3
|
||||
priority: medium
|
||||
projekt: gitops
|
||||
gitlab_iid: "47"
|
||||
related: []
|
||||
---
|
||||
# Raidplaner mit sozialer Komponente (Verfügbarkeiten, Aufgaben, Roadmap, Fotoalbum)
|
||||
|
||||
> Adoptiert aus [gitops#47](https://git.lab/axion1337.chat/axion1337.chat-gitops/-/issues/47) (2026-08-18, ADR-0019). Kommentare und Verlauf bleiben dort; kanonisch ist ab jetzt diese Datei.
|
||||
|
||||
**Wunsch sorb (2026-08-01):** gemeinsames Planungs-Tool mit sozialer Komponente — Verfügbarkeiten („wer hat wann Zeit"), Aufgaben-Zuweisung, gemeinsame Roadmap-Visualisierung, **integriertes Fotoalbum** (Bilder anlassbezogen an Aktivitäten/Board-/Roadmap-Einträge geknüpft, z. B. Bauphasen-Screenshots, Boss-Kämpfe).
|
||||
|
||||
**Rechercheergebnis (Kurzfassung):** Die Gaming-„Raidplaner"-Szene (Raid-Helper, Raid-Planner) ist durchweg **Discord-gebunden, nicht self-hosted** — passt nicht zum Matrix-Stack. Realistische Self-Hosted-Kandidaten:
|
||||
|
||||
| Kandidat | Verfügbarkeit | Aufgaben | Roadmap | Fotoalbum integriert | Einschätzung |
|
||||
|---|---|---|---|---|---|
|
||||
| **HumHub** | Kalender-Modul + Umfragen | Tasks-Modul | Kanban-artig | ✅ Gallery-Modul, an Spaces/Posts geknüpft | **Bester Fit für „sozial + Fotos"** — Community-Plattform mit Modulen; Achtung: manche Module Pro |
|
||||
| **Nextcloud** (Deck+Calendar+Polls+Memories) | Polls/Kalender | Deck-Boards | Deck + Kalender | Memories/Photos, aber nur locker verknüpfbar | Mächtig, aber „zusammengesteckt" statt integriert |
|
||||
| **Agorakit** | Termine + Umfragen | rudimentär | — | Datei-/Bildablage je Gruppe | Leichtgewichtiger Geheimtipp, kleineres Ökosystem |
|
||||
| **Vikunja / Focalboard** | — | ✅ stark | ✅ | ❌ | reine Task-Tools, Sozialteil fehlt |
|
||||
| **Mobilizon** | Events ✅ | ❌ | ❌ | ❌ | nur Event-Seite |
|
||||
|
||||
**Empfehlung zur Evaluation:** HumHub zuerst (deckt als Einziges alle vier Anforderungen in EINEM Tool), Nextcloud als Plan B falls HumHubs Modul-Lizenzmodell stört. Deployment-Ort-Frage (CFGMON? eigener Host?) und Matrix-SSO via Authentik (beide können OIDC!) gehören in die Evaluation.
|
||||
|
||||
Quellen: raid-helper.dev, raid-planner.com, alternativeto.net/software/open-event.
|
||||
|
||||
---
|
||||
*Migriert aus Gitea `sorb/axion1337.chat-gitops#49` — dort erstellt am 2026-08-01 von sorb.*
|
||||
<!-- gitea-migration: sorb/axion1337.chat-gitops#49 -->
|
||||
@@ -0,0 +1,37 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0081"
|
||||
status: open
|
||||
created: 2026-08-01
|
||||
milestone: M3
|
||||
priority: medium
|
||||
projekt: gitops
|
||||
gitlab_iid: "48"
|
||||
related: []
|
||||
---
|
||||
# Gäste-Invite-Workflow per Bot (3-Tage-Accounts, Admin-Freischaltung, begrenzte Reaktivierung)
|
||||
|
||||
> Adoptiert aus [gitops#48](https://git.lab/axion1337.chat/axion1337.chat-gitops/-/issues/48) (2026-08-18, ADR-0019). Kommentare und Verlauf bleiben dort; kanonisch ist ab jetzt diese Datei.
|
||||
|
||||
**Wunsch sorb (2026-08-01):** definierter Prozess für sichere, temporäre Gast-Einladungen:
|
||||
|
||||
1. **Einladung:** festgelegter Nutzerkreis schreibt den Bot an → Invite-Link wird generiert
|
||||
2. **Initiales Limit:** Gast-Account läuft nach **3 Tagen** automatisch ab
|
||||
3. **Permanente Freischaltung:** nur durch aktive Admin-Prüfung; sonst bleibt der Account deaktiviert
|
||||
4. **Fallback:** ohne greifbaren Admin kann der einladende Kreis den Account über den Bot **max. 2× um je 1 Tag** reaktivieren, danach zwingend Admin
|
||||
|
||||
## Architektur-Realitätscheck (Stack-Gegebenheiten)
|
||||
|
||||
- Registrierung läuft in diesem Stack **ausschließlich über Authentik** (MAS-OIDC; die `matrix-invitation`-Flow-Infrastruktur mit Invitation-Stage existiert bereits aus Issue #7!). Der natürliche Invite-Link ist also ein **Authentik-Invitation-Token** (single-use, mit Ablauf) — kein Synapse-Registration-Token.
|
||||
- **Draupnir** ist ein Moderations-Bot ohne Invite-/Lifecycle-Funktion — er kann Policy-seitig flankieren (Gast-Raumrechte), aber die Link-Generierung + Ablauf-/Reaktivierungslogik braucht einen **kleinen eigenen Bot** (Machart wie @alerts/maintenance-notify: Stdlib, Matrix-API + Authentik-API + MAS/Authentik-Deaktivierung). Empfehlung: eigener `@concierge`-Bot statt Draupnir-Verbiegung; Draupnir-Integration als Stufe 2 (z. B. Gast-Label → eingeschränkte Räume).
|
||||
- **Ablauf/Deaktivierung:** Authentik-User-Attribut `expires_at` + periodischer Bot-Check (deaktiviert via Authentik-API → MAS-Sessions enden); Reaktivierungszähler als User-Attribut (max 2), Admin-Freischaltung = Attribut entfernen + Gruppe `members`.
|
||||
- **Berechtigter Nutzerkreis:** Matrix-Raum als ACL (wer im `#einladungen`-Raum ist, darf den Bot nutzen) — einfach und sichtbar.
|
||||
|
||||
## Offene Designfragen (sorb)
|
||||
- Wer ist der „festgelegte Nutzerkreis" initial? Eigener Raum ok?
|
||||
- Soll die Admin-Prüfung im Matrix-Raum bestätigt werden (Reaktion/Kommando) oder in der Authentik-UI?
|
||||
- Namens-/Branding-Wunsch für den Bot?
|
||||
|
||||
---
|
||||
*Migriert aus Gitea `sorb/axion1337.chat-gitops#50` — dort erstellt am 2026-08-01 von sorb.*
|
||||
<!-- gitea-migration: sorb/axion1337.chat-gitops#50 -->
|
||||
@@ -0,0 +1,94 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0082"
|
||||
status: done
|
||||
created: 2026-08-01
|
||||
milestone: M1
|
||||
priority: medium
|
||||
projekt: gitops
|
||||
gitlab_iid: "49"
|
||||
related: []
|
||||
---
|
||||
# CVE-Alarme: eine Matrix-Nachricht pro CVE flutet den Security-Raum -- Zustellung derzeit stumm
|
||||
|
||||
> Adoptiert aus [gitops#49](https://git.lab/axion1337.chat/axion1337.chat-gitops/-/issues/49) (2026-08-18, ADR-0019). Kommentare und Verlauf bleiben dort; kanonisch ist ab jetzt diese Datei.
|
||||
|
||||
Aus dem Deploy von #47 (2026-08-01). Die Pipeline sammelt Daten, **die Alarm-Zustellung ist aber abgeklemmt**: in `monitoring/alertmanager/alertmanager.yml` routet `room="security"` auf einen Null-Receiver (Commit `2b715ca`).
|
||||
|
||||
## Warum
|
||||
|
||||
`TrivyCriticalVuln` und `TrivyHighVuln` erzeugen eine Alarm-Instanz **pro CVE pro Image**. Gemessen am ersten Scan-Durchlauf, bei 14 von 29 Images:
|
||||
|
||||
| | Anzahl |
|
||||
|---|---|
|
||||
| CRITICAL (feuert sofort, kein `for:`) | 59 |
|
||||
| HIGH (`for: 24h`) | 445 |
|
||||
|
||||
Hochgerechnet auf alle 29 Images grob 120 CRITICAL / 900 HIGH.
|
||||
|
||||
`group_by: [alertname, instance]` legt alle in *eine* Gruppe -> ein Webhook-POST mit ~120 Alarmen. `matrix-alerts.py` schickt daraus **eine Matrix-Nachricht pro Alarm**, sequenziell.
|
||||
|
||||
## Was es zur Schleife macht
|
||||
|
||||
`save_state()` steht in `do_POST` **hinter** der Sende-Schleife. Sobald ein Send fehlschlaegt -- Synapse rate-limitet `rc_message` per Default nach ~10 Nachrichten mit 429 -- fliegt die Exception, der State wird **nicht** gespeichert, der Receiver antwortet 502. Alertmanager wiederholt daraufhin die komplette Gruppe, und die Fingerprint-Deduplizierung (`if fp in state: continue`), die genau das verhindern soll, ist beim Retry noch leer. Das wiederholt sich, statt einmalig durchzulaufen.
|
||||
|
||||
## Zum Scharfschalten noetig
|
||||
|
||||
1. **Zustellung buendeln.** Entweder `matrix-alerts.py` auf eine Sammelnachricht pro Webhook-Batch umbauen (die fuenf Pflichtfelder je CVE als eine Zeile -- bleibt vollstaendig), oder die Regeln auf `count by (target, severity)` aggregieren und die CVE-Details im Dashboard lassen.
|
||||
2. **State inkrementell speichern**, nach jedem erfolgreichen Send, plus 429-Behandlung mit `Retry-After`.
|
||||
|
||||
Danach die `room="security"`-Route aus `alertmanager.yml` entfernen.
|
||||
|
||||
## Kleinere Punkte aus demselben Review
|
||||
|
||||
- `TrivyScanStale` kann ein Image, das **nie** erfolgreich gescannt wurde, nicht melden: ohne ersten Report gibt es keine Serie, an der `time() - trivy_last_scan_timestamp` haengen koennte. Ein dauerhaft fehlschlagendes Image bleibt still; `TargetDown` deckt nur den toten Exporter ab.
|
||||
- Der Exporter prunt den First-Seen-State bei **jedem** Scrape. Ein transienter Lesefehler (`except: continue`) loescht die Erstfund-Zeitstempel des betroffenen Targets dauerhaft.
|
||||
- `coturn/coturn:latest` ist als einziges Image ungepinnt (schon in #47 notiert).
|
||||
|
||||
## Nicht betroffen
|
||||
|
||||
Scanner, Exporter, Scrape-Job und Dashboard laufen und sind verifiziert -- Exporter-Last 0,4 s pro Scrape fuer 29 Reports, unkritisch bei 15 s Intervall. Details im `monitoring/README.md`.
|
||||
|
||||
|
||||
---
|
||||
*Migriert aus Gitea `sorb/axion1337.chat-gitops#51` — dort erstellt am 2026-08-01 von sorb.*
|
||||
<!-- gitea-migration: sorb/axion1337.chat-gitops#51 -->
|
||||
|
||||
## Richtigstellung + Abschluss 2026-08-19
|
||||
|
||||
**Die Kernaussage des Issues war überholt.** „Die Alarm-Zustellung ist abgeklemmt"
|
||||
stimmte am Tag der Erstellung — und wurde noch **am selben Tag** behoben, ohne dass das
|
||||
Issue geschlossen wurde. Ich habe es zwischenzeitlich als Produktionsblocker geführt
|
||||
(„CVE-Alarme werden gar nicht zugestellt"); das war falsch, nachgeprüft am Code:
|
||||
|
||||
| Forderung | Stand |
|
||||
|---|---|
|
||||
| Zustellung bündeln | ✅ Weg 2 gewählt: Regeln aggregieren per `count by (target, target_type, host)`, Details im Dashboard (`alerts.yml`) |
|
||||
| State inkrementell speichern | ✅ `save_state()` steht **in** der Schleife, auch im Fehlerzweig; dazu `time.sleep(1)` gegen `rc_message` |
|
||||
| `room="security"`-Null-Route entfernen | ✅ In `alertmanager.yml` nicht mehr vorhanden; der Kommentar dort hält die Historie fest |
|
||||
| `coturn:latest` pinnen | ✅ gitops `b4650dc` |
|
||||
|
||||
Alles vier per Commit `ff87cb2` (2026-08-01) bzw. gitops.
|
||||
|
||||
### Heute erledigt: die beiden verbliebenen Review-Punkte
|
||||
|
||||
**Der Exporter löschte Erstfund-Zeitstempel bei jedem Lesefehler.** `first_seen` wurde
|
||||
bei **jedem** Scrape auf das reduziert, was gerade gesehen wurde — und ein Bericht, der
|
||||
sich nicht parsen ließ, wurde per stillem `continue` übersprungen. Seine Findings kamen
|
||||
damit nicht in `seen_keys`, ihre Zeitstempel waren dauerhaft weg, und „erstmals gesehen"
|
||||
fing danach bei *jetzt* an. Gemeldet hat das nichts.
|
||||
|
||||
Geprunt wird jetzt nur noch für Targets, deren Bericht in diesem Durchgang **wirklich
|
||||
gelesen** wurde. Beidseitig gegen eine Wegwerf-Ablage belegt, nicht argumentiert: ein
|
||||
unlesbarer Bericht lässt seinen Eintrag stehen (`read_errors 1`), ein lesbarer Bericht
|
||||
ohne das Finding räumt ihn weiterhin ab.
|
||||
|
||||
**`TrivyScanStale` hat eine Blindstelle, die es prinzipiell nicht schließen kann:** Ein
|
||||
Target ohne je erfolgreichen Bericht hat keine Serie, an der `time() - …` hängen könnte —
|
||||
es bleibt still, egal wie lange es kaputt ist. Dafür verlassen den Exporter jetzt zwei
|
||||
Zahlen (`trivy_reports_total`, `trivy_report_read_errors`) mit je einer Regel:
|
||||
`TrivyReportUnreadable` (>0 für 30 min) und `TrivyNoReports` (==0 für 1 h). Das schließt
|
||||
die Lücke so weit, wie sie **ohne Soll-Liste der erwarteten Targets** zu schließen ist —
|
||||
eine solche Liste wäre der nächste Schritt, ist aber ein eigener Umfang.
|
||||
|
||||
Commit `9a10615` in `threadnet-operating`.
|
||||
@@ -0,0 +1,61 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0083"
|
||||
status: open
|
||||
created: 2026-08-01
|
||||
milestone: M1
|
||||
priority: medium
|
||||
projekt: gitops
|
||||
gitlab_iid: "50"
|
||||
related: []
|
||||
---
|
||||
# Monitoring-Deploy: geaenderte Configs greifen nicht ohne --force-recreate (Inode-Falle bei Einzeldatei-Mounts)
|
||||
|
||||
> Adoptiert aus [gitops#50](https://git.lab/axion1337.chat/axion1337.chat-gitops/-/issues/50) (2026-08-18, ADR-0019). Kommentare und Verlauf bleiben dort; kanonisch ist ab jetzt diese Datei.
|
||||
|
||||
Beim Deploy von #47 aufgefallen, betrifft aber **jede** Config-Aenderung am Monitoring-Stack.
|
||||
|
||||
## Symptom
|
||||
|
||||
`cd /opt/threadnet-operating && git pull && cd monitoring && docker compose up -d` aktiviert geaenderte Config-Dateien **nicht**. Nach dem Deploy von #47 liefen Scanner und Exporter, aber Prometheus hatte weder den neuen Scrape-Job `cve_exporter` noch die `axion-cve`-Regelgruppe geladen -- `promtool` fand 9 Regeln in der Datei, Prometheus kannte 6.
|
||||
|
||||
## Ursache
|
||||
|
||||
`prometheus.yml`, `alerts.yml` und `alertmanager.yml` sind als **einzelne Dateien** gemountet. Docker haengt so einen Bind-Mount am Inode auf. `git pull` schreibt eine neue Datei und benennt sie um -- neuer Inode. Der Container zeigt weiter auf die alte Datei.
|
||||
|
||||
Zwei Effekte, die es schwer sichtbar machen:
|
||||
|
||||
- `docker compose up -d` startet die Container nicht neu, weil die Service-Definition unveraendert ist. Es meldet `Running` und sieht erfolgreich aus.
|
||||
- Ein `SIGHUP`-Reload laedt brav neu -- nur eben den **alten** Inhalt. Kein Fehler im Log.
|
||||
|
||||
Auf der Platte steht also die neue Config, im Container die alte, und nichts meldet einen Fehler.
|
||||
|
||||
## Nachweis
|
||||
|
||||
```
|
||||
$ grep -c axion-cve monitoring/prometheus/alerts.yml # 1
|
||||
$ docker exec prometheus grep -c axion-cve /etc/prometheus/alerts.yml # 0
|
||||
```
|
||||
|
||||
## Abhilfe
|
||||
|
||||
Nach jedem `git pull`, der eine dieser Dateien anfasst:
|
||||
|
||||
```bash
|
||||
docker compose up -d --force-recreate prometheus alertmanager
|
||||
```
|
||||
|
||||
Verifikation muss **im Container** stattfinden, ein Blick auf die Platte beweist nichts.
|
||||
|
||||
## Nicht betroffen
|
||||
|
||||
Verzeichnis-Mounts (`grafana/provisioning/`, `grafana/dashboards/`) loesen ueber den Pfad auf und ziehen Aenderungen mit. Grafana liest **Provider-Definitionen** aber nur beim Start -- ein neuer Dashboard-Ordner braucht `docker compose restart grafana`. Dashboard-JSONs innerhalb eines bestehenden Providers werden laufend nachgezogen.
|
||||
|
||||
## Moegliche Dauerloesung
|
||||
|
||||
Statt Einzeldateien die Verzeichnisse mounten (`./prometheus:/etc/prometheus:ro`), dann verschwindet die Inode-Falle. Dokumentiert ist der Fallstrick vorerst in `monitoring/README.md` (Commit `2b715ca`).
|
||||
|
||||
|
||||
---
|
||||
*Migriert aus Gitea `sorb/axion1337.chat-gitops#52` — dort erstellt am 2026-08-01 von sorb.*
|
||||
<!-- gitea-migration: sorb/axion1337.chat-gitops#52 -->
|
||||
@@ -0,0 +1,81 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0084"
|
||||
status: done
|
||||
created: 2026-08-02
|
||||
milestone: M1
|
||||
priority: medium
|
||||
due: 2026-09-01
|
||||
projekt: gitops
|
||||
gitlab_iid: "51"
|
||||
related: []
|
||||
---
|
||||
# CI: CANONIZE_TOKEN für die automatische TURN-Rotation hinterlegen
|
||||
|
||||
> Adoptiert aus [gitops#51](https://git.lab/axion1337.chat/axion1337.chat-gitops/-/issues/51) (2026-08-18, ADR-0019). Kommentare und Verlauf bleiben dort; kanonisch ist ab jetzt diese Datei.
|
||||
|
||||
Der Job `canonize_rotation` in `.gitlab-ci.yml` übernimmt die monatliche TURN-Rotation automatisch (kein Handgriff mehr, kein Kalendereintrag). Der Schedule läuft, zwei Probeläufe sind durch — **es fehlt nur noch das Push-Token.**
|
||||
|
||||
## Was zu tun ist (2 Minuten, braucht deine Rechte)
|
||||
|
||||
1. *Settings → Access Tokens* in diesem Projekt: Token anlegen
|
||||
- Name z. B. `canonize-rotation`
|
||||
- Rolle **Maintainer** (nötig, weil `main` protected ist)
|
||||
- Scope **`write_repository`** — mehr nicht
|
||||
- Ablauf: setzen und im Kalender vormerken, sonst steht der Job irgendwann still
|
||||
2. *Settings → CI/CD → Variables*: Variable **`CANONIZE_TOKEN`** mit dem Wert, **masked** und **protected**
|
||||
|
||||
Danach nichts weiter — der nächste Lauf nimmt sie von selbst.
|
||||
|
||||
## Warum das nötig ist
|
||||
|
||||
Der Rotations-CronJob läuft im Cluster und erreicht git.lab nicht; er pusht seinen Branch nach Gitea. Von dort muss die Rotation über git.lab zurück, sonst überschreibt sie der nächste Mirror-Push und Flux spielt still das **alte** Shared Secret wieder ein — ein Fehler, der kein Symptom erzeugt.
|
||||
|
||||
## Stand
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| Job + Doku | ✅ `02c60cb`, CLAUDE.md in beiden Repos |
|
||||
| Schedule (täglich 17:05) | ✅ angelegt |
|
||||
| Probelauf | ✅ Pipeline 161 grün: „Keine offene Rotation" |
|
||||
| `CANONIZE_TOKEN` | ⬜ **dieses Issue** |
|
||||
|
||||
Bis dahin ist nichts kaputt: Ohne offene Rotation läuft der Job grün durch. Erst wenn am 01.09. wirklich rotiert wird und das Token fehlt, bricht er ab — laut und sichtbar, statt still das Falsche zu tun.
|
||||
|
||||
Nebenbei aufgefallen: Der Branch `turn-secret-rotation-20260728-192656` liegt auf git.lab und Gitea, steckt aber längst in `main` — eine Karteileiche vom Juli. Kann weg, ist aber harmlos.
|
||||
|
||||
## Erledigt 2026-08-19 — Token angelegt und nachweislich wirksam
|
||||
|
||||
sorb hat den Project Access Token angelegt; die Eigenschaften stimmen mit dem überein,
|
||||
was der Job braucht (per API geprüft, ohne den Wert anzufassen):
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| Name | `canonize-rotation` |
|
||||
| Rolle | **Maintainer** — nötig, weil `main` mit `push = Maintainers` geschützt ist |
|
||||
| Scopes | **nur `write_repository`** — der Job pusht, er ruft keine API auf |
|
||||
| CI-Variable | `CANONIZE_TOKEN`, maskiert **und** geschützt |
|
||||
| Ablauf | **2027-08-19** |
|
||||
|
||||
**Wirksamkeit belegt, nicht angenommen** (Pipeline 521, Wegwerf-Job, `main` unberührt):
|
||||
|
||||
```
|
||||
Sichtbar: CANONIZE_TOKEN ist im Job gesetzt.
|
||||
SCHREIBEN OK: Zweig canonize-token-probe-521 angelegt.
|
||||
Aufgeraeumt: canonize-token-probe-521 wieder entfernt.
|
||||
```
|
||||
|
||||
Der Job hat einen Wegwerf-Zweig angelegt und wieder gelöscht — damit ist gezeigt, dass
|
||||
der Wert im Job ankommt (geschützte Variable auf geschütztem Branch) **und** dass er
|
||||
schreiben darf. Der Prüf-Job ist danach wieder entfernt worden; es blieb kein Zweig
|
||||
liegen. Der erste echte Ernstfall ist die Rotation am **2026-09-01**.
|
||||
|
||||
⚠️ **Ein Ablaufdatum ist ein stiller Ausfall in der Zukunft.** Am **2027-08-19** hört der
|
||||
Token auf zu gelten. Der Job läuft im Leerlauf trotzdem grün durch — auffallen würde es
|
||||
erst bei der nächsten echten Rotation danach, also frühestens am 2027-09-01. Genau die
|
||||
Klasse aus #0104. Gehört in den Kalender, nicht in die Hoffnung.
|
||||
|
||||
**Zwei Fallen beim Prüfen**, hier notiert weil sie beim nächsten Mal Zeit kosten würden:
|
||||
Eine per API ausgelöste Pipeline hat die Quelle `api`, **nicht** `web` — die erste
|
||||
Fassung der Regel übersprang den Job deshalb wortlos. Und dieses Repo kennt keine Stage
|
||||
`pruefen` (das ist management); GitLab wies die Pipeline dafür komplett ab.
|
||||
@@ -0,0 +1,14 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0085"
|
||||
status: open
|
||||
created: 2026-08-02
|
||||
milestone: M2
|
||||
priority: low
|
||||
projekt: gitops
|
||||
gitlab_iid: "52"
|
||||
related: []
|
||||
---
|
||||
# docs/ trägt zwei Altbestände abgeschlossener Umzüge: TASKS.md und oldwiki/
|
||||
|
||||
> Adoptiert aus [gitops#52](https://git.lab/axion1337.chat/axion1337.chat-gitops/-/issues/52) (2026-08-18, ADR-0019). Kommentare und Verlauf bleiben dort; kanonisch ist ab jetzt diese Datei.
|
||||
@@ -0,0 +1,14 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0086"
|
||||
status: open
|
||||
created: 2026-08-02
|
||||
milestone: M4
|
||||
priority: low
|
||||
projekt: gitops
|
||||
gitlab_iid: "53"
|
||||
related: []
|
||||
---
|
||||
# k8s-Ressourcen heißen noch element-web-docs (Rest des ThreadNet-Rebrands)
|
||||
|
||||
> Adoptiert aus [gitops#53](https://git.lab/axion1337.chat/axion1337.chat-gitops/-/issues/53) (2026-08-18, ADR-0019). Kommentare und Verlauf bleiben dort; kanonisch ist ab jetzt diese Datei.
|
||||
@@ -0,0 +1,34 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0087"
|
||||
status: waiting
|
||||
created: 2026-08-06
|
||||
milestone: M4
|
||||
priority: low
|
||||
wartegrund: braucht eine Querformat-Wortmarke als SVG — Design-Arbeit, kein Deployment-Schritt
|
||||
projekt: gitops
|
||||
gitlab_iid: "55"
|
||||
related: []
|
||||
---
|
||||
# Logo für die Authentik-Anmeldemaske entwerfen (Querformat/SVG)
|
||||
|
||||
> Adoptiert aus [gitops#55](https://git.lab/axion1337.chat/axion1337.chat-gitops/-/issues/55) (2026-08-18, ADR-0019). Kommentare und Verlauf bleiben dort; kanonisch ist ab jetzt diese Datei.
|
||||
|
||||
Die Anmeldemaske zeigt derzeit wieder **Authentiks eigenes Logo** (`480e281`). Unser Versuch mit `vector-icons/512.png` war unbrauchbar: Authentiks Default ist ein SVG, das sich seiner Box anpasst — ein PNG nimmt dort seine Naturgröße und rendert entsprechend riesig.
|
||||
|
||||
## Was gebraucht wird
|
||||
|
||||
Eine **Wortmarke im Querformat**, wie sie der Slot vorsieht. Vorhanden ist nur Quadratisches:
|
||||
|
||||
- `vector-icons/*.png` im Client — reine Bildmarken, 24 bis 1024 px
|
||||
- `threadnet-logo-wortmarke.png` — Bildmarke **über** Schriftzug, also gestapelt und ebenfalls quadratisch (512×512). Liegt außerdem im `wiki`-Repo in der Gruppe `homelab`, die **keine Mirrors** hat: von Hetzner aus nicht erreichbar. Sie müsste erst mit dem Client ausgeliefert werden.
|
||||
|
||||
Ein SVG wäre das Richtige — dann passt es sich wie Authentiks eigenes an und die Größenfrage stellt sich nicht mehr.
|
||||
|
||||
## Wo es eingetragen wird
|
||||
|
||||
`branding_logo` im Brand-Blueprint, `apps/authentik/authentik-blueprints.yaml`.
|
||||
|
||||
⚠️ **Nicht über die Authentik-Oberfläche.** Solange die Zeile im Blueprint steht, gewinnt sie: eine Auswahl in der UI ist bis zur nächsten Reconciliation sichtbar und danach wieder weg. Steht im Kommentar an der Stelle.
|
||||
|
||||
Gehört inhaltlich zu management#29 (UI harmonisieren).
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user