Dokumentation

Aegis selbst betreiben

Alles, was hinter dem Messenger steckt – Architektur, Sicherheitsmodell und eine Schritt-für-Schritt-Anleitung, um Relay, öffentlichen Edge und optional den Controller auf einem eigenen Server aufzusetzen. Diese Seiten beschreiben den real laufenden Aufbau, nicht ein Ideal.

1 · Architektur-Überblick

Aegis besteht aus zwei bewusst getrennten Ebenen, die sich nur das App-Gehäuse teilen – nicht die Vertrauensgrenze.

Zwei Ebenen, zwei Vertrauensgrenzen

Chat-EbeneControl-Ebene
ZweckMessenger für Freunde und FamilieÜberwachung und Steuerung der eigenen Infrastruktur
Erreichbarkeitöffentlich über TLS (Port 443)nur über WireGuard-VPN, gebunden an 10.10.2.1
Vertrauen in den Serverkeines nötig – Zero-Knowledge, nur Chiffretextvoll – der Controller sieht die eigenen Metriken im Klartext
ZugangEinladung, ohne VPN nutzbarWireGuard-Peer + Biometrie + Berechtigung

Datenfluss

Öffentlicher Pfad – vom Handy bis in die Datenbank kommt nur Chiffretext an:

Android-Applibsignal · SQLCipher · OkHttp mit SPKI-Pinning
Caddy (Edge)Port 80/443, automatisches Let's Encrypt, terminiert TLS
RelayFastAPI, intern relay:8080, nicht öffentlich publiziert
PostgreSQLnur im privaten Compose-Netz, kein Port nach außen
Push-Dispatchinhaltsloser Wake-up an ntfy / UnifiedPush, nie Inhalt
App holt Mailboxentschlüsselt Schicht 1, zeigt „Neue Nachricht von X“

VPN-Pfad – unverändert abgeschottet:

App (Dashboard-Tab)WireGuard-Tunnel + Biometrie
Controller10.10.2.1:8000 · FastAPI + mobile Web-UI
VictoriaMetrics10.10.2.1:8428 · gespeist von node_exporter und cAdvisor
Agents auf den ZielservernWhitelist-Aktionen, Token-Auth, kein Root-SSH

Die WebSocket-Verbindung der App läuft über denselben Edge: wss://<host>/v1/ws. Caddy erkennt das Upgrade selbst und tunnelt es an den Relay weiter; der Relay läuft mit --proxy-headers und sieht darum weiterhin X-Forwarded-Proto: https.

Komponenten

KomponenteTechnikErreichbarkeit
Android-AppKotlin, klassische Views/XML, Material3 DayNight, libsignal, SQLCipher, OkHttp, WireGuard-GoBackend, UnifiedPushClient
RelayPython 3.13, FastAPI, Starlette-WebSockets, uvicorn, asyncpg, Pydantic v2intern relay:8080 im Docker-Netz aegis-edge
DatenbankPostgreSQL 16nur privates Compose-Netz
EdgeCaddy 2, automatisches Let's Encryptöffentlich 80/443
ControllerPython 3.13, FastAPI, Casbin, mobile Web-UI10.10.2.1:8000 – VPN-only
Metrik-SpeicherVictoriaMetrics, Aufbewahrung 12 Monate10.10.2.1:8428 – VPN-only
Metrik-Erfassungnode_exporter (Host), cAdvisor (Container)kein veröffentlichter Port, nur internes Netz
AgentPython/FastAPI-Daemon je Zielserver, Docker-SDK statt Shellnur vom Controller aus, Token-Auth

Repo-Aufbau

VerzeichnisInhalt
messenger-server/Relay (FastAPI) + Compose + Tests inkl. Echo-Testkontakt
messenger-edge/Caddy-Edge: Caddyfile, Compose, statische Seiten unter srv/
app-android/native App (Chat-Tab + Dashboard-Tab)
controller/Controller-API, Modul-Registry, Rechte, Health-Engine
agent/Least-Privilege-Daemon für die Zielserver
app/Web-UI des Dashboards (im WebView der App)
deploy/monitoring/VictoriaMetrics + node_exporter + cAdvisor
docs/Architektur, Messenger-Architektur, Infrastruktur, Konventionen, Roadmap

Protokoll in Kurzform

REST über HTTPS für Setup und Abholung, eine dauerhafte WebSocket-Verbindung für den Live-Kanal. Bis auf Registrierung und Restore verlangt jeder Aufruf Identity-Key-Auth: Der Client signiert eine Server-Nonce mit dem privaten Geräteschlüssel und schickt das Ergebnis im Header X-Aegis-Auth. Es gibt keine Passwörter.

EndpunktZweck
POST /v1/registerKonto anlegen – nur mit gültigem, einmaligem Invite-Token
POST /v1/invitesInvite-Token erzeugen (Admin-Token oder bestehender Nutzer)
POST /v1/auth/challengeNonce für die Challenge-Response holen
POST /v1/keys · GET /v1/keys/{name}eigene Prekeys nachfüllen · Bundles aller Geräte eines Kontakts holen
POST /v1/messages · GET · /ackEnvelope einliefern · Mailbox abholen · bestätigen (löscht serverseitig)
GET /v1/devices, /devices/provision*Geräteliste und QR-Provisioning für weitere Geräte
POST /v1/push/registerPush-Endpunkt für den inhaltslosen Wake-up melden
PUT /v1/backup, GET /v1/backup/…opaken, passphrase-verschlüsselten Identitäts-Blob ablegen/holen
GET /healthLebenszeichen inkl. Datenbank-Prüfung (unauthentifiziert)
WSS /v1/wsLive-Kanal: challenge · auth · deliver · ack · send · ping

Was der Server tatsächlich speichert, ist ein Envelope ohne jeden lesbaren Inhalt:

{ "to": "bob", "toDeviceId": 1,
  "type": "PREKEY | CIPHERTEXT | UNIDENTIFIED",
  "ciphertext": "<base64 libsignal-CiphertextMessage, gepolstert>" }

envelopeId und serverTs vergibt der Server. Im Sealed-Sender-Modus gibt es kein Absenderfeld – der Absender steckt authentifiziert im Chiffretext.

2 · Sicherheitsmodell

Kern-Annahme: Der Server ist nicht vertrauenswürdig. Betrachtet werden ein passiver Netz-Beobachter, ein aktiver MITM, ein vollständig übernommener Relay (Root, Live-Mitschnitt, Datenbank-Dump), Geräteverlust und der Versuch fremder Clients oder Server, unsere Nutzer zu erreichen.

Garantie: Wer Root auf dem Relay hat und den kompletten Datenbestand abzieht, erhält keinen Nachrichtenklartext und keine privaten Schlüssel – nur opake Blobs und grobe Routing-Metadaten.

Was der Server sieht – und was nicht

DatumServer siehtServer sieht nicht
Nachrichtentextopaken CiphertextKlartext; Länge nur grob, weil auf Bucket-Größen gepolstert wird
Öffentliche Schlüssel, Prekey-Bundlesja – das ist ihr Zweckdie privaten Gegenstücke
Kontenwelcher Benutzername existiertKlarname, Telefonnummer, E-Mail (gibt es nicht)
Zustell-MetadatenEmpfänger-ID, Ziel-Gerät, Zeitstempelden Absender im Sealed-Sender-Modus
Pushden Push-EndpunktInhalt – der Push ist leer
Identitäts-Backupopaken Blob + Salt/KDF-ParameterPassphrase und Inhalt
Medien geplantnur den AES-GCM-Ciphertext-Blob und seine GrößeInhalt, Dateiname, Typ – alles steckt im E2EE-Umschlag
Anrufe geplantüber coturn nur SRTP-ChiffretextAudio/Video, SRTP-Schlüssel, das komplette Signaling

Schlüsselverwaltung

  • Account-Identity-Key – die stabile Identität des Kontos. Aus ihr und der Identität des Gegenübers wird die Sicherheitsnummer berechnet.
  • Geräteschlüssel – jedes Gerät hat ein eigenes Schlüsselpaar. Das zugehörige Gerätezertifikat ist eine Signatur des Account-Schlüssels über den Geräteschlüssel; der Relay prüft sie bei Registrierung und Geräte-Verknüpfung.
  • Signed-Prekey, One-Time-Prekeys, Kyber-Prekeys – ermöglichen den Sessionaufbau, während der Empfänger offline ist. Ein Kyber-Prekey ist als last resort markiert und wird nie verbraucht, damit ein Gerät nie unerreichbar wird.
  • Content-Key – pro Gerät ein Curve25519-Schlüsselpaar für die Inhalts-Schicht. Der öffentliche Teil liegt im Prekey-Bundle, der private ausschließlich im Android-Keystore mit erzwungener Nutzer-Authentifizierung (StrongBox/TEE, nicht exportierbar).
  • Alle privaten Schlüssel und der Session-Zustand liegen ausschließlich in der SQLCipher-Datenbank des Geräts – nie im Klartext auf der Platte, nie auf dem Server.

Signaturen sind Curve25519/XEdDSA, wie libsignal sie erzeugt. Der Relay verifiziert sie, ohne je privates Material zu sehen – die Zero-Knowledge-Eigenschaft bleibt unberührt.

Zwei Schichten – warum die Benachrichtigung funktioniert

Im Hintergrund lesbar (nicht biometrisch)Erst nach Entsperrung (biometrisch)
Absender-IdentitätNachrichtentext
Zeitstempel, Nachrichtentyp, Zuordnung zur UnterhaltungMedien-Inhalte – ihr AES-Schlüssel liegt in Schicht 2
Zustell- und Lesestatus, Tipp-Anzeige, Anruf-SignaleZitate, Vorschauen, Vorschaubilder
→ genug für „Neue Nachricht von X“→ bewusst keine Inhaltsvorschau im gesperrten Zustand

Trade-off, ehrlich benannt: Der Content-Key lebt länger als die Ratchet-Schlüssel. Abgefedert wird das durch einen eigenen, ephemeren Schlüssel pro Nachricht und durch Rotation des Content-Keys. Die Forward Secrecy des Transports bleibt davon unberührt – Schicht 1 ratcheted weiter.

Warum kein Fremder hereinkommt

  1. Keine Föderation – es existiert keine Server-zu-Server-Schnittstelle. Ein fremder Server hat nichts, womit er sprechen könnte.
  2. Invite-gated – ohne gültigen, einmaligen Token wird kein Konto angelegt.
  3. Identity-Key-Auth – jeder authentifizierte Aufruf verlangt eine Signatur mit einem Geräteschlüssel, den der Server bereits kennt. Unbekannte Schlüssel werden abgewiesen.
  4. Rate-Limits und Lockout auf Registrierung, Prekey-Abruf, Versand und Backup-Download.

Ehrliche Grenzen

  • Ein einzelner, authentifizierter Relay kann Metadaten nicht vollständig eliminieren. Solange ein Gerät seine Verbindung mit dem eigenen Konto authentifiziert, ist auf Verbindungsebene ein zeitliches Muster beobachtbar. Sealed Sender entkoppelt den Envelope-Inhalt, nicht die physische Verbindung.
  • Ein in der App mitgeliefertes Client-Geheimnis ist grundsätzlich extrahierbar. Die echte Schließung sind Föderationsverzicht, Invite-Zwang und Schlüssel-Auth – nicht das App-Geheimnis.
  • Ein kompromittiertes Endgerät mit laufender Schadsoftware ist außerhalb des Modells: Dort liegt der Klartext ohnehin offen.
  • Langfristige Traffic-Analyse auf Timing-Ebene wird minimiert, aber nicht ausgeschlossen.

Backup-Hinweise

Für Nutzer

Das Identitäts-Backup ist client-seitig mit einer Passphrase verschlüsselt (Argon2id als Schlüsselableitung, AES-256-GCM); der Server hält nur den opaken Blob samt Salt und KDF-Parametern. Ohne Passphrase gibt es keine Wiederherstellung – niemand kann sie zurücksetzen. Bis der Restore-Fluss in der App verdrahtet ist, gilt: Geht das Gerät verloren, sind Identität und Verlauf weg, und Kontakte müssen die neue Sicherheitsnummer bestätigen.

Für Betreiber

  • PostgreSQL – Konten, öffentliche Schlüssel, Prekeys, wartende Envelopes: docker compose exec -T postgres pg_dump -U aegis aegis_relay | gzip > relay-$(date +%F).sql.gz
  • Volumes – die opaken Backup-Blobs (backups) und der ACME-Zustand von Caddy (caddy_data: Konto und Zertifikate). Letzterer spart bei einem Umzug neue Ausstellungen.
  • .env-Dateien gehören nie ins Repo, aber unbedingt in den Passwort-Manager – vor allem INVITE_SIGNING_SECRET, sonst werden ausgegebene Invites ungültig.
  • Release-Keystore der App offline sichern: Ohne ihn gibt es für installierte Apps keinen Update-Pfad mehr, und der Fingerabdruck in assetlinks.json stimmt nicht mehr.
  • Ein Server-Backup enthält keinen Nachrichtenklartext. Es rettet Konten und Zustellbarkeit, nicht den Gesprächsverlauf – der liegt ausschließlich auf den Geräten.

3 · Selbst hosten

Der komplette Chat-Stack besteht aus zwei Docker-Compose-Stacks, die sich ein Netz teilen. Der Controller-Teil ist optional und völlig unabhängig davon.

Voraussetzungen

  • Server: ein kleiner Linux-Server mit Docker und Compose-Plugin. Debian 13 (trixie) ist die Referenz. Für einen Freundeskreis genügen 1–2 vCPU und 2 GB RAM; Speicherbedarf entsteht praktisch nur durch wartende Envelopes und Blobs.
  • Domain: ein A-Record auf die öffentliche IP des Servers (DNS-only, kein Proxy davor – die ACME-Validierung muss den Server direkt erreichen).
  • Ports: eingehend 443/tcp zwingend, 80/tcp für den HTTP-Redirect und die HTTP-01-Challenge. Bei Anbietern mit vorgelagerter Cloud-Firewall (z. B. IONOS) müssen die Ports zweimal geöffnet werden: im Anbieter-Panel und lokal in ufw.
  • Android-Seite: zum eigenen Ausliefern der App JDK 17, Android SDK 34 und Gradle 8.7 sowie ein eigener Release-Keystore.

1 Relay aufsetzen

Beide Stacks hängen an einem gemeinsamen, externen Docker-Netz. Das wird einmalig angelegt, bevor irgendetwas startet:

docker network create aegis-edge

Danach das Repo klonen und den Relay-Teil nach dem Projekt-Muster „ein Verzeichnis pro Dienst“ ablegen, also z. B. nach /opt/aegis/messenger. Die Geheimnisse kommen in eine .env daneben – niemals ins Repo:

DB_PASSWORD=<openssl rand -base64 36>
ADMIN_TOKEN=<openssl rand -base64 36>
INVITE_SIGNING_SECRET=<openssl rand -base64 36>

Was die docker-compose.yml des Relays macht:

  • Dienst relay wird aus ./relay gebaut, lauscht intern auf 0.0.0.0:8080 ohne TLS (RELAY_TLS=off) und hat keinen ports:-Eintrag – er ist damit nie direkt aus dem Internet erreichbar, sondern nur über expose: 8080 im Netz aegis-edge.
  • Dienst postgres (PostgreSQL 16) hängt nur im privaten default-Netz, hat ebenfalls keinen veröffentlichten Port und einen pg_isready-Healthcheck, auf den der Relay wartet.
  • Volumes: pgdata für die Datenbank, backups für die opaken Identitäts-Backup-Blobs.
  • Alles mit restart: unless-stopped, damit der Stack einen Reboot übersteht.
cd /opt/aegis/messenger
docker compose up -d --build
docker compose logs -f relay

2 Edge mit TLS aufsetzen

Der Edge liegt unter /opt/aegis/edge und besteht aus einem einzigen Caddy-Container, der am selben aegis-edge-Netz hängt. Er veröffentlicht 80 und 443, mountet das Caddyfile und das statische Verzeichnis srv/ read-only und hält den ACME-Zustand im Volume caddy_data – das Volume ist wichtig, sonst wird bei jedem Neustart ein neues Zertifikat beantragt.

Das Caddyfile macht vier Dinge:

  • TLS automatisch – Let's Encrypt mit Ausstellung und Erneuerung ohne Zutun. HTTP-01 (Port 80) und TLS-ALPN-01 (Port 443) sind beide aktiv; blockiert der Anbieter Port 80, greift der ALPN-Weg auf 443 von allein.
  • Reverse-Proxy für /health und /v1/* an relay:8080. Das WebSocket-Upgrade auf /v1/ws erkennt Caddy selbst.
  • Statisches Hosting für diese Seite, den Docs-Bereich, das Download-Verzeichnis und /.well-known/assetlinks.json.
  • Header-Härtung: Referrer-Policy: no-referrer, X-Content-Type-Options: nosniff, kein Server-Header.
cd /opt/aegis/edge
docker compose up -d
docker compose logs -f caddy      # ACME-Fortschritt beobachten

Der Landing-Bereich nutzt Caddys templates-Direktive: Der Download-Button erscheint nur, wenn srv/download/aegis.apk wirklich existiert, und die angezeigte Version stammt aus einem beim Deployment erzeugten HTML-Schnipsel – sie kann also nicht veralten.

3 Firewall

Grundregel des Projekts: eingehend alles verbieten, ausgehend erlauben, und nur gezielt öffnen. Öffentlich ist ausschließlich der Chat-Pfad.

PortProtokollWofürEbene
22tcpSSH (nur mit Schlüssel, Passwort-Login aus)lokal
80tcpHTTP-Redirect und ACME HTTP-01Anbieter + lokal
443tcpREST, WebSocket, Landing-PageAnbieter + lokal
51820udpWireGuard – nur wenn der Controller-Teil betrieben wirdAnbieter + lokal
3478 / 5349 / 49160–49200 geplantudp+tcpcoturn für Anrufe (Stufe 3)Anbieter + lokal
ufw default deny incoming
ufw default allow outgoing
ufw allow 22/tcp
ufw allow 80/tcp
ufw allow 443/tcp
ufw enable

Alles andere – Datenbank, Metrik-Speicher, Git-Server, Controller – bindet an die VPN-Adresse und bekommt nie eine Freigabe nach außen.

4 Einladung erzeugen und registrieren

Die Registrierung ist geschlossen: Ohne einmaligen Invite-Token legt der Relay kein Konto an. Tokens erzeugt entweder der Betreiber mit dem Admin-Token oder ein bereits registrierter Nutzer aus der App heraus.

curl -s -X POST https://<host>/v1/invites \
  -H "X-Admin-Token: $ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"count":1}'

Daraus wird ein Einladungslink der Form

https://<host>/i/<token>#u=<einladender>&aik=<dessen Identitätsschlüssel>

Ist die App installiert, öffnet Android diesen Link dank verifizierter App-Links direkt in Aegis; der Einladende wird gepinnt und die Freundschaft automatisch angelegt. Ist sie es nicht, landet man auf dieser Website mit Download und Kopier-Button. Der Teil hinter dem # wird nie an den Server gesendet – er bleibt im Browser, weshalb der Link wortwörtlich kopiert werden muss.

5 App bauen und ausliefern

  • Im App-Modul die eigene Domain eintragen (Relay-Basis-URL sowie Host und Basis-URL für die Invite-Links) und mit einem eigenen Release-Keystore signieren. Der Keystore gehört nie ins Repo; der Build löst ihn über Umgebungsvariablen oder eine Properties-Datei auf und baut andernfalls einfach unsigniert weiter.
  • Den SHA-256-Fingerabdruck des eigenen Signaturzertifikats in srv/.well-known/assetlinks.json eintragen – nur dann verifiziert Android die App-Links und öffnet /i/… direkt in der App.
  • APK ausrollen: messenger-edge/deploy-apk.sh <pfad/zur/app-release.apk>. Das Skript kopiert die Datei atomar nach srv/download/ und schreibt version.json und version.html – Version, Größe, SHA-256 und Datum werden aus der APK selbst gelesen, nichts ist fest verdrahtet.

Prüfen lässt sich die Verknüpfung auf echter Hardware mit adb shell pm get-app-links <package> – erwartet wird „verified“.

6 Controller und Monitoring (optional)

Der Control-Teil ist vom Messenger unabhängig und bleibt strikt im VPN.

  1. WireGuard einrichten, z. B. Subnetz 10.10.2.0/24 mit dem Server auf 10.10.2.1 und je einem Peer pro Gerät. Ein Subnetz wählen, das nicht mit dem heimischen Netz kollidiert.
  2. Monitoring-Stack aus deploy/monitoring/ starten: VictoriaMetrics veröffentlicht ausschließlich 10.10.2.1:8428; node_exporter und cAdvisor bekommen keinen Port und werden über ihre Compose-Servicenamen gescrapt. Aufbewahrung 12 Monate, Daten im Volume vmdata.
  3. Controller aus controller/ starten: veröffentlicht 10.10.2.1:8000, hängt im Netz des Monitoring-Stacks und erreicht VictoriaMetrics über den Servicenamen. Die mobile Web-UI wird read-only aus app/ hineingemountet und same-origin ausgeliefert.
  4. Agents auf den Zielservern ausrollen – ein Daemon mit fester Whitelist, Token-Auth und Docker-SDK statt Shell. Kein Root-SSH vom Controller.

Danach zeigt der Dashboard-Tab der App Ampel-Status und Verlaufscharts – erreichbar nur mit aktivem Tunnel und nach Biometrie.

Zertifikat und Pinning

Zwei Wege, je nachdem, ob eine Domain im Spiel ist:

  • Mit Domain (empfohlen): Caddy holt automatisch ein Let's-Encrypt-Zertifikat. Wichtig für das Pinning in der App: Bei jeder Erneuerung – etwa alle 60 Tage – erzeugt Caddy standardmäßig einen neuen Leaf-Schlüssel, der Leaf-Pin ändert sich also regelmäßig. Deshalb nicht nur den Leaf pinnen, sondern die stabile obere Kette (Intermediate und Root) und immer mindestens einen Backup-Pin mitgeben. Es genügt, wenn irgendein Pin auf die Kette passt.
  • Ohne Domain: ein selbst-signiertes Zertifikat mit der öffentlichen IP als SAN; die App pinnt dessen SPKI. Keine CA und keine Domain nötig – dafür ist eine Rotation nur mit koordiniertem App-Update möglich, weshalb der Backup-Pin hier das Sicherheitsnetz ist.

Aktuellen Leaf-Pin jederzeit selbst ziehen:

echo | openssl s_client -connect <host>:443 -servername <host> 2>/dev/null \
  | openssl x509 -pubkey -noout \
  | openssl pkey -pubin -outform DER \
  | openssl dgst -sha256 -binary | openssl enc -base64

Die App-Links-Verifikation nutzt ohnehin den System-Trust-Store; das Pinning betrifft nur die Relay-Verbindung und kann parallel bestehen.

Betrieb und Prüfen

  • curl -s https://<host>/health – Lebenszeichen des Relays inklusive Datenbank-Prüfung.
  • docker compose logs -f relay bzw. caddy – ACME-Ausstellung, Verbindungen, Rate-Limit-Treffer.
  • Zustellung wird laufend geräumt: bestätigte Envelopes verschwinden sofort, unbestätigte nach 30 Tagen. Der Speicherbedarf bleibt dadurch von selbst klein.
  • Zum Testen ohne zweite Person gibt es einen dauerhaft laufenden Echo-Testkontakt: Er nimmt Nachrichten an, entschlüsselt sie durch beide Schichten und antwortet verschlüsselt zurück. Er ist ausdrücklich eine Testeinrichtung – ein unbeaufsichtigter Prozess auf dem Server liest, was man ihm schickt. Der Relay bleibt davon unberührt zero-knowledge.

4 · Stand und Grenzen

Fertig live

  • Zero-Knowledge-Relay mit Store-and-Forward, WebSocket-Live-Kanal und Fan-out je Gerät
  • Invite-gated Registrierung, Identity-Key-Auth, Rate-Limits
  • 1:1-Textchat Ende-zu-Ende verschlüsselt, inklusive Zwei-Schicht-Verschlüsselung
  • Echtzeit-Zustellung über einen Vordergrunddienst plus inhaltslosem Push als Rückfallweg
  • Sicherheitsnummer im Chat, TOFU-Pinning beim Hinzufügen
  • Öffentlicher Edge mit automatischem TLS, App-Links und APK-Auslieferung
  • Controller: Health-Ampel, Verlaufscharts, Modul-Registry, Casbin-Rechte, Audit-Log

Offen geplant

  • Medien und Dateien – client-seitig verschlüsselt vor dem Upload; der Server bekäme nur einen opaken Blob (Stufe 2)
  • Sprach- und Videoanrufe – WebRTC mit eigenem coturn, Signaling als E2EE-Nachricht (Stufe 3)
  • Multi-Device – die Relay-Endpunkte für QR-Provisioning stehen, der App-Flow fehlt
  • Verschlüsseltes Identitäts-Backup – serverseitig vorhanden, in der App noch nicht verdrahtet
  • Controller: Login und 2FA über Authentik, selbstheilender Watchdog, Updates mit Rollback, restic-Backups, Module für UniFi-Gateway und Plex
  • Gruppenchats – noch nicht umgesetzt
  • Ein Status-Feature ist bewusst nicht vorgesehen