docs: capture session knowledge not yet in the repo

Externalises what this session held that the migrated repo did not:

- vision/threadnet.md: the three capabilities that justify the forks beyond
  rebranding (AV scanning into encrypted rooms, call-quality defaults with a
  client-side-only privacy line, expiring guest access via @concierge).
- issue 0043 (M5): case-insensitive uniqueness in the matrix-invitation prompt
  stage — the open residual of ADR-0011.
- issue 0044 (M5): auto-restart consumers on SOPS values-secret change — the
  footgun behind the on_conflict fix sitting inactive until a manual restart.
- issue 0045 (M1): report_event.admin_message_md unset — content reports
  dead-end with no contact path (verified still open against live config).
- sources/protokolle: the raw apo-call diagnosis history, including the four
  ruled-out hypotheses and the harmful DB write, as the source behind the AAR.

STATUS.md regenerated (M5 appears for the first time). validate, gen_status
--check, upstream_drift and pruefe_prosa all green in the CI image.
This commit is contained in:
Thore Cimbal
2026-08-11 12:00:00 +00:00
parent 28e1843e8d
commit f3417a4b24
6 changed files with 216 additions and 2 deletions
@@ -0,0 +1,38 @@
---
type: issue
id: "0043"
status: open
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`.
@@ -0,0 +1,41 @@
---
type: issue
id: "0044"
status: open
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.
@@ -0,0 +1,33 @@
---
type: issue
id: "0045"
status: open
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.
@@ -0,0 +1,77 @@
# Protokoll — Diagnose „@apo kann nicht telefonieren" (2026-08-11)
Rohes Verlaufsprotokoll der Fehlersuche, als Quelle zum AAR
[docs/aar/2026-08-11-apo-calls-profile-zeile.md](../../aar/2026-08-11-apo-calls-profile-zeile.md).
Der AAR verdichtet zu Befund und Lehre; dieses Protokoll hält die tatsächliche
Reihenfolge fest — **inklusive der Irrwege**, weil die Frage „wie konnte sich die
Diagnose so lange verlaufen?" nur am ungekürzten Weg zu beantworten ist.
## Ausgangslage
`@apo` konnte sich anmelden und schreiben, aber kein Call kam zustande. Dem
vorausgegangen war ein längerer Vorgang mit mehreren manuellen Eingriffen an
`@apo`s Konto, die das Problem nicht gelöst hatten — darunter ein selbst
ausgedachter, in die MAS-Produktions-DB geschriebener `sub`-Wert und das Beenden
aller Sitzungen. Diese Eingriffe waren real schädlich und haben Vertrauen und Zeit
gekostet; sie sind der Grund, warum der Nutzer auf strukturiertem Vorgehen bestand.
## Die vier Irrwege (jeder an einer Messung gescheitert)
1. **Fehlende Cross-Signing-Verifikation.** Entkräftet: `sorb`/`clark` haben
ebenfalls keine und telefonieren.
2. **Kaputter Browser-/Client-Zustand.** Entkräftet: ein frischer Client zeigte
dasselbe Verhalten → an das Konto gebunden, nicht an den lokalen Zustand.
3. **Safari-Spezifik.** Zurückgezogen.
4. **MAS↔Synapse-Geräte-Desync.** Entkräftet: beide Seiten kannten dieselben drei
Geräte.
Das Muster war der eigentliche Fehler: aus dem, was serverseitig sichtbar war,
wurde wiederholt auf eine Ursache geschlossen, ohne sie an einem funktionierenden
Konto gegenzuprüfen. Der teuerste Einzelfall war der erfundene `sub`-Wert —
`clark` hätte die Formel sofort widerlegt, wäre sie vor dem Schreiben geprüft
worden.
## Die Kette, die zum Fund führte
1. **Vergleich statt Einzelbetrachtung.** `open_id_tokens` über *alle* aktiven
Nutzer abgefragt: `@apo` = 0, alle anderen zweistellig+. Damit war klar: der
Call-Pfad scheitert für `@apo` am allerersten Schritt (kein OpenID-Token).
2. **Confounder entfernt.** Der Testraum war **unverschlüsselt** — Krypto/
Cross-Signing damit in einem Schritt raus (der ganze Block „18 Pseudo-Geräte
aus 6 Resets" war eine Sackgasse).
3. **Live-Mitschnitt beim echten Call-Klick.** Ergebnis: *nichts* — kein
`openid/request_token`, kein `call.member`-Event. Beides erzeugt das
Element-Call-Widget-iframe; beide fehlten → das iframe wird nie aktiv.
4. **Der Nutzer-Hinweis „Anzeigename konnte nicht gesetzt werden"** lieferte den
500er mit vollständigem Stacktrace: `TypeError: 'NoneType' object is not
subscriptable` in `_check_profile_size`.
5. **Der Kontext-Hinweis des Nutzers** — „`apo` ist ein Konto von vor der
Authentik-Integration" — drehte die Suche von „angesammelter Müll" auf
„Provisionierungs-Lücke".
## Grundursache und Fix
`@apo` fehlte die Zeile in Synapses `profiles`-Tabelle (Deaktivieren löscht das
Profil, Reaktivieren legt es nicht neu an; `@apo` ging durch sechs Resets).
Fehlende Zeile → jeder Displayname-Schreibvorgang crasht → kein Displayname →
Element-Call-Widget initialisiert nie → kein OpenID-Token, kein Call. Fix: die
fehlende Registrierungs-Default-Zeile per `INSERT` nachgetragen, an `clark`/
`calltest01` gegengeprüft. Danach: Displayname setzbar, `open_id_tokens` 0→6, Call
verifiziert. `@apo` war der einzige betroffene lebende Nutzer.
## Nebenstrang: die Sicherheitslücke
Beim Anlegen des Testkontos entstand die case-sensitive Dublette `boje`/`Boje`.
Der Faden führte zu einem Kontoübernahme-Vektor: MAS stand auf
`on_conflict: add` und hätte kollidierende Localparts an bestehende Konten
verknüpft. Fix `on_conflict: fail` (ADR-0011), erst nach MAS-Neustart aktiv —
was die zweite Lehre lieferte: eine SOPS-Secret-Änderung startet den Dienst nicht
neu.
## Was das Protokoll dem AAR voraushat
Der AAR nennt die Lehren; dieses Protokoll zeigt, dass sie **teuer erkauft** waren:
vier eingesammelte Hypothesen und ein schädlicher DB-Schreib gingen dem einen
`open_id_tokens`-Vergleich voraus, der die Sache in Minuten entschied. Die Lehre
„erst vergleichen, dann schreiben" steht im AAR als Satz — hier steht, warum sie
einen Satz wert ist.
+22
View File
@@ -25,6 +25,28 @@ threadnet-operating.
chirurgische Patches statt Umbauten.
- CI beweist Releases: Tag → Pipeline → Artefakt, keine Handbuilds.
## Was die Forks über das Rebranding hinaus leisten
Das Rebranding ist die sichtbarste, aber nicht die eigentliche Rechtfertigung der
Forks. Drei Fähigkeiten gehen bewusst über Upstream hinaus und sind der Grund,
dass ThreadNet eine eigene Produkt-Linie ist und kein umgefärbtes Element:
- **Virenprüfung bis in verschlüsselte Räume.** Upstream Element scannt Uploads
nur serverseitig und nur in unverschlüsselten Räumen. ThreadNet-Web ruft den
Scan client-seitig **beim Senden und beim Empfangen** auf — damit greift die
Prüfung auch in verschlüsselten Räumen und DMs, wo der Server den Inhalt nie
sieht. Das ist die inhaltliche Begründung des Web-Forks, nicht die Optik.
- **Call-Qualität als Voreinstellung, Privatsphäre als Grenze.** threadnet-call
hebt die Medien-Defaults an (bis 1440p/60fps, H.264 für Hardware-Beschleunigung
v.a. auf iOS/Safari) und lässt die Rauschunterdrückung **bewusst
client-seitig** — kein server-seitiges ML, das bereinigtes Audio anderer
Teilnehmer verarbeiten würde. Startwerte, keine harten Grenzen; die
Privatsphäre-Linie ist die harte.
- **Gäste mit Ablaufdatum.** Registrierung läuft ausschließlich über Authentik;
der @concierge-Bot macht daraus einen nachvollziehbaren Vorgang mit eingebautem
Ablauf und zweiteiliger Berechtigung — die Gruppe entscheidet, der Raum macht
sichtbar, wer wen eingeladen hat.
## Veröffentlichungsgrad
**Die Forks werden öffentlich** (entschieden 2026-08-06) — aber erst nach einem