# 07 — Betrieb > ⚠️ Dieses Repo ist **privat**; hier stehen LAN-Zugangsdaten. Nicht öffentlich stellen. ## Zugänge | System | Zugang | |---|---| | Board SSH | `root@192.168.1.200` (WLAN, primär) / `root@192.168.1.50` (LAN), PW `123456` | | Web-GUI | `http://192.168.1.200:3000` | | MariaDB (auf dem Board) | DB `callrec`, User `callrec`, PW `callrec123` (kein `sqlite3`/CLI-Client — `mysql` nutzen) | | Gitea (diese Doku) | `http://192.168.1.199:3000`, User `sascha` | | App-User auf dem Board | `callrec`, Code in `/opt/callrec` | ## systemd-Einheiten | Einheit | Scope | Aufgabe | |---|---|---| | `callrec.service` | system | Node-App (Web-GUI :3000, Aufnahme, Pipeline) | | `callrec-btwatch.service` | system | BT-Headset-Auto-Connect (card-aware) | | `callrec-perf.service` | system | CPU-Governor `performance` + A76-Pinning | | `aime-usb-audio.service` | user | PipeWire-Topologie beim Boot (callmix, Loopbacks, Remap) | | `aime-micgate.service` | user | LADSPA-Noise-Gate-Filter-Chain | | `aime-volume-headset.service` | user | Windows-Volume → Headset-Sink (UAC2 numid=7) | | `aime-headset-keys.service` | root | H390-Inline-Tasten → Lautstärke | | `aime-iphone-route.service` | — | HFP-Routing-Watcher für iPhone-Anrufe. **Achtung:** die Unit liegt derzeit nicht unter `/etc/systemd/system/`, nur das Skript `/opt/callrec/aime-iphone-route.sh` ist da (Stand 2026-08-03, war schon vor dem eMMC-Umzug so) | | `aime-bootsync.service` | system | spiegelt `/boot` der eMMC auf die SD-Karte (Start + Shutdown), siehe Speicher-Abschnitt | | `filter-chain.service` (PipeWire-Standard) | user | **MASKIERT** — würde micgate doppeln | | `aime-audio-out` / `aime-volume` | user | ES8323-Ära, **disabled** | ## Speicher: System auf der eMMC, Bootdateien auf der SD (seit 2026-08-03) ```mermaid flowchart LR ROM["BootROM RK3588S"] --> IDB["idbloader + U-Boot
(eMMC, Sektor 64 / 16384)"] IDB --> SCR{"boot.scr + armbianEnv.txt
zuerst gefunden auf:"} SCR -->|SD-Karte| KRN["Kernel + initrd
von der SD"] KRN -->|"rootdev=UUID=3ba18bc1…"| ROOT["/ = eMMC
mmcblk0p1 „aime-emmc“"] ROOT -.->|"aime-bootsync spiegelt /boot"| KRN ``` | Gerät | Größe | Rolle | |---|---|---| | `mmcblk0p1` (eMMC, `aime-emmc`) | 58 GB, 28 GB belegt | **`/` — System, Datenbank, Aufnahmen, Modelle** | | `mmcblk1p1` (SD, `armbi_root`) | 118 GB | `boot.scr`/Kernel/initrd **und** eine vollständige Kopie des alten Systems als Rückfallebene. Nicht eingehängt — die SD wird im Betrieb weder gelesen noch beschrieben. | **Warum die eMMC:** sie ist für Dauerbetrieb gebaut (Verschleiß auslesbar: `life_time 0x01`, `pre_eol_info 0x01` = unter 10 % verbraucht), die SD-Karte ist der übliche Ausfallpunkt und stirbt meist ohne Vorwarnung. Aime schreibt viel — jede Aufnahme WAV + MP3, dazu Datenbank und Suchindex. **Der Haken, den man kennen muss:** U-Boot liest `boot.scr` und `armbianEnv.txt` von der **SD-Karte**, solange sie steckt — nachgewiesen durch einen Neustart, bei dem eine auf die SD-Root zeigende `armbianEnv.txt` das alte System hochfuhr. Der Kernel kommt also von der SD, das Root von der eMMC. Ohne Gegenmaßnahme würde ein Kernel-Update auseinanderlaufen: neue Module auf der eMMC, alter Kernel von der SD. **`aime-bootsync.service`** löst das — spiegelt `/boot` beim Start **und** beim Herunterfahren auf die SD (`rsync -a --delete --exclude='*.bak*'`), sodass beide Medien denselben Kernel tragen. Stirbt die SD-Karte, findet U-Boot dort nichts mehr und nimmt die eMMC-Bootkette, die vollständig vorhanden ist. Beide `armbianEnv.txt` (eMMC und SD) tragen `rootdev=UUID=3ba18bc1-bb8e-4a34-9c61-18a27f2c7adf`. Auf der SD liegt daneben `armbianEnv.txt.bak-vor-emmc-umzug` mit der alten SD-UUID `ab30e77b-…` — wer das alte System wieder booten will, kopiert die Datei zurück. **Wichtig bei einem neuen Dateisystem:** `mkfs.ext4` aus e2fsprogs 1.47 setzt `orphan_file` und `metadata_csum_seed`. `orphan_present` ist ein Incompat-Merkmal — ein Treiber, der es nicht kennt (U-Boots ext4-Leser), verweigert das Lesen. Vor dem ersten Start entfernen: ```bash tune2fs -O ^orphan_file,^metadata_csum_seed /dev/mmcblk0p1 && e2fsck -fy /dev/mmcblk0p1 ``` ## Wichtige Pfade auf dem Board | Pfad | Inhalt | |---|---| | `/opt/callrec/*.js` | server / recorder / llm / diar / spk / db / bt / sysstat | | `/opt/callrec/web/` | Vite-Frontend (`npx vite build` nach Änderungen an `src/`) | | `/opt/callrec/aime-*.sh` | Setup-/Bridge-/Cap-Skripte | | `/opt/callrec/milestones/m1-20260709/` | Meilenstein-1-Snapshot inkl. MANIFEST.md (Rollback) | | `/opt/callrec/*.bak-*` | Datei-Backups je Änderung (z. B. `.bak-structsum`, `.bak-anchor`) | | `/etc/sudoers.d/callrec-cpucap` | NOPASSWD für `aime-cpucap.sh` (Thermal-Cap) | ## Systemanzeige in der Kopfzeile (seit 2026-08-17) Zwischen dem Tab „Bluetooth“ und dem Hell/Dunkel-Schalter stehen zwei Symbole, die den Zustand des Boards ohne SSH zeigen: | Symbol | Zeigt an | Klappkarte bei Mauskontakt | |---|---|---| | Ring + Prozentzahl | Anteil des **Aufnahme-Vorrats**, den die Aufnahmen schon belegen | eine Zeile: Aufnahmen in GB + Dateizahl | | Ventilator | CPU-Last — je höher, desto schneller die Drehung | CPU % (+ Kernzahl), RAM % mit GB, Temperatur mit Zone | **Bezugsgröße der Prozentzahl** (Wunsch des Nutzers, 2026-08-17): nicht die Partition, sondern der Platz, den ein *frisch aufgesetztes System dieser Ausbaustufe* für Aufnahmen übrig hätte: ``` 100 % = freier Platz + Summe aller Audiodateien unter recordings/ belegt = Summe aller Audiodateien unter recordings/ ``` System, Modelle und `node_modules` zählen also weder oben noch unten mit — die Anzeige beantwortet „wie viel von meinem Aufnahme-Vorrat ist weg?“ und nicht „wie voll ist die eMMC?“. Die Klappkarte zeigt auf Wunsch des Nutzers **nur noch** die Zeile „Aufnahmen: x,y GB · n Dateien“; Restplatz, Vorratsgröße, Erklärtext und Partitionswert wurden wieder entfernt. Die Zahlen dahinter liefert `/api/sysstat` weiterhin (`freeBytes`, `budgetBytes`, `partPct`) — nur die GUI zeigt sie nicht mehr an. - Datenquelle: `GET /api/sysstat` (`app/sysstat.js`). `/proc`, `fs.statfsSync` und ein **gepufferter** Größenscan über `recordings/` (`readdir` rekursiv + `stat`, TTL 60 s, nur Audio-Endungen) — **kein** `df`/`du`/`top`-Prozess, damit die GUI im Sekundentakt fragen darf. - CPU-% aus der Differenz zweier `/proc/stat`-Messpunkte. `sysstat.js` misst dafür **selbst** alle 2 s (`setInterval(...).unref()`); ohne diesen eigenen Ticker wäre die erste Antwort nach jedem Start 0 %. - RAM rechnet mit `MemAvailable`, nicht `MemFree` — sonst stünde das Board wegen Cache dauernd bei 95 %. - Temperatur = wärmste der SoC-/Core-/Center-/GPU-/NPU-Zonen; `iwlwifi_1` (WLAN-Karte) fliegt raus, weil ihr Wert nichts über die Rechenlast sagt. - Frontend: `sysmonMarkup()` / `paintSysmon()` / `tickSysmon()` in `web/src/main.js`, Poll alle 5 s, im Hintergrund-Tab (`document.hidden`) ausgesetzt. Ein einziger Ticker für alle `render()`-Durchläufe; nach jedem `render()` werden die zuletzt geholten Werte sofort zurückgemalt, weil `render()` die Kopfzeile komplett neu schreibt. - Drehzahl des Ventilators läuft in **Stufen** (`spinSeconds()`: 7 s bei Leerlauf … 0,35 s ab 90 %). Eine stufenlos gesetzte `animation-duration` würde die laufende Drehung bei jedem Poll neu ansetzen — das ruckt sichtbar. `prefers-reduced-motion` stoppt die Animation ganz. - Icon: vier gebogene Flügel (Pinwheel) im **feststehenden** Gehäusering — nur die Flügelgruppe dreht sich, sonst wäre an einem runden Ring keine Drehung zu sehen. - Farbe: grün < 75 %, gelb ab 75 %, rot ab 90 %; beim Ventilator zählt der schlimmste Wert aus CPU, RAM und Temperatur (Temperatur skaliert 45 °C → 0 %, 90 °C → 100 %). ## Deploy-Ablauf (von Windows aus) ```mermaid flowchart LR DEV["Datei lokal ändern
(pscp vom Board holen)"] --> BAK["Backup auf Board:
cp X X.bak-<thema>"] BAK --> UP["pscp hochladen"] UP --> FE{"Frontend
geändert?"} FE -- ja --> BUILD["ssh: cd /opt/callrec/web && npx vite build"] FE -- nein --> RESTART BUILD --> RESTART["systemctl restart callrec"] RESTART --> VERIFY["Verifizieren: journalctl -u callrec,
GUI-Test, ggf. reanalyze-llm"] VERIFY --> DOC["Doku + Changelog in Gitea aktualisieren"] ``` - Windows-Seite: PuTTY-Tools (`plink -batch -pw …`, `pscp`) — OpenSSH-Keys sind nicht hinterlegt. - Node-Syntax vor Upload prüfen: `node --check datei.js`. ## Troubleshooting | Symptom | Ursache / Fix | |---|---| | Zeile hängt auf `transcribing` | Absturz mitten in Diarization → GUI „Neu transkribieren“ (`/retranscribe`) | | Summary wirkt leer nach Retranskription | GUI muss `analysis_status` pollen, nicht `status` — Analyse ist fire-and-forget | | Board plötzlich aus | Thermik prüfen — Schutzabschaltung; Cap-Skript/Sudoers intakt? (Kap. 01) | | Zu viele Sprecher | Audio geclippt? (Gain-Staging Kap. 02) Sonst „Analyse neu“ mit gepinnter Zahl | | Teilnehmer ≠ Sprecher | Teilnehmer kommen aus Diarization; Namen im Sprecher-Editor vergeben | | Kein far-end bei iPhone-Aufnahme | BT-Gerät `Trusted=yes`? HFP-Nodes erscheinen nur IM Anruf (Kap. 06) | | GUI zeigt Quelle ohne Signal | `.monitor`-Falle — Quelle mit `parecord` + `ffmpeg astats` prüfen (Kap. 02) | | `pactl load-module module-filter-chain` → „No such entity“ | Filter-Chain ist Core-Modul → Drop-in-Config nutzen (Kap. 02) | | rkllm/Whisper `rc=-6` | RAM-Contention — nicht beide NPU-resident; NPU-Lock beachten (Kap. 05) | | Analyse „KI Fehler“ bei langen Aufnahmen | Kontext-Überlauf → Chunk-Pfad greift; bei schwachem Ergebnis „Analyse neu“ | ## Offene Punkte (Stand 2026-07-16) - Bedingter Takt-Cap (nur > 15 min Aufnahmedauer) — angeboten, nicht gebaut (~1,4× schnellere Kurzclips). - Feinabstimmung Mikro/PC-Balance in einem echten Headset-Call. - Glossar Phase 2: nächtlicher systemd-Timer + large-v3-Kandidaten-Vorschläge mit Bestätigung (verschoben). - iPhone-Pfad: automatischer Aufnahme-Start + voller E2E-Test (Kap. 06). - Qwen-1.5B als schnellerer LLM-Kandidat für Kurzclips (verschoben). - Aufnahme #41 bleibt dauerhaft geclippt (Referenz für den Gain-Bug).