docs: Desktop-App — Update in der App (Handbücher, CI-Secrets, CHANGELOG)
Tessera CI/CD / Lint & Type Check (push) Successful in 48s
Tessera CI/CD / Tests (push) Successful in 1m8s
Tessera CI/CD / Desktop-Pakete bauen (push) Successful in 7m56s
Tessera CI/CD / Build & Publish Images (push) Successful in 3m12s

- Anwenderhandbuch: Menüeintrag "Update installieren", Ablauf per Klick
  unter Windows/Linux, Fehlerfall, https-Bedingung, einmaliger Wechsel für
  Clients bis 1.2.0
- Betriebshandbuch Kap. 10: Signierschlüssel (Secrets, Ablage, Sicherung,
  Verlust), Kontrollzeile /desktop/update, Manifest-Felder, zwei Fehlerbilder
- Entwicklungshandbuch: lokal `tauri build --no-sign`, Hinweise zu tauri dev
- ci-cd-setup.md: die zwei neuen Secrets, Bau-Schritte signieren,
  Fehlerbild "no private key"
- CHANGELOG: eine Zeile unter Unveröffentlicht → Neu

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-17 15:36:58 +02:00
parent 7004b5b020
commit 7479cb485f
5 changed files with 125 additions and 16 deletions
+1
View File
@@ -9,6 +9,7 @@ Diese Liste beschreibt in einfachen Worten, was sich von Version zu Version an T
- Favoriten-Widget: Reihenfolge der Links im Bearbeitungsmodus mit den Pfeilen „Nach oben“/„Nach unten“ festlegen
- Desktop-App: das Symbol im Infobereich zeigt den verbundenen Tessera-Server – im Hinweistext und als erste Zeile des Menüs; in der App auch unter Einstellungen → Desktop-App als „Verbunden mit: …“
- Desktop-App: Server-Adresse nachträglich änderbar über „Server-Adresse ändern…“ im Menü des Infobereich-Symbols – ohne Neustart
- Desktop-App: Update mit einem Klick – „Auf Version X.Y.Z aktualisieren“ im Menü des Infobereich-Symbols lädt das signierte Paket, installiert es und startet die App neu (Windows und Linux); Voraussetzung ist eine https-Adresse, bereits installierte Versionen bis 1.2.0 wechseln einmal noch über den Browser
### Geändert
+12 -2
View File
@@ -192,7 +192,7 @@ Schließen Sie das Fenster über das X, legt sich Tessera lediglich in den Infob
- **Verbunden mit …** — zeigt grau den Tessera-Server, mit dem die App verbunden ist (nicht anklickbar)
- **Öffnen**
- **Server-Adresse ändern…** — siehe [Server-Adresse ändern](#server-adresse-ändern)
- **Update herunterladen** — wird aktiv, sobald eine neue Version vorliegt (auf dem Beta-Kanal: „Neuen Beta-Stand herunterladen")
- **Update installieren** — wird aktiv, sobald eine neue Version vorliegt, und heißt dann „Auf Version X.Y.Z aktualisieren" (auf dem Beta-Kanal: „Auf Beta-Stand … aktualisieren")
- **Mit Windows starten** (unter Linux: **Beim Anmelden starten**) — mit Häkchen
- **Beenden**
@@ -212,13 +212,23 @@ Setzen oder entfernen Sie das Häkchen bei „Mit Windows starten" (bzw. „Beim
### Neue Version
Ist eine neuere Version verfügbar, meldet sich die App beim Start mit „Neue Version X.Y.Z verfügbar". Der Menüeintrag „Update herunterladen" heißt dann „Version X.Y.Z herunterladen" und öffnet mit einem Klick die Seite Einstellungen → Desktop-App im Browser — dort laden Sie die neue Version herunter und installieren sie wie oben beschrieben. Ein automatisches Aktualisieren gibt es nicht.
Beim Start (und nach einem Wechsel der Server-Adresse) prüft die App, ob Ihr Tessera-Server eine neuere Version hat. Ist das der Fall, meldet sie sich mit „Neue Version X.Y.Z verfügbar", und der Menüeintrag im Infobereich heißt „Auf Version X.Y.Z aktualisieren" (auf dem Beta-Kanal: „Auf Beta-Stand … aktualisieren").
Ein Klick auf diesen Eintrag genügt: Die App lädt das Paket im Hintergrund (der Fortschritt steht im Menü, etwa „Lädt … 42 %"), prüft, dass es unverändert von Ihrem Tessera-Server stammt, und installiert es. Unter Windows erscheint kurz das Installationsfenster mit einem Fortschrittsbalken, danach startet Tessera von selbst neu; die Server-Adresse bleibt erhalten, ebenso die Fensterposition (sie kann nach einem Update einmal auf den Standard zurückfallen). Unter Linux wird die Datei `Tessera-X.Y.Z.AppImage` an ihrem Speicherort ersetzt und die App startet neu — die Datei muss dafür an einem Ort liegen, an dem Sie schreiben dürfen (zum Beispiel in Ihrem Home-Ordner).
Schlägt das Update fehl, meldet die App den Grund und öffnet die Seite Einstellungen → Desktop-App im Browser. Dort laden Sie die neue Version herunter und installieren sie wie oben beschrieben.
Voraussetzung ist eine Server-Adresse, die mit `https` beginnt. Bei einer `http`-Adresse steht im Menü „Update nur über https möglich" — der Weg über den Browser bleibt.
**Einmaliger Wechsel:** Wer die Desktop-App in Version 1.2.0 oder älter installiert hat, lädt die nächste Version ein letztes Mal über den Browser herunter und installiert sie von Hand. Ab dann läuft das Aktualisieren über den Menüeintrag.
### Wenn etwas nicht klappt
- **„Unter dieser Adresse antwortet kein Tessera-Server"** — prüfen Sie die eingegebene Adresse; gemeint ist die Adresse, unter der Sie Tessera im Browser öffnen, nicht eine interne API-Adresse.
- **Der Download-Link fehlt auf der Anmeldeseite** — der Server trägt derzeit keine Desktop-Pakete. Fragen Sie in diesem Fall Ihren Administrator.
- **Windows zeigt die SmartScreen-Warnung** — das ist normal und erwartet, siehe [Installation unter Windows](#installation-unter-windows).
- **„Update fehlgeschlagen"** — das Netz war kurz weg, das Paket kam unvollständig an oder stammt nicht von Ihrem eigenen Server. Nehmen Sie den Weg über den Browser, wie unter [Neue Version](#neue-version) beschrieben.
- **Linux: Das Update meldet fehlende Schreibrechte** — legen Sie die AppImage-Datei in Ihren Home-Ordner und starten Sie sie von dort.
## Einen Fehler melden
+62 -7
View File
@@ -606,7 +606,10 @@ Kanal, Version, Dateinamen, Größen und Prüfsummen.
`publish` kopiert die fertigen Pakete in das API-Abbild nach
`/app/desktop-dist/`, zusammen mit einer `manifest.json` (Version, Kanal,
Dateinamen, Größen, Prüfsummen). Auf dem Beta-Kanal tragen die Dateinamen
Dateinamen, Größen, Prüfsummen; seit der Update-Funktion zusätzlich
`updateVersion` – die Form, die der Client vergleicht, `X.Y.Z` auf Live und
`X.Y.Z-beta.g{commit}` auf Beta – sowie je Plattform die `signature` des
Pakets). Auf dem Beta-Kanal tragen die Dateinamen
zusätzlich den Suffix `-beta.{commit}`, zum Beispiel
`Tessera-Setup-1.1.0-beta.742fb5c.exe` und
`Tessera-1.1.0-beta.742fb5c.AppImage`; auf Live steht dort die reine Form
@@ -617,14 +620,23 @@ Kontrolle auf dem Server:
```bash
docker compose exec api ls -l /app/desktop-dist
curl -s https://{ihre-adresse}/api-proxy/desktop/latest
curl -si "https://{ihre-adresse}/api-proxy/desktop/update?target=windows&arch=x86_64&current=0.0.0&base=https://{ihre-adresse}"
```
`ls -l` zeigt die abgelegten Dateien samt `manifest.json`; die `curl`-Abfrage
liefert dieselben Angaben als JSON (Version, Kanal, je Plattform Dateiname,
Größe, Prüfsumme und relative Download-Adresse) – das ist genau die Antwort,
die auch die Anmeldeseite und die Einstellungsseite auswerten. Antwortet die
Abfrage mit `404`, fehlt entweder das Verzeichnis oder das Manifest; die
Web-Oberfläche blendet den Download-Link dann automatisch aus.
`ls -l` zeigt die abgelegten Dateien samt `manifest.json`; die erste
`curl`-Abfrage liefert dieselben Angaben als JSON (Version, Kanal, je
Plattform Dateiname, Größe, Prüfsumme und relative Download-Adresse) – das
ist genau die Antwort, die auch die Anmeldeseite und die Einstellungsseite
auswerten. Antwortet die Abfrage mit `404`, fehlt entweder das Verzeichnis
oder das Manifest; die Web-Oberfläche blendet den Download-Link dann
automatisch aus.
Die zweite Kontrollzeile stellt die Frage, die der Desktop-Client beim Start
stellt: `200` mit `version`, `url` und `signature` bedeutet, dass sich
installierte Clients von diesem Server aktualisieren können; `204` bedeutet,
dass für diese Plattform kein signiertes Paket vorliegt (zum Beispiel ein Stand
vor September 2026 oder ein Bau ohne Schlüssel). Für Linux `target=linux`
einsetzen.
### Release-Dateien in Gitea
@@ -635,6 +647,47 @@ vorhandene Datei gleichen Namens, statt einen zweiten Anhang anzulegen. Die am
Release hinterlegte Datei ist byteidentisch mit der im Abbild ausgelieferten;
die Prüfsumme (`sha256`) aus `manifest.json` gilt für beide gleichermaßen.
### Updates in der App und der Signierschlüssel
Seit September 2026 aktualisiert sich die Desktop-App per Klick im Menü des
Infobereich-Symbols. Beim Start fragt sie `GET /api-proxy/desktop/update`
und installiert ausschließlich Pakete, deren Signatur zu dem im Client
hinterlegten öffentlichen Schlüssel passt – ein manipuliertes oder fremdes
Paket wird abgelehnt, bevor irgendetwas installiert wird. Das ist die
Vertrauensbasis der Update-Funktion, nicht die Prüfsumme im Manifest.
Der **private Schlüssel** liegt nicht im Repository. Er existiert an zwei
Stellen:
- als Gitea-Secrets `TAURI_SIGNING_PRIVATE_KEY` und
`TAURI_SIGNING_PRIVATE_KEY_PASSWORD` (Repository → Einstellungen → Actions →
Secrets); nur die beiden `tauri build`-Schritte des Jobs `desktop` sehen
sie, die Skripte kennen den Schlüssel nicht;
- als Sicherung auf dem Entwicklungsrechner unter
`~/.tessera/desktop-updater/` (`tessera-updater.key`, `password.txt` und
der öffentliche Teil `tessera-updater.key.pub`).
**Sicherung:** Legen Sie die beiden Dateien zusätzlich an einem zweiten
sicheren Ort ab. Geht der private Schlüssel verloren, können bereits
installierte Clients kein Update mehr annehmen: Es muss ein neues
Schlüsselpaar erzeugt (`pnpm --filter @tessera/desktop exec tauri signer
generate -w <pfad>`), der öffentliche Teil in
`apps/desktop/src-tauri/tauri.conf.json` unter `plugins.updater.pubkey`
eingetragen und jeder Client einmal von Hand neu installiert werden.
Ohne die Secrets bricht der CI-Bau ab („A public key has been found, but no
private key"). `tauri build --no-sign` ist ausschließlich für lokale Proben
gedacht und im CI nicht vorgesehen – ein so gebautes Paket trägt keine
Signatur, und der Update-Endpunkt antwortet dafür mit `204`.
Windows legt bei jedem Update einen Ordner `%TEMP%\Tessera-{Version}-updater-…`
(rund 100 MB) an und räumt ihn nicht auf. Das ist kein Fehler; die Ordner
können jederzeit gelöscht werden.
Der Client erlaubt Updates in der App nur über `https`. Anwender mit einer
`http`-Adresse sehen im Menü den Hinweis „Update nur über https möglich" und
nutzen weiterhin den Weg über den Browser.
### Umgebungsvariablen
Für die Desktop-Auslieferung ist keine neue Pflichtvariable nötig.
@@ -652,3 +705,5 @@ Für die Desktop-Auslieferung ist keine neue Pflichtvariable nötig.
| Client meldet „Unter dieser Adresse antwortet kein Tessera-Server" | Anwender hat die interne API-Adresse statt der Web-Adresse eingetragen, oder `/api-proxy` ist vom Client-Rechner aus nicht erreichbar | Die im Anwenderhandbuch beschriebene Adresse verwenden (dieselbe wie im Browser); Netzwerk-/Firewall-Erreichbarkeit der Web-Adresse prüfen. |
| Windows zeigt die SmartScreen-Warnung | Erwartet – die App ist für den internen Gebrauch nicht signiert (D-09) | Kein Fehler; Anwenderhandbuch, Abschnitt „Installation unter Windows", beschreibt den Ablauf. |
| Beta-Paket nennt einen älteren Commit als das laufende Abbild (Dateiname `-beta.<Commit>`, Einstellungen → Desktop-App) | Erwartet: Desktop-App seit diesem Commit unverändert, Pakete aus dem Zwischenspeicher übernommen (Abschnitt „Wann gebaut wird …") | Kein Fehler. Soll dennoch neu gebaut werden, genügt eine Änderung unter `apps/desktop/` im nächsten Push. |
| Client meldet „Update fehlgeschlagen" | Download über den Proxy abgebrochen (Größengrenze/Zeitlimit, siehe zweite Zeile dieser Tabelle), oder die Signatur passt nicht – die Pakete stammen nicht aus dem CI-Bau mit dem aktuellen Schlüssel | Kontrollzeile `/api-proxy/desktop/update` (Abschnitt „Wo die Pakete im Abbild liegen"), den Pipeline-Lauf und die Proxy-Einstellungen prüfen. Der Anwender kommt über den Browser-Weg weiter. |
| `/api-proxy/desktop/update` antwortet dauerhaft `204`, obwohl Pakete da sind | Manifest ohne `signature`/`updateVersion`: Pakete aus einem Bau vor der Update-Funktion oder mit `--no-sign` | Eine Änderung unter `apps/desktop/` pushen bzw. den Tag neu bauen lassen; im CI prüfen, dass die Secrets `TAURI_SIGNING_PRIVATE_KEY`/`_PASSWORD` gesetzt sind (Abschnitt „Updates in der App und der Signierschlüssel"). |
+19 -3
View File
@@ -140,15 +140,31 @@ Version setzen und Linux-Paket bauen:
```bash
sh .gitea/scripts/desktop-version.sh
pnpm --filter @tessera/desktop exec tauri build --bundles appimage
pnpm --filter @tessera/desktop exec tauri build --bundles appimage --no-sign
```
`desktop-version.sh` schreibt die Version des letzten Freigabe-Tags in
`tauri.conf.json`/`Cargo.toml` — die im Repository eingecheckten Versionsdateien
sind nur eine Basislinie, nicht die tatsächliche Freigabeversion. Das fertige
Paket liegt danach unter
`apps/desktop/src-tauri/target/release/bundle/appimage/`. Um es wie die
API es ausliefern würde einzusammeln:
`apps/desktop/src-tauri/target/release/bundle/appimage/`.
**`--no-sign` ist lokal Pflicht.** Seit der Update-Funktion in der App
verlangt `tauri build` den Signierschlüssel (Umgebungsvariable
`TAURI_SIGNING_PRIVATE_KEY`), weil `bundle.createUpdaterArtifacts` und der
öffentliche Schlüssel `plugins.updater.pubkey` in `tauri.conf.json` gesetzt
sind — ohne Schlüssel bricht der Bau mit „A public key has been found, but no
private key" ab. Mit `--no-sign` entsteht keine `.sig`-Datei;
`desktop-collect.sh` warnt dann nur (Kanal `dev`) und lässt das Feld
`signature` weg, und die API antwortet auf `GET /desktop/update` mit `204`.
Der In-App-Update-Weg lässt sich lokal also nur mit dem echten Schlüssel
durchspielen (Betriebshandbuch, Kapitel 10). Zwei Hinweise für `tauri dev`:
Dort läuft die App ohne AppImage — den Update-Download (`download_and_install`)
nie auslösen, er würde die Binary in `target/` überschreiben; die
Versionsprüfung selbst ist im Debug-Bau auch gegen `http://localhost`
erlaubt (das Plugin warnt nur, der Release-Bau lehnt `http` ab).
Um das Paket so einzusammeln, wie die API es ausliefern würde:
```bash
sh .gitea/scripts/desktop-collect.sh --require linux
+31 -4
View File
@@ -81,17 +81,27 @@ docker ps --filter name=gitea-runner
### Gitea Secrets (fuer die CI-Pipeline)
In Gitea unter **Repository > Settings > Actions > Secrets** werden die Secrets
fuer die Pipeline konfiguriert. Benoetigt wird genau eines:
fuer die Pipeline konfiguriert. Benoetigt werden drei:
| Secret | Beschreibung |
|--------|--------------|
| `REGISTRY_TOKEN` | Gitea-Zugangstoken (Access Token) mit Schreibrecht auf Pakete (`package: write`) und zusaetzlich auf das Repository (`repository: write`, fuer Releases). Wird im Job `publish` fuer `docker login localhost:3002 --password-stdin` verwendet und im Release-Schritt ueber `env` als `GITEA_TOKEN` an `.gitea/scripts/publish-release.sh` gereicht -- nie als Argument. |
| `TAURI_SIGNING_PRIVATE_KEY` | Inhalt der privaten Schluesseldatei des Tauri-Updaters (eine Base64-Zeile, erzeugt mit `tauri signer generate`). Wird ausschliesslich an den zwei `tauri build`-Schritten des Jobs `desktop` als `env` gesetzt; die Tauri-CLI signiert damit das AppImage und den Windows-Installer (`.sig` neben dem Bundle). Der oeffentliche Gegenpart steht in `apps/desktop/src-tauri/tauri.conf.json` (`plugins.updater.pubkey`). |
| `TAURI_SIGNING_PRIVATE_KEY_PASSWORD` | Passwort zu diesem Schluessel; gleiche Stelle, gleicher Umfang. |
Das Token erscheint nie im Log: es wird per `--password-stdin` uebergeben und
Gitea maskiert Secret-Werte in der Job-Ausgabe. Das Veroeffentlichungs-Skript
`.gitea/scripts/publish-images.sh` kennt das Token nicht; der Login bleibt im
Workflow.
Der Signierschluessel wird ebenfalls nie ausgegeben: die Skripte kennen ihn
nicht (`desktop-collect.sh` prueft nur, OB die Variable gesetzt ist, um die
Signatur zur Pflicht zu machen), nur die beiden Bau-Schritte sehen ihn --
weder `pnpm install`, `apt-get`, `cargo install cargo-xwin` noch die
Cache-Schritte. Die Sicherung des Schluessels ausserhalb der Pipeline
(`~/.tessera/desktop-updater/` auf dem Entwicklungsrechner) beschreibt das
Betriebshandbuch, Kapitel 10.
Der Push geht ueber `localhost:3002` (Gitea laeuft auf demselben Rechner wie der
Runner), weil der Nginx Proxy Manager vor `git.vicolab.de` grosse Image-Blobs
blockt. Das Pullen auf den Servern laeuft ueber `git.vicolab.de`
@@ -180,10 +190,15 @@ sh .gitea/scripts/desktop-stamp.sh stamp`.
(`cargo check`/`cargo clippy`, D-16), Bau des Linux-AppImage
(`tauri build --bundles appimage`), dann des Windows-Installers per
Cross-Bau (`tauri build --runner cargo-xwin --target
x86_64-pc-windows-msvc --bundles nsis`).
x86_64-pc-windows-msvc --bundles nsis`). Beide Bau-Schritte tragen die
Secrets `TAURI_SIGNING_PRIVATE_KEY`/`_PASSWORD` als `env` und signieren
die Bundles (quick-260917-kgc): die Tauri-CLI legt `.sig`-Dateien neben
`-setup.exe` und `.AppImage` ab -- host-unabhaengig, also auch im
Cross-Bau. Kein `--no-sign` im CI.
6. **Pakete einsammeln** (`desktop-collect.sh --require linux,windows`) --
schreibt `manifest.json` und schlaegt fehl, wenn eine der beiden Dateien
fehlt.
schreibt `manifest.json` (mit `updateVersion` und je Plattform der
`signature` aus der `.sig`-Datei) und schlaegt fehl, wenn eine der beiden
Dateien fehlt oder auf `main`/Tags eine `.sig` fehlt.
7. **Uebergabe an `publish`** per `actions/cache/save@v4` mit dem Schluessel
`desktop-dist-${{ gitea.sha }}` (ein neuer Schluessel je Commit, damit
`publish` garantiert die Pakete des gerade gebauten Standes bekommt, nicht
@@ -343,6 +358,18 @@ und einen Branch-Schutz fuer `live` anlegen (T-KU1-04).
Speicherdruck `CARGO_BUILD_JOBS` (z. B. auf `4`) als Umgebungsvariable im
Job setzen, um die parallele Uebersetzung zu drosseln.
### Job `desktop`: "A public key has been found, but no private key"
Die Tauri-CLI bricht den Bau ab, weil `plugins.updater.pubkey` in
`tauri.conf.json` gesetzt ist, aber `TAURI_SIGNING_PRIVATE_KEY` in der
Umgebung fehlt. Ursache sind fast immer fehlende oder umbenannte Secrets: in
Gitea unter **Repository > Settings > Actions > Secrets** pruefen, ob
`TAURI_SIGNING_PRIVATE_KEY` und `TAURI_SIGNING_PRIVATE_KEY_PASSWORD` unter
genau diesen Namen existieren, und ob beide Bau-Schritte in `ci.yml` den
`env`-Block tragen. Niemals `--no-sign` in `ci.yml` eintragen -- damit
entstuenden unsignierte Pakete, die kein Client als Update annimmt
(`desktop-collect.sh` bricht auf `main`/Tags ohne `.sig` ohnehin ab).
### `desktop` baut, obwohl nichts geaendert wurde -- oder uebernimmt trotz Aenderung
**Baut trotzdem:**