Compare commits
134
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
|
||||
|
||||
@@ -33,3 +45,46 @@ stillstandspruefung:
|
||||
# Befunde sind kein Betriebsausfall, aber sie sollen sichtbar bleiben. Die rote
|
||||
# Pipeline ist bei uns die Alarmanlage (gitops/CLAUDE.md, TURN-Rotation).
|
||||
allow_failure: false
|
||||
|
||||
# Offline-Gate bei jedem Push: Artefakte gegen schema.yaml, STATUS.md
|
||||
# aktuell, Framework-Dateien unveraendert (Design 2026-08-11, Slice 1).
|
||||
# Braucht nur den Baum - bewusst ohne Token und ohne Netz.
|
||||
validate:
|
||||
stage: pruefen
|
||||
image: python:3.12-alpine
|
||||
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
|
||||
- python3 scripts/gen_status.py --check
|
||||
- python3 scripts/pruefe_upstream_drift.py
|
||||
- python3 scripts/pruefe_prosa.py
|
||||
allow_failure: false
|
||||
|
||||
# Verbund-Prüfung (ADR-0012/0013): Gruppenliste vs. docs/components/,
|
||||
# Pointer-Praesenz, Meilenstein-/Prioritaetspflicht, Issue-Drift,
|
||||
# Git-Hygiene. Gleiche Regeln wie die Stillstandspruefung: geplant/von
|
||||
# Hand, rot = Alarm, Abbruch ohne Token.
|
||||
gruppenpruefung:
|
||||
stage: pruefen
|
||||
image: python:3.12-alpine
|
||||
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
|
||||
echo "GITLAB_TOKEN fehlt (Gruppen-Token mit read_api)."
|
||||
exit 1
|
||||
fi
|
||||
- python3 scripts/gruppenpruefung.py
|
||||
allow_failure: false
|
||||
|
||||
@@ -0,0 +1,206 @@
|
||||
# AGENTS.md — Canonical Agent Instructions
|
||||
|
||||
Canonical instruction set for any coding agent working in this repository
|
||||
(Claude Code, GPT-OSS harnesses, others). `CLAUDE.md` points here.
|
||||
This file is loaded into every session — keep it short. Process details
|
||||
live in `WORKFLOW.md`; read that when a task begins, not preemptively.
|
||||
|
||||
Tradeoff: these rules bias toward caution over speed. For trivial tasks,
|
||||
use judgment — but say so.
|
||||
|
||||
## 1. Operating Rules
|
||||
|
||||
### Think before coding
|
||||
- State your assumptions explicitly. If uncertain, ask.
|
||||
- If multiple interpretations exist, present them — don't pick silently.
|
||||
- If a simpler approach exists, say so. Push back when warranted.
|
||||
- If something is unclear, stop. Name what's confusing. Ask.
|
||||
|
||||
### Simplicity first
|
||||
- Minimum code that solves the problem. Nothing speculative.
|
||||
- No features beyond what was asked. No abstractions for single-use code.
|
||||
- No "flexibility" or "configurability" that wasn't requested.
|
||||
- No error handling for impossible scenarios.
|
||||
- If you write 200 lines and it could be 50, rewrite it.
|
||||
- Test: "Would a senior engineer call this overcomplicated?" If yes, simplify.
|
||||
- Before writing new code, stop at the first rung that holds:
|
||||
needed at all? → codebase already has it? → stdlib? → platform-native?
|
||||
→ installed dependency? → one line? → only then: the minimum that works.
|
||||
(Ladder after ponytail, MIT.)
|
||||
- Never cut, at any rung: trust-boundary validation, data-loss handling,
|
||||
security, accessibility.
|
||||
- Lazy about the solution, never about reading the code first.
|
||||
|
||||
### Surgical changes
|
||||
- Touch only what you must. Match existing style, even if you'd differ.
|
||||
- Don't "improve" adjacent code, comments, or formatting.
|
||||
- Don't refactor things that aren't broken.
|
||||
- If you notice unrelated dead code, mention it — don't delete it.
|
||||
- Remove imports/variables/functions that YOUR changes made unused;
|
||||
leave pre-existing dead code alone unless asked.
|
||||
- Every changed line must trace directly to the request.
|
||||
|
||||
### Goal-driven execution
|
||||
- Transform tasks into verifiable goals:
|
||||
"fix the bug" → "write a test that reproduces it, then make it pass".
|
||||
- For multi-step work, state a brief plan: step → verify, step → verify.
|
||||
- A task is well-defined only if it names all four:
|
||||
**files, action, verify, done.** Missing one? The task is too vague — say so.
|
||||
|
||||
### Verification before completion
|
||||
- Never claim something works without evidence: a test run, command
|
||||
output, a rendered result. "Should work" is not a status.
|
||||
- Report every task/slice with exactly one status:
|
||||
`DONE` | `DONE_WITH_CONCERNS` | `NEEDS_CONTEXT` | `BLOCKED`.
|
||||
- Uncertainty is reported, never swallowed. Flag your shakiest calls.
|
||||
|
||||
## 2. Project Initialization (Gate 0)
|
||||
|
||||
At session start, read `PROJECT.md`. If it does not exist, initialization
|
||||
is your first task: before anything else, ask the Gate 0 questions defined
|
||||
in `WORKFLOW.md` — response language, size-S gate exception (yes/no),
|
||||
one-line project purpose, audience — write the answers to `PROJECT.md`,
|
||||
and have `validate.py` accept it. Never guess these answers; ask.
|
||||
|
||||
## 3. Workflow
|
||||
|
||||
For anything beyond a trivial change, read `WORKFLOW.md` and follow its
|
||||
gates. At task start, propose a size class (S/M/L); the human confirms
|
||||
(possibly batched later). **Never advance past a gate without explicit
|
||||
human approval** — sole exception: size-S tasks, and only if `PROJECT.md`
|
||||
explicitly grants that exception.
|
||||
|
||||
## 4. Repository Map
|
||||
|
||||
| Path | Purpose |
|
||||
|---|---|
|
||||
| `WORKFLOW.md` | Gate 0 (init) + Gates 1–5, size classes, debugging path, session handoff, refinement ritual |
|
||||
| `PROJECT.md` | Per-project answers from Gate 0: language, size-S exception, purpose, audience |
|
||||
| `STATUS.md` | Generated overview: open issues, active designs, recent ADRs — do not edit by hand |
|
||||
| `schema.yaml` | Frontmatter schema — single source of truth for artifact structure |
|
||||
| `docs/adr/` | Architecture Decision Records — binding; never edited, only superseded |
|
||||
| `docs/design/` | One design doc per undertaking; completed ones move to `done/` |
|
||||
| `docs/aar/` | Standalone After Action Reviews (incidents, major deviations only) |
|
||||
| `docs/issues/` | In-repo issues, one file each; status lives in frontmatter |
|
||||
| `docs/wiki/` | Wiki areas as folders, created on demand — rules in `docs/wiki/index.md` |
|
||||
| `docs/sources/` | Immutable original sources; wiki pages cite them — read-only for agents |
|
||||
| `scripts/` | Deterministic tooling: `validate.py`, `gen_status.py` |
|
||||
|
||||
Before proposing options (Gate 2), read the relevant ADRs and AARs first —
|
||||
past decisions and learnings are input, not trivia.
|
||||
|
||||
## 5. Artifact Rules
|
||||
|
||||
- All artifacts are standard Markdown with YAML frontmatter conforming to
|
||||
`schema.yaml`. Standard links only (`[text](path.md)`), no wikilinks.
|
||||
Diagrams as Mermaid. This keeps every artifact portable across LLMs,
|
||||
GitLab, and Obsidian.
|
||||
- Never invent frontmatter fields or status values. `validate.py` is
|
||||
authoritative; if it rejects your artifact, fix the artifact, not the
|
||||
validator.
|
||||
- Deterministic jobs (status generation, validation, link checks) are done
|
||||
by scripts, not by you. If a deterministic job lacks a script, propose
|
||||
one instead of doing it by inference.
|
||||
|
||||
<!-- projektabschnitt -->
|
||||
|
||||
## 6. Gruppenregeln (Projekt axion1337.chat)
|
||||
|
||||
Dieses Repo steuert die Gruppe `axion1337.chat`. Die Abschnitte 1–5
|
||||
oben sind neckbeard v0.1.1 und bleiben byte-treu (Baseline:
|
||||
`docs/sources/upstream/neckbeard-v0.1.1/`, Prüfung:
|
||||
`scripts/pruefe_upstream_drift.py`); §1 ist destilliert aus den
|
||||
wortgleich archivierten
|
||||
[Karpathy-Guidelines](docs/sources/regelwerk/karpathy-guidelines.md).
|
||||
**Änderungen an dieser Datei nur mit sorb abgestimmt.** Dieser
|
||||
Abschnitt gilt für jede Session in allen Repos der Gruppe;
|
||||
Komponenten-Repos tragen nur Projektspezifika plus einen Pointer
|
||||
hierher ([ADR-0013](docs/adr/0013-gruppenregeln-kanonisch-mit-pruefung.md)).
|
||||
Ohne Lab-Zugang: dieses Repo ist als Push-Mirror unter
|
||||
`https://rohana.axion1337.de/sorb/management` lesbar — pushen dorthin ist tabu.
|
||||
|
||||
### Quelle der Wahrheit & Mirror-Topologie
|
||||
|
||||
- `git.lab/axion1337.chat/*` ist kanonisch; Gitea/rohana wird per
|
||||
Push-Mirror beliefert und bleibt Flux-Quelle, Registry und
|
||||
Release-Download ([ADR-0001](docs/adr/0001-gitlab-kanonisch-push-mirror.md),
|
||||
[ADR-0004](docs/adr/0004-site-to-site-vpn-hetzner-lab.md)). Die
|
||||
Flux-Quelle **nicht auf git.lab „geradeziehen"** — die Produktion darf
|
||||
nicht an der Lab-Verfügbarkeit hängen.
|
||||
- **Nie direkt zu Gitea pushen** (der Mirror überschreibt per Force).
|
||||
Landet doch ein Commit dort: Kanonisierungs-Verfahren in
|
||||
[verfahren/deploy-uebergabe.md](docs/wiki/deployment/deploy-uebergabe.md).
|
||||
- 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
|
||||
|
||||
- `docs/issues/` ist kanonisch für den Management-Scope
|
||||
([ADR-0012](docs/adr/0012-issues-im-repo-gitlab-als-spiegel.md));
|
||||
GitLab ist bespiegelte Ansicht. Jedes Issue trägt genau einen
|
||||
Meilenstein (M1–M5) und genau eine Priorität — das Schema erzwingt
|
||||
beides. Zeitkritisches trägt ein `due`-Datum, nicht „bald".
|
||||
- **WIP-Limit 2** (Validator-Regel). Die Zusage-Status `next` und
|
||||
`in-progress` vergibt **nur sorb**; Sessions bilden ab (`waiting` mit
|
||||
`wartegrund`, Erledigtes `done` mit Begründung im Issue-Commit),
|
||||
sagen aber nichts zu.
|
||||
- **ADR-Pflicht** bei Architektur-/Prozessentscheidungen und **jeder
|
||||
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
|
||||
echoen** — anzeigen = Exposure = Rotation. Referenz nur über
|
||||
Dateipfade (z. B. `~/.config/gitlab-lab/token`) oder maskierte
|
||||
CI-Variablen. Die Trennung ist „Credential vs. Config":
|
||||
nicht-geheime Konfiguration wird normal committet.
|
||||
|
||||
### Commit-Konventionen
|
||||
|
||||
- Nachrichten auf Englisch, Conventional-Stil; der Betreff sagt *was*,
|
||||
der Rumpf *warum*.
|
||||
- Autor- **und** Committer-Datum auf 12:00:00 UTC des laufenden Tages;
|
||||
kanonische Autor-Identität. Historien-Rewrites nur mit
|
||||
alt→neu-Zuordnung
|
||||
([ADR-0009](docs/adr/0009-commit-konventionen-und-historien-anonymisierung.md),
|
||||
Tabelle: [shared/commit-zuordnung-2026-08-07.md](docs/sources/migration/commit-zuordnung-2026-08-07.md)).
|
||||
- ⚠️ Das schützt nur die Git-Historie; Plattform-Zeitstempel (Push,
|
||||
Issues, Pipelines, Pakete) tragen die echte Uhrzeit (ADR-0009).
|
||||
|
||||
### Redlichkeit
|
||||
|
||||
- Verifiziert (Messung/Konsole) klar von Vermutung trennen;
|
||||
Korrelation ≠ Kausalität — ein plausibler Verdacht ist kein Befund.
|
||||
- Config-Dateien chirurgisch editieren, **nie re-dumpen**; vor dem Push
|
||||
validieren (`docker compose config`, YAML-Parse).
|
||||
- Fehlschläge und übersprungene Schritte benennen, nicht glätten —
|
||||
„fertig" heißt verifiziert (deckungsgleich mit §1).
|
||||
@@ -1,233 +1 @@
|
||||
# CLAUDE.md — übergreifende Arbeitskonventionen (kanonisch)
|
||||
|
||||
Diese Datei gilt für **jede Claude-/Agenten-Session in allen Projekten** der
|
||||
Gruppe (axion1337.chat-Stack, ThreadNet-Repos, CFGMON/threadnet-operating,
|
||||
Homelab). Projekt-Repos haben eigene CLAUDE.mds für ihre Spezifika (z. B. die
|
||||
ESS-/Flux-Details in „ThreadNet Server Suite" = `axion1337.chat-gitops`) — bei
|
||||
Widerspruch gilt für Arbeitsweise und Prozess **diese** Datei.
|
||||
|
||||
> **Für Sessions ohne Lab-Zugang** (CFGMON, MATRIX, …): dieses Repo ist als
|
||||
> Push-Mirror unter `https://rohana.axion1337.de/sorb/management` von überall
|
||||
> **lesbar** — dort diese Datei und die ADRs nachschlagen. Nur pushen ist tabu.
|
||||
>
|
||||
> 📋 **Kopierbare Kurzfassungen zum Voranstellen:**
|
||||
> [verfahren/textbloecke.md](verfahren/textbloecke.md) — Session-Start, Host-Session,
|
||||
> Deploy-Übergabe, Abschluss, Entscheidungsvorlage. Diese Datei hier bleibt die
|
||||
> Quelle; die Bausteine verweisen nur darauf.
|
||||
|
||||
## Projektrealitäten (Stand 2026-08-01)
|
||||
|
||||
**Das Lab ist die Quelle der Wahrheit** ([ADR-0002](decisions/0002-issues-und-management-ins-lab.md)):
|
||||
|
||||
- Kanonische Repos liegen auf `git.lab/axion1337.chat/*` (nur im Lab/VPN
|
||||
auflösbar). Gitea/rohana wird per **Push-Mirror** beliefert und bleibt
|
||||
Flux-Source, Container-/npm-Registry und Release-Download
|
||||
([ADR-0001](decisions/0001-gitlab-kanonisch-push-mirror.md)).
|
||||
- **Warum überhaupt zwei Orte — und warum das kein Altbestand ist:** Auf git.lab
|
||||
liegen die *Baupläne*, auf Gitea eine Kopie, die der Cluster **ohne verfügbares
|
||||
Lab** erreicht. Der Hetzner-Cluster muss sich bauen und neu ausrollen lassen,
|
||||
wenn das Homelab aus ist, im Umbau steckt oder niemand zu Hause ist — er darf
|
||||
deshalb nicht von einem Host abhängen, der nur im Lab antwortet.
|
||||
⚠️ **Die Flux-Quelle nicht „geradeziehen"** auf git.lab: Das sähe aufgeräumter
|
||||
aus und würde die Verfügbarkeit der Produktion an das Lab koppeln — genau das,
|
||||
was die Trennung verhindert.
|
||||
- **Nie direkt zu Gitea pushen** (gespiegelte Repos) — der Mirror überschreibt
|
||||
per Force.
|
||||
- **Gespiegelt wird nur die Gruppe `axion1337.chat`** (die fünf Produkt-Repos und
|
||||
`management`). Die Gruppe **`homelab`** (`docs`, `wiki`, `wiki-bookstack`) hat
|
||||
bewusst **keine Mirrors**: Sie beschreibt und konfiguriert ausschließlich
|
||||
Lab-Infrastruktur, und seit dem Site-to-Site-VPN
|
||||
([ADR-0004](decisions/0004-site-to-site-vpn-hetzner-lab.md)) erreichen auch
|
||||
Host-Sessions git.lab direkt — Tunnel einschalten genügt. Betriebslehren, die
|
||||
von außen lesbar sein müssen, gehören deshalb in die **AARs** unter
|
||||
`verfahren/aar/` (dieses Repo ist gespiegelt), nicht nur in die READMEs der
|
||||
Lab-Repos.
|
||||
- Landet doch ein Commit auf Gitea (z. B. aus einer Host-Session ohne Lab-Route):
|
||||
**Kanonisierungs-Verfahren** in
|
||||
[verfahren/deploy-uebergabe.md](verfahren/deploy-uebergabe.md) — `.patch`
|
||||
von Gitea ziehen, `git am` (erhält Autorschaft), Push über git.lab.
|
||||
- **Issues leben auf git.lab.** Die alten Gitea-Issues sind geschlossen und
|
||||
verweisen dorthin. ⚠️ gitops-Nummern haben sich beim Umzug verschoben
|
||||
(Gitea zählte PRs mit; z. B. Gitea#48 → GitLab#46) — alte „gitops#N"-Verweise
|
||||
meinen die Gitea-Nummer; verbindlich ist der Migrations-Fußtext im Issue.
|
||||
- **Ausnahme** (bewusst entschieden, nur noch eine): der
|
||||
TURN-Rotations-CronJob schreibt weiter nach Gitea, weil er im Cluster läuft und
|
||||
git.lab nicht erreicht.
|
||||
**Die Rotation nicht von Hand nachziehen und den PR nie auf Gitea mergen** —
|
||||
das erledigt seit 2026-08-02 der geplante CI-Job `canonize_rotation` im
|
||||
gitops-Repo täglich von git.lab aus. Scheitert er, bleibt die Pipeline rot;
|
||||
diese rote Pipeline **ist** der Alarm, einen zusätzlichen Termin gibt es
|
||||
bewusst nicht.
|
||||
- **Dokumentation** ([ADR-0006](decisions/0006-wikis-konsolidieren-docusaurus.md)):
|
||||
Das gitops-Wiki liegt seit 2026-08-02 auf git.lab (*Wiki*-Reiter im Projekt);
|
||||
⚠️ der `wiki`-**Branch** im gitops-Repo ist ein überholter Mai-Abzug von `docs/`
|
||||
und nicht die gepflegte Fassung. Alle Quellen zusammen erscheinen unter
|
||||
**axionwiki.lab** ([`homelab/wiki`](https://git.lab/homelab/wiki), Docusaurus) —
|
||||
Inhalte werden beim Bau geholt, **Änderungen gehören ins Quell-Repo**.
|
||||
|
||||
## Arbeitsframework ([ADR-0005](decisions/0005-pm-framework-kanban.md))
|
||||
|
||||
Kanban-Rückgrat mit leichten Scrum-Elementen:
|
||||
|
||||
- **Alles Offene ist ein Issue** — host-/infra-Scope hier im management-Projekt
|
||||
(`host:`-Labels, alte IDs wie `CFGMON-01` bleiben im Titel), Projekt-Scope im
|
||||
jeweiligen Projekt. Kein neues Backlog-Markdown anlegen; `hosts/`/`shared/`
|
||||
sind nur Bestand + Historie.
|
||||
- **Status über Labels**, genau eins pro Issue: `status:next` (die einzige
|
||||
Zusage), `status:doing` (**WIP-Limit 2** — auch sessionübergreifend zu
|
||||
verteidigen), `status:wartet` (nur mit benanntem Grund). Ohne Label = Backlog.
|
||||
- **ADR-Pflicht** ([decisions/](decisions/)) bei Architektur-/Prozess-
|
||||
entscheidungen und **jeder dauerhaften Ausnahme von einer Regel**. Eine
|
||||
Ausnahme nur zu dokumentieren statt sie als Entscheidung vorzulegen, ist ein
|
||||
Fehler.
|
||||
- **Deploy-Übergaben** („einer baut, ein anderer rollt aus") laufen über das
|
||||
Issue-Template und die Pflichtfelder in
|
||||
[verfahren/deploy-uebergabe.md](verfahren/deploy-uebergabe.md) — das ist
|
||||
unsere Definition of Done für Deployments. Nach Deploys mit Übergabe und nach
|
||||
Incidents: **AAR** ([verfahren/aar/](verfahren/aar/), Vorlage liegt daneben).
|
||||
- Prioritäten über `priority:*`; Zeitkritisches bekommt ein **Datum** im Issue,
|
||||
nicht „bald".
|
||||
- **Der Titel trägt keine Priorität.** Präfixe wie `[HIGH]`/`[MEDIUM]`/`[LOW]`
|
||||
gehören nicht in den Titel — die Priorität steht im Label, und zwar nur dort.
|
||||
Alte Kennungen wie `CFGMON-01` bleiben, die benennen den Gegenstand, nicht die
|
||||
Dringlichkeit.
|
||||
⚠️ Der Grund ist keine Ästhetik: Aus der Gitea-Migration trugen 34 Issues ein
|
||||
Präfix, davon **zwei mit einer anderen Aussage als ihr Label** — wer nach Titel
|
||||
sortierte, bekam ein anderes Bild als wer nach Label sortierte. Zwei Wahrheiten
|
||||
über dieselbe Sache sind schlimmer als eine unvollständige. Bereinigt 2026-08-06.
|
||||
- **Jedes Issue gehört zu genau einem Meilenstein** (Gruppen-Milestones M1–M4,
|
||||
siehe [roadmap.md](roadmap.md)). Label und Meilenstein beantworten verschiedene
|
||||
Fragen: `priority:*` sagt **wie dringend**, der Meilenstein sagt **worauf es
|
||||
einzahlt**. Ein Issue ohne Meilenstein taucht in keiner Roadmap-Ansicht auf und
|
||||
ist damit praktisch unsichtbar — es existiert nur noch für den, der es angelegt
|
||||
hat.
|
||||
M1–M4 haben **bewusst kein Enddatum**: Sie bündeln, sie simulieren keinen
|
||||
Termindruck. Termindruck steht als Datum am einzelnen Issue.
|
||||
|
||||
## Secrets & Credentials
|
||||
|
||||
- **Token-/Secret-Werte niemals anzeigen, loggen oder in Dateien echoen** —
|
||||
anzeigen = Exposure = Rotation. Echte Credentials tippt/legt sorb selbst an;
|
||||
Sessions referenzieren sie nur über Dateipfade (z. B.
|
||||
`~/.config/gitlab-lab/token`) oder maskierte CI-Variablen.
|
||||
- Nicht-geheime Konfiguration wird direkt geschrieben und committet — die
|
||||
Trennung ist „Credential vs. Config", nicht „alles über den Menschen".
|
||||
|
||||
## Commit-Konventionen (seit 2026-08-07)
|
||||
|
||||
Gilt für **alle** Repos der Gruppe `axion1337.chat` und die ThreadNet-Dienste.
|
||||
|
||||
- **Nachrichten auf Englisch**, Conventional-Commit-Stil: `feat:`, `fix:`,
|
||||
`docs:`, `chore:`, `ci:`, `refactor:`. Der Betreff sagt *was*, der Rumpf *warum*.
|
||||
- **Zeitstempel anonymisieren.** Autor- **und** Committer-Datum werden auf
|
||||
**12:00:00 UTC des laufenden Tages** gesetzt, damit sich aus der Historie keine
|
||||
persönlichen Arbeitszeiten ablesen lassen:
|
||||
|
||||
```bash
|
||||
export GIT_AUTHOR_DATE="$(date -u +%Y-%m-%d)T12:00:00Z" \
|
||||
GIT_COMMITTER_DATE="$(date -u +%Y-%m-%d)T12:00:00Z"
|
||||
git commit -m "…"
|
||||
```
|
||||
|
||||
⚠️ **Beide Variablen setzen.** Nur `GIT_AUTHOR_DATE` zu setzen bringt nichts —
|
||||
`git log` zeigt zwar das Autordatum, das Committer-Datum bleibt aber im Objekt
|
||||
und ist über `git log --format=%cd` und in jeder Weboberfläche sichtbar.
|
||||
|
||||
📎 Die Umstellung der Alt-Historie am 2026-08-07 hat 251 Commits neue SHAs
|
||||
gegeben. Ältere Verweise bleiben über
|
||||
[`shared/commit-zuordnung-2026-08-07.md`](shared/commit-zuordnung-2026-08-07.md)
|
||||
auflösbar — **statt** geschriebene Issue-Kommentare nachträglich zu ändern. Wer
|
||||
eine SHA nicht findet, hat einen Commit von vor der Grenze vor sich; der gilt
|
||||
unverändert.
|
||||
|
||||
⚠️ **Das schützt nur die Git-Historie.** Push-Zeiten, Issue- und
|
||||
Kommentar-Zeitstempel, Pipeline-Läufe und Paket-Veröffentlichungen tragen
|
||||
weiterhin die echte Uhrzeit und liegen im selben GitLab bzw. auf dem
|
||||
öffentlichen Gitea-Spiegel. Wer daraus wirklich keine Muster ableitbar haben
|
||||
will, muss dort ansetzen — die Commit-Datumsregel allein reicht dafür nicht.
|
||||
|
||||
## Redlichkeit & gelebte Lehren
|
||||
|
||||
- **Aussagen mit Quelle:** Verifiziert (Messung/Konsole) klar von Vermutung
|
||||
trennen; nicht selbst Geprüftes als solches kennzeichnen. Korrelation ≠
|
||||
Kausalität — ein plausibler Verdacht ist kein Befund.
|
||||
- **Config-Dateien textuell/chirurgisch editieren, nie re-dumpen** (YAML/JSON
|
||||
neu serialisieren hat zweimal real Schaden angerichtet). Compose-/YAML-
|
||||
Änderungen vor dem Push validieren (`docker compose config`, YAML-Parse).
|
||||
- Fehlschläge und übersprungene Schritte werden benannt, nicht geglättet;
|
||||
„fertig" heißt verifiziert.
|
||||
|
||||
---
|
||||
name: karpathy-guidelines
|
||||
description: Behavioral guidelines to reduce common LLM coding mistakes. Use when writing, reviewing, or refactoring code to avoid overcomplication, make surgical changes, surface assumptions, and define verifiable success criteria.
|
||||
license: MIT
|
||||
---
|
||||
|
||||
# Karpathy Guidelines
|
||||
|
||||
Behavioral guidelines to reduce common LLM coding mistakes, derived from [Andrej Karpathy's observations](https://x.com/karpathy/status/2015883857489522876) on LLM coding pitfalls.
|
||||
|
||||
**Tradeoff:** These guidelines bias toward caution over speed. For trivial tasks, use judgment.
|
||||
|
||||
## 1. Think Before Coding
|
||||
|
||||
**Don't assume. Don't hide confusion. Surface tradeoffs.**
|
||||
|
||||
Before implementing:
|
||||
- State your assumptions explicitly. If uncertain, ask.
|
||||
- If multiple interpretations exist, present them - don't pick silently.
|
||||
- If a simpler approach exists, say so. Push back when warranted.
|
||||
- If something is unclear, stop. Name what's confusing. Ask.
|
||||
|
||||
## 2. Simplicity First
|
||||
|
||||
**Minimum code that solves the problem. Nothing speculative.**
|
||||
|
||||
- No features beyond what was asked.
|
||||
- No abstractions for single-use code.
|
||||
- No "flexibility" or "configurability" that wasn't requested.
|
||||
- No error handling for impossible scenarios.
|
||||
- If you write 200 lines and it could be 50, rewrite it.
|
||||
|
||||
Ask yourself: "Would a senior engineer say this is overcomplicated?" If yes, simplify.
|
||||
|
||||
## 3. Surgical Changes
|
||||
|
||||
**Touch only what you must. Clean up only your own mess.**
|
||||
|
||||
When editing existing code:
|
||||
- Don't "improve" adjacent code, comments, or formatting.
|
||||
- Don't refactor things that aren't broken.
|
||||
- Match existing style, even if you'd do it differently.
|
||||
- If you notice unrelated dead code, mention it - don't delete it.
|
||||
|
||||
When your changes create orphans:
|
||||
- Remove imports/variables/functions that YOUR changes made unused.
|
||||
- Don't remove pre-existing dead code unless asked.
|
||||
|
||||
The test: Every changed line should trace directly to the user's request.
|
||||
|
||||
## 4. Goal-Driven Execution
|
||||
|
||||
**Define success criteria. Loop until verified.**
|
||||
|
||||
Transform tasks into verifiable goals:
|
||||
- "Add validation" → "Write tests for invalid inputs, then make them pass"
|
||||
- "Fix the bug" → "Write a test that reproduces it, then make it pass"
|
||||
- "Refactor X" → "Ensure tests pass before and after"
|
||||
|
||||
For multi-step tasks, state a brief plan:
|
||||
```
|
||||
1. [Step] → verify: [check]
|
||||
2. [Step] → verify: [check]
|
||||
3. [Step] → verify: [check]
|
||||
```
|
||||
|
||||
Strong success criteria let you loop independently. Weak criteria ("make it work") require constant clarification.
|
||||
|
||||
---
|
||||
|
||||
*Der Karpathy-Block oben ist wortgleich aus der gitops-CLAUDE.md übernommen und
|
||||
darf nicht bearbeitet werden (stehende Regel von sorb). Änderungen an dieser
|
||||
Datei insgesamt: nur mit sorb abgestimmt — sie ist die gemeinsame
|
||||
Arbeitsgrundlage aller Sessions.*
|
||||
Read AGENTS.md — the canonical instruction file for this repository. All rules live there.
|
||||
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
---
|
||||
type: project
|
||||
language: de
|
||||
size_s_exception: true
|
||||
purpose: "Steuerungsrepo der Gruppe axion1337.chat: Roadmap, Entscheidungen, Verfahren und Host-/Infrastrukturwissen für die fünf ThreadNet-Komponenten und das Lab, das sie betreibt."
|
||||
audience: "sorb und Agenten-Sessions (auch Host-Sessions ohne Lab-Zugang, lesend über den Gitea-Mirror); keine weiteren Menschen."
|
||||
---
|
||||
|
||||
# PROJECT.md — Gate-0-Antworten
|
||||
|
||||
Beantwortet von sorb am 2026-08-11 (Session 2 des Neckbeard-Feldtests).
|
||||
|
||||
- **Antwortsprache:** Deutsch. Commit-Messages bleiben englisch
|
||||
(Commit-Konvention der Gruppe, seit 2026-08-07).
|
||||
- **Size-S-Ausnahme:** gewährt — triviale Ein-Datei-Änderungen ohne
|
||||
Einzelfreigabe; M und L stoppen immer.
|
||||
- **Zweck:** siehe Frontmatter.
|
||||
- **Publikum:** Owner plus Agenten-Sessions; keine weiteren Menschen.
|
||||
Für die spätere Wiki-Pflicht heißt das: keine fremdgerichteten
|
||||
Bereiche (user-guide) verpflichtend.
|
||||
@@ -3,7 +3,7 @@
|
||||
Steuerungs-Repo für alles über den einzelnen Projekten: Visionen, Roadmap,
|
||||
Entscheidungen (ADR), Arbeitsverfahren, AARs — und der Bestand der Hosts.
|
||||
Framework: **Kanban-Rückgrat mit leichten Scrum-Elementen**, begründet und
|
||||
im Detail festgelegt in [ADR-0005](decisions/0005-pm-framework-kanban.md).
|
||||
im Detail festgelegt in [ADR-0005](docs/adr/0005-pm-framework-kanban.md).
|
||||
|
||||
*(Bis 2026-08-01 hieß dieses Repo `Backlogs` und führte offene Punkte als
|
||||
Markdown — die leben jetzt als Issues, siehe unten.)*
|
||||
@@ -12,16 +12,16 @@ Markdown — die leben jetzt als Issues, siehe unten.)*
|
||||
|
||||
**Kanonisch lebt dieses Repo auf `git.lab`** (`axion1337.chat/management`, nur im
|
||||
Lab bzw. via VPN erreichbar — das Lab ist die Quelle der Wahrheit,
|
||||
[ADR-0002](decisions/0002-issues-und-management-ins-lab.md)).
|
||||
[ADR-0002](docs/adr/0002-issues-und-management-ins-lab.md)).
|
||||
`rohana.axion1337.de/sorb/management` ist ein **Push-Mirror**: git.lab
|
||||
überschreibt ihn bei jedem Push per Force. Deshalb **nie direkt zu Gitea
|
||||
pushen** — solche Commits gehen beim nächsten Mirror-Lauf verloren (Rettung:
|
||||
`.patch` von Gitea ziehen + `git am`, siehe
|
||||
[Kanonisierung](verfahren/deploy-uebergabe.md)).
|
||||
[Kanonisierung](docs/wiki/deployment/deploy-uebergabe.md)).
|
||||
|
||||
**Keine Ausnahmen mehr.** Die **Deploy-Übergabe-Issues** liefen bis 2026-08-02 auf
|
||||
dem Gitea-Tracker, weil Hosts außerhalb des Labs `git.lab` nicht erreichten. Mit dem
|
||||
Site-to-Site-VPN ([ADR-0004](decisions/0004-site-to-site-vpn-hetzner-lab.md)) ist der
|
||||
Site-to-Site-VPN ([ADR-0004](docs/adr/0004-site-to-site-vpn-hetzner-lab.md)) ist der
|
||||
Grund entfallen — bei eingeschaltetem Tunnel erreicht CFGMON git.lab. Sie sind
|
||||
umgezogen (LABNET-03), der Gitea-Tracker ist leer, die Vorlage liegt als
|
||||
GitLab-Issue-Template. **Alle Issues leben auf git.lab.**
|
||||
@@ -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](verfahren/deploy-uebergabe.md), [Refinement & Retro](verfahren/refinement.md), [AARs](verfahren/aar/), Werkzeuge |
|
||||
| `hosts/`, `shared/` | **Bestand + Historie** je Host/Thema — u. a. [Branding](shared/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](decisions/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
|
||||
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
# STATUS
|
||||
|
||||
<!-- Generated by scripts/gen_status.py — do not edit. -->
|
||||
|
||||
## Issues (58 open, 41 closed)
|
||||
|
||||
Verteilung: M1 11 · M2 17 · M3 4 · M4 11 · M5 15
|
||||
|
||||
| Issue | Status | Meilenstein | Priorität | Title |
|
||||
|---|---|---|---|---|
|
||||
| [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 |
|
||||
| [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 |
|
||||
| [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 |
|
||||
| [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? |
|
||||
| [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 |
|
||||
| [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) | 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) |
|
||||
| [0040](docs/issues/0040-neckbeard-rueckmeldungen-einreichen.md) | open | M2 | low | neckbeard-Rückmeldungen aus dem Feldtest einreichen |
|
||||
| [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 (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) | 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) | 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 (0)
|
||||
|
||||
_none — nothing awaiting harvest_
|
||||
+140
@@ -0,0 +1,140 @@
|
||||
# WORKFLOW.md — Gates, Sizing, and Rituals
|
||||
|
||||
Read this when a task begins, not preemptively. `AGENTS.md` holds the
|
||||
always-on rules; this file holds the process.
|
||||
|
||||
## Size Classes
|
||||
|
||||
Propose one at task start; the human confirms — individually, or batched
|
||||
at the next refinement session.
|
||||
|
||||
| Class | Scope | Process |
|
||||
|---|---|---|
|
||||
| S | One file / one small change, no design decisions | Direct. AGENTS.md rules only. The one-line go-ahead **before starting is the stop** — waived only if `PROJECT.md` grants the size-S exception. |
|
||||
| M | Few files, minor decisions, fits one session | Slice plan in chat, no file. **STOP: plan approval before any code.** Then implement; each slice reports evidence and status inline. Gate 5 is a short AAR note in chat, filed to the wiki only if it produced a real learning. |
|
||||
| L | New feature, multiple files or sessions, real decisions | Full design doc in `docs/design/` following Gates 1–5 below. |
|
||||
|
||||
When in doubt between two classes, pick the larger.
|
||||
|
||||
## Gate 0 — Project Initialization
|
||||
|
||||
Runs once per project, triggered by a missing `PROJECT.md`. Ask, never guess:
|
||||
|
||||
1. Response language? (e.g. de / en)
|
||||
2. Size-S gate exception granted? (yes / no)
|
||||
3. One-line project purpose?
|
||||
4. Audience — who uses this besides the owner? (Drives which wiki areas
|
||||
become mandatory later; see `docs/wiki/index.md`.)
|
||||
|
||||
Write the answers to `PROJECT.md` (frontmatter per `schema.yaml`), run
|
||||
`validate.py`, and confirm the result with the human.
|
||||
|
||||
## Gates 1–5 (size L)
|
||||
|
||||
Each gate is a section of the design doc. A gate ends with **STOP**:
|
||||
present the section, wait for explicit approval. Do not pre-fill later
|
||||
sections.
|
||||
|
||||
### Gate 1 — Product
|
||||
- Problem statement: what user problem, for whom.
|
||||
- Verifiable acceptance criterion. A real number where one exists;
|
||||
otherwise a concretely checkable outcome. "Works" is not a criterion.
|
||||
- Non-goals: what this deliberately does not do.
|
||||
- Announcement paragraph (3–5 sentences): what it is, who it's for, why
|
||||
it's good. If you can't write it, the product isn't understood yet.
|
||||
- UI involved? Plain-HTML mockups of the affected screens.
|
||||
|
||||
**STOP.**
|
||||
|
||||
### Gate 2 — Architecture
|
||||
- Read first: the actual codebase, relevant ADRs, relevant AARs.
|
||||
Past decisions and learnings are input, not trivia.
|
||||
- How it fits the real system: endpoints, tables/schemas, query
|
||||
outlines, the end-to-end flow (Mermaid).
|
||||
- Constraints: non-functional requirements, proportional to the project.
|
||||
- Options & trade-offs where more than one viable way exists: pro/contra
|
||||
each, chosen option, and why. Feature-local decisions stay here.
|
||||
- Lasting directional decisions discovered here become ADRs (one each),
|
||||
linked from the design doc.
|
||||
|
||||
**STOP.**
|
||||
|
||||
### Gate 3 — Program Design
|
||||
- File locations: exact paths, new and touched.
|
||||
- Types and method signatures — no bodies.
|
||||
- Call stack for the main flow(s).
|
||||
- What the tests will assert.
|
||||
- Boundaries: an explicit DO NOT CHANGE list.
|
||||
- Shakiest calls: name the decisions you are least confident about.
|
||||
|
||||
**STOP.**
|
||||
|
||||
### Gate 4 — Vertical Slices
|
||||
- Slice 1 is the tracer bullet: a thin end-to-end path that runs
|
||||
(mocks and stubs allowed). Only then real logic, one testable slice
|
||||
at a time. Never build layer-by-layer horizontally.
|
||||
- Every slice lists its tasks; every task names **files, action,
|
||||
verify, done**.
|
||||
- Each slice ends with verification evidence, a status
|
||||
(`DONE` | `DONE_WITH_CONCERNS` | `NEEDS_CONTEXT` | `BLOCKED`),
|
||||
and a **STOP** for human review before the next slice.
|
||||
|
||||
### Gate 5 — Closeout
|
||||
- AAR section in the design doc: planned / actual / why the
|
||||
difference / learnings.
|
||||
- Harvest: learnings useful to future readers go to the wiki
|
||||
(FAQ, Stolpersteine) with source links. A missing or wrong framework
|
||||
rule becomes a framework issue or update.
|
||||
- Good analyses produced along the way may be filed as wiki pages
|
||||
(with citations) instead of dying in chat history.
|
||||
- Move the design doc to `docs/design/done/`. Run `gen_status.py`.
|
||||
|
||||
## Debugging Path
|
||||
|
||||
For bugs and incidents, any size:
|
||||
|
||||
1. Reproduce first. No reproduction, no fix.
|
||||
2. Hypothesize the root cause; verify the hypothesis with evidence
|
||||
before changing anything.
|
||||
3. Route the failure before fixing (diagnostic failure routing):
|
||||
- **Intent issue** — we built toward the wrong goal → back to Gate 1.
|
||||
- **Spec issue** — the design/plan was wrong → fix the spec
|
||||
(Gate 2/3), then the code.
|
||||
- **Code issue** — plan right, code wrong → fix in place.
|
||||
4. Fix, plus a test that would have caught it.
|
||||
5. Incidents and major misdiagnoses get a standalone AAR in `docs/aar/`.
|
||||
|
||||
## Session Handoff
|
||||
|
||||
- When a slice completes, or context quality degrades, write the current
|
||||
state into the design doc's **Handoff block** — done slices, open
|
||||
decisions, next step — then start a fresh session that resumes from
|
||||
the doc. The doc is the memory; the session is disposable.
|
||||
- End every working session by answering: "Which choices did I make that
|
||||
I'm least confident about?" File the answer in the design doc.
|
||||
|
||||
## Refinement Session
|
||||
|
||||
A recurring, human-triggered ritual. Agenda:
|
||||
|
||||
1. Batched confirmations: size classes and small approvals queued since
|
||||
last time.
|
||||
2. Backlog triage over `docs/issues/`: close, reprioritize, split.
|
||||
3. AAR harvest: walk recent AARs; update the wiki (FAQ, Stolpersteine);
|
||||
propose framework changes.
|
||||
4. Wiki lint (content-level, beyond `validate.py`): contradictions
|
||||
between pages, claims superseded by newer sources, orphan pages,
|
||||
missing cross-references, gaps worth a new page or a web search.
|
||||
5. STATUS review: anything stale or surprising in `STATUS.md`.
|
||||
|
||||
## Knowledge Handling (summary)
|
||||
|
||||
Full rules live in `docs/wiki/index.md`. The short version:
|
||||
|
||||
- Original sources live in `docs/sources/`, immutable — agents read
|
||||
them, never modify them. Wiki pages cite the sources they draw on.
|
||||
- Contradictions are resolved or explicitly flagged — never left
|
||||
silently coexisting.
|
||||
- If the wiki has no confident answer, say so. Never file a
|
||||
low-confidence synthesis back as knowledge.
|
||||
- Git is the changelog. No separate log file.
|
||||
@@ -1,19 +0,0 @@
|
||||
# Architecture Decision Records (ADR)
|
||||
|
||||
Eine Datei pro Entscheidung, fortlaufend nummeriert, Format siehe
|
||||
[template.md](template.md) (MADR-light). ADRs werden **nie umgeschrieben** —
|
||||
eine revidierte Entscheidung bekommt ein neues ADR, das alte wird im Status
|
||||
auf `abgelöst durch NNNN` gesetzt.
|
||||
|
||||
**Wann ist ein ADR Pflicht:**
|
||||
|
||||
- Architektur- oder Prozessentscheidungen, die mehrere Repos/Hosts betreffen
|
||||
- **Jede dauerhafte Ausnahme von einer bestehenden Regel** — eine Ausnahme, die
|
||||
nur dokumentiert, aber nicht entschieden wurde, ist ein Fehler (gelernt beim
|
||||
git.lab-Cutover 2026-08-01: die „Übergabe-Issues bleiben auf Gitea"-Ausnahme
|
||||
hätte als Entscheidungsvorlage kommen müssen, nicht als Fußnote)
|
||||
- Verworfene Wege, deren erneute Prüfung Zeit kosten würde („warum haben wir
|
||||
das damals nicht gemacht?")
|
||||
|
||||
Kleine, repo-lokale Entscheidungen bleiben im jeweiligen Projekt (Commit-Message
|
||||
oder Issue) — nicht jede Abwägung braucht ein ADR.
|
||||
@@ -1,19 +0,0 @@
|
||||
# NNNN — Titel (Aussagesatz der Entscheidung)
|
||||
|
||||
**Status:** vorgeschlagen | akzeptiert | abgelöst durch NNNN · **Datum:** JJJJ-MM-TT · **Entscheider:** sorb
|
||||
|
||||
## Kontext
|
||||
|
||||
Was ist das Problem, was zwingt zur Entscheidung? (2–5 Sätze)
|
||||
|
||||
## Entscheidung
|
||||
|
||||
Was wurde entschieden — als klare Aussage, umsetzbar ohne den Kontext zu lesen.
|
||||
|
||||
## Konsequenzen
|
||||
|
||||
Was wird dadurch besser, was nehmen wir bewusst in Kauf, was ist jetzt Pflicht.
|
||||
|
||||
## Verworfene Alternativen
|
||||
|
||||
Je Alternative ein Satz, warum nicht.
|
||||
+8
-1
@@ -1,3 +1,10 @@
|
||||
---
|
||||
type: aar
|
||||
status: harvested
|
||||
date: 2026-08-01
|
||||
related: []
|
||||
---
|
||||
|
||||
# AAR — CVE-Pipeline `gitops#47`
|
||||
|
||||
**Datum:** 2026-08-01 · **Host/Stack:** CFGMON, `/opt/threadnet-operating/monitoring`
|
||||
@@ -51,7 +58,7 @@ Befund 2 wurde nur sichtbar, weil die Config **im Container** geprüft wurde
|
||||
aus, `up -d` meldete `Running`, und ein SIGHUP-Reload lud klaglos den alten Inhalt.
|
||||
|
||||
Diese beiden Punkte sind als Verfahren festgehalten:
|
||||
[../deploy-uebergabe.md](../deploy-uebergabe.md).
|
||||
[../deploy-uebergabe.md](../wiki/deployment/deploy-uebergabe.md).
|
||||
|
||||
## 5. Offen
|
||||
|
||||
+8
-1
@@ -1,3 +1,10 @@
|
||||
---
|
||||
type: aar
|
||||
status: harvested
|
||||
date: 2026-08-01
|
||||
related: []
|
||||
---
|
||||
|
||||
# AAR — LABNET-02, CFGMON-Seite (Übergabe `sorb/management#2`)
|
||||
|
||||
**Datum:** 2026-08-01 · **Host/Stack:** CFGMON, WireGuard-Client gegen UDM
|
||||
@@ -56,7 +63,7 @@ wurde statt der Briefing-Annahme zu folgen. `ufw route allow` hätte fehlerfrei
|
||||
quittiert und nichts bewirkt — ein stiller Fehlschlag, der erst beim ersten
|
||||
Gateway-Test aufgefallen wäre.
|
||||
|
||||
Beides sind die Punkte 1 und 2 aus [../deploy-uebergabe.md](../deploy-uebergabe.md)
|
||||
Beides sind die Punkte 1 und 2 aus [../deploy-uebergabe.md](../wiki/deployment/deploy-uebergabe.md)
|
||||
in der Praxis: Mengengerüst bzw. Verifikation dort, wo der Dienst liest.
|
||||
|
||||
## 5. Offen
|
||||
@@ -1,3 +1,10 @@
|
||||
---
|
||||
type: aar
|
||||
status: harvested
|
||||
date: 2026-08-01
|
||||
related: []
|
||||
---
|
||||
|
||||
# AAR — LABNET-02, Lab-Seite (UDM/UniFi, Einzäunung und Abnahme)
|
||||
|
||||
**Datum:** 2026-08-01 · **Host/Stack:** MorninglightMountain (UDM Pro), UniFi Policy Engine
|
||||
+8
-1
@@ -1,3 +1,10 @@
|
||||
---
|
||||
type: aar
|
||||
status: harvested
|
||||
date: 2026-08-02
|
||||
related: []
|
||||
---
|
||||
|
||||
# AAR — Wiki-Rollout, Themes und Desktop-Clients (Nacht 2026-08-01/02)
|
||||
|
||||
**Datum:** 2026-08-01 22:00 – 2026-08-02 09:30 · **Beteiligt:** sorb + Mac-Session
|
||||
@@ -132,7 +139,7 @@ erfundene Palette kein Symptom, auf das man stoßen könnte.
|
||||
in den Skill-Beschreibungen **nicht verlässlich** — „Warm Sand · backgrounds"
|
||||
findet sich bei einem Theme, dessen Showcase-Seite dunkel ist. Belastbar ist nur
|
||||
`theme-showcase.pdf`: Seiten rendern, Hintergrundfarbe messen. Werte und Fallen
|
||||
stehen in [`shared/branding.md`](../../shared/branding.md).
|
||||
stehen in [`shared/branding.md`](../wiki/architecture/branding.md).
|
||||
|
||||
**Bestätigung des Musters aus Abschnitt 4.** Auch das war kein Analysefehler,
|
||||
sondern eine **ungeprüfte Änderung** — dieselbe Wurzel wie Healthcheck, toter
|
||||
+7
@@ -1,3 +1,10 @@
|
||||
---
|
||||
type: aar
|
||||
status: harvested
|
||||
date: 2026-08-09
|
||||
related: []
|
||||
---
|
||||
|
||||
# AAR — Refinement, Betrieb voranbringen, Git-Historie anonymisiert
|
||||
|
||||
**Datum:** 2026-08-09 · **Host/Stack:** git.lab, Gitea, K3s-Cluster (Authentik,
|
||||
@@ -0,0 +1,112 @@
|
||||
---
|
||||
type: aar
|
||||
status: harvested
|
||||
date: 2026-08-11
|
||||
related: []
|
||||
---
|
||||
|
||||
# AAR — `@apo` konnte nicht telefonieren: fehlende Synapse-`profiles`-Zeile
|
||||
|
||||
**Datum:** 2026-08-11 · **Beteiligt:** sorb + Mac-Session · **Stack:** Synapse,
|
||||
MAS, Authentik, Element Web / Element Call, MatrixRTC (K3s-Cluster)
|
||||
**Auftrag:** `@apo` kann sich anmelden und schreiben, aber **kein Call kommt
|
||||
zustande** — Grundursache finden und beheben, ohne weiter zu raten.
|
||||
|
||||
## 1. Ergebnis
|
||||
|
||||
**Behoben und verifiziert:**
|
||||
- `@apo` telefoniert wieder. Grundursache belegt: dem Konto fehlte die Zeile in
|
||||
Synapses `profiles`-Tabelle. Fix war ein einzelnes `INSERT` der
|
||||
Registrierungs-Default-Zeile, an zwei gesunden Konten (`clark`,
|
||||
`calltest01`) gegengeprüft.
|
||||
- Gegenprobe nach dem Fix: `displayname` gesetzt (vorher keine Zeile),
|
||||
`open_id_tokens` **0 → 6**, aktives `org.matrix.msc3401.call.member` im Raum.
|
||||
- Dokumentiert: Runbook `docs/troubleshooting/CALLS-FEHLEN-PROFILE-ZEILE.md` im
|
||||
gitops-Repo (inkl. Index-Eintrag), Merksatz im Session-Gedächtnis.
|
||||
|
||||
**Nebenbefund, separat behoben:**
|
||||
- **Kontoübernahme-Lücke:** Der MAS-Upstream-Provider stand auf
|
||||
`claims_imports.localpart.on_conflict: add` — bei Localpart-Kollision verknüpfte
|
||||
MAS die neue Upstream-Identität mit einem **bestehenden** Konto (inkl.
|
||||
Dienstkonten ohne Upstream-Link). Auf `on_conflict: fail` umgestellt
|
||||
(gitops `ef04d86`, nach git.lab gepusht), dokumentiert als
|
||||
[gitops#61](https://git.lab/axion1337.chat/axion1337.chat-gitops/-/issues/61),
|
||||
`priority:high`. Ausgelöst durch die live reproduzierte case-sensitive Dublette
|
||||
`boje`/`Boje`; das Zweitkonto `boje` (Authentik-ID 11) wurde gelöscht.
|
||||
**Deployment verifiziert:** Das SOPS-Values-Secret aktualisierte Flux, aber MAS
|
||||
lief noch mit der alten Config im Speicher (Pod älter als die Änderung) — erst
|
||||
ein `rollout restart` machte `fail` aktiv. „Committet" ≠ „deployed" ≠ „aktiv".
|
||||
|
||||
## 2. Die Kausalkette (belegt, nicht vermutet)
|
||||
|
||||
| Glied | Beleg |
|
||||
|---|---|
|
||||
| `@apo` hat **keine `profiles`-Zeile** | `SELECT count(*) … = 0`, während `clark`/`sorb`/`calltest01` je eine haben |
|
||||
| Displayname-Setzen crasht | `PUT …/displayname → 500`, `TypeError: 'NoneType' object is not subscriptable` in `_check_profile_size` (`storage/databases/main/profile.py:354`) — `txn.fetchone()` liefert `None`, `row[0]` fliegt |
|
||||
| kein Displayname → Widget-Init bricht ab | Call-Klick erzeugte **null** Server-Aktivität: kein `openid/request_token`, kein `call.member`; Browser-Log damals „Messaging present but not yet started" (iframe meldet nie `ContentLoaded`) |
|
||||
| kein Widget → kein Token → keine SFU | `@apo` als einziger aktiver Nutzer mit **0** Einträgen in `open_id_tokens` (die nicht geprunt werden) |
|
||||
|
||||
Herkunft der fehlenden Zeile: `@apo` ist ein **Vor-Authentik-Konto**, das durch
|
||||
sechs Identitäts-Resets ging. Deaktivieren löscht in Synapse das Profil,
|
||||
Reaktivieren legt es nicht neu an. `frank` (noch älter, nie zurückgesetzt) behielt
|
||||
seine Zeile. Ob einer der früheren manuellen Eingriffe der auslösende Reset war,
|
||||
ist nicht mehr zweifelsfrei zu klären — die Zeile ist jetzt wieder da.
|
||||
|
||||
## 3. Was ausgeschlossen wurde (gemessen)
|
||||
|
||||
| Verdacht | Warum entkräftet |
|
||||
|---|---|
|
||||
| Krypto / Cross-Signing (18 Pseudo-Geräte aus 6 Resets) | Testraum ist **unverschlüsselt** → Call braucht keine Krypto; `clark` telefoniert mit ebenfalls zurückgesetzten Schlüsseln |
|
||||
| Server-Call-Pfad (SFU, RTC-Auth, OpenID-Endpoint) | `calltest01`/`sorb` bekommen sauber 200 auf `openid/request_token` und die Federation-Auflösung |
|
||||
| `@apo`s Token / Session | `/sync` läuft durchgehend mit 200, Messaging intakt |
|
||||
| Login-Verknüpfung MAS↔Authentik | `subject` = Authentik-`uid` `2fafe38b…`, korrekt |
|
||||
|
||||
## 4. Was zur Lösung geführt hat
|
||||
|
||||
- **Der Sprung von „welcher Nutzer telefoniert nicht" zu „welche *Tabelle* ist
|
||||
anders".** Der Durchbruch war die `open_id_tokens`-Abfrage über *alle* aktiven
|
||||
Nutzer: `@apo` = 0, alle anderen zweistellig+. Ein Vergleich statt einer
|
||||
Einzelbetrachtung.
|
||||
- **Ein unverschlüsselter Testraum** hat das größte Ablenkungsfeld
|
||||
(Cross-Signing) in einem Schritt geschlossen.
|
||||
- **Der Live-Mitschnitt beim echten Call-Klick** zeigte die Abwesenheit jeder
|
||||
Aktivität — nicht ein Fehler, sondern *nichts* war der Befund.
|
||||
- **Der Nutzer-Hinweis „Anzeigename konnte nicht gesetzt werden"** lieferte den
|
||||
500er mit vollständigem Stacktrace — die letzte Meile von Korrelation zu
|
||||
Ursache.
|
||||
- **Der entscheidende Kontext kam von sorb:** „`apo` ist ein Alt-Konto von vor
|
||||
der Authentik-Integration." Das lenkte die Suche von „angesammelter Müll" auf
|
||||
„Migrations-/Provisionierungs-Lücke".
|
||||
|
||||
## 5. Lehren für die Zukunft
|
||||
|
||||
1. **Bei Call-Problemen zuerst `open_id_tokens` je Nutzer vergleichen.** 0 bei
|
||||
einem sonst aktiven Konto ist das schnellste, eindeutigste Alarmsignal und
|
||||
trennt Client- von Server-Ursache in einer Abfrage.
|
||||
2. **Immer im unverschlüsselten Raum reproduzieren, bevor man Krypto verdächtigt.**
|
||||
Das schließt einen ganzen Ursachenblock kostenlos aus.
|
||||
3. **„Nichts passiert" ist ein Messergebnis, kein Sackgassen-Signal.** Die
|
||||
Abwesenheit eines `openid`-Aufrufs hat den Fehler lokalisiert, nicht ein
|
||||
Fehlercode.
|
||||
4. **Alt-/mehrfach-zurückgesetzte Konten gegen frisch provisionierte diffen,
|
||||
nicht nur gegen die Erwartung.** Der Unterschied war eine *fehlende* Zeile —
|
||||
sichtbar nur im direkten Vergleich mit `clark`/`calltest01`.
|
||||
5. **Jeder DB-Schreib strukturiert: betroffene Zeile vorher anzeigen, an einem
|
||||
gesunden Konto gegenprüfen, per `INSERT … ON CONFLICT DO NOTHING` statt
|
||||
Überschreiben.** Das ist die direkte Konsequenz aus den früheren
|
||||
unstrukturierten MAS-Eingriffen dieses Vorgangs — und diesmal eingehalten.
|
||||
6. **Beiläufige Symptome ernst nehmen:** die Dublette `boje`/`Boje` beim
|
||||
Testkonto-Anlegen war der Faden, der die Kontoübernahme-Lücke (gitops#61)
|
||||
aufdeckte — ein Sicherheitsfund, der ohne den `@apo`-Vorgang unentdeckt
|
||||
geblieben wäre.
|
||||
|
||||
## 6. Offen / Folgetodos
|
||||
|
||||
- **gitops#61** (`on_conflict`-Härtung) ist gepusht und rollt über Flux; der
|
||||
case-insensitive Eindeutigkeits-Check im `matrix-invitation`-Prompt-Stage
|
||||
(damit der Nutzer schon bei der Registrierung statt erst beim Login scheitert)
|
||||
ist dort als bewusst offener Rest vermerkt.
|
||||
- Verwaiste Altlasten bei `@apo` (10 `local_notification_settings` für längst
|
||||
gelöschte Geräte, 18 Cross-Signing-Pseudoeinträge) sind **kosmetisch** und
|
||||
wurden bewusst **nicht** angefasst — sie haben mit dem Call-Problem nichts zu
|
||||
tun, und ein weiterer Eingriff widerspräche der Lehre oben.
|
||||
@@ -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.
|
||||
@@ -0,0 +1,34 @@
|
||||
---
|
||||
type: aar
|
||||
status: open # open | harvested
|
||||
date: YYYY-MM-DD
|
||||
related: [] # design docs, issues, ADRs involved
|
||||
---
|
||||
|
||||
<!-- Copy to docs/aar/YYYY-MM-DD-slug.md. Delete comments when filling in.
|
||||
Standalone AARs are for incidents and major deviations only —
|
||||
normal undertakings get their AAR as Gate 5 inside the design doc. -->
|
||||
|
||||
# AAR: Title
|
||||
|
||||
## What was planned / expected
|
||||
|
||||
## What happened
|
||||
|
||||
<!-- Facts and timeline, not blame. -->
|
||||
|
||||
## Why the difference
|
||||
|
||||
<!-- Root cause. For failures, name the routing class:
|
||||
intent issue / spec issue / code issue. -->
|
||||
|
||||
## Learnings
|
||||
|
||||
<!-- What future-you should know. Blunt beats polite. -->
|
||||
|
||||
## Actions
|
||||
|
||||
<!-- Concrete: wiki pages updated (FAQ, Stolpersteine) with links,
|
||||
framework issues opened, tests added. When all actions are done,
|
||||
set status: harvested. The refinement session walks all AARs
|
||||
still marked open. -->
|
||||
+10
@@ -1,3 +1,13 @@
|
||||
---
|
||||
type: adr
|
||||
id: "0001"
|
||||
status: accepted
|
||||
date: 2026-07-31
|
||||
supersedes: null
|
||||
superseded_by: null
|
||||
related: []
|
||||
---
|
||||
|
||||
# 0001 — git.lab ist kanonisch, Gitea wird per Push-Mirror beliefert
|
||||
|
||||
**Status:** akzeptiert · **Datum:** 2026-07-31 (rückwirkend dokumentiert 2026-08-01) · **Entscheider:** sorb
|
||||
+10
@@ -1,3 +1,13 @@
|
||||
---
|
||||
type: adr
|
||||
id: "0002"
|
||||
status: superseded
|
||||
date: 2026-08-01
|
||||
supersedes: null
|
||||
superseded_by: docs/adr/0019-komponenten-issues-adoptiert.md
|
||||
related: []
|
||||
---
|
||||
|
||||
# 0002 — Issues und Management-Repo ziehen ins Lab („das Lab ist die Quelle der Wahrheit")
|
||||
|
||||
**Status:** akzeptiert · **Datum:** 2026-08-01 · **Entscheider:** sorb
|
||||
@@ -1,3 +1,13 @@
|
||||
---
|
||||
type: adr
|
||||
id: "0003"
|
||||
status: accepted
|
||||
date: 2026-08-01
|
||||
supersedes: null
|
||||
superseded_by: null
|
||||
related: []
|
||||
---
|
||||
|
||||
# 0003 — CVE-Meldeweg: aggregierte Alarme, eigener Security-Raum, gleicher Bot
|
||||
|
||||
**Status:** akzeptiert · **Datum:** 2026-08-01 · **Entscheider:** sorb
|
||||
+11
-1
@@ -1,3 +1,13 @@
|
||||
---
|
||||
type: adr
|
||||
id: "0004"
|
||||
status: accepted
|
||||
date: 2026-08-01
|
||||
supersedes: null
|
||||
superseded_by: null
|
||||
related: []
|
||||
---
|
||||
|
||||
# 0004 — Site-to-Site-VPN Hetzner-Projektnetz ↔ Lab, schaltbar über die UDM
|
||||
|
||||
**Status:** akzeptiert (umgesetzt und abgenommen 2026-08-01, Testreihe 1–7 in [management#12](https://git.lab/axion1337.chat/management/-/issues/12)) · **Datum:** 2026-08-01 · **Entscheider:** sorb
|
||||
@@ -63,4 +73,4 @@ Client". Die Richtung wurde deshalb gedreht:
|
||||
- Tunnel dauerhaft an: widerspricht dem Bedarfsfall-Prinzip ohne echten Gewinn.
|
||||
- git.lab öffentlich exponieren: größte Angriffsfläche, klar verworfen.
|
||||
- Eigene UniFi-Zone für den Tunnel: technisch nicht möglich (VPN-Server bleiben in der
|
||||
VPN-Zone), siehe [AAR Lab-Seite](../verfahren/aar/2026-08-01-labnet02-lab.md).
|
||||
VPN-Zone), siehe [AAR Lab-Seite](../aar/2026-08-01-labnet02-lab.md).
|
||||
@@ -1,3 +1,13 @@
|
||||
---
|
||||
type: adr
|
||||
id: "0005"
|
||||
status: accepted
|
||||
date: 2026-08-01
|
||||
supersedes: null
|
||||
superseded_by: null
|
||||
related: []
|
||||
---
|
||||
|
||||
# 0005 — Projektmanagement: Kanban-Rückgrat mit leichten Scrum-Elementen
|
||||
|
||||
**Status:** akzeptiert · **Datum:** 2026-08-01 · **Entscheider:** sorb
|
||||
+10
@@ -1,3 +1,13 @@
|
||||
---
|
||||
type: adr
|
||||
id: "0006"
|
||||
status: superseded
|
||||
date: 2026-08-02
|
||||
supersedes: null
|
||||
superseded_by: docs/adr/0014-wikijs-loest-docusaurus-ab.md
|
||||
related: []
|
||||
---
|
||||
|
||||
# 0006 — Wikis ins Lab konsolidieren, Docusaurus als gemeinsame Lesefläche
|
||||
|
||||
**Status:** akzeptiert · **Datum:** 2026-08-02 · **Entscheider:** sorb
|
||||
+10
@@ -1,3 +1,13 @@
|
||||
---
|
||||
type: adr
|
||||
id: "0007"
|
||||
status: superseded
|
||||
date: 2026-08-02
|
||||
supersedes: null
|
||||
superseded_by: docs/adr/0014-wikijs-loest-docusaurus-ab.md
|
||||
related: []
|
||||
---
|
||||
|
||||
# 0007 — Wiki-Oberfläche: Docusaurus läuft, BookStack als Gegenentwurf
|
||||
|
||||
**Status:** vorgeschlagen (Entscheidung offen → [Issue #20](https://git.lab/axion1337.chat/management/-/issues/20)) · **Datum:** 2026-08-02 · **Entscheider:** sorb
|
||||
+11
-1
@@ -1,3 +1,13 @@
|
||||
---
|
||||
type: adr
|
||||
id: "0008"
|
||||
status: accepted
|
||||
date: 2026-08-06
|
||||
supersedes: null
|
||||
superseded_by: null
|
||||
related: []
|
||||
---
|
||||
|
||||
# 0008 — Agenten-Sessions auf CFGMON laufen root-äquivalent über die docker-Gruppe
|
||||
|
||||
**Status:** akzeptiert · **Datum:** 2026-08-06 (Struktur-Workshop [#17](https://git.lab/axion1337.chat/management/-/issues/17)) · **Entscheider:** sorb
|
||||
@@ -19,7 +29,7 @@ Konfigurationsfehler — aber es hat zwei Folgen, die benannt gehören:
|
||||
docker-Gruppe geschieht, ist im Nachhinein nicht aus den üblichen
|
||||
Protokollen rekonstruierbar.
|
||||
|
||||
Aufgedeckt im [CFGMON-AAR](../verfahren/aar/2026-08-01-labnet02-cfgmon.md)
|
||||
Aufgedeckt im [CFGMON-AAR](../aar/2026-08-01-labnet02-cfgmon.md)
|
||||
(Befund 3, MEDIUM), erfasst als
|
||||
[#14](https://git.lab/axion1337.chat/management/-/issues/14).
|
||||
|
||||
+12
-2
@@ -1,8 +1,18 @@
|
||||
---
|
||||
type: adr
|
||||
id: "0009"
|
||||
status: accepted
|
||||
date: 2026-08-07
|
||||
supersedes: null
|
||||
superseded_by: null
|
||||
related: []
|
||||
---
|
||||
|
||||
# 0009 — Commit-Konventionen und rückwirkende Anonymisierung der Historie
|
||||
|
||||
**Status:** akzeptiert · **Datum:** 2026-08-07 (Regel) / 2026-08-09 (Durchführung) · **Entscheider:** sorb
|
||||
|
||||
> Nachgetragen am 2026-08-09 in der [Retro](../verfahren/retro/2026-08-09.md). Die
|
||||
> Nachgetragen am 2026-08-09 in der [Retro](../sources/protokolle/retro-2026-08-09.md). Die
|
||||
> Entscheidung war getroffen und ausgeführt, bevor sie als ADR vorlag — das ist
|
||||
> genau der Fehler, den die ADR-Pflicht verhindern soll, und wird hier benannt
|
||||
> statt geglättet.
|
||||
@@ -51,7 +61,7 @@ Uhrzeit verschwindet.
|
||||
- **Alle SHAs im Bereich sind neu.** Verweise in Issues, Doku und Commit-Texten
|
||||
zeigen ins Leere. Die Doku wurde nachgezogen (12 Stellen); für alles andere gibt
|
||||
es die dauerhafte Zuordnungstabelle
|
||||
[`shared/commit-zuordnung-2026-08-07.md`](../shared/commit-zuordnung-2026-08-07.md).
|
||||
[`shared/commit-zuordnung-2026-08-07.md`](../sources/migration/commit-zuordnung-2026-08-07.md).
|
||||
- **Issue-Kommentare wurden bewusst NICHT umgeschrieben.** Eine Tabelle
|
||||
nachzuschlagen ist zumutbar; nachträglich zu ändern, was jemand geschrieben hat,
|
||||
beschädigt dieselbe Nachvollziehbarkeit ein zweites Mal.
|
||||
+10
@@ -1,3 +1,13 @@
|
||||
---
|
||||
type: adr
|
||||
id: "0010"
|
||||
status: accepted
|
||||
date: 2026-08-09
|
||||
supersedes: null
|
||||
superseded_by: null
|
||||
related: []
|
||||
---
|
||||
|
||||
# 0010 — Härtung ist ein eigener Meilenstein (M5); M1 misst nur Kaputtes
|
||||
|
||||
**Status:** akzeptiert · **Datum:** 2026-08-09 · **Entscheider:** sorb
|
||||
@@ -0,0 +1,69 @@
|
||||
---
|
||||
type: adr
|
||||
id: "0011"
|
||||
status: accepted
|
||||
date: 2026-08-11
|
||||
supersedes: null
|
||||
superseded_by: null
|
||||
related: []
|
||||
---
|
||||
|
||||
# 0011 — Provisionierung verweigert Localpart-Kollisionen, statt an bestehende Konten zu verknüpfen
|
||||
|
||||
**Status:** akzeptiert · **Datum:** 2026-08-11 · **Entscheider:** sorb
|
||||
|
||||
## Kontext
|
||||
|
||||
Der MAS-Upstream-Provider für Authentik stand auf
|
||||
`claims_imports.localpart.on_conflict: add`. MAS-Semantik: Kollidiert der aus dem
|
||||
Authentik-Claim abgeleitete Localpart mit einem **bestehenden** Matrix-Konto,
|
||||
verknüpft MAS die neue Upstream-Identität mit diesem Konto — ohne Abbruch, ohne
|
||||
Warnung. Authentiks eigene Benutzernamen-Eindeutigkeit fängt das nicht ab: sie
|
||||
gilt nur innerhalb von Authentik und ist case-sensitive (`boje` neben `Boje` ging
|
||||
live durch). Folge: Ein Inhaber eines Einladungstokens konnte einen (auch nur in
|
||||
der Schreibweise abweichenden) Namen eines bestehenden Kontos registrieren und
|
||||
würde beim ersten Login in dessen Konto verknüpft — inklusive Dienstkonten ohne
|
||||
Upstream-Link (`draupnir`, `alerts`, `maintenance-notify`). Das ist ein
|
||||
Kontoübernahme-Vektor, entdeckt am 2026-08-11 beim Anlegen eines Testkontos
|
||||
([gitops#61](https://git.lab/axion1337.chat/axion1337.chat-gitops/-/issues/61)).
|
||||
|
||||
## Entscheidung
|
||||
|
||||
**Identitäts-Provisionierung verknüpft eine neue Upstream-Identität niemals mit
|
||||
einem bereits bestehenden lokalen Konto.** Konkret: `on_conflict: fail` im
|
||||
`claims_imports.localpart`-Block des MAS-Upstream-Providers
|
||||
(`gitops/apps/production/custom-configs/mas-secret.yaml`). Ein kollidierender
|
||||
Localpart bricht die Provisionierung ab. Dies ist ab jetzt stehende Regel, nicht
|
||||
nur der aktuelle Wert — jede künftige Änderung an diesem Verhalten braucht ein
|
||||
ablösendes ADR.
|
||||
|
||||
## Konsequenzen
|
||||
|
||||
- **Besser:** Der Übernahme-Weg ist geschlossen. Bestehende Konten (besonders die
|
||||
ohne Upstream-Link) können nicht mehr durch eine kollidierende Neuregistrierung
|
||||
gekapert werden. Bestehende, korrekte Verknüpfungen bleiben unberührt.
|
||||
- **In Kauf genommen:** Ein Nutzer, der einen bereits vergebenen Namen wählt,
|
||||
erhält die Fehlermeldung erst **beim Login** (wenn MAS provisioniert), nicht
|
||||
schon bei der Registrierung in Authentik. Das ist eine schlechtere UX, aber kein
|
||||
Sicherheitsproblem.
|
||||
- **Jetzt Pflicht:**
|
||||
- Als offene Härtung eine **case-insensitive Eindeutigkeitsprüfung im
|
||||
`matrix-invitation`-Prompt-Stage**, damit die Kollision schon bei der
|
||||
Registrierung sichtbar wird (verfolgt in gitops#61).
|
||||
- **Nach jeder Änderung an einem SOPS-verwalteten Values-Secret den
|
||||
konsumierenden Dienst per `rollout restart` neu ausrollen und verifizieren**,
|
||||
dass der Pod jünger als die Änderung ist. Beim Ausrollen dieses Fixes lief
|
||||
MAS noch mit der alten Config im Speicher, obwohl das Secret bereits `fail`
|
||||
zeigte — „committet" ≠ „deployed" ≠ „aktiv" (MAS liest Config nur beim Start).
|
||||
|
||||
## Verworfene Alternativen
|
||||
|
||||
- **`on_conflict: add` belassen und allein auf Authentiks Eindeutigkeit
|
||||
vertrauen** — verworfen: die greift nur innerhalb Authentiks und
|
||||
case-sensitive, deckt Kollisionen mit vorbestehenden Matrix-Konten also nicht ab.
|
||||
- **Nur den Prompt-Stage-Check bauen, MAS auf `add` lassen** — verworfen: der
|
||||
Client-seitige Check ist umgehbar (direkter Flow-Aufruf), der MAS-seitige
|
||||
Abbruch ist die eigentliche Sicherheitsgrenze. Der Prompt-Check ist die
|
||||
UX-Ergänzung, nicht der Schutz.
|
||||
- **Betroffene Dienstkonten einfach mit Upstream-Links versehen** — verworfen:
|
||||
behandelt nur das Symptom für heute bekannte Konten, nicht den Mechanismus.
|
||||
@@ -0,0 +1,88 @@
|
||||
---
|
||||
type: adr
|
||||
id: "0012"
|
||||
status: accepted
|
||||
date: 2026-08-11
|
||||
supersedes: null
|
||||
superseded_by: null
|
||||
related:
|
||||
- "docs/design/done/2026-08-11-neckbeard-migration.md"
|
||||
- "docs/adr/0002-issues-und-management-ins-lab.md"
|
||||
- "docs/adr/0005-pm-framework-kanban.md"
|
||||
---
|
||||
|
||||
# ADR-0012: Issues leben im Repo; GitLab wird deterministisch bespiegelt
|
||||
|
||||
## Kontext
|
||||
|
||||
Die Gruppe führt 111 Issues auf git.lab, davon 71 offen; die Disziplin ist
|
||||
belegt intakt (F-014: 71/71 mit genau einem Meilenstein, 71/71 mit
|
||||
Priorität, WIP-Limit gehalten). Neckbeards ADR-0002 macht In-Repo-Issues
|
||||
zum Default und vertagt die Spiegel-Option C. Der Feldtest zeigt beides:
|
||||
Die Forge erzwingt sichtbar, was Prosa nicht hält (F-001, F-017 —
|
||||
Dokumente widersprechen dem Board), und Host-Sessions ohne Lab-Zugang
|
||||
können GitLab-Issues gar nicht lesen, wohl aber den Gitea-Mirror dieses
|
||||
Repos. Der alte Grundsatz „Alles Offene ist ein Issue" (altes ADR-0005)
|
||||
scheiterte nur dort, wo Arbeitspunkte in `hosts/`-Markdown lebten (F-004)
|
||||
— am zweiten Backlog, nicht am Board.
|
||||
|
||||
## Optionen
|
||||
|
||||
**A: GitLab bleibt kanonisch, Repo hält nur einen Export.** Tagesablauf
|
||||
unverändert, Board bleibt Arbeitsfläche. Aber: dauerhafte Ausnahme von
|
||||
neckbeards ADR-0002 (nach eigener Regel ADR-pflichtig), Issues bleiben
|
||||
für Host-Sessions unsichtbar und für Agenten nur per API erreichbar, und
|
||||
die Klasse „Prosa widerspricht Board" (F-001) bleibt strukturell offen —
|
||||
generierte Dokumente hingen an einem Netzzugriff.
|
||||
|
||||
**B: Reine In-Repo-Issues, GitLab-Issues geschlossen.** Sauberste
|
||||
neckbeard-Form. Aber: das Gruppenboard verliert den Management-Scope,
|
||||
Meilenstein-Ansichten werden unvollständig, das Refinement liest zwei
|
||||
Systeme — genau die belegte Disziplin (F-014) würde ihres Werkzeugs
|
||||
beraubt. Der Report warnt ausdrücklich: nicht per Board-Löschung
|
||||
migrieren.
|
||||
|
||||
**C: Repo kanonisch, GitLab als generierter Spiegel.** Die Issue-Wahrheit
|
||||
liegt als `docs/issues/NNNN-slug.md` im Repo (grepbar, offline, über den
|
||||
Gitea-Mirror überall lesbar); ein deterministisches Skript spiegelt
|
||||
Titel, Status, Meilenstein, Priorität und Fälligkeit nach GitLab, damit
|
||||
Board-, Meilenstein- und Label-Ansichten weiterarbeiten. Eine
|
||||
Drift-Prüfung meldet Abweichungen zwischen Board und Repo rot.
|
||||
|
||||
## Entscheidung
|
||||
|
||||
**Option C, beschränkt auf den Management-Scope.**
|
||||
|
||||
- `docs/issues/` wird kanonisch für die Issues des management-Projekts.
|
||||
Die offenen management-Issues werden aus dem GitLab-Stand importiert
|
||||
und behalten ihre Nummern (GitLab-iid = Datei-id; keine dritte
|
||||
Nummernwelt). Alt-IDs wie `CFGMON-01` bleiben im Titel.
|
||||
- Das Schema trägt die belegten Pflichten: `milestone` (Pflicht, M1–M5)
|
||||
und `priority` (Pflicht, high/medium/low), dazu `due` (Datum statt
|
||||
„bald"), optional `host`/`area`. Der Status-Enum wird um die
|
||||
Board-Spalten erweitert (`next`, `waiting` mit benanntem Grund); das
|
||||
WIP-Limit (max. 2 in-progress) wird eine Validator-Regel.
|
||||
- Der Spiegel ist **ein** deterministisches Skript (Repo → GitLab),
|
||||
Standard `--dry-run`; echte Läufe stößt sorb an. Board-Handgriffe
|
||||
bleiben erlaubt, sind aber nicht kanonisch: Was nicht nachgezogen
|
||||
wird, meldet die Drift-Prüfung. Die Zusage-Spalten (`next`,
|
||||
`in-progress`) vergibt weiterhin nur sorb — Prozessregel, nicht
|
||||
Mechanik.
|
||||
- **Komponenten-Tracker bleiben unangetastet** (gitops 60 Issues usw.),
|
||||
bis die jeweilige Komponente selbst adoptiert; das wird als
|
||||
Folge-Issues angelegt. Bis dahin gilt für Komponenten-Issues GitLab
|
||||
als Wahrheit — ausgewiesen, nicht verschwiegen.
|
||||
|
||||
## Konsequenzen
|
||||
|
||||
- Statusänderung = Commit; `git log` ersetzt die Issue-Chronik. STATUS.md
|
||||
und Roadmap-Zahlen werden generiert statt behauptet (F-001-Klasse
|
||||
geschlossen).
|
||||
- Host-Sessions lesen den vollständigen Management-Backlog erstmals von
|
||||
überall (Gitea-Mirror des Repos).
|
||||
- GitLab-seitige Änderungen ohne Nachzug sind ab jetzt ein Befund, kein
|
||||
stiller Zustand — die Drift-Prüfung übernimmt die Alarmfunktion der
|
||||
roten Pipeline.
|
||||
- Das geschlossene GitLab-Altbestand-Archiv (40 geschlossene Issues)
|
||||
wird nicht importiert; es bleibt als Historie auf git.lab, erreichbar
|
||||
über die bestehenden Verweise.
|
||||
@@ -0,0 +1,85 @@
|
||||
---
|
||||
type: adr
|
||||
id: "0013"
|
||||
status: accepted
|
||||
date: 2026-08-11
|
||||
supersedes: null
|
||||
superseded_by: null
|
||||
related:
|
||||
- "docs/design/done/2026-08-11-neckbeard-migration.md"
|
||||
- "docs/adr/0001-gitlab-kanonisch-push-mirror.md"
|
||||
---
|
||||
|
||||
# ADR-0013: Gruppenregeln kanonisch im management-Repo, Komponenten zeigen und werden geprüft
|
||||
|
||||
## Kontext
|
||||
|
||||
Fünf Komponenten-Repos und dieses Repo teilen ein Regelwerk. Neckbeards
|
||||
ADR-0001 löst „ein Repo, viele Harnesse", nicht „viele Repos, ein
|
||||
Regelwerk" — die schärfste Lücke des Feldtests. Der alte Ansatz war
|
||||
bereits Pointer-basiert („Projekt-Repos haben eigene CLAUDE.mds", die
|
||||
Arbeitsgrundlage liegt im management-Repo, über den Gitea-Mirror von
|
||||
überall lesbar) und scheiterte nicht am Mechanismus, sondern an der
|
||||
Anwendung: 4 von 5 Komponenten haben schlicht keine Pointer-Datei
|
||||
(F-011), und nichts prüfte das. Zusätzlich tragen fünf Komponenten vier
|
||||
Namensschemata, ohne dass ein Artefakt den kanonischen Slug festhält
|
||||
(F-008) — diese Session musste die Slugs erfragen.
|
||||
|
||||
## Optionen
|
||||
|
||||
**A: Regelkopien in jede Komponente stempeln** (generiert, mit
|
||||
Quell-SHA; Prüfskript vergleicht). Funktioniert offline im
|
||||
Komponenten-Checkout. Aber: sechs Kopien derselben Regeln sind genau die
|
||||
Drift-Maschine, die ADR-0001 upstream verwirft — der Stempel macht Drift
|
||||
erkennbar, nicht unmöglich, und jeder Regeländerung folgt ein
|
||||
Sechs-Repo-Commit-Zug.
|
||||
|
||||
**B: Git-Submodule/Subtree eines Regel-Repos.** Mechanisch streng, aber:
|
||||
koppelt jeden Komponenten-Clone an Lab-Erreichbarkeit, ist in Obsidian
|
||||
und Forge-Ansichten sperrig, und die Gruppe hat mit Submodules keinerlei
|
||||
Praxis — Reibung ohne belegten Bedarf.
|
||||
|
||||
**C: Pointer + deterministische Prüfung.** Die Gruppenregeln stehen
|
||||
genau einmal, im AGENTS.md dieses Repos (das gespiegelt und damit
|
||||
überall lesbar ist). Jede Komponente trägt nur Projektspezifika plus
|
||||
einen Pointer auf die Gruppenregeln (git.lab-Pfad und Mirror-URL). Neu
|
||||
gegenüber dem alten Ansatz ist der prüfende Teil: ein Artefakt benennt
|
||||
die Gruppe, ein Skript prüft die Anwendung.
|
||||
|
||||
## Entscheidung
|
||||
|
||||
**Option C.**
|
||||
|
||||
- **Kanonisch:** die Gruppenregeln leben als ausgewiesener Abschnitt im
|
||||
`AGENTS.md` dieses Repos. `CLAUDE.md` wird Ein-Zeilen-Pointer
|
||||
(neckbeard ADR-0001).
|
||||
- **Komponenten-Artefakt:** `docs/components/<slug>.md` (neuer
|
||||
Schema-Typ) deklariert je Repo den kanonischen Slug, Anzeigenamen,
|
||||
Mirror-Pfad und die Phase (`active` / `staged` / `external`) — damit
|
||||
ist F-008 maschinenlesbar beantwortet und die bewusst gestaffelte
|
||||
Dormanz von `thread-net-git`/`threadnet-operating` (F-009-Addendum)
|
||||
erstmals repräsentierbar statt nur mündlich.
|
||||
- **Prüfung, zweigeteilt:** offline prüft `validate.py` die
|
||||
Komponenten-Artefakte wie jedes andere Artefakt; in der Lab-CI prüft
|
||||
die Stillstandsprüfungs-Familie (a) dass jede deklarierte Komponente
|
||||
die Pointer-Datei tatsächlich trägt (schließt F-011) und (b) dass die
|
||||
**zur Laufzeit gelesene** Gruppenliste und `docs/components/`
|
||||
deckungsgleich sind — die Projektliste bleibt bewusst ungehärtet im
|
||||
Code (Retro-Lehre: eine gepflegte Liste ist die Stelle, an der ein
|
||||
neues Repo jahrelang durchrutscht); neu auftauchende Repos werden
|
||||
Befund statt Lücke.
|
||||
- **Rollout** der Pointer-Dateien in die fünf Komponenten ist nicht Teil
|
||||
dieser Undertaking: fünf Folge-Issues, eines je Komponente.
|
||||
|
||||
## Konsequenzen
|
||||
|
||||
- Regeländerung = ein Commit in einem Repo; Komponenten folgen per
|
||||
Verweis, nicht per Kopie.
|
||||
- Eine Komponente ohne Pointer ist ab dem Rollout ein roter
|
||||
CI-Befund, kein stiller Zustand über Wochen (F-011-Klasse).
|
||||
- Die Slug-Unregelmäßigkeiten selbst (`thread-net-git`,
|
||||
CamelCase-`ThreadNet-Web`) werden hier **nicht** bereinigt — ein
|
||||
Rename fasst Forge-Zustand an und wird eigenes Issue mit eigener
|
||||
Abwägung; das Artefakt dokumentiert bis dahin den Ist-Stand.
|
||||
- Host-Sessions ohne Lab finden Regeln und Gruppenliste über den
|
||||
Gitea-Mirror; der Pointer nennt beide Wege.
|
||||
@@ -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.
|
||||
@@ -0,0 +1,37 @@
|
||||
---
|
||||
type: adr
|
||||
id: "0000"
|
||||
status: proposed # proposed | accepted | superseded
|
||||
date: YYYY-MM-DD
|
||||
supersedes: null # path to older ADR, e.g. docs/adr/0002-old.md
|
||||
superseded_by: null # filled in on the OLD adr when a new one replaces it
|
||||
related: [] # optional: paths to design docs / issues
|
||||
---
|
||||
|
||||
<!-- Copy to docs/adr/NNNN-slug.md. Delete all comments when filling in. -->
|
||||
|
||||
# ADR-0000: Title
|
||||
|
||||
## Context
|
||||
|
||||
<!-- The situation and the forces at play. Constraints upfront:
|
||||
deadlines, scale, team knowledge, existing decisions. -->
|
||||
|
||||
## Options Considered
|
||||
|
||||
<!-- Name each option, even the one you lean toward. Pros/cons per
|
||||
option; a small dimension table (complexity, cost, maintenance,
|
||||
familiarity) where it helps. Keep proportional to the decision. -->
|
||||
|
||||
## Decision
|
||||
|
||||
<!-- The choice, in one or two sentences. -->
|
||||
|
||||
## Consequences
|
||||
|
||||
<!-- What becomes easier, what becomes harder, what we will need to
|
||||
revisit. Honest cons included. -->
|
||||
|
||||
<!-- Rules: an accepted ADR is never edited — write a new ADR that
|
||||
supersedes it and set superseded_by here. Lasting directional
|
||||
decisions only; feature-local choices belong in the design doc. -->
|
||||
@@ -0,0 +1,16 @@
|
||||
---
|
||||
type: component
|
||||
slug: "ThreadNet-Web"
|
||||
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
|
||||
|
||||
Element-Web/-Desktop-Fork unter eigener Marke. ⚠️ Slug ist der einzige in CamelCase (F-008) — GitLab behandelt Pfade case-insensitiv; kanonisch ist exakt diese Schreibweise. Ein Rename ist bewusst NICHT Teil der Migration (eigenes Issue bei Bedarf, ADR-0013).
|
||||
@@ -0,0 +1,14 @@
|
||||
---
|
||||
type: component
|
||||
slug: "axion1337.chat-gitops"
|
||||
anzeigename: "ThreadNet Server Suite"
|
||||
phase: active
|
||||
gitlab: "axion1337.chat/axion1337.chat-gitops"
|
||||
mirror: "rohana.axion1337.de/sorb/axion1337.chat-gitops"
|
||||
related:
|
||||
- "docs/adr/0013-gruppenregeln-kanonisch-mit-pruefung.md"
|
||||
---
|
||||
|
||||
# ThreadNet Server Suite
|
||||
|
||||
ESS-/Flux-Deployment des axion1337.chat-Stacks; Gitea bleibt Flux-Quelle ([Mirror-Topologie](../wiki/architecture/mirror-topologie.md)). Trägt die Hälfte des Gruppen-Backlogs (Feldtest F-009).
|
||||
@@ -0,0 +1,14 @@
|
||||
---
|
||||
type: component
|
||||
slug: "game-operating"
|
||||
anzeigename: "Game-Operating"
|
||||
phase: external
|
||||
gitlab: "axion1337.chat/game-operating"
|
||||
mirror: "rohana.axion1337.de/sorb/game-operating" # privat — anonym nicht lesbar (F-007-Addendum)
|
||||
related:
|
||||
- "docs/adr/0013-gruppenregeln-kanonisch-mit-pruefung.md"
|
||||
---
|
||||
|
||||
# Game-Operating
|
||||
|
||||
Nicht ThreadNet-bezogen (über geplante Monitoring-Aufnahme hinaus). Mirror existiert **privat** — ein anonymer ls-remote-Fehlschlag ist hier kein Beleg für Nichtexistenz (Feldtest-Lehre, F-007-Addendum).
|
||||
@@ -0,0 +1,14 @@
|
||||
---
|
||||
type: component
|
||||
slug: "gameserver"
|
||||
anzeigename: "Gameserver"
|
||||
phase: external
|
||||
gitlab: "axion1337.chat/gameserver"
|
||||
mirror: null # kein Mirror — Issue 0032
|
||||
related:
|
||||
- "docs/adr/0013-gruppenregeln-kanonisch-mit-pruefung.md"
|
||||
---
|
||||
|
||||
# Gameserver
|
||||
|
||||
Nicht ThreadNet-bezogen. **Kein Push-Mirror**; auf Gitea liegt ein gleichnamiges Repo mit anderem Stand — verfolgt in [Issue 0032](../issues/0032-gameserver-hat-keinen-push-mirror-und-auf-gitea.md).
|
||||
@@ -0,0 +1,14 @@
|
||||
---
|
||||
type: component
|
||||
slug: "management"
|
||||
anzeigename: "Management"
|
||||
phase: active
|
||||
gitlab: "axion1337.chat/management"
|
||||
mirror: "rohana.axion1337.de/sorb/management"
|
||||
related:
|
||||
- "docs/adr/0013-gruppenregeln-kanonisch-mit-pruefung.md"
|
||||
---
|
||||
|
||||
# Management
|
||||
|
||||
Dieses Repo: Steuerung der Gruppe, kanonische Gruppenregeln (AGENTS.md §6), In-Repo-Issues (ADR-0012).
|
||||
@@ -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,14 @@
|
||||
---
|
||||
type: component
|
||||
slug: "thread-net-git"
|
||||
anzeigename: "ThreadNet Git"
|
||||
phase: staged
|
||||
gitlab: "axion1337.chat/thread-net-git"
|
||||
mirror: "rohana.axion1337.de/sorb/thread-net-git"
|
||||
related:
|
||||
- "docs/adr/0013-gruppenregeln-kanonisch-mit-pruefung.md"
|
||||
---
|
||||
|
||||
# ThreadNet Git
|
||||
|
||||
Gitea-Betriebskonfiguration. **Bewusst gestaffelt dormant** (sorb, 2026-08-10, Feldtest F-009-Addendum): erst Basis-Funktionsumfang, dann Monitoring-/Security-Ausbau — keine Politur-Umwege. ⚠️ Slug bricht das threadnet-Muster (F-008); Ist-Stand dokumentiert, kein Rename hier.
|
||||
@@ -0,0 +1,14 @@
|
||||
---
|
||||
type: component
|
||||
slug: "threadnet-call"
|
||||
anzeigename: "ThreadNet Call"
|
||||
phase: active
|
||||
gitlab: "axion1337.chat/threadnet-call"
|
||||
mirror: "rohana.axion1337.de/sorb/threadnet-call"
|
||||
related:
|
||||
- "docs/adr/0013-gruppenregeln-kanonisch-mit-pruefung.md"
|
||||
---
|
||||
|
||||
# ThreadNet Call
|
||||
|
||||
Element-Call-Fork für ThreadNet.
|
||||
@@ -0,0 +1,14 @@
|
||||
---
|
||||
type: component
|
||||
slug: "threadnet-operating"
|
||||
anzeigename: "ThreadNet Operating"
|
||||
phase: staged
|
||||
gitlab: "axion1337.chat/threadnet-operating"
|
||||
mirror: "rohana.axion1337.de/sorb/threadnet-operating"
|
||||
related:
|
||||
- "docs/adr/0013-gruppenregeln-kanonisch-mit-pruefung.md"
|
||||
---
|
||||
|
||||
# ThreadNet Operating
|
||||
|
||||
CFGMON-Betrieb (Monitoring/Konfiguration). **Bewusst gestaffelt dormant** wie thread-net-git (F-009-Addendum).
|
||||
@@ -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.
|
||||
@@ -0,0 +1,642 @@
|
||||
---
|
||||
type: design
|
||||
status: done
|
||||
date: 2026-08-11
|
||||
size: L
|
||||
related:
|
||||
- "PROJECT.md"
|
||||
- "docs/adr/0005-pm-framework-kanban.md"
|
||||
- "docs/adr/0009-commit-konventionen-und-historien-anonymisierung.md"
|
||||
- "docs/adr/0010-haertung-eigener-meilenstein.md"
|
||||
- "docs/adr/0012-issues-im-repo-gitlab-als-spiegel.md"
|
||||
- "docs/adr/0013-gruppenregeln-kanonisch-mit-pruefung.md"
|
||||
---
|
||||
|
||||
# Design: Migration des Management-Systems auf neckbeard
|
||||
|
||||
Grundlage: der Feldtest-Report auf dem Branch `Neckbeard-v0.1.1-analyse-1`
|
||||
(Session 1, eingefroren; Befunde F-001…F-017), gemessen gegen neckbeard
|
||||
v0.1.1 @ `823a08cac6b03a47d7e2f661200a49ac6e09d38d`. Bindende Vorgabe aus
|
||||
der Übergabe: **erst die Fehlermuster beider Ansätze durcharbeiten und den
|
||||
Wert des alten Ansatzes in neckbeard einfalten — Übernahme erst danach.**
|
||||
|
||||
## Gate 1 — Produkt
|
||||
|
||||
### Problem
|
||||
|
||||
Das Management-Repo steuert fünf Komponenten-Repos und sich selbst mit
|
||||
einem eigenen Regelwerk. Der Feldtest zeigt: Das Regelwerk ist nicht
|
||||
verfallen, sondern **ungleich durchgesetzt**. Wo ein Werkzeug die Regel
|
||||
hält, hält sie vollständig — alle 71 offenen Issues haben genau einen
|
||||
Meilenstein, das WIP-Limit steht, 0 von 161 Dokument-Links sind tot
|
||||
(F-014). Wo nichts prüft — Prosa, Git-Metadaten, Übereinstimmung zweier
|
||||
Dateien — versagt dasselbe Regelwerk wiederholt, in vier Mustern:
|
||||
|
||||
- **A** — Entscheidung im Werkzeug vollzogen, Doku nicht nachgezogen:
|
||||
M5 existiert und trägt 14 Issues, aber `roadmap.md` stellt ihn als
|
||||
offene Frage dar und die kanonische Arbeitsgrundlage bindet Issues an
|
||||
„M1–M4" (F-001; ferner F-005, F-007, F-010, F-017).
|
||||
- **B** — Regel repo-weit erklärt, auf eine Teilmenge angewandt:
|
||||
Zeitstempel-Anonymisierung erreicht 1 von 6 Repos, 5 Autor-Identitäten
|
||||
einer Person überleben, 4 von 5 Komponenten haben kein versprochenes
|
||||
CLAUDE.md, fünf Komponenten tragen vier Namensschemata (F-002, F-003,
|
||||
F-008, F-011).
|
||||
- **C** — zwei Backlogs, eine Regel: fünf offene Arbeitspunkte leben nur
|
||||
in `hosts/`-Markdown, unsichtbar für Board, Meilenstein und Priorität
|
||||
(F-004, F-009).
|
||||
- **D** — Artefakte überleben ihren Zweck ohne Eigentümer: verwaiste
|
||||
Branches publizieren Vor-Rewrite-Historie, zitierte SHAs sind
|
||||
unauflösbar (F-006, F-012).
|
||||
|
||||
Betroffen sind sorb und jede Agenten-Session: Jede neue Session wird von
|
||||
der kanonischen Datei falsch geprimt und würde vollzogene Entscheidungen
|
||||
rückgängig machen. Neckbeard adressiert genau diese Klasse — hält aber
|
||||
selbst sieben im Feldtest belegte Lücken, allen voran: ADR-0001 löst
|
||||
„ein Repo, viele Harnesse", dieses Projekt ist „viele Repos, ein
|
||||
Regelwerk", und für die Frage, wo die 71 offenen GitLab-Issues nach der
|
||||
Migration leben, existiert nur eine aufgeschobene Option C. Beide
|
||||
Entscheidungen fallen in Gate 2, jeweils als ADR.
|
||||
|
||||
Das Produkt dieser Undertaking: das Management-System dieses Repos auf
|
||||
neckbeard umziehen, so dass die vorhandene Disziplin von Stellen, die
|
||||
nur ein Mensch prüfen kann, an Stellen wandert, die ein Skript prüft —
|
||||
nachdem der Wert des alten Ansatzes (F-013…F-016, Meilenstein-/
|
||||
Prioritäts-Evidenz, Mirror-Topologie-Prosa) in neckbeard eingefaltet
|
||||
wurde.
|
||||
|
||||
### Akzeptanzkriterien (verifizierbar)
|
||||
|
||||
1. **Deterministische Gates grün:** `scripts/validate.py` meldet auf dem
|
||||
migrierten Repo 0 Fehler; `scripts/gen_status.py --check` meldet
|
||||
STATUS.md aktuell.
|
||||
2. **Entscheidungen portiert:** alle Entscheidungen aus `decisions/`
|
||||
liegen als ADRs mit schema-konformem Frontmatter unter `docs/adr/` —
|
||||
11/11 validieren *(bei Gate-1-Freigabe 10; `decisions/0011` kam am
|
||||
2026-08-11 hinzu, siehe Nachtrag in Gate 3)*.
|
||||
3. **Ein Backlog:** die fünf Arbeitspunkte aus F-004 (OVERMIND-01,
|
||||
CFGMON-11/12/13, MATRIX-05) existieren als Issues im kanonischen
|
||||
System — 5/5; 0 offene „Nächste Schritte" in `hosts/` ohne
|
||||
Issue-Referenz.
|
||||
4. **Generierte statt behaupteter Zustand:** 0 handgepflegte Zählungen
|
||||
und „Stand"-Etiketten in kanonischen Dateien, wo ein Generat sie
|
||||
ersetzt; kein kanonisches Dokument widerspricht dem Werkzeugstand
|
||||
bei den Meilensteinen (M1–M5).
|
||||
5. **Muster → Mechanismus:** für jedes Driftmuster A–D benennt das
|
||||
Design mindestens einen deterministischen Check, und pro Muster feuert
|
||||
mindestens ein Check nachweislich auf dem Vor-Migrations-Stand — 4/4
|
||||
demonstriert.
|
||||
6. **Ernte dokumentiert:** 7/7 neckbeard-Lücken mit Disposition
|
||||
(eingefaltet / als Framework-Issue notiert / verworfen mit Grund);
|
||||
4/4 Works-well-Befunde mit benanntem Erhaltungsmechanismus oder
|
||||
begründetem Verzicht.
|
||||
|
||||
### Nicht-Ziele
|
||||
|
||||
- **Keine Historien-Umschreibung.** Die F-002/F-003-Remediation ist ein
|
||||
eigener Vorgang mit eigener bindender Auflage (Zuordnung im Stil von
|
||||
`shared/commit-zuordnung-2026-08-07.md`); diese Undertaking darf ihr
|
||||
nur nicht im Weg stehen.
|
||||
- **Kein Push** nach git.lab oder Gitea; der Branch bleibt lokal bis zur
|
||||
Freigabe durch sorb.
|
||||
- **Keine Änderung am neckbeard-Upstream.** Lücken werden hier
|
||||
dispositioniert; sie dort einzureichen ist ein eigener Akt.
|
||||
- **Kein Rollout in die fünf Komponenten-Repos** über das hinaus, was
|
||||
die Shared-Ruleset-Entscheidung (Gate 2) zwingend erfordert; der
|
||||
Rollout wird als Folge-Issues angelegt, nicht hier gebaut.
|
||||
- **Kein Forge-Zustand wird zerstört:** keine Löschung von
|
||||
GitLab-Issues, Labels, Meilensteinen oder dem Board durch die
|
||||
Migration selbst.
|
||||
- **`analysis/` bleibt eingefroren** — der Branch von Session 1 wird
|
||||
weder verändert noch umgebaut.
|
||||
- **Kein inhaltliches Umschreiben** des Host-/Visions-/Verfahrenswissens:
|
||||
Umzug, Frontmatter und Korrektur werkzeugwidersprechender Aussagen ja,
|
||||
Neuformulierung nein.
|
||||
|
||||
### Ankündigung
|
||||
|
||||
Das Management-Repo der Gruppe axion1337.chat zieht auf das
|
||||
neckbeard-Framework um. Die vorhandene Disziplin — Meilensteinpflicht,
|
||||
Status-Disziplin, ADR-Pflicht, AARs — bleibt erhalten, wandert aber von
|
||||
Stellen, die nur ein Mensch prüfen kann, an Stellen, die ein Skript
|
||||
prüft: Frontmatter statt Prosa, generiertes STATUS.md statt
|
||||
handgepflegter Zählungen, `validate.py` statt Konventionstreue aus dem
|
||||
Gedächtnis. Die vier Driftmuster des Feldtests bekommen je einen
|
||||
deterministischen Check, und was der alte Ansatz besser kann als
|
||||
neckbeard, wird zuerst ins Framework eingefaltet statt verworfen.
|
||||
Zielgruppe sind sorb und alle Agenten-Sessions, die künftig von einer
|
||||
Quelle starten, die sich nicht selbst widerspricht.
|
||||
|
||||
### UI
|
||||
|
||||
Keine UI beteiligt — Artefakte sind Markdown-Dateien, die Oberfläche
|
||||
bleibt GitLab/Obsidian/Editor. Mockups entfallen.
|
||||
|
||||
## Gate 2 — Architektur
|
||||
|
||||
### Gelesen (Pflichtlektüre vor den Optionen)
|
||||
|
||||
Alt-Ansatz: `CLAUDE.md`, `roadmap.md`, `decisions/README.md` und die
|
||||
tragenden Entscheidungen 0001, 0002, 0005, 0009, 0010,
|
||||
[verfahren/refinement.md](../../wiki/admin/refinement.md),
|
||||
[verfahren/stillstandspruefung.md](../../wiki/admin/stillstandspruefung.md),
|
||||
`.gitlab-ci.yml`, Auszüge aus `hosts/`. Neckbeard v0.1.1: AGENTS.md,
|
||||
WORKFLOW.md, ADR-0001…0004/0006, `schema.yaml`, `validate.py`,
|
||||
`gen_status.py`, Schöpfungs-AAR, `docs/wiki/index.md`. Session-1-Daten
|
||||
(lesend vom Analyse-Branch): `gitlab_issues.json` — 111 Issues, 71
|
||||
offen; **71/71 mit genau einem Meilenstein (M1 19 · M2 22 · M3 4 ·
|
||||
M4 12 · M5 14) und 71/71 mit genau einer Priorität** (low 32,
|
||||
medium 34, high 5). Die Entscheidungen 0003/0004/0006/0007/0008 werden
|
||||
bei der Portierung (Gate 4) vollständig gelesen; sie tragen keine
|
||||
Architekturfrage dieser Undertaking.
|
||||
|
||||
### Ernte, Teil 1 — die Fehlermuster beider Ansätze
|
||||
|
||||
Wo genau versagte der alte Ansatz, was hält neckbeard dagegen, und wo
|
||||
bleibt auch mit neckbeard ein Loch:
|
||||
|
||||
| Muster | Wurzel im Alt-Ansatz | Neckbeard-Gegenstück | Verbleibendes Loch → Mechanismus dieser Migration |
|
||||
|---|---|---|---|
|
||||
| **A** — Doku nicht nachgezogen (F-001, F-005, F-007, F-010, F-017) | Zustand steht als behauptete Zahl/Prosa an mehreren Stellen; nichts vergleicht | Generiertes STATUS.md (`gen_status.py --check` in CI), ADRs nie editiert nur abgelöst | Prosa, die *Forge*-Zustand behauptet, prüft neckbeard nicht → Drift-Prüfung Repo↔GitLab; Meilenstein-Satz als Schema-Enum (eine Quelle); „Stand"-Etiketten entfallen ersatzlos (git log antwortet) |
|
||||
| **B** — Regel repo-weit, Anwendung Teilmenge (F-002, F-003, F-008, F-011) | Regel gilt „für alle Repos", kein Artefakt zählt die Repos auf, kein Skript läuft über alle | **Lücke** — ADR-0001 endet an der Repo-Grenze | Komponenten-Artefakt + Abgleich gegen die zur Laufzeit gelesene Gruppenliste ([ADR-0013](../../adr/0013-gruppenregeln-kanonisch-mit-pruefung.md)); Git-Hygiene-Prüfung (12:00Z-Zeitstempel, kanonische Identität) über alle deklarierten Repos |
|
||||
| **C** — zwei Backlogs (F-004, F-009) | `hosts/`-Markdown hielt „Nächste Schritte" neben dem Board | In-Repo-Issues, ein Ort | Wiki-Seiten können wieder Aufgabenprosa ansammeln → Prüfregel: Aufgaben-Marker („Nächster Schritt", offene Checkboxen) in Wiki-Seiten ohne Issue-Verweis sind ein Befund |
|
||||
| **D** — Artefakte ohne Eigentümer überleben (F-006, F-012) | Branches/SHA-Zitate hat niemand je gelesen | `warn_if_orphan` nur für Wiki-Seiten | Branch-Hygiene (Alter/Divergenz verwaister Branches) und SHA-Auflösung inkl. Zuordnungstabelle in der Prüf-Familie; Refinement-Agenda erhält den Punkt |
|
||||
|
||||
Die sieben neckbeard-Lücken, Disposition (Akzeptanzkriterium 6, 7/7):
|
||||
|
||||
| # | Lücke | Disposition |
|
||||
|---|---|---|
|
||||
| 1 | Viele Repos, ein Regelwerk | **Eingefaltet:** [ADR-0013](../../adr/0013-gruppenregeln-kanonisch-mit-pruefung.md) (Pointer + Prüfung) |
|
||||
| 2 | Kein Komponenten-Artefakt | **Eingefaltet:** Schema-Typ `component`, `docs/components/` (ADR-0013) |
|
||||
| 3 | Kein Meilenstein-Konzept | **Eingefaltet:** Pflichtfeld `milestone` im Issue-Schema ([ADR-0012](../../adr/0012-issues-im-repo-gitlab-als-spiegel.md)) |
|
||||
| 4 | SHA-Zitate unaufgelöst | **Eingefaltet (projektseitig):** Prüfskript nach Vorbild `inv_shas.py`; Upstream-Kandidat |
|
||||
| 5 | Git-Hygiene außerhalb des Blickfelds | **Eingefaltet (projektseitig):** Hygiene-Prüfung in der CI-Familie; Upstream-Kandidat |
|
||||
| 6 | Externe Link-Ziele ungeprüft | **Teilweise eingefaltet:** Sperrliste stillgelegter Ziele (toter Gitea-Tracker, F-005) als deterministische Prüfung; echte Erreichbarkeitsprüfung **verworfen** (netzabhängig, nichtdeterministisch — widerspricht validate-Philosophie) |
|
||||
| 7 | Prioritätsfeld als YAGNI verworfen | **Eingefaltet:** Pflichtfeld `priority` — der Feldtest liefert die Evidenz (71/71, klar getrennt vom Meilenstein), die das Schöpfungs-AAR fürs Wiedervorlegen verlangte |
|
||||
| +8 | *(neu, diese Session)* `validate.py` lehnt Verzeichnis-Links ab | Migration ersetzt Verzeichnis- durch Datei-Ziele; Upstream-Kandidat (Meinungsfrage) |
|
||||
| +9 | *(neu)* Kein definierter Ort für Projektregeln im übernommenen AGENTS.md | Projektregeln als ausgewiesener eigener Abschnitt unter den unveränderten Upstream-Abschnitten; Upstream-Kandidat |
|
||||
|
||||
### Ernte, Teil 2 — Wert des Alt-Ansatzes, eingefaltet (4/4 + Zusatz)
|
||||
|
||||
| Wert | Erhaltungsmechanismus |
|
||||
|---|---|
|
||||
| **F-014** Issue-Hygiene (Meilensteinpflicht, eine Priorität, ein Status, WIP-Limit, keine ID-Wiederverwendung) | Wird von Konvention zu Schema: `milestone`/`priority` Pflichtfelder, Status-Enum, WIP-Limit als Validator-Regel, Duplikat-ID-Prüfung existiert in `validate.py` bereits; Board bleibt via Spiegel erhalten (ADR-0012) |
|
||||
| **F-013** Mirror-Topologie mit Begründung, Gegenargument, Rettungspfad | Alt-ADRs 0001/0004 werden unverändert portiert; die „Warum zwei Orte"-Prosa und der Rettungspfad ziehen als Wiki-Seiten um; Mirror-Sync bleibt Stillstandsprüfung |
|
||||
| **F-015** Rewrite-Zuordnung, 251/251 verifiziert | `shared/commit-zuordnung-2026-08-07.md` → `docs/sources/` (unveränderlich, agentenschreibgeschützt); SHA-Prüfung löst über die Tabelle auf; die Zuordnungs-Auflage für künftige Rewrites steht im portierten ADR-0009 |
|
||||
| **F-016** Redliche Selbstdokumentation | „Redlichkeit"-Regeln ziehen in den Projektabschnitt von AGENTS.md; AAR-/Retro-Kultur bleibt (AARs → `docs/aar/`, Retro-Protokolle → `docs/sources/`) |
|
||||
| Stillstandsprüfungs-Prinzipien | Bleiben wörtlich: Prüfungen nur aus realen Fällen; „kann nicht prüfen" ist Befund, nicht Skip; Abbruch statt stillem Überspringen; Projektliste zur Laufzeit. Die neuen Gruppen-Prüfungen (ADR-0013, Hygiene, Drift) treten dieser Familie bei |
|
||||
| Board-Pflege-Rechte (Zusage-Spalten nur sorb) | Prozessregel im AGENTS.md-Projektabschnitt; Refinement-Ablauf zieht als Wiki-Seite um und instanziiert die WORKFLOW-Agenda (Board rechts-nach-links, Nachziehen, Entscheidungsvorlagen mit Empfehlung, Datumspflicht) |
|
||||
| ADR-Pflicht bei dauerhaften Ausnahmen | Übernommen in den Projektabschnitt — neckbeard kennt diese Regel selbst nicht (Upstream-Kandidat) |
|
||||
|
||||
### Zielarchitektur
|
||||
|
||||
**Migrationslandkarte** (alt → neu; Inhalte unverändert, sofern nicht
|
||||
werkzeugwidersprechend — Nicht-Ziel „kein Umschreiben"):
|
||||
|
||||
| Alt | Neu |
|
||||
|---|---|
|
||||
| `CLAUDE.md` | Ein-Zeilen-Pointer; Regeln → `AGENTS.md` (Upstream-Abschnitte wörtlich + Abschnitt „Gruppenregeln"); Karpathy-Block wortgleich → `docs/sources/regelwerk/karpathy-guidelines.md`, aus AGENTS.md zitiert *(freigegeben von sorb, 2026-08-11)* |
|
||||
| `decisions/0001…0011` | `docs/adr/0001…0011`, Frontmatter ergänzt, Text unverändert; `decisions/` entfällt, Verweise nachgezogen |
|
||||
| `roadmap.md` | Bleibt als Linien/Reihenfolge-Prosa; alle Zählungen und „Stand"-Blöcke raus (→ generiertes STATUS.md); M5 statt „offene Frage" (F-001) |
|
||||
| `verfahren/aar/*` (5) | `docs/aar/*`, Frontmatter (`open`/`harvested` nach Retro-Lage) |
|
||||
| `verfahren/retro/*` | `docs/sources/protokolle/*` (unveränderliche Protokolle) |
|
||||
| `verfahren/refinement.md` | `docs/wiki/admin/refinement.md` |
|
||||
| `verfahren/deploy-uebergabe.md` | `docs/wiki/deployment/deploy-uebergabe.md` |
|
||||
| `verfahren/stillstandspruefung.md` | `docs/wiki/admin/stillstandspruefung.md` |
|
||||
| `verfahren/textbloecke.md` | `docs/wiki/admin/textbloecke.md` (Pfade angepasst) |
|
||||
| `verfahren/issue-migration/` | `docs/sources/migration/issue-migration/` |
|
||||
| `verfahren/aar-vorlage.md` | ersetzt durch neckbeards `docs/aar/template.md` |
|
||||
| `hosts/*` (4) | `docs/wiki/admin/<host>.md`; offene Arbeitspunkte → Issues (F-004, 5/5) |
|
||||
| `vision/*` (3) | `docs/wiki/vision/*` (neue Wiki-Area `vision` — Alt-Wert „eine Datei je Linie", altes ADR-0005) |
|
||||
| `shared/branding.md`, `lab-netzwerk.md`, `zone-axion1337.md` | `docs/wiki/architecture/*` |
|
||||
| `shared/commit-zuordnung-2026-08-07.md` | `docs/sources/migration/commit-zuordnung-2026-08-07.md` |
|
||||
| — *(neu)* | `docs/sources/upstream/neckbeard-v0.1.1/` — gepinnte Originale als Baseline für den Drift-Check |
|
||||
| — *(neu)* | `PROJECT.md` ✓, `WORKFLOW.md` (wörtlich v0.1.1), `schema.yaml` (v0.1.1 + ausgewiesene Erweiterungen), `STATUS.md` (generiert), `docs/components/` (6 Deklarationen: 5 Komponenten + management; `game-operating`/`gameserver` als `external`), `docs/issues/` (importierte offene management-Issues + F-004-Nachzügler) |
|
||||
| `scripts/stillstandspruefung.py`, `ci/` | Bleiben; dazu `validate.py`, `gen_status.py` (v0.1.1) und die neuen Prüfskripte; `.gitlab-ci.yml` erhält einen Offline-Job `validate` (jeder Push) neben der geplanten Stillstandsprüfung |
|
||||
|
||||
**Prüf-Architektur — zwei Familien, scharfe Grenze:**
|
||||
|
||||
- **Offline & deterministisch** (`validate.py`, `gen_status.py --check`,
|
||||
SHA-Auflösung, Wiki-Aufgabenmarker, Sperrlisten-Check): läuft bei
|
||||
jedem Push, braucht nur den Baum. Kein Netz, keine Uhrzeit.
|
||||
- **Verbund & Laufzeit** (Stillstandsprüfungs-Familie: Mirror-Sync,
|
||||
Issue-Drift Repo↔GitLab, Pointer-Präsenz, Gruppenliste↔`docs/components/`,
|
||||
Git-Hygiene über die Gruppe): geplant/manuell in der Lab-CI, Token
|
||||
über maskierte Variablen, **Abbruch statt stillem Skip**, Befund =
|
||||
rote Pipeline = Alarmanlage.
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
S[Session-Start] --> A[CLAUDE.md → AGENTS.md<br/>+ PROJECT.md + STATUS.md]
|
||||
A --> W[Arbeit nach Gates<br/>Artefakte in docs/]
|
||||
W --> C[Commit 12:00Z]
|
||||
C --> V{CI: validate.py +<br/>gen_status --check}
|
||||
V -- rot --> W
|
||||
V -- grün --> M[Spiegel-Skript<br/>dry-run → sorb triggert]
|
||||
M --> B[GitLab-Board/Meilensteine<br/>= Ansicht, nicht Wahrheit]
|
||||
B --> R[Refinement sonntags<br/>Board + STATUS.md]
|
||||
R --> W
|
||||
P[Stillstandsprüfung + Gruppen-Checks<br/>geplant, Lab-CI] -. Befund = Issue .-> R
|
||||
```
|
||||
|
||||
### Entscheidungen
|
||||
|
||||
Die zwei tragenden Richtungsentscheidungen stehen als ADRs (Status
|
||||
`proposed`, werden mit diesem Gate wirksam):
|
||||
|
||||
- **[ADR-0012](../../adr/0012-issues-im-repo-gitlab-als-spiegel.md)** —
|
||||
Issues im Repo kanonisch (Management-Scope), GitLab als
|
||||
deterministisch bespielter Spiegel; Optionen A/B/C abgewogen im ADR.
|
||||
- **[ADR-0013](../../adr/0013-gruppenregeln-kanonisch-mit-pruefung.md)** —
|
||||
Gruppenregeln kanonisch hier, Komponenten tragen Pointer, ein
|
||||
Komponenten-Artefakt macht die Gruppe prüfbar; Kopie/Submodule
|
||||
verworfen im ADR.
|
||||
|
||||
Feature-lokale Entscheidungen (bleiben hier):
|
||||
|
||||
1. **Framework-Dateien wörtlich** übernehmen (AGENTS.md-Abschnitte 1–5,
|
||||
WORKFLOW.md, Templates, Skripte) — jede Abweichung vom Upstream
|
||||
bleibt per Diff gegen v0.1.1 sichtbar; Projektspezifika leben
|
||||
ausschließlich im ausgewiesenen AGENTS-Abschnitt, in ADRs, Wiki und
|
||||
`schema.yaml`-Erweiterungen.
|
||||
2. **Issue-Nummern:** GitLab-iid = Datei-id für Importierte; neue Issues
|
||||
zählen ab Maximum weiter; `gitlab_iid`-Feld hält die Spiegelung.
|
||||
Keine dritte Nummernwelt, keine ID-Wiederverwendung.
|
||||
3. **Status-Enum erweitert** um `next` und `waiting` (Grund-Pflicht bei
|
||||
`waiting`) — die Board-Spalten sind belegter Alt-Wert; ein Mapping
|
||||
auf nur `open/in-progress` würde die einzige Zusage-Semantik
|
||||
(`status:next`) wegwerfen.
|
||||
4. **Slugs werden nicht umbenannt** (F-008): Rename = Forge-Eingriff,
|
||||
eigenes Issue; das Komponenten-Artefakt dokumentiert den Ist-Stand.
|
||||
5. **Verzeichnis-Links** in Prosa werden auf Datei-Ziele umgestellt
|
||||
(Lücke +8).
|
||||
6. **`analysis/` und `drafts/`** des Analyse-Branches bleiben dort;
|
||||
nichts davon wird auf diesen Branch geholt.
|
||||
7. **`docs/sources/` wird nach Quellenart untergliedert** (Vorschlag
|
||||
sorb, 2026-08-11): `regelwerk/` (wortgleiche Regeltexte),
|
||||
`upstream/` (gepinnte Framework-Originale), `protokolle/`
|
||||
(Retro-/Workshop-Protokolle), `migration/` (Zuordnungen,
|
||||
Umzugsunterlagen). Kriterium bleibt *lebendig → Wiki, unveränderlich
|
||||
→ sources*; AARs sind Artefakte mit Lebenszyklus, keine Quellen.
|
||||
8. **Drift-Check gegen Upstream-Baseline:** die v0.1.1-Originale liegen
|
||||
unter `docs/sources/upstream/neckbeard-v0.1.1/`; ein Offline-Check
|
||||
vergleicht die Instruktionsdateien (CLAUDE.md-Pointer, AGENTS.md bis
|
||||
zur Projektabschnitts-Marke, WORKFLOW.md, Templates) byteweise.
|
||||
Stilles Umschreiben durch eine Session wird damit roter Befund;
|
||||
Framework-Upgrade = bewusste Baseline-Aktualisierung. `schema.yaml`
|
||||
und die Skripte sind **erklärt projekterweitert** — Original liegt
|
||||
zur Diffbarkeit bei, wird aber nicht byte-erzwungen.
|
||||
9. **AGENTS.md-Änderungsschutz:** die alte Fußzeilen-Regel zieht in den
|
||||
Projektabschnitt um — Änderungen an AGENTS.md nur mit sorb
|
||||
abgestimmt.
|
||||
10. **Issue-Import liest Beschreibungstexte** der offenen
|
||||
management-Issues read-only über den Token (freigegeben von sorb,
|
||||
2026-08-11); Kommentare bleiben auf GitLab, der Tokenwert erscheint
|
||||
nirgends.
|
||||
|
||||
### Constraints
|
||||
|
||||
- Mirror-Topologie unangetastet: Flux-Quelle bleibt Gitea, keine
|
||||
direkten Gitea-Pushes, Kanonisierungs-Verfahren gilt weiter.
|
||||
- Kein API-Schreibzugriff ohne menschlichen Trigger; das Spiegel-Skript
|
||||
hat `--dry-run` als Default. Diese Session pusht nichts.
|
||||
- Commit-Konventionen (englisch, 12:00:00 UTC, kanonische Identität)
|
||||
gelten für jeden Migrations-Commit.
|
||||
- Artefaktsprache Deutsch (`PROJECT.md`), Upstream-Framework-Texte
|
||||
bleiben englisch — der Diff-Abgleich gegen v0.1.1 wiegt schwerer als
|
||||
Sprachreinheit.
|
||||
- Secrets-Regeln unverändert (Token nur per Pfad/maskierter Variable).
|
||||
- Die Historien-Remediation (F-002/F-003) bleibt draußen; jedes künftige
|
||||
Rewrite trägt die Zuordnungs-Auflage (portiertes ADR-0009).
|
||||
|
||||
### Rückmeldungen an neckbeard (Kandidaten, eigener Akt — nicht Teil dieser Undertaking)
|
||||
|
||||
Lücken 1–5 und 7 mit Feldtest-Evidenz, dazu +8 (Verzeichnis-Links), +9
|
||||
(Ort für Projektregeln), die fehlende ADR-Pflicht bei dauerhaften
|
||||
Ausnahmen, und als Erfahrungswert: die Stillstandsprüfungs-Prinzipien
|
||||
(Prüfungen nur aus realen Fällen; Abbruch statt Skip) als Muster für
|
||||
eine künftige Laufzeit-Prüf-Familie neben `validate.py`.
|
||||
|
||||
## Gate 3 — Programm-Design
|
||||
|
||||
### Dateiorte (vollständig)
|
||||
|
||||
**Wurzel — neu:** `AGENTS.md` (Upstream §1–5 wörtlich, dann Marke
|
||||
`<!-- projektabschnitt -->`, dann „§6 Gruppenregeln"), `WORKFLOW.md`
|
||||
(wörtlich v0.1.1), `schema.yaml` (v0.1.1 + Erweiterungen, im Kopf
|
||||
ausgewiesen), `STATUS.md` (generiert).
|
||||
**Wurzel — geändert:** `CLAUDE.md` → Pointer (wörtlich v0.1.1),
|
||||
`roadmap.md` (Zahlen/„Stand" raus, M5 rein, Datei-Links),
|
||||
`README.md` (Pfade/Struktur nachgezogen, Datei-Links),
|
||||
`.gitlab-ci.yml` (+ Job `validate`, Stage `pruefen`, bei jedem Push).
|
||||
**Wurzel — entfällt (git mv):** `decisions/`, `hosts/`, `verfahren/`,
|
||||
`vision/`, `shared/`.
|
||||
|
||||
**`docs/adr/`:** `0001…0011` portiert (Frontmatter ergänzt; Datum =
|
||||
Original-Datum; Body unverändert bis auf umgezogene Link-Ziele),
|
||||
`0012`/`0013` (accepted), `template.md` (v0.1.1).
|
||||
**`docs/aar/`:** die 6 AARs aus `verfahren/aar/` (Dateinamen bleiben,
|
||||
Frontmatter: die vier vom 2026-08-01/02 `harvested` — von der Retro
|
||||
2026-08-09 geerntet; `2026-08-09-refinement-und-betrieb.md` und
|
||||
`2026-08-11-apo-calls-profile-zeile.md` `open`),
|
||||
`template.md` (v0.1.1; ersetzt `aar-vorlage.md`).
|
||||
**`docs/issues/`:** Import aller offenen management-Issues als
|
||||
`NNNN-slug.md` (NNNN = GitLab-iid, vierstellig; Slug deterministisch
|
||||
aus dem Titel: Kleinbuchstaben, Umlaute ae/oe/ue/ss, sonst `-`,
|
||||
Alt-IDs bleiben im Titel), plus 5 neue Issues für die
|
||||
F-004-Arbeitspunkte (IDs ab max(iid)+1), `template.md` (v0.1.1).
|
||||
**`docs/components/`:** `management.md`, `threadnet-call.md`,
|
||||
`thread-net-git.md`, `threadnet-operating.md`,
|
||||
`axion1337.chat-gitops.md`, `ThreadNet-Web.md` (Dateiname = Slug,
|
||||
buchstabengetreu), dazu `game-operating.md`, `gameserver.md`
|
||||
(`phase: external`).
|
||||
**`docs/wiki/`:** `index.md` (projektangepasst: Area-Tabelle + `vision`;
|
||||
nicht in der Baseline), `admin/`: `cfgmon.md`, `game.md`, `matrix.md`,
|
||||
`overmind.md`, `refinement.md`, `stillstandspruefung.md`,
|
||||
`textbloecke.md`; `deployment/`: `deploy-uebergabe.md`;
|
||||
`architecture/`: `branding.md`, `lab-netzwerk.md`, `zone-axion1337.md`;
|
||||
`vision/`: `axion1337-chat.md`, `homelab.md`, `threadnet.md`.
|
||||
**`docs/sources/`:** `regelwerk/karpathy-guidelines.md`;
|
||||
`upstream/neckbeard-v0.1.1/` (AGENTS.md, CLAUDE.md, WORKFLOW.md,
|
||||
schema.yaml, die 4 Templates, validate.py, gen_status.py, dazu
|
||||
`HERKUNFT.md` mit Tag/SHA); `protokolle/retro-2026-08-09.md`;
|
||||
`migration/commit-zuordnung-2026-08-07.md`,
|
||||
`migration/issue-migration/README.md`,
|
||||
`migration/import_issues.py` + `migration/issue-import-protokoll.md`
|
||||
(Einmal-Werkzeug und sein Protokoll — Aufzeichnung, kein Dauerbetrieb).
|
||||
**`scripts/`:** `validate.py` (v0.1.1 + 3 Regeln), `gen_status.py`
|
||||
(v0.1.1 + Meilenstein-/Prioritätsspalten und -verteilung),
|
||||
`pruefe_upstream_drift.py` (neu), `pruefe_prosa.py` (neu),
|
||||
`gruppenpruefung.py` (neu), `spiegel_issues.py` (neu);
|
||||
`stillstandspruefung.py` unangetastet.
|
||||
|
||||
### Schema-Erweiterungen (exakt)
|
||||
|
||||
```yaml
|
||||
# issue — zusätzlich:
|
||||
required: [type, id, status, created, milestone, priority]
|
||||
status: { enum: [open, next, in-progress, waiting, done, rejected] }
|
||||
milestone: { enum: [M1, M2, M3, M4, M5] }
|
||||
priority: { enum: [high, medium, low] }
|
||||
due: { kind: date, nullable: true }
|
||||
host: { enum: [cfgmon, overmind, matrix, game], nullable: true }
|
||||
area: { enum: [security, infrastructure, database, element], nullable: true }
|
||||
wartegrund: { kind: str, nullable: true }
|
||||
gitlab_iid: { pattern: "^\\d+$", nullable: true }
|
||||
rules: [waiting_requires_reason] # + global: wip_limit
|
||||
# component — neuer Typ:
|
||||
component:
|
||||
dir: "docs/components"
|
||||
filename: "^[A-Za-z0-9.-]+\\.md$"
|
||||
required: [type, slug, anzeigename, phase]
|
||||
fields:
|
||||
slug: { kind: str } # rule: slug_matches_filename
|
||||
anzeigename: { kind: str }
|
||||
phase: { enum: [active, staged, external] }
|
||||
gitlab: { kind: str }
|
||||
mirror: { kind: str, nullable: true }
|
||||
related: { kind: links }
|
||||
rules: [slug_matches_filename]
|
||||
# wiki-page.area — Enum + vision
|
||||
```
|
||||
|
||||
### Signaturen (keine Rümpfe)
|
||||
|
||||
```text
|
||||
validate.py [repo-root] # + Regeln: wip_limit (≤2 in-progress, repoweit),
|
||||
# waiting_requires_reason, slug_matches_filename
|
||||
gen_status.py [--check] [repo-root] # Issues-Tabelle + Spalten milestone/priority
|
||||
# + Verteilungszeile je Meilenstein
|
||||
pruefe_upstream_drift.py [repo-root] # Byte-Vergleich Arbeitsdatei ↔ sources/upstream;
|
||||
# AGENTS.md: Präfix bis Marke; exit 1 bei Abweichung
|
||||
pruefe_prosa.py [repo-root] # (a) SHA-Zitate in docs/** + Wurzel-*.md auflösen
|
||||
# (git cat-file, sonst Zuordnungstabelle, sonst FEHLER)
|
||||
# (b) Aufgabenmarker in docs/wiki/** ohne Issue-Verweis
|
||||
# (c) Sperrliste stillgelegter URL-Muster (toter Gitea-Tracker)
|
||||
gruppenpruefung.py # Lab-CI, Token aus Umgebung, Abbruch ohne Token:
|
||||
# Gruppenliste (Laufzeit) ↔ docs/components/;
|
||||
# Pointer-Präsenz je active/staged-Komponente;
|
||||
# Issue-Drift docs/issues ↔ GitLab (Titel/Status/
|
||||
# Meilenstein/Priorität); Meilenstein- und
|
||||
# Prioritätspflicht über ALLE offenen Gruppen-Issues
|
||||
# (realer Fall: gitops#61, siehe Nachtrag);
|
||||
# Git-Hygiene (Commits nach
|
||||
# 2026-08-07 ≠ 12:00:00Z oder fremde Identität = Befund)
|
||||
spiegel_issues.py [--ausfuehren] # Default Dry-Run: druckt geplante API-Aufrufe;
|
||||
# --ausfuehren nur durch sorb; Repo → GitLab, nie zurück
|
||||
```
|
||||
|
||||
**CI-Fluss:** Job `validate` (jeder Push, offline):
|
||||
`validate.py && gen_status.py --check && pruefe_upstream_drift.py &&
|
||||
pruefe_prosa.py`. Job `stillstandspruefung` (geplant/manuell) wie
|
||||
bisher; `gruppenpruefung` daneben, gleiche Regeln (rot = Alarm,
|
||||
Abbruch statt Skip).
|
||||
|
||||
### Was die Prüfungen zusichern (inkl. Muster-Demonstration, Kriterium 5)
|
||||
|
||||
| Prüfung | Zusicherung / Demo |
|
||||
|---|---|
|
||||
| Negativtests (Scratch-Bäume, je Regel einer) | 3× in-progress → Fehler; `waiting` ohne `wartegrund` → Fehler; Component-Slug ≠ Dateiname → Fehler; 1 Byte Abweichung in WORKFLOW.md → Fehler; Schöpfungs-AAR-Lehre: grüner Validator ohne Negativtest zählt nicht |
|
||||
| **Muster A** | Meilenstein-Abgleich gegen den eingefrorenen Session-1-Export: Alt-`CLAUDE.md` („M1–M4") ↔ Export (M5 existiert) → feuert |
|
||||
| **Muster B** | Git-Hygiene über die lokalen Komponenten-Klone → feuert (Erwartung: die 237 Echtzeit-Commits aus F-002) |
|
||||
| **Muster C** | `pruefe_prosa.py` auf dem Vor-Migrations-Stand von `hosts/` → feuert auf die 5 F-004-Punkte; nach Migration: 0 |
|
||||
| **Muster D** | SHA-Auflösung auf Vor-Migrations-Stand → feuert auf die 6 verwaisten Zitate aus F-012; Auflösung über die Zuordnungstabelle nachgewiesen |
|
||||
|
||||
### DO NOT CHANGE
|
||||
|
||||
- Der Analyse-Branch und alles unter `analysis/`.
|
||||
- Substanz der portierten Texte: ADR-Bodies, AARs, Retro, Zuordnung,
|
||||
Karpathy-Block, Hosts-/Visions-Prosa — nur Umzug, Frontmatter,
|
||||
Link-Ziele; inhaltliche Korrektur **nur** wo ein Dokument dem
|
||||
Werkzeugstand widerspricht (roadmap M5, Alt-CLAUDE-Regeln gehen in
|
||||
AGENTS §6 in korrigierter Fassung).
|
||||
- `scripts/stillstandspruefung.py`, `ci/lab-ca-chain.crt`,
|
||||
`.gitlab/issue_templates/` — unangetastet.
|
||||
- GitLab-Zustand: kein Issue, Label, Meilenstein, Board wird verändert;
|
||||
`spiegel_issues.py` läuft in dieser Undertaking nur als Dry-Run.
|
||||
- Kein `git push`; Tokenwert erscheint in keiner Ausgabe.
|
||||
- Upstream-Framework-Texte §1–5 / WORKFLOW / Templates: byte-treu.
|
||||
|
||||
### Wackligste Annahmen (benannt, Stand Gate 3)
|
||||
|
||||
1. **AAR-Erntestatus**: „die vier alten AARs sind geerntet" schließe ich
|
||||
aus der Retro-Existenz, nicht aus einer Erntemarke — sorb kann das
|
||||
im Refinement kippen.
|
||||
2. **Enums aus dem Ist-Stand eingefroren** (host/area/M1–M5): jeder
|
||||
neue Host oder Meilenstein braucht künftig einen Schema-Commit.
|
||||
Gewollt (sichtbare Änderung), aber Reibung.
|
||||
3. **Slug-Erzeugung aus deutschen Titeln** muss deterministisch und
|
||||
kollisionsfrei sein; bei Kollision entscheidet die iid, nicht der
|
||||
Slug.
|
||||
4. **Git-Hygiene per API vs. lokale Klone**: die Demo läuft auf den
|
||||
lokalen Klonen; die CI-Fassung per API kann bei großen Historien
|
||||
paginieren müssen — begrenzt auf Commits seit 2026-08-07.
|
||||
5. **Verdichtung von Alt-CLAUDE.md nach AGENTS §6**: Welche Sätze
|
||||
Regelrang behalten und welche ins Wiki wandern, ist Urteilssache;
|
||||
Volltext überlebt in ADRs/Wiki/sources, aber eine tragende Nuance
|
||||
könnte aus dem Immer-geladen-Teil fallen.
|
||||
6. **`gen_status.py`-Fork-Tiefe**: je mehr das Generat zeigt, desto
|
||||
weiter entfernt es sich vom Upstream; gewählt ist die kleinste
|
||||
Erweiterung, die die Roadmap-Zahlen ersetzt.
|
||||
|
||||
### Nachtrag 2026-08-11 — die Realität lief nach Gate-3-Freigabe weiter
|
||||
|
||||
Hinweis von sorb bei der Gate-3-Freigabe, per Fetch und Live-API
|
||||
(read-only) verifiziert:
|
||||
|
||||
- **management `main` +3 Commits:** AAR
|
||||
`2026-08-11-apo-calls-profile-zeile.md` (+ Nachtrag) und — kritisch —
|
||||
**`decisions/0011`** (Enrollment-Localpart-Kollision). Das alte Schema
|
||||
zählt parallel weiter; die Session-ADRs kollidierten mit der Nummer
|
||||
und wurden zu **0012/0013** umnummeriert (genau die Duplikat-ID-Klasse,
|
||||
die `validate.py` künftig mechanisch meldet). Branch auf
|
||||
`origin/main` rebasiert.
|
||||
- **gitops +2 Commits** (MAS-Fix, Runbook); beide und alle drei
|
||||
management-Commits halten die Hygiene-Regeln (12:00:00Z, kanonische
|
||||
Identität) — geprüft.
|
||||
- **Live-Backlog: 72 offen** (Import zählt beim Lauf, nicht aus diesem
|
||||
Text). **gitops#61 trägt keinen Meilenstein** und das neue Label
|
||||
`area:authentik` — die 100%-Meilenstein-Disziplin (F-014) ist binnen
|
||||
zwei Tagen real gerissen. Konsequenz: `gruppenpruefung.py` prüft die
|
||||
Meilenstein-/Prioritätspflicht über alle offenen Gruppen-Issues (der
|
||||
reale Fall, den die Stillstandsprüfungs-Regel für neue Prüfungen
|
||||
verlangt, existiert hiermit). Management-Scope: 26 offene Issues,
|
||||
iids 1–32.
|
||||
- Zahlen im Dokument nachgezogen: 11 Alt-ADRs, 6 AARs,
|
||||
Akzeptanzkriterium 2 = 11/11. Die Enums bleiben, wie in Annahme 2
|
||||
benannt, aus dem management-Scope abgeleitet; `area:authentik` liegt
|
||||
außerhalb (gitops) und wird erst bei dessen Adoption Schema-Thema.
|
||||
|
||||
## Gate 4 — Vertikale Slices
|
||||
|
||||
Jeder Slice endet mit Nachweis, Status und **STOP**.
|
||||
|
||||
**Slice 1 — Tracer Bullet: die Framework-Kette läuft Ende-zu-Ende.**
|
||||
Baseline (`docs/sources/upstream/neckbeard-v0.1.1/` + `HERKUNFT.md`),
|
||||
Karpathy-Block wortgleich nach `docs/sources/regelwerk/`, `AGENTS.md`
|
||||
(§1–5 byte-treu + §6 Gruppenregeln), `CLAUDE.md`-Pointer, `WORKFLOW.md`,
|
||||
Templates, erweitertes `schema.yaml`, `validate.py` (+3 Regeln),
|
||||
`gen_status.py` (Fork), `pruefe_upstream_drift.py`, generiertes
|
||||
`STATUS.md`, CI-Job `validate`, README-Verzeichnis-Link entschärft.
|
||||
*Verify:* validate 0 Fehler · gen_status --check aktuell · Drift-Check
|
||||
grün · vier Negativtests feuern · Baseline byte-identisch zur Referenz.
|
||||
|
||||
**Slice 2 — ADR-Port.** `decisions/0001…0011` → `docs/adr/` mit
|
||||
Frontmatter, Verweise nachgezogen, `decisions/` entfällt.
|
||||
*Verify:* 11/11 validieren, Duplikat-ID-Prüfung greift, validate grün.
|
||||
|
||||
**Slice 3 — Wiki, Sources, AARs.** `verfahren/`/`hosts/`/`vision/`/
|
||||
`shared/` an ihre Zielorte, Wiki-Index, `pruefe_prosa.py`; Demos
|
||||
Muster C (F-004-Punkte auf Vor-Stand) und D (6 verwaiste SHAs).
|
||||
*Verify:* validate + pruefe_prosa grün auf Endstand, Demos feuern auf
|
||||
Vor-Stand, alte Wurzelordner leer.
|
||||
|
||||
**Slice 4 — Issue-Import.** `import_issues.py` liest die offenen
|
||||
management-Issues live (read-only), 26+ Dateien + 5 F-004-Issues,
|
||||
`roadmap.md` verliert Zahlen an STATUS.md.
|
||||
*Verify:* alle Issue-Dateien validieren (Pflicht-Meilenstein/-Priorität),
|
||||
Import-Protokoll unter sources/migration, Muster-C-Endstand = 0.
|
||||
|
||||
**Slice 5 — Komponenten, Gruppenprüfung, Spiegel.** 8
|
||||
Komponenten-Deklarationen, `gruppenpruefung.py` (+ CI-Job),
|
||||
`spiegel_issues.py` (Dry-Run-Demo); Demos Muster A (eingefrorener
|
||||
Export ↔ Alt-CLAUDE) und B (Hygiene über lokale Klone), Live-Befund
|
||||
gitops#61.
|
||||
*Verify:* Dry-Run-Ausgabe plausibel, Demos feuern, kein API-Write.
|
||||
|
||||
Alle fünf Slices sind mit Nachweis und STOP abgenommen worden
|
||||
(Freigaben sorb, 2026-08-11); die Commits `e36ed33`…`865d761` tragen
|
||||
die Evidenz je Slice im Commit-Text.
|
||||
|
||||
## Gate 5 — Closeout (AAR)
|
||||
|
||||
### Geplant
|
||||
|
||||
Gate 0–5 nach WORKFLOW.md; Zwei-Wege-Ernte vor Übernahme (bindende
|
||||
Vorgabe der Session-1-Übergabe); fünf Slices; sechs Akzeptanzkriterien.
|
||||
|
||||
### Tatsächlich
|
||||
|
||||
Alle Gates und Slices wie geplant, mit vier realitätsgetriebenen
|
||||
Abweichungen:
|
||||
|
||||
1. **Die Realität lief während der Undertaking weiter** (Hinweis sorb
|
||||
bei Gate-3-Freigabe): `main` +3 Commits mit `decisions/0011` →
|
||||
Nummernkollision mit den Session-ADRs, Umnummerierung auf 0012/0013,
|
||||
Rebase; gitops#61 entstand **ohne Meilenstein** und riss die
|
||||
100%-Disziplin aus F-014 binnen zwei Tagen — es wurde der reale Fall
|
||||
für die neue gruppenweite Pflicht-Prüfung.
|
||||
2. **F-004 war feiner als der Befund:** MATRIX-05 seit 2026-08-01
|
||||
erledigt (kein Issue nötig — „Alles *Offene* ist ein Issue"),
|
||||
CFGMON-12/13 bereits per git.lab-Issues verfolgt (nur die toten
|
||||
Gitea-Links verdeckten das). Statt 5/5 neuen Issues: 2 neue (0033,
|
||||
0034), 2 verifizierte Verweise, 1 begründeter Verzicht — mit sorb
|
||||
abgestimmt; Details im
|
||||
[Import-Protokoll](../../sources/migration/issue-import-protokoll.md).
|
||||
3. **F-005 war größer als der Befund:** nicht ein toter Tracker-Link,
|
||||
sondern acht, quer durch Host-Seiten und einen importierten
|
||||
Issue-Fußtext; zwei per Live-Titelabgleich verifiziert umgezogen,
|
||||
sechs zu ehrlichen Historien-Zitaten entschärft.
|
||||
4. **Hex ist nicht gleich Git-SHA:** die SHA-Prüfung fand eine
|
||||
Authentik-uid und zwei Alertmanager-Silence-IDs — gelöst über die
|
||||
kuratierte Ausnahmenliste mit Grund je Zeile statt über eine
|
||||
schlauere Heuristik.
|
||||
|
||||
Akzeptanzkriterien: **6/6 erfüllt** — (1) alle deterministischen Gates
|
||||
grün; (2) 11/11 ADRs portiert; (3) 0 issuelose Arbeitspunkte im Wiki,
|
||||
F-004-Disposition dokumentiert; (4) 0 Handzählungen, kein Dokument
|
||||
widerspricht dem Werkzeugstand M1–M5; (5) 4/4 Muster-Demos gefeuert
|
||||
(A: „M1–M4"↔M5-Export; B: 222 Echtzeit-Commits, deckungsgleich mit den
|
||||
Session-1-Zahlen; C: 6→0 Aufgabenblöcke; D: verwaiste SHAs aufgelöst
|
||||
oder kuratiert); (6) 9/9 Lücken-Dispositionen und 4/4
|
||||
Erhaltungsmechanismen in Gate 2, final abgehakt.
|
||||
|
||||
### Warum die Differenz
|
||||
|
||||
Die Undertaking hat einen lebenden Verbund migriert, keinen
|
||||
eingefrorenen: Jede Abweichung entstand daraus, dass zwischen Analyse
|
||||
(2026-08-09/10) und Bau (2026-08-11) weitergearbeitet wurde. Genau die
|
||||
Driftklassen, die die Migration schließen soll, traten währenddessen
|
||||
frisch auf — und wurden zu Testfällen statt zu Störungen.
|
||||
|
||||
### Lehren (geerntet nach [Stolpersteine](../../wiki/stolpersteine/neckbeard-migration.md))
|
||||
|
||||
- Ein hexförmiges Wort ist nicht automatisch ein Git-SHA; kuratierte
|
||||
Ausnahmen mit Grund schlagen schlauere Raterei.
|
||||
- Der Link-Checker ist das beste Umzugswerkzeug: erst bewegen, dann die
|
||||
gemeldeten Ziele reihum fixen — kein Verweis blieb offen.
|
||||
- Frische Importe sind Prüfmaterial: beide Prosa-Prüfungen fanden auf
|
||||
den eben importierten Texten sofort echte Fälle.
|
||||
- Bestätigt aus dem Upstream-Schöpfungs-AAR: ein grüner Validator zählt
|
||||
erst mit Negativtests (vier gebaut, alle feuern).
|
||||
- `gen_status.py` braucht lokal Python ≥ 3.10 (`write_text(newline=)`);
|
||||
CI nutzt 3.12, lokal läuft ein venv.
|
||||
- Session-1-Lehre erneut bestätigt: `TZ` gehört an den git-Prozess
|
||||
(`--date=format-local` + `TZ=UTC` in `gruppenpruefung.py`).
|
||||
|
||||
### Wackligste Entscheidungen dieser Session (WORKFLOW.md, Session-Ende)
|
||||
|
||||
1. **AAR-Erntestatus der vier alten AARs** aus der Retro-Existenz
|
||||
geschlossen, nicht aus einer Erntemarke — beim nächsten Refinement
|
||||
gegenprüfen.
|
||||
2. **Die §6-Verdichtung der Alt-CLAUDE.md**: von sorb quergelesen und
|
||||
freigegeben, aber ob jede tragende Nuance den Sprung geschafft hat,
|
||||
zeigt erst der Betrieb.
|
||||
3. **Generische `wartegrund`-Platzhalter** bei 7 importierten
|
||||
waiting-Issues — Issue 0041.
|
||||
4. **Die fünf eigenen Identitäten stehen als Konstante im
|
||||
Hygiene-Skript** — bei einer künftigen Identitäts-Remediation
|
||||
(F-003) muss die Liste mitgepflegt werden.
|
||||
5. **Spiegelumfang bewusst schmal** (keine Beschreibungen): richtig für
|
||||
Kommentar-Erhalt, heißt aber, dass Beschreibungs-Änderungen im Repo
|
||||
auf GitLab nicht sichtbar werden — Board-Nutzer sehen den Stand der
|
||||
Migration, nicht jede Textpflege.
|
||||
|
||||
### Offene Folgearbeit (als Issues, nicht als Prosa)
|
||||
|
||||
[0035–0039](../../issues/0035-rollout-agents-pointer-axion1337-chat-gitops.md) Pointer-Rollout je Komponente (fünf Issues) ·
|
||||
[0040](../../issues/0040-neckbeard-rueckmeldungen-einreichen.md)
|
||||
neckbeard-Rückmeldungen einreichen ·
|
||||
[0041](../../issues/0041-wartegrund-der-importierten-waiting-issues.md)
|
||||
wartegrund präzisieren ·
|
||||
[0042](../../issues/0042-migration-in-betrieb-nehmen-push-spiegel-schedule.md)
|
||||
Inbetriebnahme (Push, erster Spiegel-Lauf, CI-Schedule).
|
||||
@@ -0,0 +1,110 @@
|
||||
---
|
||||
type: design
|
||||
status: gate-1 # gate-1 | gate-2 | gate-3 | gate-4 | gate-5 | done
|
||||
date: YYYY-MM-DD
|
||||
size: L # this template is for size L
|
||||
related: [] # issues, ADRs spawned or read
|
||||
---
|
||||
|
||||
<!-- Copy to docs/design/YYYY-MM-DD-slug.md. Delete comments when filling in.
|
||||
Fill ONE gate at a time; each gate ends with STOP — do not pre-fill
|
||||
later gates. Advance `status` only after human approval. -->
|
||||
|
||||
# Design: Title
|
||||
|
||||
## Gate 1 — Product
|
||||
|
||||
**Problem.** <!-- What user problem, for whom. -->
|
||||
|
||||
**Acceptance criterion.** <!-- Verifiable. A real number where one
|
||||
exists; otherwise a concretely checkable outcome. "Works" is not one. -->
|
||||
|
||||
**Non-goals.** <!-- What this deliberately does NOT do. The cheapest
|
||||
scope-creep brake there is. -->
|
||||
|
||||
**Announcement.** <!-- 3–5 sentences: what it is, who it's for, why
|
||||
it's good. Can't write it? The product isn't understood yet. -->
|
||||
|
||||
**Mockups.** <!-- Only if UI is involved: plain-HTML mockups, linked. -->
|
||||
|
||||
> **STOP — awaiting Gate 1 approval.**
|
||||
|
||||
## Gate 2 — Architecture
|
||||
|
||||
**Inputs read.** <!-- Which ADRs and AARs were read; one line each on
|
||||
why they matter here. -->
|
||||
|
||||
**System fit.** <!-- Endpoints, tables/schemas, query outlines,
|
||||
end-to-end flow as Mermaid. Against the actual codebase. -->
|
||||
|
||||
**Constraints.** <!-- Non-functional, proportional to the project:
|
||||
performance, security, operations, compatibility. "None relevant"
|
||||
is a valid answer — but say it. -->
|
||||
|
||||
**Options & trade-offs.** <!-- Where more than one viable way exists:
|
||||
name the options, pro/contra each, state the chosen one and WHY.
|
||||
This is the feature-local decision record. Only lasting, binding
|
||||
decisions graduate to an ADR below. -->
|
||||
|
||||
**New ADRs.** <!-- Lasting decisions discovered here → one ADR each,
|
||||
linked. None is a valid answer. -->
|
||||
|
||||
> **STOP — awaiting Gate 2 approval.**
|
||||
|
||||
## Gate 3 — Program Design
|
||||
|
||||
**Files.** <!-- Exact paths, new and touched. -->
|
||||
|
||||
**Signatures.** <!-- Types and method signatures, no bodies. -->
|
||||
|
||||
**Call stack.** <!-- For the main flow(s). -->
|
||||
|
||||
**Test assertions.** <!-- What the tests will assert. -->
|
||||
|
||||
**Boundaries — DO NOT CHANGE.** <!-- Explicit list. -->
|
||||
|
||||
**Shakiest calls.** <!-- The decisions you are least confident about. -->
|
||||
|
||||
> **STOP — awaiting Gate 3 approval.**
|
||||
|
||||
## Gate 4 — Vertical Slices
|
||||
|
||||
<!-- Slice 1 is the tracer bullet: thin end-to-end, runs with mocks.
|
||||
Then real logic, one testable slice at a time. Per task:
|
||||
files / action / verify / done. After each slice: evidence,
|
||||
status, STOP. -->
|
||||
|
||||
### Slice 1 — Tracer bullet
|
||||
- [ ] Task: … — files: … — action: … — verify: … — done: …
|
||||
|
||||
**Evidence:** <!-- command output, test run, screenshot ref -->
|
||||
**Status:** <!-- DONE | DONE_WITH_CONCERNS | NEEDS_CONTEXT | BLOCKED -->
|
||||
|
||||
> **STOP — slice review.**
|
||||
|
||||
### Slice 2 — …
|
||||
|
||||
### Handoff
|
||||
|
||||
<!-- The single place session state lives. Overwrite on every handoff;
|
||||
git keeps the history.
|
||||
Done slices: …
|
||||
Open decisions: …
|
||||
Next step: … -->
|
||||
|
||||
## Gate 5 — Closeout (AAR)
|
||||
|
||||
**Planned vs. actual.** <!-- What was planned, what happened. -->
|
||||
|
||||
**Why the difference.** <!-- Root causes, honestly. -->
|
||||
|
||||
**Learnings.** <!-- What future-you should know. -->
|
||||
|
||||
**Harvested.** <!-- Wiki pages updated (FAQ, Stolpersteine, …) with
|
||||
links; framework issues opened, if a rule was missing or wrong. -->
|
||||
|
||||
**Open uncertainties.** <!-- Session-handoff answers to: "Which choices
|
||||
did I make that I'm least confident about?" -->
|
||||
|
||||
<!-- After approval: set status: done, move this file to
|
||||
docs/design/done/, run gen_status.py. -->
|
||||
@@ -0,0 +1,40 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0001"
|
||||
status: done
|
||||
created: 2026-08-01
|
||||
milestone: M2
|
||||
priority: low
|
||||
gitlab_iid: "1"
|
||||
related: []
|
||||
---
|
||||
# MATRIX-03: www.matrix.axion1337.de ist überflüssig
|
||||
|
||||
> Import aus [management#1](https://git.lab/axion1337.chat/management/-/issues/1) (2026-08-11). Kommentare und Verlauf bleiben dort; kanonisch ist ab jetzt diese Datei (ADR-0012).
|
||||
|
||||
A-Record `www.matrix.axion1337.de` → `49.13.132.245`, nach IONOS-Default-Muster
|
||||
angelegt. Begründung, warum `www.` bei einer Subdomain überflüssig ist: siehe ZONE-01.
|
||||
**Nicht verifiziert**, ob auf dem Host etwas auf den Namen hört.
|
||||
|
||||
**Nächster Schritt:** prüfen und sonst löschen.
|
||||
|
||||
Quelle: [hosts/matrix.md](https://git.lab/axion1337.chat/management/-/blob/main/hosts/matrix.md)
|
||||
|
||||
---
|
||||
*Ü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.
|
||||
@@ -0,0 +1,67 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0002"
|
||||
status: waiting
|
||||
created: 2026-08-01
|
||||
milestone: M1
|
||||
priority: medium
|
||||
host: game
|
||||
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: []
|
||||
---
|
||||
# GAME-01: Host von CFGMON aus nicht erreichbar, 2 Prometheus-Targets down
|
||||
|
||||
> Import aus [management#2](https://git.lab/axion1337.chat/management/-/issues/2) (2026-08-11). Kommentare und Verlauf bleiben dort; kanonisch ist ab jetzt diese Datei (ADR-0012).
|
||||
|
||||
Zwei Scrape-Targets sind down (`gameserver_cadvisor` 157.90.155.206:8080,
|
||||
`pterodactyl_host_node` :9100, beide `context deadline exceeded`) — bestand schon
|
||||
**vor** dem Monitoring-Rework; `up == 1` in 45 Tagen Retention **nie**.
|
||||
|
||||
**Eingrenzung 2026-08-01 (von CFGMON aus):** Port 80/443 offen und antworten sofort;
|
||||
22/8080/9100 Timeout (nicht refused → Signatur eines Paketfilters davor); ICMP 100 %
|
||||
Verlust; Host ist **nicht** im vSwitch 10.0.0.0/24. Damit ist „Host tot/umgezogen"
|
||||
ausgeschlossen und die **Hetzner-Cloud-Firewall die wahrscheinliche Ursache**;
|
||||
Zusatzbedingung möglich: Exporter binden nur 127.0.0.1.
|
||||
|
||||
**Empfehlung: nicht über die öffentliche IP freigeben**, sondern den Host in den
|
||||
Hetzner-vSwitch aufnehmen (Modell k3s: CFGMON scrapt 10.0.0.2:9100 privat, keine im
|
||||
Internet offenen Exporter-Ports). Danach in `threadnet-operating`
|
||||
`monitoring/prometheus/prometheus.yml` die Targets von der rohen IP auf die private
|
||||
Adresse umstellen.
|
||||
|
||||
⚠️ **Alerting-Silences laufen am 2026-08-04 01:30 UTC ab** (`abedb8a2…` und
|
||||
`0f64aa3c…`); danach melden sich beide `TargetDown`-Alarme alle 4 h zurück. Verlängern:
|
||||
`docker compose exec alertmanager amtool silence expire <id>
|
||||
--alertmanager.url=http://localhost:9093` aus `/opt/threadnet-operating/monitoring`.
|
||||
|
||||
Quelle: [hosts/game.md](https://git.lab/axion1337.chat/management/-/blob/main/hosts/game.md)
|
||||
|
||||
---
|
||||
*Ü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.
|
||||
@@ -0,0 +1,40 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0003"
|
||||
status: done
|
||||
created: 2026-08-01
|
||||
milestone: M2
|
||||
priority: low
|
||||
gitlab_iid: "3"
|
||||
related: []
|
||||
---
|
||||
# GAME-02: www.game.axion1337.de ist überflüssig
|
||||
|
||||
> Import aus [management#3](https://git.lab/axion1337.chat/management/-/issues/3) (2026-08-11). Kommentare und Verlauf bleiben dort; kanonisch ist ab jetzt diese Datei (ADR-0012).
|
||||
|
||||
A-Record `www.game.axion1337.de` → `157.90.155.206` nach IONOS-Default-Muster
|
||||
(Begründung siehe ZONE-01). Nicht verifiziert, ob etwas auf den Namen hört — der Host
|
||||
ist von CFGMON aus nicht erreichbar (GAME-01).
|
||||
|
||||
**Nächster Schritt:** prüfen, ob der Name irgendwo verlinkt/konfiguriert ist, sonst
|
||||
A-Record löschen.
|
||||
|
||||
Quelle: [hosts/game.md](https://git.lab/axion1337.chat/management/-/blob/main/hosts/game.md)
|
||||
|
||||
---
|
||||
*Ü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`).
|
||||
@@ -0,0 +1,36 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0004"
|
||||
status: waiting
|
||||
created: 2026-08-01
|
||||
milestone: M1
|
||||
priority: low
|
||||
host: overmind
|
||||
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: []
|
||||
---
|
||||
# OVERMIND-02: e1000e-NIC-Hang — Beobachtung nach EEE-Fix + Firmware-Update
|
||||
|
||||
> Import aus [management#4](https://git.lab/axion1337.chat/management/-/issues/4) (2026-08-11). Kommentare und Verlauf bleiben dort; kanonisch ist ab jetzt diese Datei (ADR-0012).
|
||||
|
||||
Host-Ausfall 2026-07-31 ~19:15: `e1000e Detected Hardware Unit Hang` auf `eno1`
|
||||
(bekanntes EEE-Problem) — Host lief, war aber netzwerktot. **Fix aktiv:** EEE per
|
||||
`ethtool` aus + persistente udev-Regel (`71-disable-eee-eno1.rules`).
|
||||
**NIC-/BIOS-Firmware 2.4.0.0 → 2.5.2.0 erledigt** (Wartungsfenster 2026-08-01, sorb).
|
||||
|
||||
**Rest = Beobachtung:** Falls der Hang trotz EEE-off + neuer Firmware wiederkehrt,
|
||||
gezielter ASPM-Fix statt globalem Kernel-Parameter. Ohne Wiederauftreten nach ~4 Wochen
|
||||
(Ende August) schließen.
|
||||
|
||||
Volle Zeitleiste: [hosts/overmind.md](https://git.lab/axion1337.chat/management/-/blob/main/hosts/overmind.md)
|
||||
|
||||
---
|
||||
*Ü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.
|
||||
@@ -0,0 +1,112 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0005"
|
||||
status: done
|
||||
created: 2026-08-01
|
||||
milestone: M2
|
||||
priority: low
|
||||
area: infrastructure
|
||||
gitlab_iid: "5"
|
||||
related: []
|
||||
---
|
||||
# ZONE-01: IONOS-Default-Records bereinigen (www-Paare, tote Mail-Sätze)
|
||||
|
||||
> Import aus [management#5](https://git.lab/axion1337.chat/management/-/issues/5) (2026-08-11). Kommentare und Verlauf bleiben dort; kanonisch ist ab jetzt diese Datei (ADR-0012).
|
||||
|
||||
IONOS legt je Subdomain automatisch `www.`-Paare und komplette Mail-Sätze an
|
||||
(MX, SPF ~all, DKIM-CNAMEs, autodiscover) — auch für Hosts ohne Mail. Ungenutzte
|
||||
Subdomain mit gültigem MX + Softfail-SPF = Spoofing-Vektor; ohne MX weichen Absender
|
||||
per RFC 5321 auf A/AAAA aus. Richtig: explizit „keine Mail" erklären — **Null-MX
|
||||
(RFC 7505), `v=spf1 -all`, `_dmarc p=reject`** — statt ersatzlos löschen.
|
||||
|
||||
**Stand:** `rohana` + `selendis` in Arbeit (sorb setzt direkt um, seit 2026-07-30).
|
||||
Offen: `matrix` (Mail-Satz kann weg — MATRIX-01 hat verifiziert, dass weder Synapse
|
||||
noch MAS Mail versenden), `www.game`/`www.matrix` (GAME-02, MATRIX-03), `ftp` (zeigt
|
||||
auf IONOS-Hosting — Ballast, löschen falls ungenutzt).
|
||||
|
||||
⚠️ Beim SPF-Ändern **bestehenden TXT editieren**, nie zweiten anlegen (PermError).
|
||||
Nach Umsetzung verifizieren: www-Namen lösen nicht mehr auf, genau EIN SPF pro Name,
|
||||
A/AAAA von rohana/selendis unangetastet (Gitea/Grafana weiter per HTTPS erreichbar).
|
||||
|
||||
Detail-Rezepte pro Name (Löschen/Anlegen-Tabellen): Git-Historie von
|
||||
[shared/zone-axion1337.md](https://git.lab/axion1337.chat/management/-/blob/main/shared/zone-axion1337.md)
|
||||
|
||||
---
|
||||
*Übernommen aus dem Backlogs-Markdown beim Framework-Umbau 2026-08-01 (voller Wortlaut: Git-Historie der Datei).*
|
||||
|
||||
---
|
||||
|
||||
## Rezepte pro Name (aus dem Backlogs-Markdown übernommen)
|
||||
|
||||
### rohana.axion1337.de
|
||||
**Löschen:** `A www.rohana`, `AAAA www.rohana`
|
||||
**Anlegen:** MX `rohana` = `.` (Prio 0) · TXT `rohana` = `v=spf1 -all` · TXT `_dmarc.rohana` = `v=DMARC1; p=reject;`
|
||||
**Nicht anfassen:** `A`/`AAAA rohana` — daran hängen Gitea und das Zertifikat.
|
||||
|
||||
### selendis.axion1337.de
|
||||
**Löschen:** `MX mx00/mx01.ionos.de`, `CNAME s1-ionos._domainkey`, `s2-ionos._domainkey`, `s42582890._domainkey`, `CNAME autodiscover.selendis`, `A`/`AAAA www.selendis`
|
||||
**Ändern:** TXT `selendis` von `v=spf1 include:_spf-eu.ionos.com ~all` auf `v=spf1 -all` — **bestehenden Record editieren, keinen zweiten anlegen** (PermError!)
|
||||
**Anlegen:** MX `selendis` = `.` (Prio 0) · TXT `_dmarc.selendis` = `v=DMARC1; p=reject;`
|
||||
**Vorab prüfen:** ob im IONOS-Mail-Bereich Postfach/Weiterleitung für `selendis` existiert (dann entfallen die MX-Änderungen). Falls IONOS `.` als MX-Ziel ablehnt: MX weglassen, TXT reicht.
|
||||
|
||||
### 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.
|
||||
@@ -0,0 +1,50 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0006"
|
||||
status: done
|
||||
created: 2026-08-01
|
||||
milestone: M1
|
||||
priority: low
|
||||
area: security
|
||||
gitlab_iid: "6"
|
||||
related: []
|
||||
---
|
||||
# ZONE-02: Apex-DMARC ist p=none und schützt nichts
|
||||
|
||||
> Import aus [management#6](https://git.lab/axion1337.chat/management/-/issues/6) (2026-08-11). Kommentare und Verlauf bleiben dort; kanonisch ist ab jetzt diese Datei (ADR-0012).
|
||||
|
||||
`_dmarc.axion1337.de` = `v=DMARC1; p=none;` — reines Monitoring, kein Schutz
|
||||
gegen gefälschte Mail. Zusätzlich fehlt `sp=`: **alle Subdomains erben p=none**, auch
|
||||
künftige. `sp=reject` am Apex wäre der effiziente Hebel und macht die einzelnen
|
||||
`_dmarc`-Records aus ZONE-01 auf Dauer entbehrlich.
|
||||
|
||||
**Reihenfolge wichtig:** erst für jeden real sendenden Namen SPF/DKIM korrekt setzen,
|
||||
dann `sp=reject` — umgekehrt zerlegt es Mailversand unbemerkt. Für den Apex selbst
|
||||
(echte IONOS-Mail): `p=none` → `p=quarantine` → Reports beobachten → `p=reject`.
|
||||
Auffällig: `s1._domainkey.axion1337.de` hatte keinen DKIM-Record, obwohl die
|
||||
Subdomains IONOS-DKIM-CNAMEs haben — beim Härten mitprüfen.
|
||||
|
||||
Quelle: [shared/zone-axion1337.md](https://git.lab/axion1337.chat/management/-/blob/main/shared/zone-axion1337.md)
|
||||
|
||||
---
|
||||
*Ü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.
|
||||
@@ -0,0 +1,92 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0007"
|
||||
status: done
|
||||
created: 2026-08-01
|
||||
milestone: M1
|
||||
priority: high
|
||||
due: 2026-09-28
|
||||
host: cfgmon
|
||||
gitlab_iid: "7"
|
||||
related: []
|
||||
---
|
||||
# CFGMON-01: Zertifikatserneuerung braucht offene Ports — zeitkritisch ab 2026-09-28
|
||||
|
||||
> Import aus [management#7](https://git.lab/axion1337.chat/management/-/issues/7) (2026-08-11). Kommentare und Verlauf bleiben dort; kanonisch ist ab jetzt diese Datei (ADR-0012).
|
||||
|
||||
Certs für `selendis`/`rohana` laufen am **2026-10-28** ab; Traefik erneuert ab
|
||||
Ende September via TLS-ALPN-01 — braucht **Port 443 offen aus dem ganzen Internet**
|
||||
(LE veröffentlicht keine Validierungs-IPs, Multi-Perspective-Validation). Der
|
||||
Normalzustand der Umgebung (443 auf eigene IP beschränkt) lässt die Erneuerung
|
||||
**still** scheitern → Self-Signed-Default-Cert. IPv6-Pfad ist geprüft frei
|
||||
(`::/0` separat in der Hetzner-Firewall-Regel; `0.0.0.0/0` deckt IPv6 NICHT ab) —
|
||||
die September-Erneuerung ist aber der **erste** Lauf, der IPv6 überhaupt versucht.
|
||||
|
||||
**Entscheidung nötig:**
|
||||
- **A — Ports offen lassen** bzw. zur Erneuerung öffnen (Kalendereintrag Mitte
|
||||
September, nicht aufs Ablaufdatum!)
|
||||
- **B — auf DNS-01 umstellen (empfohlen):** TXT-Validierung, kein offener Port,
|
||||
ermöglicht Wildcards. Braucht IONOS-API-Token als Traefik-Secret; Voraussetzung
|
||||
(versionierter Traefik-Stack) ist seit CFGMON-02 erfüllt.
|
||||
|
||||
Quelle: [hosts/cfgmon.md](https://git.lab/axion1337.chat/management/-/blob/main/hosts/cfgmon.md)
|
||||
|
||||
---
|
||||
*Ü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.
|
||||
@@ -0,0 +1,181 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0008"
|
||||
status: done
|
||||
created: 2026-08-01
|
||||
milestone: M1
|
||||
priority: medium
|
||||
host: cfgmon
|
||||
area: security
|
||||
gitlab_iid: "8"
|
||||
related: []
|
||||
---
|
||||
# CFGMON-03: Prometheus-Remote-Write und Loki öffentlich ohne Auth — Weg A, nachgelagerte Prüfung
|
||||
|
||||
> Import aus [management#8](https://git.lab/axion1337.chat/management/-/issues/8) (2026-08-11). Kommentare und Verlauf bleiben dort; kanonisch ist ab jetzt diese Datei (ADR-0012).
|
||||
|
||||
Prometheus 9090 (`--web.enable-remote-write-receiver`) und Loki 3100 sind
|
||||
öffentlich ohne Auth — Fremde könnten Metriken einspeisen und Daten/Logs auslesen.
|
||||
Absender-Inventur: k3s/Matrix pusht längst privat (10.0.0.3); öffentlich bräuchte die
|
||||
Ports nur der GAME-Host (→ GAME-01).
|
||||
|
||||
**Weg A beschlossen (sorb 2026-08-01):** Hetzner-Cloud-Firewall — 9090/3100 nur für
|
||||
bekannte Absender. **Nachgelagerte Prüfung nötig:** Beim Baseline-Check vom Mac waren
|
||||
9090/3100 bereits zu, OHNE dass der Console-Klick gemacht war — die reale
|
||||
Firewall-Lage weicht vom Backlog-Bild ab. Vor dem Abhaken **gemeinsam in die
|
||||
Hetzner-Console schauen**: welche Regeln existieren wirklich, und läuft der GAME-Push
|
||||
(nach GAME-01) noch durch? Weg B (GAME in den vSwitch, Ports ganz zu) bleibt die
|
||||
saubere Endstufe; Weg C (BasicAuth via Traefik) verworfen.
|
||||
|
||||
Quelle: [hosts/cfgmon.md](https://git.lab/axion1337.chat/management/-/blob/main/hosts/cfgmon.md)
|
||||
|
||||
---
|
||||
*Ü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.
|
||||
@@ -0,0 +1,26 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0009"
|
||||
status: open
|
||||
created: 2026-08-01
|
||||
milestone: M2
|
||||
priority: low
|
||||
host: cfgmon
|
||||
gitlab_iid: "9"
|
||||
related: []
|
||||
---
|
||||
# CFGMON-04: Grafana-Admin-Credentials aus .env gelten nicht für die HTTP-API
|
||||
|
||||
> Import aus [management#9](https://git.lab/axion1337.chat/management/-/issues/9) (2026-08-11). Kommentare und Verlauf bleiben dort; kanonisch ist ab jetzt diese Datei (ADR-0012).
|
||||
|
||||
`GF_SECURITY_ADMIN_USER/PASSWORD` greifen nur beim allerersten Start mit leerem
|
||||
Volume; der Live-Admin wurde später in der UI geändert — die `.env` sieht aus wie die
|
||||
Quelle der Wahrheit, ist es aber nicht. Verifikation läuft deshalb über `grafana.db`.
|
||||
|
||||
**Nächster Schritt:** Service-Account mit API-Token für Verifikationszwecke anlegen
|
||||
(sauberer als das echte Admin-Passwort in die `.env` nachzuziehen).
|
||||
|
||||
Quelle: [hosts/cfgmon.md](https://git.lab/axion1337.chat/management/-/blob/main/hosts/cfgmon.md)
|
||||
|
||||
---
|
||||
*Übernommen aus dem Backlogs-Markdown beim Framework-Umbau 2026-08-01 (voller Wortlaut: Git-Historie der Datei).*
|
||||
@@ -0,0 +1,93 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0010"
|
||||
status: rejected
|
||||
created: 2026-08-01
|
||||
milestone: M1
|
||||
priority: medium
|
||||
host: cfgmon
|
||||
area: security
|
||||
gitlab_iid: "10"
|
||||
related: []
|
||||
---
|
||||
# CFGMON-09: Gitea-Backups off-host (Borg/Storage Box) — Backup-Cron ist DEAKTIVIERT
|
||||
|
||||
> Import aus [management#10](https://git.lab/axion1337.chat/management/-/issues/10) (2026-08-11). Kommentare und Verlauf bleiben dort; kanonisch ist ab jetzt diese Datei (ADR-0012).
|
||||
|
||||
⚠️ **Seit 2026-07-30 laufen KEINE Gitea-Backups** — der nächtliche Cron ist
|
||||
auskommentiert (Crontab `rantanplan`), letzter Stand
|
||||
`/opt/backup/gitea-dump-2026-07-30.tar.gz`. Beim Erledigen/Verwerfen dieses Punkts
|
||||
den Cron wieder aktivieren.
|
||||
|
||||
**Plan: eigenes Borg-Repo auf einer Hetzner Storage Box** (spricht Borg nativ über
|
||||
SSH Port 23). Dump **unkomprimiert** an Borg geben (gzip im Script entfällt, sonst
|
||||
greift Dedup nicht); Retention via `borg prune` (7d/4w/6m); optional Sub-Account.
|
||||
Kontext: Platte 73 % voll, Script rotiert auf genau einen Stand, Off-host-Kopie
|
||||
fehlt komplett — bei Verlust des Hosts wäre Gitea (inkl. Mirror-Kopien) weg.
|
||||
|
||||
**Voraussetzungen (User):** Storage-Box/Sub-Account im Robot anlegen; Host hat keinen
|
||||
SSH-Key → generieren und Public Key in der Storage Box hinterlegen.
|
||||
|
||||
Quelle: [hosts/cfgmon.md](https://git.lab/axion1337.chat/management/-/blob/main/hosts/cfgmon.md)
|
||||
|
||||
---
|
||||
*Ü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.
|
||||
@@ -0,0 +1,44 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0014"
|
||||
status: open
|
||||
created: 2026-08-01
|
||||
milestone: M2
|
||||
priority: low
|
||||
host: cfgmon
|
||||
area: security
|
||||
gitlab_iid: "14"
|
||||
related: [docs/adr/0008-agenten-sessions-root-aequivalent.md]
|
||||
---
|
||||
# CFGMON-14: Root-Zugang über die docker-Gruppe umgeht sudo und hinterlässt keine Spur
|
||||
|
||||
> Import aus [management#14](https://git.lab/axion1337.chat/management/-/issues/14) (2026-08-11). Kommentare und Verlauf bleiben dort; kanonisch ist ab jetzt diese Datei (ADR-0012).
|
||||
|
||||
Aus dem [CFGMON-AAR](https://git.lab/axion1337.chat/management/-/blob/main/verfahren/aar/2026-08-01-labnet02-cfgmon.md) (Befund 3, MEDIUM), Entscheidung liegt bei sorb.
|
||||
|
||||
`sudo` ist aus einer Agenten-Session nicht bedienbar (kein TTY: *„a terminal is required to read the password"*). Die LABNET-02-Schritte liefen deshalb über die **docker-Gruppenmitgliedschaft** des Kontos `rantanplan` — privilegierter Container plus `nsenter` in die Host-Namespaces. Das ist **root-äquivalent**.
|
||||
|
||||
**Konsequenz:** Die sudo-Passwortabfrage ist für dieses Konto keine wirksame Sicherheitsgrenze, und dieser Weg hinterlässt **keinen Eintrag in `auth.log`**. Auf Linux ist das normales Verhalten der docker-Gruppe und kein Konfigurationsfehler — aber es sollte eine bewusste Entscheidung sein.
|
||||
|
||||
**Optionen:**
|
||||
- **A — so lassen**, aber dokumentieren (dann ist „sudo mit Passwort" auf diesem Host explizit kein Kontrollmechanismus mehr)
|
||||
- **B — Konto aus der docker-Gruppe nehmen** und Docker-Zugriff über eine gezielte sudo-Regel führen (auditierbar, aber Agenten-Sessions brauchen dann einen anderen Weg)
|
||||
- **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.
|
||||
@@ -0,0 +1,141 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0015"
|
||||
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: [docs/issues/0027-audit-01-acht-widersprueche-aus-dem-labnet-02.md]
|
||||
---
|
||||
# CFGMON-15: Token-Hygiene — Einmal-Tokens der LABNET-02-Nacht widerrufen
|
||||
|
||||
> Import aus [management#15](https://git.lab/axion1337.chat/management/-/issues/15) (2026-08-11). Kommentare und Verlauf bleiben dort; kanonisch ist ab jetzt diese Datei (ADR-0012).
|
||||
|
||||
Gemeldet von der CFGMON-Session am Ende der LABNET-02-Nacht.
|
||||
|
||||
Während der Arbeit entstanden **vier Einmal-Tokens** für Issue-Kommentare/Pushes, dazu existiert noch das **erste git.lab-Token** aus dem Erstzugang. Alle sind nach Abschluss von LABNET-02 funktionslos.
|
||||
|
||||
**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.
|
||||
@@ -0,0 +1,47 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0018"
|
||||
status: rejected
|
||||
created: 2026-08-01
|
||||
milestone: M2
|
||||
priority: low
|
||||
area: infrastructure
|
||||
gitlab_iid: "18"
|
||||
related: [docs/adr/0014-wikijs-loest-docusaurus-ab.md]
|
||||
---
|
||||
# DOC-01: Wiki-Rollout abschließen — CI-Freigaben, Zeitplan, Dokploy-Stack, wiki.lab
|
||||
|
||||
> Import aus [management#18](https://git.lab/axion1337.chat/management/-/issues/18) (2026-08-11). Kommentare und Verlauf bleiben dort; kanonisch ist ab jetzt diese Datei (ADR-0012).
|
||||
|
||||
Das Wiki-Repo [`homelab/wiki`](https://git.lab/homelab/wiki) steht ([ADR-0006](https://git.lab/axion1337.chat/management/-/blob/main/decisions/0006-wikis-konsolidieren-docusaurus.md)), der Bau ist lokal verifiziert (46 Seiten, 3,6 MB). Zum Betrieb fehlen noch Schritte, die Rechte oder die Dokploy-Oberfläche brauchen:
|
||||
|
||||
1. **Job-Token-Freigaben** — in jedem Quell-Repo unter *Settings → CI/CD → Job token permissions* das Projekt `homelab/wiki` erlauben: `axion1337.chat/axion1337.chat-gitops` (fürs Wiki-Repo!), `homelab/docs`, `axion1337.chat/management`. Ohne das schlägt `sync-sources.sh` in der CI fehl.
|
||||
2. **Erste Pipeline** in `homelab/wiki` laufen lassen (manuell) und prüfen, dass `registry.git.lab/homelab/wiki:latest` entsteht.
|
||||
3. **Pipeline-Zeitplan** anlegen (*Build → Pipeline schedules*, Vorschlag: täglich nachts) — das ist der eigentliche Aktualisierungsmechanismus.
|
||||
4. **Dokploy-Stack** aus `docker-compose.yml`, Domain `wiki.lab` → Port 80, Zertifikat von der aXionLabs-CA.
|
||||
5. **Lab-DNS**: `wiki.lab` → `10.58.73.17`.
|
||||
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.
|
||||
@@ -0,0 +1,24 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0019"
|
||||
status: open
|
||||
created: 2026-08-01
|
||||
milestone: M2
|
||||
priority: low
|
||||
area: infrastructure
|
||||
gitlab_iid: "19"
|
||||
related: []
|
||||
---
|
||||
# DOC-02: Veralteten `wiki`-Branch im gitops-Repo entfernen?
|
||||
|
||||
> Import aus [management#19](https://git.lab/axion1337.chat/management/-/issues/19) (2026-08-11). Kommentare und Verlauf bleiben dort; kanonisch ist ab jetzt diese Datei (ADR-0012).
|
||||
|
||||
Der Branch `wiki` im Repo `axion1337.chat-gitops` (Commit `0ff598e8`, **2026-05-14**) ist ein Abzug des damaligen `docs/`-Verzeichnisses — **nicht** das gepflegte Wiki (das lag auf Gitea und liegt seit 2026-08-02 im GitLab-Wiki, siehe [ADR-0006](https://git.lab/axion1337.chat/management/-/blob/main/decisions/0006-wikis-konsolidieren-docusaurus.md)).
|
||||
|
||||
Er ist damit eine Fehlerquelle: Wer ihn findet, hält ihn für Dokumentation und liest drei Monate alte Stände.
|
||||
|
||||
**Aktuell** ist er in README und CLAUDE.md ausdrücklich als überholt markiert — das ist die minimale, nicht-destruktive Maßnahme.
|
||||
|
||||
**Zu entscheiden:** löschen (sauberer, die Historie bleibt über den Mirror und die Reflogs erreichbar) oder als Archiv behalten. Wenn löschen: erst prüfen, ob der Branch Inhalte enthält, die es **nirgends sonst** gibt — `docs/oldwiki/` und `docs/setup/` sahen im Vergleich danach aus.
|
||||
|
||||
**Nicht ungefragt gelöscht**, weil ein Branch-Löschen im gespiegelten Repo auch den Mirror trifft.
|
||||
@@ -0,0 +1,41 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0020"
|
||||
status: done
|
||||
created: 2026-08-02
|
||||
milestone: M2
|
||||
priority: medium
|
||||
due: 2026-08-31
|
||||
area: infrastructure
|
||||
gitlab_iid: "20"
|
||||
related: []
|
||||
---
|
||||
# DOC-03: Wiki-Oberfläche entscheiden — Docusaurus oder BookStack
|
||||
|
||||
> 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 |
|
||||
|---|---|---|
|
||||
| **Docusaurus** | läuft unter `axionwiki.lab` | [homelab/wiki](https://git.lab/homelab/wiki) |
|
||||
| **BookStack** | Stack fertig, noch nicht deployt | [homelab/wiki-bookstack](https://git.lab/homelab/wiki-bookstack) |
|
||||
|
||||
**Die eigentliche Frage** ist nicht das Werkzeug, sondern: Soll Dokumentation künftig **im Repo** entstehen (Commit, Review, Git-Historie) oder **im Browser** (WYSIWYG, Rechte je Buch, eingebaute Suche)? Mit BookStack entsteht eine **zweite Quelle der Wahrheit** neben git.lab — das kann richtig sein, muss aber bewusst entschieden werden.
|
||||
|
||||
**Zum Ausprobieren:** BookStack deployen (Anleitung im README, drei Pflicht-Secrets), zwei bis drei Seiten anlegen, beide Oberflächen im Alltag vergleichen. Themes liegen in beiden Wunschfarben bei (Gruvbox Dark und Sunset Boulevard, farbgleich zu den Element-Themes), damit der Vergleich nicht an der Optik hängt.
|
||||
|
||||
**Verfallsdatum setzen:** Doppelter Betrieb ist nur als Vergleich vertretbar. Vorschlag: Entscheidung im ersten Refinement (#17), spätestens Ende August — danach wird die Verliererseite abgeräumt, nicht „für später" behalten.
|
||||
|
||||
⚠️ Falls BookStack gewinnt: **Backup wird Pflicht** (Datenbank!), Anschluss an das Verfahren aus CFGMON-09.
|
||||
@@ -0,0 +1,33 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0021"
|
||||
status: waiting
|
||||
created: 2026-08-02
|
||||
milestone: M2
|
||||
priority: medium
|
||||
area: infrastructure
|
||||
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: []
|
||||
---
|
||||
# OVERMIND-03: Windows-Build-VM verschwindet — CI kann sie nur starten, nicht anlegen
|
||||
|
||||
> Import aus [management#21](https://git.lab/axion1337.chat/management/-/issues/21) (2026-08-11). Kommentare und Verlauf bleiben dort; kanonisch ist ab jetzt diese Datei (ADR-0012).
|
||||
|
||||
Der CI-Job `start_windows_vm` macht ausschließlich `docker start windows-runner`. Existiert der Container nicht, scheitert er mit `No such container: windows-runner` — so geschehen am 2026-08-02 (Job 498), nachdem der Container zwischenzeitlich verschwunden war (vermutlich durch einen Dokploy-Redeploy oder den Host-Neustart; nicht verifiziert).
|
||||
|
||||
sorb hat ihn manuell neu gestartet, danach lief der Build. Der Fall wiederholt sich aber, sobald der Stack erneut angefasst wird.
|
||||
|
||||
**Optionen:**
|
||||
- **A** — Job robuster machen: bei fehlendem Container den Dokploy-Stack `windows-runner` per API neu deployen statt nur zu starten.
|
||||
- **B** — Container über `restart: unless-stopped` dauerhaft halten. ⚠️ Widerspricht dem On-demand-Prinzip (die VM belegt 8 GB) und war eine bewusste Entscheidung.
|
||||
- **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).
|
||||
@@ -0,0 +1,39 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0022"
|
||||
status: open
|
||||
created: 2026-08-02
|
||||
milestone: M4
|
||||
priority: low
|
||||
area: infrastructure
|
||||
gitlab_iid: "22"
|
||||
related: []
|
||||
---
|
||||
# BUILD-01: macOS-Client reproduzierbar bauen — aktuell nur manuell auf sorbs Mac
|
||||
|
||||
> Import aus [management#22](https://git.lab/axion1337.chat/management/-/issues/22) (2026-08-11). Kommentare und Verlauf bleiben dort; kanonisch ist ab jetzt diese Datei (ADR-0012).
|
||||
|
||||
Der macOS-Client wurde am 2026-08-02 erstmals gebaut (Release [desktop-1.12.17-themes](https://git.lab/axion1337.chat/ThreadNet-Web/-/releases/desktop-1.12.17-themes)), aber **von Hand auf sorbs Mac** und mit zwei Umgehungen. Reproduzierbar ist das so nicht.
|
||||
|
||||
## Was gemacht werden musste
|
||||
|
||||
| Hürde | Umgehung | Dauerhaft? |
|
||||
|---|---|---|
|
||||
| `Can't find rustc` (native Module sqlcipher/seshat) | rustup installiert, nach dem Build wieder entfernt | ❌ bei jedem Build neu |
|
||||
| `Failed to check actool version. Is Xcode 26 or higher installed?` beim **DMG** | DMG mit `hdiutil` statt electron-builder gebaut | ⚠️ funktioniert, aber ohne Installer-Layout |
|
||||
| Code-Signing | unsigniert, `CSC_IDENTITY_AUTO_DISCOVERY=false` | ❌ Nutzer müssen `xattr -dr com.apple.quarantine` ausführen |
|
||||
|
||||
Das **ZIP** baut electron-builder 26 problemlos; nur das DMG-Target verlangt `actool` aus dem vollen Xcode (~10 GB, nur über den App Store mit Apple-ID).
|
||||
|
||||
## Optionen
|
||||
|
||||
- **A — so lassen**: macOS bleibt ein manueller Build vor jedem Release. Billig, aber jedes Mal dieselben Handgriffe und leicht zu vergessen.
|
||||
- **B — Mac-Runner im Lab**: braucht Apple-Hardware, volles Xcode und einen GitLab-Runner darauf. Löst auch das DMG-Problem.
|
||||
- **C — DMG dauerhaft per `hdiutil`** in einem Skript im Repo: nimmt electron-builder das DMG ab, funktioniert ohne Xcode. Signing bleibt offen.
|
||||
|
||||
Empfehlung: **C jetzt** (kostet eine Stunde, macht den Build ohne Xcode vollständig), **B**, wenn macOS ein regelmäßiges Ziel wird.
|
||||
|
||||
## Hängt zusammen mit
|
||||
|
||||
- ThreadNet-Web#6 (Signing/Notarisierung) — ohne Signatur bleibt die Gatekeeper-Hürde für jeden Nutzer.
|
||||
- ThreadNet-Web#10 (Rebrand) — bereits teilweise umgesetzt: `apps/desktop/axion1337/build.json` (Commit `c8d4587`) macht aus `Element.app` eine `ThreadNet.app` mit eigenem Icon.
|
||||
@@ -0,0 +1,34 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0023"
|
||||
status: rejected
|
||||
created: 2026-08-02
|
||||
milestone: M2
|
||||
priority: low
|
||||
area: infrastructure
|
||||
gitlab_iid: "23"
|
||||
related: [docs/adr/0014-wikijs-loest-docusaurus-ab.md]
|
||||
---
|
||||
# DOC-04: Navbar-Logo im Docusaurus-Wiki wird ausgeliefert, ist aber nicht sichtbar
|
||||
|
||||
> Import aus [management#23](https://git.lab/axion1337.chat/management/-/issues/23) (2026-08-11). Kommentare und Verlauf bleiben dort; kanonisch ist ab jetzt diese Datei (ADR-0012).
|
||||
|
||||
Stand aus dem [AAR](https://git.lab/axion1337.chat/management/-/blob/main/verfahren/aar/2026-08-02-wiki-und-desktop-clients.md) — bisher nur dort notiert, deshalb jetzt als Issue.
|
||||
|
||||
Auf `axionwiki.lab` erscheint links neben dem Titel kein sichtbares Logo, obwohl alle Bestandteile nachweislich korrekt ausgeliefert werden:
|
||||
|
||||
- **HTML**: `<div class="navbar__logo">` enthält beide Theme-Varianten, beide mit `src="/img/logo.png"`
|
||||
- **CSS**: `.navbar__logo{height:2.6rem}` und `.navbar__logo img{height:100%;width:auto}` stehen im ausgelieferten Stylesheet
|
||||
- **Bild**: `/img/logo.png` liefert HTTP 200, 183 × 128 px, Motiv füllt die Fläche vollständig
|
||||
|
||||
Da alle drei Teile stimmen, hilft nur ein Blick in die Entwicklerkonsole: Wird das Bild geladen (Network-Tab) oder scheitert es? Und welche berechnete Höhe hat das `img`-Element tatsächlich (Elements → Computed)? Denkbar ist, dass eine Docusaurus-eigene Regel mit höherer Spezifität die Höhe auf 0 oder 2rem zwingt, oder dass die Theme-Umschaltung beide Varianten ausblendet.
|
||||
|
||||
**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.
|
||||
@@ -0,0 +1,26 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0024"
|
||||
status: done
|
||||
created: 2026-08-02
|
||||
milestone: M2
|
||||
priority: low
|
||||
area: infrastructure
|
||||
gitlab_iid: "24"
|
||||
related: []
|
||||
---
|
||||
# Wiki-Hostname klären: wiki.lab oder axionwiki.lab?
|
||||
|
||||
> 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.
|
||||
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0025"
|
||||
status: done
|
||||
created: 2026-08-01
|
||||
milestone: M1
|
||||
priority: medium
|
||||
area: infrastructure
|
||||
gitlab_iid: "25"
|
||||
related: [docs/issues/0030-der-restore-ist-nie-geprobt-sicherungen-sind.md]
|
||||
---
|
||||
# Deploy-Übergabe: CVE-Alarme aggregiert + Receiver-Robustheit (gitops#51, ff87cb2)
|
||||
|
||||
> Import aus [management#25](https://git.lab/axion1337.chat/management/-/issues/25) (2026-08-11). Kommentare und Verlauf bleiben dort; kanonisch ist ab jetzt diese Datei (ADR-0012).
|
||||
|
||||
### Stand
|
||||
threadnet-operating `ff87cb2` (git.lab; Gitea-Mirror folgt — auf CFGMON vorher `git fetch && git reset --hard origin/main`, der gerettete Kollegen-Commit heißt jetzt `0bd77e2`)
|
||||
|
||||
### Testtiefe
|
||||
ungetestet — Lints grün (promtool 9 Regeln, amtool, py_compile), kein Laufzeittest
|
||||
|
||||
### Mengengerüst
|
||||
Erwartete Matrix-Nachrichten beim Scharfschalten: **eine pro Image mit CRITICAL-Funden**, obere Schranke 29 (gemessen an 14 Images hatten die meisten CRITICALs → realistisch ~15–25 Nachrichten, je 1 s gedrosselt ≈ unter 30 s). HIGH-Alarme folgen frühestens nach 24 h (`for: 24h`), gleiche Schranke. Danach nur Deltas (neue Images/Severity-Wechsel) und ✅-Edits. Kein Pro-CVE-Verkehr mehr: Regeln sind `count by (target, target_type, host)`, die ~1200 Einzelserien erzeugen keine Alarme mehr (bleiben aber als `trivy_vuln_info` fürs Dashboard).
|
||||
|
||||
### Vollständiges Deploy-Kommando
|
||||
```
|
||||
cd /opt/threadnet-operating && git fetch && git reset --hard origin/main && cd monitoring && docker compose up -d --force-recreate matrix-alerts && docker compose exec prometheus kill -HUP 1 && docker compose exec alertmanager kill -HUP 1
|
||||
```
|
||||
(reset --hard wegen Hash-Wechsel 2b715ca→0bd77e2; force-recreate lädt das ro-gemountete Receiver-Skript neu; HUPs laden Regeln/Route ohne Neustart)
|
||||
|
||||
### Woran erkennt man, dass es wirklich greift
|
||||
1. `docker compose logs matrix-alerts --since 5m` — keine Fehler, keine 502-Schleife
|
||||
2. Security-Raum: binnen ~2 min (group_wait 1m) trudeln die aggregierten 🔴-Nachrichten „Image X: N CRITICAL-CVEs" einzeln im Sekundentakt ein — **gezählt ≤ 29**, keine Pro-CVE-Flut
|
||||
3. `curl -s localhost:9090/api/v1/rules | grep -c TrivyCriticalVulns` → 1 (neue Regel geladen)
|
||||
4. Alertmanager-Retry-Probe: Log darf nach Abschluss der Zustellung keine wiederholten identischen Batches zeigen
|
||||
|
||||
### Außenwirkung und Not-Aus
|
||||
Außenwirkung: nur der Security-Matrix-Raum (interner Kreis). **Not-Aus:** in `monitoring/alertmanager/alertmanager.yml` die Route `room="security"` wieder auf einen `"null"`-Receiver biegen (Muster steht in der Git-Historie, Commit `0bd77e2`) + `docker compose exec alertmanager kill -HUP 1` — wirkt sofort, Pipeline läuft weiter.
|
||||
|
||||
### Rollback
|
||||
`git revert ff87cb2` (ein Commit, betrifft nur alerts.yml/alertmanager.yml/matrix-alerts.py) + dieselben drei Kommandos wie beim Deploy. State-Datei ist abwärtskompatibel (neues Format kapselt das alte unter `alerts`).
|
||||
|
||||
### Bewusst offen gelassen
|
||||
- Grafana-Dashboard als **Matrix-Widget** im Security-Raum (Wunsch sorb): braucht `allow_embedding` in Grafana + Lese-Zugang ohne Login — eigener Punkt, nicht Teil dieses Deploys
|
||||
- gitops#52 (Inode-Falle) unberührt
|
||||
- Erste HIGH-Welle kommt erst nach 24 h — bewusst, keine Fehlfunktion
|
||||
|
||||
---
|
||||
*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.
|
||||
@@ -0,0 +1,374 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0027"
|
||||
status: open
|
||||
created: 2026-08-02
|
||||
milestone: M2
|
||||
priority: medium
|
||||
area: infrastructure
|
||||
gitlab_iid: "27"
|
||||
related: [docs/adr/0008-agenten-sessions-root-aequivalent.md]
|
||||
---
|
||||
# AUDIT-01: Acht Widersprüche aus dem LABNET-02-Nachlauf (Selbst-Audit CFGMON-Session)
|
||||
|
||||
> Import aus [management#27](https://git.lab/axion1337.chat/management/-/issues/27) (2026-08-11). Kommentare und Verlauf bleiben dort; kanonisch ist ab jetzt diese Datei (ADR-0012).
|
||||
|
||||
Selbst-Audit der CFGMON-Session (2026-08-02) auf sorbs Bitte: eigene Arbeit gegen `CLAUDE.md`, ADR-0005 und die Verfahren geprüft. **Regel dieses Issues: Widersprüche werden dokumentiert und referenziert, nicht still aufgelöst.** Auflösung einzeln oder gesammelt im Struktur-Workshop (#17). Alle Messungen von heute sind als solche gekennzeichnet.
|
||||
|
||||
## W1 — ADR-0004 (as-built) vs. tatsächliche Split-DNS-Konfiguration
|
||||
|
||||
ADR-0004, CFGMON-Zeile: *„Split-DNS nur `~lab` → 10.58.73.1"*. **Real** (seit 2026-08-01 spätabends, auf sorbs Ansage, `/etc/wireguard/lab.conf` + CFGMON-AAR Nachtrag 2): **vier Zonen** — `~lab`, `~lab.de`, `~axion1337.de`, `~axionlabs.de`. Das ADR beschreibt den As-built-Stand also unvollständig. Brisanz: `~axion1337.de` über den Lab-Resolver betrifft auch `rohana.axion1337.de` (Gitea-Mirror!) — löst der Lab-DNS die Zone anders auf als öffentlich, ändert sich unbemerkt der Pfad zum Mirror. **Auflösung:** ADR nachführen *oder* Zonen auf `~lab` zurückbauen — Entscheidung sorb.
|
||||
|
||||
## W2 — dokumentierte Bootstrap-Routen sind seit der Einzäunung nicht reproduzierbar
|
||||
|
||||
CFGMON-AAR Nachtrag 2 dokumentiert die CA-Verifikation über `ca.axionlabs.de:666` (step-ca, health + roots.pem, Fingerprint-Abgleich). **Messung heute von CFGMON:** `:666` = Timeout (Einzäunung greift, erwartungsgemäß), `git.lab:443` = HTTP 302 ✓, **ICMP zu `10.58.73.17` = 100 % Verlust**. Konsequenzen: (a) Die im AAR beschriebene Verifikationsroute funktioniert nicht mehr — ein künftiger Truststore-Neuaufbau bräuchte eine bewusste Firewall-Ausnahme (**ADR-Pflicht** laut CLAUDE.md). (b) Erreichbarkeits-Checks von CFGMON müssen per **HTTPS statt ping** laufen — alle bisherigen Runbook-Gewohnheiten (`ping 10.58.73.17`) schlagen fehl, obwohl alles gesund ist. Fehldiagnose-Falle für die nächste Session.
|
||||
|
||||
## W3 — `hosts/cfgmon.md` widerspricht sich selbst und der Realität
|
||||
|
||||
Die Dienste-Tabelle (Zeile 27) listet `runner | gitea/act_runner:0.6.1` als laufenden Container — die **eigene Historie** derselben Datei (CFGMON-11-Abschnitt) meldet ihn als am 2026-07-31 restlos entfernt. „Stand: 2026-07-30" deckt zudem nicht: WireGuard-Tunnel (`wg-quick@lab` + systemd-Drop-in `10-after-docker.conf`), aXionLabs-Root-CA im Truststore, git.lab-Zugang via `~/.netrc`. Wenn `hosts/` „Bestand + Historie" ist (CLAUDE.md), ist der Bestand-Teil veraltet; wenn er eingefroren sein soll, fehlt die Kennzeichnung. **Nicht von mir korrigiert** — erst klären, was „Bestand" hier heißen soll.
|
||||
|
||||
## W4 — Schließung von #16 vs. Inhalt von #16 und CLAUDE.md-Secrets-Regel
|
||||
|
||||
Der Schlusskommentar von #16 nennt „die **drei** hier gesammelten Nacharbeiten (Feinschliff)". Das Issue enthielt **fünf** Punkte plus einen Korrektur-Kommentar der CFGMON-Session. Still mitgeschlossen wurden: **Punkt 4** (Repo-Zuhause für `lab.conf`, systemd-Drop-in, Root-CA — Reproduzierbarkeit) und **Punkt 5** (Schlüsselrotation). Bei Punkt 5 kollidiert die Schließung mit der Secrets-Regel der CLAUDE.md (*„anzeigen = Exposure = Rotation"*): Der aktive WG-Private-Key und beide git.lab-PATs liefen im Klartext durch den Chat bzw. das buffer-Repo. Das buffer-Repo ist vernichtet ✓, aber Chat-/Session-Transkripte existieren weiter. Nach der Regel ist die Rotation nicht optional — auch mein eigener Kommentar in #15 („regulär rotieren") war daran gemessen **zu lasch**. **Auflösung:** entweder sorb bestätigt die Schließung ausdrücklich für alle fünf Punkte (dann ist die Secrets-Regel für diesen Fall bewusst ausgesetzt → ADR-Pflicht für die Ausnahme), oder Punkte 4+5 werden als eigenes Issue reaktiviert.
|
||||
|
||||
## W5 — Secrets-Regel vs. gelebte Bootstrap-Praxis der LABNET-02-Nacht
|
||||
|
||||
CLAUDE.md: *„Token-/Secret-Werte niemals […] in Dateien echoen; echte Credentials tippt/legt sorb selbst an; Sessions referenzieren sie nur über Dateipfade."* **Praxis:** Die CFGMON-Session (ich) hat beide PATs selbst in `~/.netrc` geschrieben und den WG-Private-Key nach `/etc/wireguard/` — es gab schlicht keinen anderen Übergabekanal auf einen headless Host. Der Widerspruch ist strukturell, nicht böswillig: Die Regel kennt den Fall „sorb kann die Datei auf dem Zielhost nicht selbst anlegen" nicht. **Auflösung:** Bootstrap-Klausel in die Regel (erlaubt, aber Exposure gilt ⇒ Rotationspflicht + dokumentieren wo), oder Verfahren definieren (z. B. sorb legt per SSH selbst ab, Session referenziert Pfad).
|
||||
|
||||
## W6 — AAR-Vorlage vs. Nachtrag-Praxis
|
||||
|
||||
`verfahren/aar-vorlage.md`: fünf Abschnitte, Gebot „Kurz", Befund-Status mit Issue-Referenz („notiert · Issue"). Der CFGMON-AAR hat inzwischen **acht Abschnitte** (drei Nachträge) und eine Befunde-Tabelle **ohne** Issue-Referenzen (Befund 3 → heute #14; die Issues entstanden erst nach dem AAR). Die Nachtrag-Mechanik — die sich zweimal bewährt hat (Reboot-Korrektur, Auflösung) — ist **nirgends im Verfahren definiert**. **Auflösung:** Vorlage um eine Nachtrag-Regel ergänzen (append-only, datiert, Fundstellen verweisen auf den Nachtrag) oder Nachträge verbieten und Folge-AARs verlangen. Die Befunde-Tabellen der beiden LABNET-02-AARs könnten danach um Issue-Refs ergänzt werden (reine Vervollständigung).
|
||||
|
||||
## W7 — Commit-Autorschaft: drei Identitäten, keine Konvention
|
||||
|
||||
Im management-Repo committen Agenten-Sessions unter drei Identitäten: `sorb <gamemaster@axion1337.de>` ohne Agent-Kennzeichnung (CFGMON-Session: `e8e1b36`, `28cd06c`, `001f59f`; auch `b647645` der Mac-Session), und seit heute `Thore Cimbal <cfx@riot.8shield.net>` **mit** `Co-Authored-By: Claude`-Trailer (`09bdd94`, `ae982cd`, …). Das Kanonisierungs-Verfahren betont Autorschafts-Erhalt als Wert — der ist wenig wert, wenn dieselbe Person/verschiedene Agenten unter wechselnden Identitäten schreiben. Eigenes Versäumnis der CFGMON-Session eingeschlossen: der Claude-Trailer fehlt bei meinen Commits. **Auflösung:** eine Zeile in CLAUDE.md — welcher Author-Name, welche E-Mail, Trailer ja/nein.
|
||||
|
||||
## W8 — UDM-SSH: aktivierte Reständerung ohne Doku
|
||||
|
||||
Für die Fehlersuche wurde SSH auf der UDM aktiviert (`root@10.58.73.1`, eigenes Passwort). Der Lab-AAR erwähnt es nicht, kein Issue trägt es, Status vermutlich „noch an". Root-Shell-Zugang auf dem zentralen Gateway ist ein sicherheitsrelevanter Dauerzustand, wenn er bleibt. **Auflösung:** deaktivieren oder bewusst belassen und als Bestand dokumentieren (analog zur docker-Gruppen-Entscheidung in #14).
|
||||
|
||||
---
|
||||
|
||||
**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.
|
||||
@@ -0,0 +1,75 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0028"
|
||||
status: done
|
||||
created: 2026-08-02
|
||||
milestone: M2
|
||||
priority: low
|
||||
area: infrastructure
|
||||
gitlab_iid: "28"
|
||||
related: []
|
||||
---
|
||||
# MIRROR-01: Ein Ausfall der Push-Mirrors bleibt unbemerkt — Produktion friert still ein
|
||||
|
||||
> Import aus [management#28](https://git.lab/axion1337.chat/management/-/issues/28) (2026-08-11). Kommentare und Verlauf bleiben dort; kanonisch ist ab jetzt diese Datei (ADR-0012).
|
||||
|
||||
Der Push-Mirror ist der **einzige** Weg von git.lab in die Produktion: Flux zieht ausschließlich aus Gitea ([ADR-0001](https://git.lab/axion1337.chat/management/-/blob/main/decisions/0001-gitlab-kanonisch-push-mirror.md)). Fällt er aus, passiert nichts Lautes — Flux reconciled weiter den zuletzt gespiegelten Stand. Die Produktion wirkt gesund und ist eingefroren.
|
||||
|
||||
**Kein Alarm, keine rote Pipeline, kein Log, das jemand liest.** Auffallen würde es erst, wenn sich jemand wundert, warum ein Deploy „nicht ankommt".
|
||||
|
||||
## Warum das jetzt zählt
|
||||
|
||||
Aus [#15](https://git.lab/axion1337.chat/management/-/issues/15): **Welches Credential in den Mirrors hinterlegt ist, ist nicht rekonstruierbar** — die API maskiert es, keine Session hat es dokumentiert. Wir wissen also nicht, ob es ein Ablaufdatum hat. Läuft es ab, tritt genau der stille Fall oben ein.
|
||||
|
||||
Stand 2026-08-02 laufen alle geprüften Mirrors fehlerfrei (`update_status: finished`, `last_error: —`) — das ist eine Momentaufnahme, keine Zusicherung.
|
||||
|
||||
## Was zu tun ist
|
||||
|
||||
Ein Check auf den Mirror-Status der sechs gespiegelten Repos der Gruppe `axion1337.chat`:
|
||||
|
||||
```bash
|
||||
curl -sS -H "PRIVATE-TOKEN: $TOKEN" \
|
||||
"https://git.lab/api/v4/projects/<id>/remote_mirrors"
|
||||
# relevant: .update_status != "finished" oder .last_error != null
|
||||
```
|
||||
|
||||
Offen ist **wo** er läuft — beides ist vertretbar:
|
||||
|
||||
- **Prometheus/Alertmanager auf CFGMON** (`threadnet-operating`) — passt zum vorhandenen Alarmweg, braucht aber ein git.lab-Token auf CFGMON und den Tunnel.
|
||||
- **Scheduled CI-Job auf git.lab**, wie `canonize_rotation` im gitops-Repo — läuft im Lab, kein zusätzliches Credential nach außen, meldet sich über eine rote Pipeline. Dafür merkt er nichts, wenn das Lab selbst aus ist (was aber gerade der Fall ist, in dem die Mirrors ohnehin nicht laufen).
|
||||
|
||||
## Abgrenzung
|
||||
|
||||
Nicht Teil dieses Issues: die Rotation der Tokens selbst ([#15](https://git.lab/axion1337.chat/management/-/issues/15)) und die Frage, welches Credential dort hinterlegt ist. Hier geht es allein darum, einen Ausfall **zu bemerken**.
|
||||
|
||||
---
|
||||
*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.
|
||||
@@ -0,0 +1,45 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0029"
|
||||
status: open
|
||||
created: 2026-08-06
|
||||
milestone: M4
|
||||
priority: medium
|
||||
gitlab_iid: "29"
|
||||
related: []
|
||||
---
|
||||
# UI harmonisieren: gleiche Farben und Formen über alle Oberflächen
|
||||
|
||||
> Import aus [management#29](https://git.lab/axion1337.chat/management/-/issues/29) (2026-08-11). Kommentare und Verlauf bleiben dort; kanonisch ist ab jetzt diese Datei (ADR-0012).
|
||||
|
||||
Die Plattform besteht aus mehreren Oberflächen, die nacheinander im selben Nutzerweg auftauchen — und jede bringt ihr eigenes Design-System mit. Das fällt am stärksten an der Anmeldung auf: Authentik (PatternFly) und der Client (Elements Compound) stehen direkt hintereinander und sehen aus wie zwei verschiedene Produkte.
|
||||
|
||||
## Was zu harmonisieren ist
|
||||
|
||||
| Oberfläche | Design-System | heute eingestellt |
|
||||
|---|---|---|
|
||||
| ThreadNet-Web (Client) | Compound | 17 eigene Themes, Markenfarbe `#ed4f4c` |
|
||||
| Authentik (Anmeldung) | PatternFly | nur `branding_title`/Favicon/Hintergrund; `branding_custom_css` **ungenutzt** |
|
||||
| BookStack | eigenes | sorbs Terrakotta-Beige, liegt nur in der DB |
|
||||
| Docusaurus-Wiki | Infima | bislang nur die Akzentfarbe |
|
||||
| Grafana | eigenes | unangetastet |
|
||||
|
||||
## Woran es konkret hängt
|
||||
|
||||
1. **Farben.** Es gibt bereits eine Markenfarbe (`#ed4f4c`) und sorbs Terrakotta-Palette. Beide sind dokumentiert (`shared/branding.md`), aber nur teilweise ausgerollt.
|
||||
2. **Formen.** Radien, Schatten und Button-Höhen unterscheiden sich zwischen den Systemen — mal rund, mal eckig. Das ist das, was den Bruch spürbar macht, noch vor der Farbe.
|
||||
3. **Typografie.** Bisher nirgends vereinheitlicht.
|
||||
|
||||
## Vorschlag für den Zuschnitt
|
||||
|
||||
Nicht alles auf einmal. Sinnvolle Reihenfolge nach sichtbarer Wirkung pro Aufwand:
|
||||
|
||||
1. **Authentik an den Client angleichen** — der Bruch mitten im Anmeldeweg ist der auffälligste. Hebel ist `branding_custom_css` auf dem Brand-Blueprint, also deklarativ und rückbaubar. ⚠️ Vorher klären, ob Authentiks Flow-Komponenten Shadow DOM nutzen — dann greift normales CSS nicht und es braucht `::part()`-Selektoren.
|
||||
2. **Farbwerte an einer Stelle festschreiben**, statt sie je Oberfläche einzutippen. Heute ist die Kopie in `shared/branding.md` die Quelle; ob daraus etwas Maschinenlesbares wird, ist die eigentliche Entscheidung.
|
||||
3. Wiki und BookStack nachziehen.
|
||||
|
||||
## Vorbedingung
|
||||
|
||||
Die offene Frage aus `shared/branding.md` — ob Terrakotta das Stammschema ablöst oder eine Alternative bleibt — sollte **vorher** entschieden sein. Sonst harmonisiert man auf einen Zielwert, der danach wechselt.
|
||||
|
||||
Aufgenommen aus der Session vom 2026-08-06, in der Titelbild und Authentik-Brand gesetzt wurden.
|
||||
@@ -0,0 +1,307 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0030"
|
||||
status: in-progress
|
||||
created: 2026-08-06
|
||||
milestone: M1
|
||||
priority: medium
|
||||
area: security
|
||||
gitlab_iid: "30"
|
||||
related: [docs/adr/0016-notfallhandbuch-nicht-spiegeln.md]
|
||||
---
|
||||
# Der Restore ist nie geprobt — Sicherungen sind bisher eine Vermutung
|
||||
|
||||
> Import aus [management#30](https://git.lab/axion1337.chat/management/-/issues/30) (2026-08-11). Kommentare und Verlauf bleiben dort; kanonisch ist ab jetzt diese Datei (ADR-0012).
|
||||
|
||||
Es wird gesichert: jeden Sonntag, alle Dienste, drei Versionen vorgehalten, GitLab auf Overmind mit Datenbank **und** Volumes nach MinIO auf dem DSM. Das ist mehr, als die meisten haben.
|
||||
|
||||
**Was fehlt, ist der Beweis, dass sich daraus etwas wiederherstellen lässt.** Es gibt kein dokumentiertes Verfahren und keinen je durchgespielten Versuch. Eine Suche über `docs/` und `verfahren/` findet nur Erwähnungen in `install.md` — keine Anleitung, keine Protokolle.
|
||||
|
||||
## Warum das der wichtigste der offenen Punkte ist
|
||||
|
||||
Eine Sicherung, die nie zurückgespielt wurde, ist eine **Vermutung**. Die typischen Fehler zeigen sich ausschließlich beim Zurückspielen und nie beim Sichern:
|
||||
|
||||
- die Datenbank ist gesichert, aber ohne das Volume mit den Uploads ist sie wertlos
|
||||
- der Dump ist da, aber der Verschlüsselungsschlüssel lag nur auf dem Host, der weg ist
|
||||
- es liegen drei Versionen, aber alle drei sind seit Wochen leer, weil ein Pfad umgezogen ist und keiner es gemerkt hat
|
||||
- niemand weiß, in welcher Reihenfolge die Dienste hochkommen müssen
|
||||
|
||||
Der letzte Punkt ist hier besonders relevant: **Der SOPS-age-Schlüssel entschlüsselt alle Secrets im Cluster.** Wenn der nur an einer Stelle liegt, ist die Frage nicht, ob die Sicherung funktioniert, sondern ob sie überhaupt etwas nützt.
|
||||
|
||||
## Was zu tun ist
|
||||
|
||||
1. **Zuerst das Billigste:** stichprobenartig in die aktuellen Sicherungen hineinschauen. Sind sie plausibel groß? Enthalten sie, was sie sollen? Das findet stille Ausfälle sofort.
|
||||
2. Ein echtes Wiederherstellungsverfahren schreiben — als Ablauf, nicht als Prosa: welcher Dienst zuerst, woher der SOPS-Schlüssel, woher der kubeconfig.
|
||||
3. **Einmal wirklich durchspielen**, gegen eine Wegwerf-Umgebung, nicht gegen die Produktion. Was dabei fehlt, ist das Ergebnis.
|
||||
4. Ergebnis als Verfahren in `verfahren/` ablegen und danach in bekanntem Abstand wiederholen.
|
||||
|
||||
⚠️ Bewusst **nicht** vorschlagen: die Sicherung erweitern, bevor die vorhandene geprüft ist. Mehr zu sichern, ohne zu wissen, ob das Vorhandene trägt, verschiebt das Problem nur.
|
||||
|
||||
## Grenzen dieses Issues
|
||||
|
||||
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.
|
||||
@@ -0,0 +1,70 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0031"
|
||||
status: done
|
||||
created: 2026-08-09
|
||||
milestone: M1
|
||||
priority: low
|
||||
area: infrastructure
|
||||
gitlab_iid: "31"
|
||||
related: []
|
||||
---
|
||||
# Stillstandsprüfung: GITEA_TOKEN und Authentik-Teil nachziehen
|
||||
|
||||
> Import aus [management#31](https://git.lab/axion1337.chat/management/-/issues/31) (2026-08-11). Kommentare und Verlauf bleiben dort; kanonisch ist ab jetzt diese Datei (ADR-0012).
|
||||
|
||||
Die [Stillstandsprüfung](../wiki/admin/stillstandspruefung.md) läuft. Offen ist nur noch **ein optionaler Teil**.
|
||||
|
||||
## Was fehlt
|
||||
|
||||
`AUTHENTIK_URL` und `AUTHENTIK_TOKEN` als CI-Variablen im management-Repo. Ohne sie überspringt die Prüfung den Blueprint-Test und weist das im Ergebnis aus:
|
||||
|
||||
```
|
||||
Uebersprungen:
|
||||
- Authentik-Blueprints: AUTHENTIK_URL/AUTHENTIK_TOKEN fehlen —
|
||||
genau der Fall, der uns am laengsten unbemerkt lief
|
||||
```
|
||||
|
||||
## Warum das der ärgerlichste blinde Fleck ist
|
||||
|
||||
Der `matrix-recovery-flow`-Blueprint wurde **tagelang bei jedem Durchlauf verworfen** — während Flux grün meldete, die ConfigMap aktuell war und im Cluster alles gesund aussah. Gefunden wurde es nur, weil jemand für eine ganz andere Sache in die Authentik-Datenbank schaute (gitops#60).
|
||||
|
||||
Von allen sechs stillen Fehlern des Monats ist das der, der am längsten unentdeckt lief. Die Prüfung deckt fünf davon ab — ausgerechnet diesen nicht.
|
||||
|
||||
## Was nötig wäre
|
||||
|
||||
**In Authentik:** *Admin → Verzeichnis → Tokens & App-Passwörter → Erstellen*. Sauber wäre ein eigenes Dienstkonto mit reinem Lesezugriff auf `/api/v3/managed/blueprints/`; ein Token des Admin-Kontos ginge auch, hätte dann aber dessen volle Rechte.
|
||||
|
||||
**In GitLab** (management → Einstellungen → CI/CD → Variablen):
|
||||
|
||||
| Schlüssel | Wert | Flags |
|
||||
|---|---|---|
|
||||
| `AUTHENTIK_URL` | `https://auth.axion1337.chat` | — |
|
||||
| `AUTHENTIK_TOKEN` | das Token | maskiert, geschützt |
|
||||
|
||||
Mehr ist nicht zu tun — der Code steht, er wartet nur auf die Zugänge.
|
||||
|
||||
---
|
||||
|
||||
## Erledigt (2026-08-09)
|
||||
|
||||
- ✅ `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.
|
||||
@@ -0,0 +1,38 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0032"
|
||||
status: open
|
||||
created: 2026-08-09
|
||||
milestone: M2
|
||||
priority: medium
|
||||
area: infrastructure
|
||||
gitlab_iid: "32"
|
||||
related: []
|
||||
---
|
||||
# gameserver hat keinen Push-Mirror — und auf Gitea liegt ein anderer Stand
|
||||
|
||||
> Import aus [management#32](https://git.lab/axion1337.chat/management/-/issues/32) (2026-08-11). Kommentare und Verlauf bleiben dort; kanonisch ist ab jetzt diese Datei (ADR-0012).
|
||||
|
||||
Gefunden beim ersten Lauf der Stillstandsprüfung (2026-08-09) — **beides war vorher niemandem bekannt.**
|
||||
|
||||
Die Gruppe `axion1337.chat` hat **acht** Projekte, nicht sechs. Zwei davon haben **keinen aktiven Push-Mirror**:
|
||||
|
||||
| Projekt | Zustand |
|
||||
|---|---|
|
||||
| `game-operating` | in einer Session am 2026-08-06 angelegt, nie gespiegelt — auf Gitea existiert es **gar nicht** (HTTP 404) |
|
||||
| `gameserver` | kein Mirror konfiguriert; auf Gitea liegt ein gleichnamiges Repo mit **anderem** Stand (`d5c6ccb2` vs. `48441a50`) |
|
||||
|
||||
## Warum das zählt
|
||||
|
||||
`CLAUDE.md` sagt: *„Gespiegelt wird nur die Gruppe `axion1337.chat`"* — als Eigenschaft der Gruppe, nicht als Liste einzelner Repos. Diese beiden widersprechen dem still. Wer sich auf die Aussage verlässt, nimmt an, dass ein Verlust von git.lab folgenlos wäre. Für diese beiden stimmt das nicht.
|
||||
|
||||
⚠️ Bei `gameserver` ist es unangenehmer als bei `game-operating`: Dort existieren **zwei Repos mit demselben Namen und verschiedenen Ständen**. Wer das eine für eine Kopie des anderen hält, liegt falsch.
|
||||
|
||||
## Zu entscheiden
|
||||
|
||||
Pro Repo eines von beidem:
|
||||
|
||||
1. **Push-Mirror nachziehen** — dann stimmt die Topologie wieder. Bei `gameserver` ⚠️ **vorher prüfen, welcher Stand der richtige ist**: Ein Mirror überschreibt die Gitea-Seite per Force, und der dortige Stand ginge verloren.
|
||||
2. **Ausnahme begründen** — dann gehört sie in die `CLAUDE.md`, nicht ins Schweigen. Für `game-operating` ist das plausibel: Das Repo bildet nur ein Compose-Setup ab, es ist ausdrücklich „Abbild, keine Quelle".
|
||||
|
||||
Die Prüfung meldet beide so lange, bis eines von beidem passiert ist — das ist beabsichtigt.
|
||||
@@ -0,0 +1,24 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0033"
|
||||
status: open
|
||||
created: 2026-08-11
|
||||
milestone: M2
|
||||
priority: low
|
||||
host: overmind
|
||||
related: []
|
||||
gitlab_iid: "33"
|
||||
---
|
||||
|
||||
# OVERMIND-01 — element-desktop-build von rohana in die Lab-Registry umziehen
|
||||
|
||||
> Angelegt bei der neckbeard-Migration (Feldtest-Befund F-004: dieser
|
||||
> Arbeitspunkt lebte nur in Host-Prosa und war für Board, Meilenstein
|
||||
> und Priorität unsichtbar). `gitlab_iid` folgt mit dem ersten
|
||||
> Spiegel-Lauf.
|
||||
|
||||
ThreadNet-Web-CI umstellen: `desktop_image`-Push-Ziel und
|
||||
`desktop_linux`-Image-Referenz von rohana auf `registry.git.lab`.
|
||||
Bewusst zurückgestellt, bis kein Auto-Job das alte Image parallel
|
||||
referenziert — Reihenfolge: erst neues Image bauen, dann Referenz
|
||||
umstellen. Kontext: [overmind](../wiki/admin/overmind.md).
|
||||
@@ -0,0 +1,25 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0034"
|
||||
status: open
|
||||
created: 2026-08-11
|
||||
milestone: M2
|
||||
priority: medium
|
||||
host: cfgmon
|
||||
related: []
|
||||
gitlab_iid: "34"
|
||||
---
|
||||
|
||||
# CFGMON-11 — Gitea-CI-Rückbau abschließen (sicher rückbaubare Schritte)
|
||||
|
||||
> Angelegt bei der neckbeard-Migration (Feldtest-Befund F-004).
|
||||
> `gitlab_iid` folgt mit dem ersten Spiegel-Lauf.
|
||||
|
||||
Die als „sicher rückbaubar" dokumentierten Schritte ausführen
|
||||
([cfgmon](../wiki/admin/cfgmon.md), Abschnitt Gitea-CI-Rückbau):
|
||||
Actions-Toggle bei ThreadNet-Web/threadnet-call deaktivieren, die
|
||||
ersetzten Workflow-Dateien entfernen, Runner-Identität deregistrieren —
|
||||
und den **npm-Token aus der untracked `.npmrc` revoken/rotieren**
|
||||
(Klartext-Fund vom 2026-07-30; der Anteil ist der Grund für
|
||||
`priority: medium`). Dazu der kosmetische Handgriff auf CFGMON:
|
||||
`cd /opt/thread-net-git && git checkout main && git pull`.
|
||||
@@ -0,0 +1,35 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0035"
|
||||
status: done
|
||||
created: 2026-08-11
|
||||
milestone: M2
|
||||
priority: medium
|
||||
related:
|
||||
- "docs/adr/0013-gruppenregeln-kanonisch-mit-pruefung.md"
|
||||
---
|
||||
|
||||
# Rollout Gruppenregeln-Pointer: `axion1337.chat-gitops`
|
||||
|
||||
> Folge-Issue aus [ADR-0013](../adr/0013-gruppenregeln-kanonisch-mit-pruefung.md)
|
||||
> (Feldtest F-011: 4 von 5 Komponenten trugen keine Pointer-Datei).
|
||||
> `gitlab_iid` folgt mit dem ersten Spiegel-Lauf.
|
||||
|
||||
In `axion1337.chat-gitops` anlegen: `CLAUDE.md` als Ein-Zeilen-Pointer und ein
|
||||
`AGENTS.md` mit **nur** Projektspezifika plus Verweis auf die
|
||||
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).
|
||||
@@ -0,0 +1,35 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0036"
|
||||
status: done
|
||||
created: 2026-08-11
|
||||
milestone: M2
|
||||
priority: medium
|
||||
related:
|
||||
- "docs/adr/0013-gruppenregeln-kanonisch-mit-pruefung.md"
|
||||
---
|
||||
|
||||
# Rollout Gruppenregeln-Pointer: `ThreadNet-Web`
|
||||
|
||||
> Folge-Issue aus [ADR-0013](../adr/0013-gruppenregeln-kanonisch-mit-pruefung.md)
|
||||
> (Feldtest F-011: 4 von 5 Komponenten trugen keine Pointer-Datei).
|
||||
> `gitlab_iid` folgt mit dem ersten Spiegel-Lauf.
|
||||
|
||||
In `ThreadNet-Web` anlegen: `CLAUDE.md` als Ein-Zeilen-Pointer und ein
|
||||
`AGENTS.md` mit **nur** Projektspezifika plus Verweis auf die
|
||||
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).
|
||||
@@ -0,0 +1,34 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0037"
|
||||
status: done
|
||||
created: 2026-08-11
|
||||
milestone: M2
|
||||
priority: medium
|
||||
related:
|
||||
- "docs/adr/0013-gruppenregeln-kanonisch-mit-pruefung.md"
|
||||
---
|
||||
|
||||
# Rollout Gruppenregeln-Pointer: `threadnet-call`
|
||||
|
||||
> Folge-Issue aus [ADR-0013](../adr/0013-gruppenregeln-kanonisch-mit-pruefung.md)
|
||||
> (Feldtest F-011: 4 von 5 Komponenten trugen keine Pointer-Datei).
|
||||
> `gitlab_iid` folgt mit dem ersten Spiegel-Lauf.
|
||||
|
||||
In `threadnet-call` anlegen: `CLAUDE.md` als Ein-Zeilen-Pointer und ein
|
||||
`AGENTS.md` mit **nur** Projektspezifika plus Verweis auf die
|
||||
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).
|
||||
@@ -0,0 +1,35 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0038"
|
||||
status: done
|
||||
created: 2026-08-11
|
||||
milestone: M2
|
||||
priority: medium
|
||||
related:
|
||||
- "docs/adr/0013-gruppenregeln-kanonisch-mit-pruefung.md"
|
||||
---
|
||||
|
||||
# Rollout Gruppenregeln-Pointer: `thread-net-git`
|
||||
|
||||
> Folge-Issue aus [ADR-0013](../adr/0013-gruppenregeln-kanonisch-mit-pruefung.md)
|
||||
> (Feldtest F-011: 4 von 5 Komponenten trugen keine Pointer-Datei).
|
||||
> `gitlab_iid` folgt mit dem ersten Spiegel-Lauf.
|
||||
|
||||
In `thread-net-git` anlegen: `CLAUDE.md` als Ein-Zeilen-Pointer und ein
|
||||
`AGENTS.md` mit **nur** Projektspezifika plus Verweis auf die
|
||||
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).
|
||||
@@ -0,0 +1,35 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0039"
|
||||
status: done
|
||||
created: 2026-08-11
|
||||
milestone: M2
|
||||
priority: medium
|
||||
related:
|
||||
- "docs/adr/0013-gruppenregeln-kanonisch-mit-pruefung.md"
|
||||
---
|
||||
|
||||
# Rollout Gruppenregeln-Pointer: `threadnet-operating`
|
||||
|
||||
> Folge-Issue aus [ADR-0013](../adr/0013-gruppenregeln-kanonisch-mit-pruefung.md)
|
||||
> (Feldtest F-011: 4 von 5 Komponenten trugen keine Pointer-Datei).
|
||||
> `gitlab_iid` folgt mit dem ersten Spiegel-Lauf.
|
||||
|
||||
In `threadnet-operating` anlegen: `CLAUDE.md` als Ein-Zeilen-Pointer und ein
|
||||
`AGENTS.md` mit **nur** Projektspezifika plus Verweis auf die
|
||||
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).
|
||||
@@ -0,0 +1,35 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0040"
|
||||
status: open
|
||||
created: 2026-08-11
|
||||
milestone: M2
|
||||
priority: low
|
||||
related:
|
||||
- "docs/design/done/2026-08-11-neckbeard-migration.md"
|
||||
gitlab_iid: "35"
|
||||
---
|
||||
|
||||
# neckbeard-Rückmeldungen aus dem Feldtest einreichen
|
||||
|
||||
> Eigener Akt, bewusst nicht Teil der Migration (Nicht-Ziel im Design).
|
||||
> `gitlab_iid` folgt mit dem ersten Spiegel-Lauf.
|
||||
|
||||
Das fertige Übergabedokument liegt unter
|
||||
[docs/sources/migration/neckbeard-uebergabe-feldtest.md](../sources/migration/neckbeard-uebergabe-feldtest.md)
|
||||
— von dort als Issues im neckbeard-Repo (`oss-projekte/ai/neckbeard`) einreichen,
|
||||
mit Feldtest-Evidenz aus Gate 2 des Design-Dokuments:
|
||||
|
||||
1. Viele Repos, ein Regelwerk (Lücke 1; hiesige Lösung: ADR-0013)
|
||||
2. Komponenten-Artefakt (Lücke 2)
|
||||
3. Meilenstein-Konzept (Lücke 3)
|
||||
4. SHA-Auflösung in validate (Lücke 4; Referenz: `pruefe_prosa.py`)
|
||||
5. Git-Hygiene-Prüffamilie (Lücke 5; Referenz: `gruppenpruefung.py`)
|
||||
6. Sperrlisten für stillgelegte externe Ziele (Lücke 6, Teil-Lösung)
|
||||
7. Prioritätsfeld: Feldtest-Evidenz für das im Schöpfungs-AAR vertagte
|
||||
Revisit (71/71 mit Priorität, getrennt vom Meilenstein)
|
||||
8. `validate.py` lehnt Verzeichnis-Links ab (Meinungsfrage)
|
||||
9. Definierter Ort für Projektregeln im übernommenen AGENTS.md
|
||||
10. ADR-Pflicht bei dauerhaften Ausnahmen fehlt upstream
|
||||
11. Stillstandsprüfungs-Prinzipien als Muster für eine
|
||||
Laufzeit-Prüf-Familie neben validate.py
|
||||
@@ -0,0 +1,40 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0041"
|
||||
status: done
|
||||
created: 2026-08-11
|
||||
milestone: M2
|
||||
priority: low
|
||||
related: []
|
||||
---
|
||||
|
||||
# wartegrund der 7 importierten waiting-Issues präzisieren
|
||||
|
||||
> Nacharbeit aus dem Issue-Import (Protokoll unter
|
||||
> `docs/sources/migration/`). `gitlab_iid` folgt mit dem Spiegel-Lauf.
|
||||
|
||||
0002, 0004, 0008, 0014, 0021, 0025, 0027 tragen den generischen
|
||||
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.
|
||||
@@ -0,0 +1,55 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0042"
|
||||
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
|
||||
|
||||
> Die Schritte, die nur sorb ausführt (Nicht-Ziele der Migration:
|
||||
> kein Push, kein API-Write durch die Session).
|
||||
|
||||
1. ~~Branch `Neckbeard-v0.1.1-migration-1` sichten und nach `main`
|
||||
bringen; Push über git.lab (Mirror zieht nach).~~ ✅ Erledigt
|
||||
2026-08-11 (Fast-Forward-Merge + Push, von sorb beauftragt).
|
||||
2. Ersten Spiegel-Lauf ausführen: `python3 scripts/spiegel_issues.py
|
||||
--ausfuehren` (legt 0033–0042 auf GitLab an); danach die vergebenen
|
||||
iids als `gitlab_iid` nachtragen — ab dann meldet
|
||||
`gruppenpruefung.py` die Hinweise nicht mehr.
|
||||
3. CI-Schedule für den Job `gruppenpruefung` anlegen (wie
|
||||
Stillstandsprüfung; Gruppen-Token mit `read_api` liegt als maskierte
|
||||
Variable bereits vor, siehe management#31).
|
||||
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.
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user