Änderungen¶
T17 – Standort: Kern (GPS, Geo-Funktionen)¶
2026-10-24
- Neu:
LocationService(src/app/core/location/location.service.ts) bestimmt den eigenen Standort. Er holt auf Android die Berechtigung (locationodercoarseLocation), fragt den GPS mit hoher Genauigkeit ab und setzt den Fix inmyFix. Daneben gibt es die Signalelocatingunderrormit den Codeslocation-denied,location-timeoutundlocation-unavailable. - Im Browser (und im Mock) gibt es keinen echten GPS: nach 600 ms liefert der Dienst den Demo-Fix aus Luzern
(
47.05021,8.30934, Genauigkeit 8 m). Der GPS wartet höchstens 20 Sekunden; ein Fix gilt 10 Minuten lang als frisch (isFresh). - Neu:
src/app/core/location/geo.tsmit reinen Funktionen für Entfernung (Haversine), Anfangskurs, die acht Himmelsrichtungen und die Texte für Distanz, Koordinate, Genauigkeit und Alter des Fixes. Die Koordinaten runden auf sechs Nachkommastellen (roundCoord). - Das Android-Manifest hat die Berechtigungen
ACCESS_COARSE_LOCATIONundACCESS_FINE_LOCATION; der GPS ist als optionales Feature deklariert, damit die App auch ohne GPS läuft. Das Plugin@capacitor/geolocationist eingebunden. - Das Senden und Empfangen von Standorten ändert sich nicht: das lief schon mit T23 über die Host-API (
msg.sendmittype: "geo"). - Noch ohne Oberfläche: die Standort-Screens folgen mit T18, der Live-Standort mit T19.
T06 – Natives USB-Serial-Plugin, UsbSerialTransport und Keystore-Plugin¶
2026-10-24
- Auf Android spricht die App die Antenne jetzt über das native Capacitor-Plugin
UsbSerial(Java) an: 115200 Baud, 8N1, kein Flow Control, DTR und RTS sofort auf low. - Das Plugin sucht die Geräte über
UsbSerialProber(inklusiveCdcAcmSerialDriver), fragt die USB-Berechtigung an und meldet Daten, Abziehen und Lesefehler als Ereignisse. Die Reject-Codes übersetzt der Transport inpermission-denied,open-failedundno-device. - Der Platzhalter
UnavailableSerialTransportist entfallen: Auf Android liefertUsbSerialTransport, im Browser weiterhin das simulierte Gerät. Die empfangenen Bytes dekodiert der Transport mit einemTextDecoderim Stream-Modus, damit Mehrbyte-Zeichen über Paketgrenzen hinweg ganz bleiben. - Das native Keystore-Plugin
OffgridKeystorelegt den HMAC-Schlüsseloffgrid_unlocknicht exportierbar im Android Keystore an und rechnet nur den HMAC; X25519, HKDF und AES-GCM bleiben in TypeScript. - Nach einem
resumeder App verbindet sich die Antenne erneut, wenn siedisconnectedist. Der Manifest verlangt USB-Host. - Neu getestet:
UsbSerialTransportgegen ein gemocktes Plugin (Fehlercodes, UTF-8 über Chunk-Grenzen,detached).
T09 – Screens Verbinden und Name, Onboarding-Guard¶
2026-10-24
- Der Screen „Verbinden“ startet die Antennenverbindung, zeigt den Fortschritt als Live-Schrittliste und bietet „Weiter“ sowie „Erneut suchen“. „SCHRITT 1 VON 2“ erscheint nur im Onboarding.
- „Erneut suchen“ entsperrt ein gesperrtes Gerät neu (
unlock()), stattconnect()zu rufen: Bei einem gesperrten, aber verbundenen Gerät kehrtconnect()sofort zurück, ein fehlgeschlagenes Entsperren war darüber nicht wiederholbar. In allen anderen Zuständen sucht die Schaltfläche die Antenne weiterhin mitconnect(). - Der Screen „Name“ prüft den Anzeigamen nach 1 bis 24 UTF-8-Bytes, richtet ein frisches Gerät ein oder gibt den Namen an ein entsperrtes Gerät. Danach schliesst er das Onboarding ab und wechselt zu den Kontakten.
- Der funktionale Onboarding-Guard schützt alle Routen ausser
/verbindenund/name. Ohne abgeschlossenes Onboarding führt er zu/verbinden; bei einem laufenden App-Start wartet er auf die geladenen Einstellungen. - Mit
localStorage og.noseed = '1'startet die Browser-Demo ohne Demo-Daten und mit einem frischen Mock-Gerät. - Neue Tests prüfen Guard, Verbindungszustände, Navigation, Bytegrenzierung, Geräteeinrichtung und die sichtbaren Fehlertexte beider Screens.
T10 – Screen Kontakte¶
2026-10-24
- Neu: Der Screen „Kontakte“ (
/kontakte) listet die Kontakte mit letzter Nachricht, Vorschau, Zeit, ungelesenen Nachrichten und Signalbalken. Jede Zeile ist ein Link in den Chat. - Neu: Die Antennen-Statuszeile zeigt „Antenne verbunden“ mit der Frequenz des Geräts oder „Antenne getrennt“ mit „· Tippen zum Verbinden“. Die Frequenz liest die App beim Gerät ab (
radio.get), nicht aus einem festen Wert. - Neu: Ausstehende Kontakte (der Schlüssel fehlt noch) stehen als eigene, nicht klickbare Zeile mit „Schlüssel ausstehend“ hinter den Kontakten.
- Neu: Der Leerzustand mit „Noch keine Kontakte“, der Erklärung zum QR-Handshake und dem Button „Ersten Kontakt hinzufügen“.
- Neu im
MessagingService: das Signalcontacts(je Kontakt die letzte Nachricht, die Anzahl der ungelesenen Nachrichten und das Signal-Level, sortiert nach letzter Nachricht) undunreadCount(contactId). Die Liste lädt bei jeder Datenänderung neu. - Das Signal-Level einer Kontaktzeile kommt aus der Feldstärke und dem Alter der letzten eingehenden Nachricht: Älter als 15 Minuten zählt als „Ausser Reichweite“.
- Neue Tests prüfen Reihenfolge, Signalbalken, Vorschauen, Badge, die Statuszeile, den Leerzustand und das Hochschieben eines Kontakts bei einer neuen Nachricht.
T02 – UI-Basis 2: Zustandskomponenten¶
2026-10-24
- Neun Zustandskomponenten liegen unter
src/app/ui/:og-avatar,og-signal-bars,og-bannermit Aktion und Hinweis,og-step-list,og-message-bubble,og-composerundog-qr. Der Barrelsrc/app/ui/index.tsexportiert sie zusammen mit ihren Typen. og-avatarzeigt ein 5 × 5-Schlüsselraster in fünf Grössen und im Zustandpending.avatarPattern(seed)erzeugt deterministisch 25 spiegelsymmetrische Zellwerte mit mindestens fünf aktiven Zellen.og-signal-barszeigt die Stufen 0 bis 4 in den Grössenmdundsm.og-step-listunterstützt die Zuständedone,activeundtodomit Puls oder Spinner; bei reduzierter Bewegung sind die Animationen ausgeschaltet.og-message-bubblebildet eingehende und ausgehende Nachrichten in den Zuständenqueued,sent,deliveredundfailedab. Der fehlgeschlagene Zustand bietet „Erneut senden“ und löstretryaus.og-composerzählt Bytes ausschliesslich über den InputcountBytes, sperrt leere oder zu lange Texte standardmässig und sendet mit Enter. MitblockOverLimit=falsebleibt das Senden bei einer Überschreitung möglich.og-qrerzeugt ausdataein SVG-Raster mit dem Paketqrcodeund den Typen aus@types/qrcode. Die benötigten Farbwerte sind als einzige Hex-Ausnahme inui/qr/qr-colors.tsausgelagert.
T23 – Protokoll auf Offgrid Link v1 umgestellt¶
2026-10-24
- Die Antenne (Heltec V3) besitzt jetzt Identität, Kontaktschlüssel, Verschlüsselung, Zähler und Kontaktliste. Die App ist Oberfläche und Verlaufsspeicher und spricht
die Host-API „Offgrid Link v1“ (JSON Lines über USB-Serial). Verbindliche Quelle sind die Spezifikationen des Firmware-Teams;
docs/esp-schnittstelle.mdverweist darauf. - Neu:
SerialClient(Zeilen,ids, höchstens 4 Anfragen in Flight, Zeitlimits 2 s und 5 s,sys.helloalle 500 ms, Heartbeat alle 5 s),AntennaPortmit den Zuständendisconnected,unprovisioned,lockedundunlockedsowie das Mock-GerätMockDevice, das für den Browser dieselben Befehle, Antworten und Ereignisse liefert. - Neu:
KeystoreServicehält das Entsperr-Geheimnis (Android Keystore bzw. Arbeitsspeicher im Browser). Das Token wird mit X25519, HKDF und AES-256-GCM zu 60 Bytes verpackt und stimmt mit dem Testvektor der Spezifikation überein. Das native Plugin folgt mit T06. - Nachrichten gehen als Klartext über
msg.send; längere Texte als 83 Bytes teilt die App in mehrere Nachrichten. Der Status kommt ausmsg.status, eingehende Nachrichten ausmsg.received.maxContentBytesgibt das gemeldete Limit unverändert zurück. - Das Pairing macht die Antenne:
pair.beginliefert den QR-Text,pair.completeden Kontakt samt Fingerabdruck. Der Fingerabdruck ersetzt das frühere Vergleichsbild. - Die Datenbank hat kein Schlüssel-Feld mehr:
contactsspiegelt die Kontakte der Antenne,messageshatctrundmsg_refstatt der Funk-Id, die Tabellehandshakesentfällt. Migration 1 wurde dafür einmalig umgeschrieben (noch keine veröffentlichte App). - Entfernt: die App-seitige Verschlüsselung von Nachrichten und das Vergleichsbild aus Schlüsseln. Skills und Doku wurden auf den neuen Vertrag umgestellt.
T04 – Datenbank, Repositories, Settings, App-Start¶
2026-10-24
- Auf Android speichert die App Kontakte, Nachrichten, Koordinaten, Handshakes und Einstellungen in der SQLite-Datenbank
offgrid. (Die Tabelle für Handshakes entfiel mit T23.) Migration 1 legt das Schema fest;PRAGMA user_versionsteuert spätere Migrationen. - Im Browser arbeiten dieselben Repositories auf Daten im Arbeitsspeicher. Die Plattform-Weiche in
app.config.tswählt SQLite oder Memory und stellt für jede Variante dieselbe Instanz bereit. - Kontakte, Nachrichten, Koordinaten, Handshakes und Einstellungen haben jeweils ein abstraktes Repository sowie eine SQLite- und eine Memory-Implementierung. Das Löschen eines Kontakts entfernt in beiden Varianten seine Nachrichten und Koordinaten.
- Der Browser startet mit Demo-Kontakten und sechs Nachrichten (mit T23: vier Kontakte, „Jonas Keller“ ist ausstehend). Mit
localStorage og.noseed = '1'startet er leer.DataChangedServicemeldet Datenänderungen über eine Version. SettingsServicestellt Anzeigename, Install-Id, Hintergrundempfang, Vibration und Onboarding-Status bereit. Die Install-Id wird beim ersten Start erzeugt und danach weiterverwendet.- Der App-Start öffnet die Datenbank, lädt die Einstellungen und bereitet auf Android Statusleiste und Splashscreen vor. Der Antennenstart kam mit T23 dazu.
T03 – Zeitformate, Hex/Base64, Signal-Level¶
2026-10-23
- Alle Zeiten der App werden in der Zeitzone
Europe/Zurichformatiert: Uhrzeit, „Gestern“, Wochentag, Datum für die Tagestrenner im Chat und „zuletzt …“ in der Kontaktliste. - Die Frequenz der Antenne erscheint als MHz-Text (z. B. „868 MHz“).
- Hex- und Base64-Hilfen für Bytes. (Die ursprünglich gebauten Dienste für Schlüssel und Vergleichsbild entfielen mit T23, weil die Antenne Schlüssel und Verschlüsselung übernimmt.)
- Die Signalstärke wird aus Feldstärke und letztem Kontakt zu fünf Leveln; ein Signal älter als 15 Minuten zählt als „Ausser Reichweite“.
- Die Länge von Texten und Anzeigenamen wird in UTF-8-Bytes ohne JSON-Escapes gerechnet. Der Gerätevertrag „OffGrid Link“ setzt maximal 83 Bytes für Textinhalte (§7.5) und 24 Bytes für Anzeigenamen (§7.2/§7.3).
T01 – UI-Basis 1: Grundkomponenten¶
2026-10-23
- 13
og--Grundkomponenten liegen untersrc/app/ui/: Icon, Button, Link-Button, Icon-Button, Topbar, Abschnittstitel, Karte, Info-Zeile, Statuspunkt, Badge, Eingabefeld, Schalter und Segmentumschalter. - Alle Komponenten sind Standalone, nutzen OnPush und kommen ohne Services aus;
src/app/ui/index.tsexportiert sie alle. - Farben, Radien und Grössen kommen ausschliesslich aus den Design-Tokens, die Schriften aus den
Typografie-Mixins in
src/theme/typography.scss. og-buttonrendert ein<button>,og-link-buttonundog-icon-buttonmit Route ein<a>.og-switchträgt die Rolleswitch,og-segmenteddie Rolletablist– beide mit den passenden ARIA-Attributen.- Die Zustandskomponenten (Avatar, Signalbalken, Banner, Schrittliste, Nachrichtenblase, Composer, QR) folgen mit T02.
T21 – CI-Checks (lokal + GitHub Actions) und Doku-Grundgerüst (MkDocs → GitHub Pages)¶
2026-10-23
npm run checkführt Lint, Tests und Build in einem Durchgang aus – derselbe Ablauf wie in der CI.- GitHub Actions prüft jeden PR gegen
mainmit den Checksapp-checksunddocs-check. - Nach jedem Merge auf
mainwird die Doku-Website auf GitHub Pages veröffentlicht. - Die Doku liegt in
doku/(MkDocs Material) mit Befehlennpm run docs:install,docs:build,docs:serveunddocs:screens. - Playwright ist als Entwicklungs-Abhängigkeit installiert und erzeugt die Screenshots der Screens für die Doku.
T00 – Projekt-Setup und Theme¶
2026-10-23
- Leeres, lauffähiges Ionic-Angular-Capacitor-Projekt (Angular 22, Ionic 9, Capacitor 8) für Android, zoneless und mit Signalen.
- Dunkles OffGrid-Theme: die Design-Tokens in
src/theme/tokens.scsssind die Quelle der Wahrheit für Farben, Radien, Abstände und Grössen. - Alle Seiten und Routen der App sind als Stubs angelegt, ohne Texte und ohne Guard.
- Die Typen für Kontakt, Nachricht, Handshake, Antenne, Settings und Fehlercodes liegen in
src/app/core/models/. - Die drei Prüfbefehle
npm run lint,npm run test:ciundnpm run buildlaufen fehlerfrei.