Lokaler Kalender in Rust mit CalDAV-Sync gegen Nextcloud (Linux, Windows, Android)
  • Rust 78.5%
  • Slint 18.5%
  • Kotlin 1.7%
  • Python 0.8%
  • Shell 0.5%
Find a file
audioprog 6c0d40bd06
All checks were successful
test / test (push) Successful in 20m31s
android / android (push) Successful in 21m23s
FORTSCHRITT: 0.7.2 im internen Track
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 13:46:31 +01:00
.forgejo/workflows Release-Text auch, wenn der Android-Lauf das Release schon angelegt hat 2026-10-27 11:56:21 +01:00
.vscode Umbenennung auf ngenNcCalendar 2026-08-15 07:59:47 +02:00
doc/iphone Die iPhone-Anleitung auch auf englisch 2026-09-14 11:38:23 +02:00
icons SVG-Icons statt Unicode-Zeichen auf beiden Oberflaechen 2026-10-26 08:28:35 +01:00
ngen_nc_cal_android Fassung 0.7.2 2026-11-02 12:54:54 +01:00
ngen_nc_cal_core Englische Locals im neuen Code, OccurrenceKey ohne Klon 2026-10-30 16:18:28 +01:00
ngen_nc_cal_desktop Restliche Review-Befunde: leere Auflistung, DELETE ohne ETag, Sperre, goAsync 2026-10-30 16:08:30 +01:00
pruefung K-02 mit zwei echten Clients belegt 2026-08-20 16:21:17 +02:00
tests Pruefmittel: der volle Tag kehrt monatlich wieder, sieben Reihen 2026-10-25 18:07:25 +01:00
.gitignore Betriebsunterlagen des Demoservers bleiben draußen 2026-08-25 10:32:04 +02:00
ARCHITEKTUR.md Abgelehnte Aenderungen lassen sich verwerfen 2026-10-24 17:19:52 +02:00
Cargo.lock Fassung 0.7.2 2026-11-02 12:54:54 +01:00
Cargo.toml Fassung 0.7.2 2026-11-02 12:54:54 +01:00
check.sh Auch die Rust-Texte sprechen Englisch 2026-09-14 15:31:21 +02:00
CLAUDE.md self_update 0.44 -> 1.3, zip 6 -> 9, und der zip6-Behelf entfaellt 2026-10-24 14:36:52 +02:00
DATENSCHUTZ.md Protokoll in eine Datei, und ein Knopf, der es oeffnet 2026-10-24 16:49:46 +02:00
FORTSCHRITT.md FORTSCHRITT: 0.7.2 im internen Track 2026-11-02 13:46:31 +01:00
GELERNT.md Android: Kontoblatt rollt bis zum letzten Kalender 2026-11-02 12:15:53 +01:00
LICENSE Release-Workflow: ein Tag baut Linux und Windows 2026-08-11 16:50:37 +02:00
PRUEFLAUF.md Protokollierung im Schreibtisch-Client, Befund zurückgenommen 2026-08-20 16:40:36 +02:00
PRUEFPLAN.md Doku: Vergleichsregel fuer Ausnahmen, Reihe nimmt Ausnahmen mit 2026-10-30 16:12:50 +01:00
README.md Suchen und Ersetzen am Schreibtisch, mit Vorschau 2026-10-26 08:37:50 +01:00
REVIEW.md Englische Locals im neuen Code, OccurrenceKey ohne Klon 2026-10-30 16:18:28 +01:00

ngenNcCalendar

Lokaler Kalender in Rust mit CalDAV-Anbindung an Nextcloud, für Linux, Windows und Android. Die Daten liegen lokal; der Server ist ein Replikat, nicht die Voraussetzung zum Arbeiten.

Dieses Projekt ist KI-Programmiert und vom Menschen geleitet.

Stand: v0.1.0 veröffentlicht, Meilensteine 1 bis 5 abgeschlossen — Monatsansicht, lokaler SQLite-Speicher, CalDAV in beide Richtungen, Konto-Einrichtung, Wiederholungsregeln, Zeitzonen samt Windows-Namen, Anlegen/Ändern/Löschen, Konfliktauflösung, selbsttätiger Abgleich, Erinnerungen als Systembenachrichtigung und ICS-Export. Alles gegen eine echte Nextcloud durchgespielt. Offen: Android.

Wo das Projekt liegt

Zuhause auf einem selbst betriebenen Forgejo, gespiegelt nach GitLab:

Quelle https://git.bg-ak.de/leonhard.suckau/ngenNcCalendar — hier laufen CI und Releases
Spiegel https://gitlab.com/audioprog/ngennccalendar — öffentlich erreichbar, trägt die Datenschutzerklärung für Google Play

Gespiegelt wird serverseitig: Forgejo schiebt bei jedem Push nach GitLab (Repo-Einstellungen → Mirror-Einstellungen). Lokal bleibt es bei einem einzigen Remote — zwei Push-Adressen am selben Remote wären der andere Weg, aber dann hinge der Spiegel an diesem einen Rechner, und ein Push von woanders käme dort nie an.

Der Spiegel überträgt Commits und Tags, keine Releases: die Archive und die APK liegen weiterhin nur am Forgejo.

Aufbau

ngen_nc_cal_core     Plattformunabhängiger Kern — kennt keine GUI und kein OS.
                  Domänenmodell, Kalenderrechnung, später iCalendar, SQLite,
                  CalDAV-Sync. Vollständig headless testbar.
ngen_nc_cal_desktop  Desktop-GUI (Slint), Binary `ngennccalendar`.
                  Hält nur den Ansichtszustand und füllt die Slint-Modelle.

Diese Trennung ist der Kern des Entwurfs: Android bekommt später ein eigenes Frontend auf demselben Kern — kein zweiter Sync-Pfad, kein zweites Datenmodell.

  • ngen_nc_cal_core/src/model.rs – Kalender, Termine, die drei iCalendar-Zeitformen
  • ngen_nc_cal_core/src/month.rs – 6×7-Monatsraster, Kalenderwoche, deutsche Datumsformate
  • ngen_nc_cal_core/src/source.rs – EventSource: die Abfrage-Schnittstelle der Frontends
  • ngen_nc_cal_core/src/sqlite.rs – lokale Datenbank: Schema, Migration, Bereichsabfrage
  • ngen_nc_cal_core/src/store.rs – dieselbe Schnittstelle im Arbeitsspeicher (Tests, Beispieldaten)
  • ngen_nc_cal_core/src/ical.rs – iCalendar lesen (RFC 5545)
  • ngen_nc_cal_core/src/icalwrite.rs – iCalendar schreiben: neu anlegen, im Rohtext ändern
  • ngen_nc_cal_core/src/icalexport.rs – einen ganzen Kalender als eine .ics ausgeben
  • ngen_nc_cal_core/src/projection.rs – daraus die Zeilen für die Anzeige, in der Anzeigezone
  • ngen_nc_cal_core/src/recurrence.rs – Wiederholungsregeln (RRULE) auswerten
  • ngen_nc_cal_core/src/search.rs – Titel und Ort aus dem Rohtext, für die Suche und die Ortsliste
  • ngen_nc_cal_core/src/reminders.rs – wann eine Erinnerung (VALARM) fällig ist
  • ngen_nc_cal_core/src/windowszones.rs – Windows-Zonennamen auf IANA-Zonen abbilden
  • ngen_nc_cal_core/src/series.rs – eine .ics für einen Zeitraum ausrechnen, samt Ausnahmen
  • ngen_nc_cal_core/src/caldav/ – CalDAV-Client; xml.rs ist die serverfreie Hälfte
  • ngen_nc_cal_core/src/sync.rs – der Abgleich: ctag → Delta → Vollabgleich als Rückfall
  • ngen_nc_cal_core/src/demo.rs – Beispieldaten, solange kein Konto eingerichtet ist
  • ngen_nc_cal_desktop/ui/appwindow.slint – die GUI
  • ngen_nc_cal_desktop/src/main.rs – Verdrahtung Kern ↔ GUI
  • ngen_nc_cal_desktop/src/account.rs – Anmeldung und Abgleich im Hintergrund-Thread
  • ngen_nc_cal_desktop/src/form.rs – Termin-Formular: zwischen Textfeldern und Modell
  • ngen_nc_cal_desktop/src/secrets.rs – App-Passwort: Schlüsselbund oder eigene Datei
  • ngen_nc_cal_desktop/src/paths.rs – wo die Datenbank liegt (XDG bzw. %APPDATA%)
  • ngen_nc_cal_desktop/src/notify.rs – fällige Erinnerung an den Benachrichtigungsdienst des Systems
  • ngen_nc_cal_desktop/src/update.rs – Selbst-Aktualisierung über die Forgejo-Releases
  • ngen_nc_cal_desktop/ui/icon.svg – Programmsymbol (Quelle); icon.ico ist das daraus gebaute Windows-Symbol

Veröffentlichen

Ein Tag vX.Y.Z auf main löst .forgejo/workflows/release.yml aus: Linux und Windows werden gebaut und als Release veröffentlicht. Die Version im Tag muss zur Workspace-Version in Cargo.toml passen — der Workflow bricht sonst ab, denn sonst böte die Selbst-Aktualisierung dasselbe Update endlos erneut an.

# Vor dem Tag: baut es überhaupt für Windows?
cargo check --target x86_64-pc-windows-gnu -p ngen_nc_cal_desktop

# Version in Cargo.toml anheben, committen, dann:
git tag v0.2.0 && git push origin v0.2.0

Der Windows-Check vor dem Tag ist kein Ritual. Der erste Release-Versuch scheiterte daran, dass notify_rust::Notification::hint() nur unter Unix existiert — hier fällt das nie auf, weil hier für Linux gebaut wird. Im Workflow kommt der Windows-Bau nach dem Linux-Bau, ein solcher Fehler kostet dort also erst eine Viertelstunde und dann einen verbrannten Tag. Bewusst nicht ins Tor von test.yml genommen: die Prüfung für ein zweites Ziel dauert rund zwölf Minuten und würde jeden Push ausbremsen.

Warum -gnu und nicht das CI-Ziel -msvc: für MSVC braucht cargo xwin noch llvm-lib und lld-link, die hier nicht installiert sind. Für diese Art Fehler genügt -gnu — beide Ziele sehen dieselben cfg(windows)-Zweige, sie unterscheiden sich erst in C-Laufzeit und Linker.

Je Release entstehen drei Assets:

Asset wofür
ngenNcCalendar-x86_64-unknown-linux-gnu.tar.gz Linux, mit Lizenz, Symbol und Desktop-Eintrag — das zieht der Updater
ngennccalendar-x86_64-unknown-linux-gnu dieselbe Binärdatei nackt: herunterladen, chmod +x, starten
ngenNcCalendar-x86_64-pc-windows-msvc.zip Windows

Ein Tag baut zusätzlich das Android-Paket (.forgejo/workflows/android.yml) und hängt zwei weitere Assets an:

Asset wofür
ngenNcCalendar-android.aab zum Hochladen in die Google Play Console
ngenNcCalendar-android.apk zum direkten Installieren, ohne Play

Nicht beides auf demselben Gerät. Play signiert die verteilte App mit seinem eigenen Schlüssel neu; die APK hier trägt den Upload-Schlüssel. Android hält das für zwei verschiedene Apps und verweigert die Aktualisierung („App nicht installiert"). Der Wechsel ginge nur über Deinstallieren — und das löscht die lokale Datenbank samt allem, was noch nicht beim Server ist. Einen Weg wählen und dabei bleiben; siehe GELERNT.md.

Dafür braucht es vier Secrets — ANDROID_KEYSTORE_BASE64, ANDROID_KEYSTORE_PASSWORD, ANDROID_KEY_ALIAS, ANDROID_KEY_PASSWORD —, sonst bricht der Lauf gleich am Anfang ab statt nach zwanzig Minuten.

Die Namen sind Vertrag mit ngen_nc_cal_desktop/src/update.rs: gesucht wird per Target-Triple nach einem Asset, dessen Name auch ngenNcCalendar enthält, mit ngennccalendar im Wurzelverzeichnis des Archivs. Die nackte Binärdatei ist deshalb klein geschrieben — so fällt sie aus der Auswahl des Updaters heraus und kann dem Archiv nicht den Rang ablaufen. asset_for nimmt nämlich den ersten Treffer, nicht den besten. Ein Test in update.rs liest die Workflow-Datei und schlägt fehl, wenn jemand daran dreht. Voraussetzung ist ein Secret PACKAGES_TOKEN mit Scope write:repository.

Die Datenbank liegt unter ~/.local/share/ngennccalendar/ngennccalendar.sqlite (Windows: %APPDATA%\ngenNcCalendar\). Die Statuszeile im Fenster nennt den Pfad und sagt, ob gerade echte Daten oder Beispieldaten zu sehen sind.

Distributionspakete

Derselbe Tag löst zusätzlich .forgejo/workflows/packages.yml aus: ein .deb und ein .rpm, beide in der Paketregistry von Forgejo — nicht als Release-Asset. Die Asset-Namen im Release sind Vertrag mit der Selbst-Aktualisierung; zwei weitere Dateien dort, deren Namen zufällig x86_64 enthalten, wären ein Risiko ohne Gegenwert.

Die Registry-Seite in Forgejo zeigt dieselben Zeilen mit $distribution und $component — das sind Platzhalter, keine Shell-Variablen. Kopiert man sie unverändert in eine Shell, werden sie zu nichts, und apt meldet „Missgestalteter Eintrag … (Suite)": eine Meldung, die aufs Format zeigt und nicht auf die Ursache. Hier stehen die Werte deshalb ausgeschrieben.

Die Quelle heißt nach dem Registry-Eigentümer, nicht nach diesem Projekt. Unter leonhard.suckau/debian liegen alle Pakete dieses Kontos, ngenAudioRecord eingeschlossen. Wer je Projekt eine eigene .list anlegt, hat zweimal dieselbe Quelle mit womöglich verschiedenen Schlüsselpfaden — und apt verweigert dann den Dienst komplett: „Widersprüchliche Werte gesetzt für Option Signed-By".

# Debian, Ubuntu und Verwandte
sudo install -d /etc/apt/keyrings
curl -fsSL https://git.bg-ak.de/api/packages/leonhard.suckau/debian/repository.key \
  | sudo tee /etc/apt/keyrings/forgejo-leonhard.suckau.asc >/dev/null
echo "deb [signed-by=/etc/apt/keyrings/forgejo-leonhard.suckau.asc] https://git.bg-ak.de/api/packages/leonhard.suckau/debian bookworm main" \
  | sudo tee /etc/apt/sources.list.d/forgejo-leonhard.suckau.list
sudo apt update && sudo apt install ngennccalendar

Der Schlüssel wird ASCII-armiert ausgeliefert; signed-by kommt mit .asc unmittelbar zurecht, ein gpg --dearmor ist unnötig.

# openSUSE
# Der Schlüssel muss VOR dem Hinzufügen eingelesen werden: Forgejo signiert die
# repomd.xml, und zypper bricht sonst mit „Signature verification failed" ab und
# überspringt die Quelle stillschweigend — die Installation scheitert dann mit
# „No provider of 'ngennccalendar' found", was wie ein fehlendes Paket aussieht.
sudo rpm --import https://git.bg-ak.de/api/packages/leonhard.suckau/rpm/repository.key
# Auch hier der Eigentümer als Name der Quelle, nicht das Projekt:
sudo zypper ar https://git.bg-ak.de/api/packages/leonhard.suckau/rpm/leap16 forgejo-leonhard.suckau
sudo zypper in ngennccalendar

Jedes Paket wird in einem Container seiner Zieldistribution gebaut, nicht einmal gebaut und zweimal verpackt. Das ist keine Sorgfaltsgeste: bookworm bringt glibc 2.36, Leap 16.0 bringt 2.40 (nachgemessen). Ein auf Leap gebautes Programm verlangt Symbole, die bookworm nicht kennt — das Paket ließe sich installieren und stürbe beim ersten Start. Aus demselben Grund heißt die zypper-Gruppe leap16 und nicht stable: ein späteres Tumbleweed-Paket gehört in eine eigene, nicht in dieselbe.

Die Abhängigkeiten stehen zur Hälfte von Hand da. dpkg-shlibdeps und find-requires lesen die SONAMEs aus dem ELF-Header, sehen also nur, was der Linker eingetragen hat. Beim ersten Bau kamen genau zwei Einträge heraus: libc6 und libfontconfig1. Alles Weitere — X11, Wayland, EGL, GL, xkbcommon — holt sich Slint zur Laufzeit per dlopen, und davon steht nichts im Header. Das Paket hätte sich installieren lassen und wäre beim Start gescheitert. Die Liste in ngen_nc_cal_desktop/Cargo.toml stammt deshalb aus dem Binary selbst:

strings -a target/release/ngennccalendar \
  | grep -oE 'lib[A-Za-z0-9_+-]+\.so(\.[0-9]+)?' | sort -u

Beim .deb stehen dort Debian-Paketnamen, beim .rpm die SONAMEs als Capability — openSUSE benennt seine Pakete anders, aber jedes RPM stellt den SONAMEn bereit, den es mitbringt. Die automatische Hälfte bleibt trotzdem eingeschaltet, deshalb sind dpkg-dev und rpm-build im Container Pflicht.

Beides ist am 31.08.2026 gegen die Registry durchgespielt worden, jeweils in einem leeren Container: apt-get install ngennccalendar zieht unter bookworm und unter Ubuntu 26.04 alle zwölf Bibliotheken mit, zypper in ngennccalendar löst die SONAMEs unter Leap 16.0 auf openSUSE-eigene Paketnamen auf (Mesa-libEGL1, Mesa-libGL1 …) und installiert 56 Pakete. Das ist der Grund für die SONAME-Schreibweise: eine Liste geratener openSUSE-Paketnamen wäre hier falsch gewesen.

Aus dem Paket installiert heißt: kein Selbst-Ersetzen. Liegt die Binärdatei unter /usr/bin, verweigert der Aktualisieren-Knopf die Arbeit und nennt stattdessen apt upgrade bzw. zypper up. Erkannt wird das am Pfad, nicht am Schreibrecht — als root dürfte das Programm nach /usr/bin schreiben und täte damit genau das Falsche: dpkg und rpm führen Buch über jede Datei, die sie angelegt haben.

Voraussetzung ist, dass PACKAGES_TOKEN zusätzlich den Scope write:package trägt. Für release.yml genügt write:repository; die Registry ist ein eigener Scope. Der erste Schritt jedes Paketjobs prüft das Recht, bevor eine Viertelstunde Bauzeit verbrannt ist.

Entwicklung

cargo run                                        # Desktop-GUI starten
cargo test -p ngen_nc_cal_core                      # Kern ohne Netz und ohne HTTP-Stack
cargo test -p ngen_nc_cal_core --features caldav    # zusätzlich CalDAV und Abgleich

Der Netzzugriff steckt hinter dem Feature caldav. Ohne das Feature hat ngen_nc_cal_core keinen HTTP-Stack — das hält die Kern-Tests schnell und den späteren Android-Build klein. Die CI prüft beide Varianten.

Klemmt die Verbindung zum Server, zerlegt dieses Werkzeug die Kette in Namensauflösung, TCP zu jeder gefundenen Adresse und die echte Anfrage:

cargo run -p ngen_nc_cal_core --features caldav --example connection -- cloud.example.de

Klemmt es erst beim Abgleich, geht dieses Werkzeug mit dem eingerichteten Konto den ganzen CalDAV-Ablauf durch — nur lesend, und ohne das App-Passwort auszugeben:

cargo run -p ngen_nc_cal_core --features caldav --example sync

Gegen eine echte Nextcloud prüfen lässt sich der ganze Ablauf mit dem Integrationstest. Er schreibt, deshalb läuft er nur mit ausdrücklich gesetzter Kalender-Adresse und legt ausschließlich Dateien mit dem Präfix ngencal-test- an, die er hinterher wieder löscht — trotzdem gehört dafür ein eigens angelegter Testkalender:

export NGENNCCAL_TEST_CALENDAR=https://cloud.example.de/remote.php/dav/calendars/leo/test/
export NGENNCCAL_TEST_USER=leo
export NGENNCCAL_TEST_PASSWORD=<App-Passwort>
cargo test -p ngen_nc_cal_core --features caldav --test nextcloud -- --nocapture

Statt eines vorhandenen Kalenders geht auch nur die Server-Adresse — dann legt sich der Test seinen Kalender selbst an und entfernt ihn wieder:

export NGENNCCAL_TEST_SERVER=https://cloud.example.de

Ohne diese Variablen überspringt er sich selbst; die CI braucht also keinen Server. Für einen Lauf ohne fremde Instanz liegt in tests/ eine Container-Beschreibung bereit:

podman-compose -f tests/nextcloud-compose.yml up -d
./tests/nextcloud-wait.sh
export NGENNCCAL_TEST_SERVER=http://127.0.0.1:8081
export NGENNCCAL_TEST_USER=admin NGENNCCAL_TEST_PASSWORD=admin-passwort
cargo test -p ngen_nc_cal_core --features caldav --test nextcloud -- --nocapture
podman-compose -f tests/nextcloud-compose.yml down

Das läuft rootless und ohne Sonderrechte; warum Host-Netz statt Port-Weiterleitung benutzt wird, steht in der Compose-Datei.

Die .slint-Datei lässt sich ohne Rust-Code vorschauen (lädt bei jedem Speichern neu):

slint-viewer --auto-reload ngen_nc_cal_desktop/ui/appwindow.slint

Ohne Daten aus Rust bleibt das Raster dabei leer — die Vorschau dient dem Layout, nicht dem Inhalt.

Bedienung

  • ‹ / ›, Bild↑ / Bild↓ oder das Mausrad — Monat blättern (in der Wochenansicht die Woche); Strg+Bild↑/↓, Umschalt+Rad oder das Seitwärtsrad — ein Jahr

  • Pfeiltasten — den ausgewählten Tag bewegen, Pos1 / Ende — Wochenanfang und -ende; verlässt die Auswahl den Monat, blättert er mit

  • Heute oder T — zurück zum aktuellen Monat

  • N — neuer Termin, Eingabe — den ersten Termin des Tages öffnen, / — suchen, M / W — Monats- oder Wochenansicht, Esc — Blatt schließen bzw. Suche leeren, Strg+Z / Strg+Umschalt+Z (oder Strg+Y) — zurücknehmen und wiederherstellen

  • Mehrtägige Termine laufen als durchgehender Balken über die Tage, im Monatsraster wie in der Wochenansicht (dort unter den Tagesköpfen). Was in einer Zelle keinen Platz mehr hat, steht als Uhrzeit oder als farbiges Zeichen da — Kreis mit Uhrzeit, Viereck ganztägig.

  • Klick auf einen Tag — Termine des Tages in der rechten Spalte; ein Klick auf einen Randtag blättert in dessen Monat

  • Konto einrichten — Adresse der Nextcloud eintragen, im Browser bestätigen

  • Abgleichen — von Hand; von selbst passiert es kurz nach dem Start und danach alle 15 Minuten

  • + Neuer Termin in der Tagesspalte, Klick auf einen Termin zum Ändern. Titel, Ort, Beschreibung, Zeitraum, Wiederholung und Erinnerung stehen im Dialog. Bringt die Datei eine ungewöhnliche Erinnerung mit, steht sie zusätzlich in der Liste; was sich nicht schreiben lässt — mehrere, per E-Mail, zu einem festen Zeitpunkt —, bleibt unangetastet.

  • Suchen und Ersetzen (Strg+H oder „Ersetzen …" über der Trefferliste) — mit Vorschau je Termin und Haken zum Abwählen; ein Schritt zurück nimmt alles zurück. Termine in nur lesbar freigegebenen Kalendern bleiben unberührt und sagen es.

  • Suchen — über Titel, Ort und Beschreibung. Passt nur die Beschreibung, steht der Ausschnitt um die Fundstelle unter dem Treffer; sonst wüsste man nicht, warum er dasteht.

  • Symbol im Benachrichtigungsfeld — sofern der Schreibtisch eines anzeigt. Dann schließt der ✕ das Fenster nur weg: das Programm läuft weiter und erinnert weiter, das Menü am Symbol öffnet es wieder, gleicht ab oder beendet. Der Kurzhinweis am Symbol nennt den nächsten Termin. Zeigt der Schreibtisch kein Benachrichtigungsfeld, gibt es auch kein Symbol — dann beendet der ✕ das Programm wie zuvor, statt es unsichtbar weiterlaufen zu lassen.

    Unter XFCE fehlt das Feld oft nur in der Leiste: Rechtsklick auf die Leiste → Neue Elemente hinzufügen → Status Tray-Erweiterung. Datum und Uhrzeit werden getippt (TT.MM.JJJJ, HH:MM); 1.8.26 und 9:00 gehen auch. Änderungen sind sofort im Kalender und gehen gleich zum Server — ohne Netz warten sie in der Warteschlange

Wenn zwei Geräte denselben Termin ändern

Dann gewinnt niemand. Die Fassung des Servers steht im Kalender, die eigene daneben — mit Warnzeichen, eigenem Rand und dem Hinweis, dass sie nur lokal existiert. Ein Klick darauf öffnet sie mit zwei Möglichkeiten:

  • Meine übernehmen — sie geht gegen den aktuellen Stand erneut zum Server.
  • Meine verwerfen — nur die lokale Kopie verschwindet.

Darunter steht, worin sich beide unterscheiden — je Feld der Wert vom Server und ein Knopf, der ihn übernimmt. Die Zeile verschwindet dabei, so sieht man, was noch offen ist. Am Ende gilt, was im Formular steht: den Titel von der einen Fassung, den Ort von der anderen, ein Klick je Feld.

Automatisch zusammengeführt wird nichts, denn bei zwei verschiedenen Uhrzeiten gäbe es keine richtige Wahl.

Konto verbinden

Es muss kein App-Passwort von Hand angelegt werden: die App fragt eines an, der Browser öffnet sich zur einmaligen Bestätigung, danach kommt das fertige App-Passwort zurück. Das Kennwort des Kontos selbst sieht das Programm nie, und das App-Passwort lässt sich in Nextcloud jederzeit einzeln zurückziehen.

Wo das App-Passwort liegt, entscheidest du im selben Panel:

  • Schlüsselbund des Systems — Secret Service unter Linux, Credential Manager unter Windows.
  • Selbst speichern — eine Datei im Datenverzeichnis, unter Unix mit 0600. Nötig ohne Schlüsselbund (headless Linux), sonst eine bewusste Wahl.

Die getroffene Wahl steht im Panel, damit niemand über die Ablage seines Passworts im Unklaren ist.

iPhone und iPad

Für iOS gibt es ngenNcCalendar nicht, und das bleibt vorerst so: iOS spricht CalDAV selbst, und ein Nextcloud-Kalender landet dort mit ein paar Handgriffen im System-Kalender — samt Siri, Uhr und allem, was daran hängt. Eine eigene App wäre dagegen ein Rückschritt.

Die Anleitung dafür, mit dem Konfigurationsprofil der offiziellen Nextcloud-App: doc/iphone/ — English.

Warum kein fertiger Client

Die Entscheidung, unter Android nicht den System-Kalender und DAVx⁵ zu benutzen, sondern eine eigenständige App zu bauen, ist bewusst getroffen — Begründung und Konsequenzen stehen in ARCHITEKTUR.md, Abschnitt „Android".