Repository navigation
release: 2.0.1 - #20
Merged
Merged
Conversation
…s Lager (SV-1, SV-2)
Audit vom 16.10.2026, SV-1 und SV-2 (beide Mittel).
SV-1: Jede Anfrage nach einer Lager-Marke rechnete zwei SELECT SUM ueber
blob_marken, synchron im WebSocket-Handler und damit auf dem einzigen
Event-Loop: je Adresse ueber bis zu 25 600 Zeilen (gemessen 76 ms), ueber
den ganzen Relay ueber bis zu 204 800 (498 ms). Solange lief keine
Zustellung, keine Anmeldung, keine Quittung - fuer alle Verbindungen.
SV-2: Die Tagesmenge des ganzen Relays (200 GiB) wurde beim AUSSTELLEN
der Marken angerechnet. Eine Marke kostet keinen Upload; neun kostenlose
Adressen holten sich die 200 GiB in Sekunden, luden nichts hoch, und
danach bekam jeder Nutzer 24 Stunden lang "Zwischenlager fuer heute
ausgelastet". Der Relay kann das nicht selbst beheben: er erfaehrt nie,
ob hochgeladen wurde, und erreicht das Lager nicht.
Behebung:
- relay_server.py: blob_marken haelt eine Zeile je Adresse und
angefangener Stunde (blob_menge_anrechnen), nicht mehr eine je Marke.
Die Summe je Adresse laeuft damit ueber hoechstens 25 Zeilen, und die
Tabelle waechst mit den Adressen statt mit den Marken. Gezaehlt werden
Stunden, deren Ende in den letzten 24 Stunden liegt (BLOB_FENSTER =
25 h, die strengere Richtung). Alte Einzelzeilen zaehlen bis zu ihrem
Ablauf weiter.
- relay_server.py: die Summe ueber den ganzen Relay und
BLOB_TAGESMENGE_GESAMT sind entfernt. Die Grenze je Adresse bleibt beim
Ausstellen (wer Marken verfallen laesst, verbraucht sein eigenes
Kontingent).
- blob_server.py: das Lager zaehlt die Tagesmenge des ganzen Lagers nach
Bytes, die wirklich unter ihrem Namen liegen (TAGESMENGE_GESAMT, Env
BITDM_BLOB_QUOTA_TOTAL, 200 GiB), je Stunde, mit Vormerkung laufender
Uploads und Nachzaehlen aus den Aenderungszeiten nach einem Neustart.
Abgebrochene Uploads kosten nichts. Am Dach 507 "Zwischenlager fuer
heute ausgelastet" - fuer die App seit jeher LagerVoll ("spaeter noch
einmal"), also auch fuer 2.0.0 ohne Aenderung verstaendlich.
- docs/ZWISCHENLAGER.md und deploy/README.md nachgezogen.
Verträglichkeit: kein Rahmen und keine Antwort des Relays aendert sich
fuer die App; "Zwischenlager fuer heute ausgelastet" kommt jetzt vom
Lager als 507 statt vom Relay als Fehlerrahmen. Beim Aufspielen zuerst
das Lager, dann den Relay - sonst gibt es dazwischen keine Gesamtgrenze
ausser BITDM_BLOB_MIN_FREE.
Tests (ohne die Behebung rot, gegengeprueft):
test_sv1_marken_einer_stunde_sind_eine_zeile,
test_sv1_keine_summe_ueber_den_ganzen_relay,
test_sv2_marken_anderer_adressen_sperren_niemanden_aus (ersetzt
test_die_tagesmenge_des_ganzen_relays, die den Befund festschrieb),
test_blob.py::test_sv2_* (Tagesmenge nach geschriebenen Bytes,
Abbruch, Vormerkung, Neustart, Fenster).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…(KR-2, Server)
Audit vom 16.10.2026, KR-2 (Mittel, mit SV-9 zusammengefuehrt).
Bis 2.0.0 signiert die App bei der Anmeldung das Nonce des Relays roh:
/ws `nonce` bzw. `nonce || uint32be(geraet)`, /register
`nonce || sha256(buendel)` - ohne Zweck, ohne Relay, ohne Adresse. Den
Teil "fremde Formate signieren" (33 Byte 0x05||K, 64 Byte Registrierung)
schliesst die App seit 2.0.0 selbst (Nonce genau 32 Byte). Offen war die
Domaenentrennung: ein boesartiger Relay A reicht die Challenge von Relay
B an das Opfer weiter und meldet sich mit der Signatur bei B als das
Opfer an (Warteschlange abholen und damit loeschen, Geraet mit 4409
verdraengen, eigenes Buendel registrieren).
Behebung im Relay - beide Formen werden angenommen:
- Neue Form, gekennzeichnet durch das Feld `relay` (der Name, unter dem
die App den Relay erreicht hat):
/ws: "BitDM-Relay-Anmeldung-v1\n" host "\n" adresse "\n"
geraet "\n" nonce
/register: "BitDM-Relay-Registrierung-v1\n" host "\n" nonce
sha256(buendel)
Angenommen nur fuer Namen aus BITDM_RELAY_NAMEN; eine Signatur fuer
einen anderen Relay gilt hier nicht. Das Geraet steht ausdruecklich in
den Bytes, auch Geraet 1. Mit Geraeteliste unterschreibt der
Geraeteschluessel dieselben Bytes hinter ANMELDE_PRAEFIX.
- Challenge und Antwort auf /register/challenge nennen die Namen
(`relay`), wenn welche eingestellt sind; ohne BITDM_RELAY_NAMEN bleiben
sie Byte fuer Byte wie bisher.
- Ohne `relay` gilt die alte Form unveraendert - die App 2.0.0 meldet
sich weiter an. BITDM_ANMELDUNG_PFLICHT=1 weist die alte Form ab; das
ist der Schalter fuer spaeter, Vorgabe aus.
- install-relay.sh traegt BITDM_RELAY_NAMEN=<domain> in die Unit ein,
deploy/README.md beschreibt beide Variablen. Auf relay.bitdm.net muss
beim Aufspielen "relay.bitdm.net,<onion>.onion" gesetzt werden.
Wirksam gegen den Angriff wird das erst mit der App-Seite (folgender
Commit) und endgueltig mit BITDM_ANMELDUNG_PFLICHT=1, wenn keine App vor
der neuen Form mehr unterwegs ist.
Tests: test_kr2_* in test_relay.py (neue Form meldet an, bindet Geraet,
fremder Relay-Name abgewiesen bei /ws und /register ohne Nonce-Verbrauch,
Geraeteliste, Schalter, Challenge mit/ohne Namen, Bytes fuer die App).
Ohne die Behebung rot: 7 von 11; die uebrigen 4 sind Gegenproben
(alte Form der App 2.0.0 gilt weiter, fremder Name wird abgewiesen).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…R-2, App)
Audit vom 16.10.2026, KR-2, App-Teil. Gegenstueck zum vorigen Commit im
Relay.
Die 32-Byte-Pruefung (seit 2.0.0) haelt fremde Formate ab, aber nicht die
weitergereichte Challenge eines anderen Relays. relay_client.dart
signiert jetzt in der neuen Form, wenn die Challenge (bzw. die Antwort
auf /register/challenge) im Feld `relay` den eigenen Namen nennt:
- /ws: "BitDM-Relay-Anmeldung-v1\n" host "\n" adresse "\n"
geraet "\n" nonce (Geraet 1 ausdruecklich)
- /register: "BitDM-Relay-Registrierung-v1\n" host "\n" nonce
sha256(buendel)
`host` ist baseUri.host - der Name, unter dem die App den Relay erreicht
hat, nie ein Wert des Relays. Die Antwort traegt `relay: host`; mit
Geraeteliste unterschreibt der Geraeteschluessel dieselben Bytes hinter
anmeldePraefix.
Nennt der Relay keine Namen (Relay bis 2.0.0) oder fehlt der eigene
darin (etwa die Onion-Adresse nicht eingetragen), bleibt es bei der
alten Form - kein Aussperren, in keiner Reihenfolge des Aufspielens.
Einem Relay, der die Liste absichtlich weglaesst, gibt die App nur, was
sie ihm bisher auch gab; ein Relay mit BITDM_ANMELDUNG_PFLICHT=1 nimmt
diese alte Form nicht mehr an.
Tests:
- test/net/anmeldung_kontext_test.dart: ein nachgebauter Relay prueft die
Signatur gegen die ausgeschriebenen Bytes (neue Form mit Name, Geraet
in den Bytes, Rueckfall ohne/mit fremder Liste, Unsinn im Feld), dazu
der echte relay_server.py mit BITDM_ANMELDUNG_PFLICHT=1: ein
vollstaendiger Nutzer kommt online. Ohne die App-Aenderung rot (neue
Form, Geraet, echter Relay), gegengeprueft.
- test/support/relay_process.dart: Test-Relays laufen mit
BITDM_RELAY_NAMEN=127.0.0.1, damit jeder App-Test gegen den echten
Relay die neue Form faehrt (Registrierung, Geraeteliste, mehrere
Geraete). test/net und test/core: 429 gruen.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…icht gibt (SV-11)
Audit vom 16.10.2026, SV-11 (Hinweis).
install.sh schloss mit "Settings > Receiving > server address,
wss://<domain>". Die App hat diese Einstellung seit dem 26.07.2026 nicht
mehr (docs/EIGENER-SERVER.md); der Relay wird beim Bauen gewaehlt
(--dart-define=BITDM_RELAY). Wer einen Relay aufsetzte, suchte die
Einstellung vergeblich und erfuhr nicht, warum. docs.html ("In der App
eintragen") sagte dasselbe.
Behebung: install.sh und docs.html sagen jetzt, dass die ausgelieferten
Apps nur mit relay.bitdm.net sprechen und wie man die App fuer einen
eigenen Relay baut (BITDM_RELAY, BITDM_RELAY_ONION, BITDM_LAGER), samt
Hinweis auf BITDM_RELAY_NAMEN fuer die Onion-Adresse (KR-2).
Test: test_sv11_install_sh_verweist_auf_keine_einstellung_die_es_nicht_gibt
(ohne die Behebung rot, gegengeprueft) - prueft auch, dass die App den
genannten Schalter BITDM_RELAY noch hat.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…SV-8) Audit vom 16.10.2026, SV-8 (Hinweis). push_endpunkt_gueltig prueft Schema, Host (push.bitdm.net) und Pfad (/up...), aber nicht Port und Benutzerangabe. "https://push.bitdm.net:<beliebig>/up..." wurde angenommen; ohne BITDM_PUSH_TARGET (Vorgabe fuer fremde Relays, die hinausduerfen) ginge der leere POST genau dorthin - an jeden Port der Maschine hinter push.bitdm.net, sobald der Relay einmal hinaus darf. Behebung: nur Port 443 oder keiner, keine Benutzer-/Passwortangabe; ein Port ausserhalb 0..65535 gilt als ungueltig statt als Ausnahme. UnifiedPush-Endpunkte haben beides nie, die App 2.0.0 ist nicht betroffen. Test: test_sv8_push_endpunkt_ohne_fremden_port_und_ohne_nutzer (ohne die Behebung rot, gegengeprueft). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…(SV-10) Audit vom 16.10.2026, SV-10 (Hinweis). client_ip nahm den ersten Eintrag von X-Forwarded-For, gleich von wem die Verbindung kam. Mit den Vorlagen in deploy/ war das nicht ausnutzbar (nginx ersetzt die Kopfzeile). Lauscht ein selbst betriebener Relay aber versehentlich ohne nginx nach aussen, genuegten `X-Forwarded-For: 127.0.0.1` und `X-BitDM-Onion: 1`, und jede Bremse je Herkunft war aus. Behebung: client_ip liefert request.client.host. Die Kopfzeile wertet uvicorn bereits aus, und nur, wenn der unmittelbare Gegenueber nginx auf der Schleife ist (--proxy-headers --forwarded-allow-ips 127.0.0.1 in der Unit, bei uvicorn ohnehin die Vorgabe). Hinter nginx kommt damit dieselbe IP heraus wie bisher; von jedem anderen Gegenueber dessen eigene. Damit zaehlt auch X-BitDM-Onion/-Kreis nur ueber die Schleife. Tests: test_sv10_ohne_nginx_hilft_eine_gefaelschte_kopfzeile_nichts (ohne die Behebung rot, gegengeprueft) und die Gegenprobe hinter nginx. Der Testhelfer _Kopf traegt X-Forwarded-For jetzt auch in `client` ein, so wie uvicorn hinter nginx; `gegenueber` stellt den Fall ohne nginx nach. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Audit vom 16.10.2026, SV-5 (Niedrig). Die Behebung von B3 gibt jeder Herkunft acht eigene Nonce-Plaetze je (Adresse, Geraet); am Dach des Paares (16 Herkuenfte x 8 = 128) faellt aber das aelteste, gleich von welcher Herkunft. Herkunft war bei IPv6 das /64 - ein einziger Heimanschluss (/56 = 256 /64) brachte die 16 Herkuenfte kostenlos zusammen und verdraengte das Nonce des Opfers zwischen dessen Challenge und /register. Behebung: nonce_herkunft zaehlt IPv6 je /48 (limit_schluessel bekommt dafuer v6_praefix). Ein Anschluss ist damit eine Herkunft und verdraengt nur sich selbst; fuer 16 Herkuenfte braucht ein Stoerer 16 Anschluesse. Die Bremse je Herkunft bleibt beim /64 - je /48 wuerde sie ganze Mobilfunknetze in einen Eimer werfen; bei den Nonces teilen sich nur Challenges fuer dieselbe Adresse einen Platz. Bewusst nicht gewaehlt: fremde Eintraege am Dach gar nicht mehr zu verdraengen - dann hielte ein Stoerer das Dach dauerhaft voll. Die zustandslose Challenge (Vorschlag des Audits) waere die vollstaendige Loesung, braucht aber, dass die App ihr Nonce bei /register mitschickt; das tut 2.0.0 nicht. Tests: test_sv5_ein_heimanschluss_verdraengt_das_nonce_nicht, test_sv5_die_bremse_zaehlt_weiter_je_64 (beide ohne die Behebung rot, gegengeprueft). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…(SV-7) Audit vom 16.10.2026, SV-7 (Niedrig). Jede gepufferte Zeile startete eine eigene Aufgabe, und jede baute einen neuen httpx.AsyncClient - samt TLS-Kontext und CA-Buendel, synchron auf dem Event-Loop (gemessen 180 ms unter Windows), auch auf relay.bitdm.net, wo der POST ohne TLS ueber die Schleife geht. Die Zahl der Aufgaben war unbegrenzt; wer an ein abwesendes Geraet mit Push-Endpunkt flutete, auch an eine eigene Zweitadresse, liess die Zustellung fuer alle stocken. Behebung: - push_klient(): EIN AsyncClient, gemerkt mit seiner Event-Loop (im Betrieb gibt es eine; die Tests bekommen je asyncio.run einen neuen), geschlossen im lifespan. - stosse_im_hintergrund_an: hoechstens ein Anstoss je (Adresse, Geraet) unterwegs; was waehrenddessen kommt, loest danach GENAU EINEN weiteren aus - eine Zeile, die nach dem Wecken eintraf, bleibt nicht ohne Anstoss liegen. - Hoechstens BITDM_PUSH_PARALLEL (200) Geraete gleichzeitig; darueber entfaellt der Anstoss (gemeldet ueber _push_ging_daneben), die Nachricht bleibt wie bei einem stummen Push-Server in der Warteschlange. Fuer die App aendert sich nichts: sie wird weiterhin geweckt, wenn etwas fuer sie liegt. Tests (ohne die Behebung rot, gegengeprueft): test_sv7_ein_client_fuer_alle_anstoesse, test_sv7_eine_flut_an_ein_geraet_weckt_es_nicht_hundertmal, test_sv7_die_zahl_gleichzeitiger_anstoesse_ist_gedeckelt. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Audit vom 16.10.2026, SV-3 (Mittel). Neue Adressen werden ab IDENTITAETEN_MAX (50 000) mit 507 abgewiesen. Eine Registrierung kostete je Herkunft nur 5 von 120 Marken der allgemeinen Bremse (rund 34 500 am Tag), bei IPv6 zaehlte jedes /64 als eigene Herkunft (ein Heimanschluss hat 256), ueber Tor ohne Circuit-Kennung gab es keine Bremse. Ein Akteur fuellte das Dach in Minuten und hielt es mit einer Erneuerung im Monat; danach konnte sich niemand mehr neu anmelden. Behebung (drei Riegel, alle nur fuer NEUE Adressen): 1. neue_adresse_ok: eigener, enger Eimer je Herkunft und Tag (BITDM_NEUE_ADRESSEN, 100), IPv6 je /48. Ueber den Onion-Dienst zusaetzlich ein gemeinsamer Eimer aller Tor-Nutzer (BITDM_NEUE_ADRESSEN_ONION, 500), weil Circuits billig zu wechseln sind. Geprueft erst nach dem Besitznachweis; 429 "zu viele neue Adressen von hier". Bundle erneuern und weitere Geraete anlegen kostet nichts daraus. 2. raeume_vergessene_geraete raeumt Adressen, die sich nach dem Registrieren nie per /ws angemeldet haben (last_seen = 0), nach BITDM_NIE_VERBUNDEN_TTL (48 h) statt nach 30 Tagen. Bestandszeilen tragen den Zeitpunkt der Wanderung und sind nicht betroffen; eine App, die doch noch kommt, bekommt 4401 und registriert sich neu. 3. Ab 80 % des Dachs eine Zeile im Journal, hoechstens einmal je Stunde, ohne Adresse und ohne Herkunft. Preis, bewusst: hinter einer IPv4 (Carrier-Grade-NAT) bzw. in einem /48 teilen sich mehrere Kunden den Eimer fuer neue Adressen; 100 am Tag sind fuer diesen Relay viel und per Umgebungsvariable einstellbar. Ganz schliesst sich SV-3 so nicht: wer viele IPv4-Adressen oder /48 hat, fuellt das Dach weiterhin, nur langsamer. Ein Arbeitsnachweis bei der Registrierung braeuchte eine App-Aenderung und wuerde 2.0.0 aussperren. App 2.0.0: unveraendert verstaendlich (429 bei der Registrierung kennt sie schon). Test-Relays der App-Tests bekommen BITDM_NEUE_ADRESSEN=5000, aus demselben Grund wie BITDM_RATE_BURST. Tests (ohne die Behebung rot, gegengeprueft): test_sv3_* - ein Anschluss legt nicht beliebig viele Adressen an, wer schon da ist kommt durch, gemeinsamer Tor-Eimer, nie verbundene Adresse nach zwei Tagen, Meldung vor dem Dach. App: test/net und test/core gegen den echten Relay 429 gruen. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
neue_adresse_ok lief in /register vor den Pruefungen, an denen eine Registrierung noch scheitern kann (Kyber-Signaturen, Geraeteliste, Adressdach 507, Speichern). Jeder Fehlversuch kostete so die ganze Herkunft eine Marke; ein dauerhaft scheiternder Client hinter einer geteilten IPv4 (CGNAT) hielt den Eimer fuer alle dort leer. Jetzt wird die Marke erst im Zweig fuer eine noch unbekannte Adresse genommen, nach dem Adressdach und direkt vor dem INSERT, unter schreibsperre: "Adresse unbekannt" und "Marke genommen" sind ein Schritt, parallele Anfragen kommen nicht ueber die Grenze, und zwei gleichzeitige erste Registrierungen derselben Adresse zahlen einmal. Laesst sich das Buendel danach nicht speichern, geht die Marke zurueck (_gib_zurueck). Scheitert ueber Tor der gemeinsame Onion-Eimer, bekommt der Circuit-Eimer seine Marke ebenfalls zurueck. Tests: Fehlversuche (Kyber, Geraeteliste, INSERT, Adressdach) kosten keine Marke; die Grenze von 100 je Herkunft und Tag gilt genau, auch mit Fehlversuchen dazwischen und bei 20 gleichzeitigen Anfragen; eine bekannte Adresse (Erneuern, weitere Geraete) zahlt nichts. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
RELAY_NAME_MUSTER ([a-z0-9.:-]{1,253}) liess wegen des Doppelpunkts fuer
IPv6 auch "relay.bitdm.net:443" als Namen durch. Die App nennt ihren
Namen ohne Port (Uri.host), fand sich nicht in der Liste und fiel still
auf die alte Anmeldeform zurueck; mit BITDM_ANMELDUNG_PFLICHT=1 waere sie
ausgesperrt worden, ohne Hinweis im Journal.
Jetzt prueft relay_name_ok jeden Eintrag: Hostname aus Labels nach
RFC 1123, IPv4 oder IPv6 (ein Doppelpunkt nur dort, keine Klammern,
keine Zonenkennung). Ungueltige Eintraege werden nie uebernommen.
melde_relay_namen() schreibt beim Start (lifespan) jeden abgelehnten
Eintrag laut mit [!] ins Journal und die gueltigen Namen einmal mit
[i]; ein falscher Eintrag laesst den Dienst weiterlaufen.
deploy/README.md nennt die Regel.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- pubspec 2.0.1+23, fassung.dart 2.0.1 (fassung_test gruen) - CHANGELOG: Datum 24.10.2026, Audit-Befunde des Relays (#19), Hinweis "erst das Zwischenlager, dann den Relay" - fastlane changelogs/23.txt de-DE (385 Zeichen) und en-US (327) - fdroid/com.bitdm.bitdm.yml: versionName 2.0.1, versionCode 23; commit bleibt Nullen, den traegt der Tag-Workflow ein Downloads, Badges, Hashes und install.sh folgen nach dem Tag und dem Signieren (Checkliste Phase 6). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Store-Texte 23.txt (de 434, en 390 Zeichen): versprachen, ein Relay koenne eine Abmeldung nicht mehr verschweigen; das widerspricht "Was bleibt" im CHANGELOG. Jetzt: Kontakte erfahren sie auch ueber die Nachrichten. Dazu: dauerhaft im Hintergrund nur noch mit "Nie". - CHANGELOG: Schutz vor Sitzungen unter fremder Geraetekennung gilt erst mit der Pflicht ab 2.0.2 (geraeteNachweisPflicht ist in 2.0.1 aus); /48 nur fuer Nonces und neue Adressen; camera 0.12.1 genannt. - README/README.de: Abzeichen und /DVersion auf 2.0.1. - Bluetooth-Seite: versprach, nur Kontakte erkennten, wer sendet. Bis 2.0.1 sind die Funkpakete nicht eigens verschluesselt (Buendel-Antwort offen mit user_id, real_messenger_core.dart; Fix liegt auf fix/nahfunk-verschluesselung, noch nicht ausgeliefert). Ehrlicher Text, de und en. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Henner4746
marked this pull request as ready for review
October 24, 2026 12:13
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.


Entwurf, wartet auf Henriks Ja. Fassungssprung auf 2.0.1+23 auf dem Stand von
mainplus #19 (Audit-Fixes; solange #19 offen ist, stehen dessen Commits hier mit im Diff).pubspec.yaml2.0.1+23,lib/fassung.dart2.0.1 (fassung_testgrün)CHANGELOG.md: Abschnitt 2.0.1 mit Datum 24.10.2026 — Geräteliste (fix(geraeteabmeldung): ein Relay, der eine Abmeldung verschweigt (§12) #14), App-Sperre (fix(sperre): Faktor nur mit frischem Nachweis, Sperre greift im Hintergrund (2.0.1) #18), Audit-Befunde des Relays (fix(relay): offene Befunde aus dem Audit vom 16.10. (SV-1, SV-2, SV-3, SV-5, SV-7, SV-8, SV-10, SV-11, KR-2) #19), certifi (fix(relay, ci): certifi 2026.10.23, py-Starter in der CI auf 3.12 #15)changelogs/23.txt(de 385, en 327 Zeichen)Noch nicht enthalten (kommt nach Tag und Signieren, Checkliste Phase 6): Downloads und Hashes auf der Seite und in den READMEs, Badges,
install.shREF.Ablauf:
C:\KI-Workstation\BitDM-RELEASE-2.0.1-CHECKLISTE.md. Reihenfolge beim Ausrollen: Zwischenlager → Relay → App.🤖 Generated with Claude Code