Schluss mit dem Keychain-Gefrage: Opt-in-OS-Keychain-Verschlüsselung für gespeicherte Secrets

Montagmorgen, du öffnest die Hermes-Desktop-App, und statt deiner Chat-Liste bekommst du einen macOS-Dialog: „Hermes möchte auf deine Keychain zugreifen. Gib dein Passwort ein, um das zu erlauben.“ Du tippst es ein, die App lädt. Dienstag: derselbe Dialog. Bei jedem Start, für immer – weil die Secrets der App mit einem Schlüssel verschlüsselt waren, der in der Login-Keychain liegt, und deine Keychain gesperrt, fehlend oder korrupt war. Es ist die Art Gefrage, die dich am liebsten das Gerät aus dem Fenster werfen lässt. Hermes v0.20.6 (Commit 6a6e16fa5d) behebt das an der Wurzel: Die Keychain-gestützte Verschlüsselung für gespeicherte Secrets ist jetzt ein explizites Opt-in, und der Standardpfad ruft die Keychain überhaupt nicht mehr auf.
Die Änderung betrifft im Desktop gespeicherte Secrets: Remote-Gateway-Tokens, Cloudflare-Access-Header und native OAuth-Token-Sets. Früher legte Electrons safeStorage einen App-spezifischen Schlüssel („Hermes Key“) in der macOS-Login-Keychain ab, und jede safeStorage-Berührung – sogar der Check „ist Verschlüsselung verfügbar?“ – konnte auf Maschinen mit gesperrter oder korrupter Standard-Keychain einen blockierenden Keychain-Dialog werfen. Das war ein inakzeptabler Standard für eine Chat-App, also wurde das Verhalten umgedreht: Verschlüsselung ist jetzt Opt-in, einfache Speicherung ist der Standard, und eine Einmal-Migration räumt hinter dem alten Verhalten auf.
Was sich geändert hat
Die Policy ist in einem eigenständigen Modul definiert (electron/secret-storage-policy.ts) – bewusst frei von import 'electron', damit es sich sauber unit-testen lässt – mit drei Eigenschaften:
- Setting OFF (Standard): Secrets werden mit Kodierung
'plain'geschrieben, und keine safeStorage-API wird jemals aufgerufen – inklusiveisEncryptionAvailable(), das selbst die Keychain anfasst. Keine Keychain, kein Dialog, kein Prompt. - Setting ON: das bisherige Verhalten – strikte safeStorage-Verschlüsselung, lautes Scheitern, wenn die Keychain nicht verfügbar ist, und ein Klartext-Bestätigungsdialog pro Speicherung als Notausstieg.
- Einmal-Migration: Legacy-Blobs, die vor der Einführung des Flags geschrieben wurden, sind auf der Platte safeStorage-kodiert. Bei ausgeschaltetem Setting versucht Hermes einen Migrationsdurchgang (entschlüsseln → als plain neu schreiben). Der Durchgang wird in derselben Settings-Datei festgehalten, ob er nun gelingt oder nicht – eine kaputte Keychain kostet also höchstens einen Prompt beim ersten Start nach dem Update, nie einen pro Start.
Das on-Flag nutzt strikte === true-Koerzion: Ein truthy-aber-nicht-true-Wert darf Keychain-Prompts nicht stillschweigend aktivieren (Spiegelbild der allowPlainText-Koerzionsregel in hardening.ts). Die Policy-Datei liegt unter secure-token-storage.json.
So verwendest du es
Desktop (empfohlen): Einstellungen → Gateway öffnen, den Keychain-/Secure-Storage-Schalter finden und umlegen. Das Umlegen kodiert jeden gespeicherten Secret-Store an Ort und Stelle neu – v1 connection.json, v2 connections.json und native-oauth-tokens.json – und die At-Rest-Spezifikation deckt beide Haltungen ab: Opt-in mit unverändertem Vertrag, Standard-Speicherung ohne Secure Storage, Owner-Only-Bits und Restart-Round-Trip.
Das Migrationsdetail, das sich zu wissen lohnt: Wenn du von einer älteren Version kommst, versucht der erste Start nach dem Update mit dem Standard-Setting (aus) die Einmal-Migration – bestehende safeStorage-Blobs werden zu einfachen 0600-Dateien entschlüsselt. Nicht entschlüsselbare Blobs (etwa eine tote Keychain) bleiben auf der Platte, werden danach aber als nicht vorhanden gelesen und als ‘drop’ klassifiziert – eine tote Keychain promptet also höchstens einmal. Unter Linux ist das zugrunde liegende Keychain-Backend konfigurierbar: desktop.password_store akzeptiert auto (die Session-Keychain erkennen – KWallet über KDE-Session-Env-Variablen, GNOME Keyring / jeden org.freedesktop.secrets-Provider wie KeePassXC über D-Bus) oder ein erzwungenes Backend (gnome-libsecret, kwallet, kwallet5, kwallet6); basic bedeutet einen unverschlüsselten Store. Eine explizite Env-Variable HERMES_DESKTOP_PASSWORD_STORE gewinnt weiterhin gegen die Config.
Was gleich bleibt
- Opt-in-Maschinen behalten den vollen Vertrag: strikte safeStorage-Verschlüsselung, lautes Scheitern, wenn die Keychain nicht verfügbar ist.
- Owner-Only-Dateiberechtigungen auf den einfachen Fallback-Dateien (0600) halten die Haltung „unverschlüsselt at rest“ vernünftig: nur für deinen Nutzer lesbar.
- Secrets bleiben Secrets. Es geht um wo der Verschlüsselungsschlüssel lebt (OS-Keychain vs. einfache Owner-Only-Dateien), nicht darum, Tokens in Logs oder Modell-sichtbaren Inhalten auszusetzen – die bestehende Redaction-Mechanik (
security.redact_secrets) bleibt unangetastet.
Wann Opt-in sinnvoll ist
- Opt-in, wenn du OS-Level-At-Rest-Verschlüsselung willst und deine Keychain gesund ist – die Standard-Haltung für Laptops, geteilte Maschinen oder Compliance-orientierte Setups.
- Aus lassen, wenn deine Keychain gesperrt/fehlend/korrupt ist (die Dialog-Population), wenn du keinen App-spezifischen Schlüssel in der Login-Keychain liegen haben willst oder wenn dich der Prompt bei jedem Start wahnsinnig gemacht hat. Der Standardpfad ist jetzt von Design her prompt-frei.
Das größere Bild
Das ist Teil des Zuverlässigkeits-Themas von v0.20.6: Dasselbe Fenster, das Web-Search-Caching und Lean-Tail-Kompression zu Standards machte, hat auch verhindert, dass der Updater Gateways per Tree-Kill beendet (siehe unseren Guide zum sanften Upgrade). Die Secret-Speicherung war das letzte Desktop-Ding, das deinen Morgen mit einem Passwort-Dialog blockieren konnte – jetzt ist sie Opt-in, leise und umkehrbar. Für die komplette Release-Übersicht sieh dir die v0.20.6-Release-Notes an.
Ein Schalter. Kein tägliches „Hermes möchte auf deine Keychain zugreifen“ mehr – außer du willst die Verschlüsselung, dann ist sie immer noch da, einen Schalter entfernt.