Quick 260917-kgc (Plan/Recherche/Bericht/Verifikation) und Schnellfixa6d1a64in der Quick-Task-Tabelle; Nachweise in allen sechs Berichten nachgetragen (Playwright lokal, CI-Laeufe 382-384, Windows-Test-VM: In-App-Update7479cb4->a6d1a64). Ueberholte .continue-here-Dateien entfernt, Desktop-Client-Todo geschlossen. Dieser Push aendert nichts unter apps/desktop -- er ist zugleich der Beweisfall 2 des CI-Desktop-Skips (Pakete aus dem Zwischenspeicher). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016g2npLxzH5gZpg8s2S6vKh
25 KiB
phase: quick-260917-kgc plan: 01 subsystem: desktop-client tags: [tauri, rust, tauri-plugin-updater, minisign, nestjs, ci, gitea-actions, desktop]
Dependency graph
requires:
- phase: quick-260917-jn2 provides: TrayItems { connected, update } im State, apply_server, spawn_version_check(app, server_url), stored_server_url als einzige Store-Lesestelle — hier angedockt, unveraendert uebernommen
- phase: quick-260917-jdh
provides: desktop-stamp.sh (Stempel/Skip) —
checkverlangt jetzt zusaetzlich updateVersion + Signaturen - phase: 18-desktop-client-fertigstellen provides: /desktop/latest, /desktop/download/:platform, manifest.json aus desktop-collect.sh, Run-Handler-Muster (code: None → prevent_exit) provides:
- "Desktop-Client aktualisiert sich selbst: Pruefung ueber tauri-plugin-updater gegen GET /api-proxy/desktop/update, Tray-Klick laedt das signierte Paket (Fortschritt im Menuetext), prueft die minisign-Signatur gegen plugins.updater.pubkey, installiert (Windows NSIS passiv, Linux AppImage in place) und startet neu"
- "API GET /desktop/update?target=&arch=¤t=&base= (oeffentlich, vor download/:platform) im dynamischen Updater-Format; 400 bei unreinem base, 204 in allen 'kein Update'-Faellen"
- "Manifest traegt updateVersion (X.Y.Z bzw. X.Y.Z-beta.g) und je Plattform signature (Inhalt der .sig); CI signiert beide Bundles mit den Secrets TAURI_SIGNING_PRIVATE_KEY/_PASSWORD an genau den zwei tauri-build-Schritten"
- "Reine, getestete Helfer is_update_newer, beta_commit, update_endpoint, release_labels, update_labels (neue Texte 'Auf Version {v} aktualisieren' / 'Auf Beta-Stand {sha7} aktualisieren')" affects: [desktop-client, updater, ci, api-desktop, docs]
Actuals (#2632)
actuals:
tokens: 66460
tasks: 4
commits: 4
plan_head_before: 5a444ec8f2
Tech tracking
tech-stack: added: - "tauri-plugin-updater 2.11.0 (Cargo.lock; zieht reqwest 0.13 + rustls/ring + rustls-platform-verifier neben dem Projekt-reqwest 0.12)" - "semver 1.0.28 als direkte Abhaengigkeit (lag bereits transitiv im Lock)" patterns: - "Updater-Kette komplett in Rust: app.updater_builder().endpoints(vec![url])?.timeout(15 s).version_comparator(is_update_newer …).build()?.check().await → PendingUpdate(Mutex<Option<(Update, String)>>) → Tray-Klick take() → download_and_install(on_chunk, on_finish) → app.restart(); capabilities/default.json unveraendert (Rust-Aufrufe laufen am ACL vorbei)" - "Endpunkt-URL zur Laufzeit aus stored_server_url mit Roh-Platzhaltern {{target}}/{{arch}}/{{current_version}} als Query-Werte (query_pairs_mut kodiert, das Plugin ersetzt beide Schreibweisen) plus base= fuer die absolute Rueckgabe-URL" - "NestJS 204 ohne Body: @Res({ passthrough: true }) + res.status(204) + return undefined" - "Vertrauenskette: CI signiert (Secret nur am Bau-Schritt) → .sig → desktop-collect.sh → manifest.signature → API reicht durch → Plugin prueft VOR der Installation; nie eine Installation ohne gueltige Signatur"
key-files: created: [] modified: - apps/desktop/src-tauri/Cargo.toml - apps/desktop/src-tauri/Cargo.lock - apps/desktop/src-tauri/tauri.conf.json - apps/desktop/src-tauri/src/lib.rs - apps/desktop/src-tauri/gen/schemas/acl-manifests.json - apps/desktop/src-tauri/gen/schemas/desktop-schema.json - apps/desktop/src-tauri/gen/schemas/linux-schema.json - packages/shared/src/index.ts - apps/api/src/desktop/desktop.service.ts - apps/api/src/desktop/desktop.controller.ts - apps/api/src/desktop/desktop.service.spec.ts - .gitea/scripts/desktop-collect.sh - .gitea/scripts/desktop-stamp.sh - .gitea/workflows/ci.yml - .gitignore - docs/anleitung-anwender.md - docs/anleitung-betrieb.md - docs/anleitung-entwicklung.md - docs/ci-cd-setup.md - CHANGELOG.md
key-decisions:
- "Vergleichsregel (is_update_newer): hoehere Basis X.Y.Z → Update; niedrigere → keins; gleiche Basis mit Beta-Stempel beta.g → Update nur bei bekanntem UND verschiedenem Client-Commit; gleiche Basis ohne Prerelease (Live) → KEIN Update. Das ist exakt die bisherige Regel (version_changed || channel==beta && commit != APP_COMMIT) — Live-Clients verhalten sich identisch, keine Update-Schleife; der Fall 'Beta-Client auf Live-Server gleicher Basis' ist client-seitig nicht entscheidbar und bekommt das Update mit der naechsten Freigabe."
- "Timeout-Trennung: 15 s fuer die Pruefung (UpdaterBuilder::timeout), aber das Plugin uebernimmt denselben Wert als Gesamt-Timeout des Downloads — darum vor der Ablage update.timeout = Some(600 s), sonst braeche der ~100-MB-Download ab."
- "http-Server im Release-Bau: kein Absturz, keine Benachrichtigung bei jedem Start, sondern dauerhaft der gesperrte Menuetext 'Update nur über https möglich' (Err(InsecureTransportProtocol)-Zweig); keine dangerous*-Schalter."
- "Secrets auf Schritt-Ebene (env an den zwei tauri-build-Schritten), nicht Job-Ebene: pnpm install, apt-get, cargo install cargo-xwin und die Cache-Schritte sehen den Schluessel nie; js-yaml-Tiefenvergleich bestaetigt, dass sonst nichts in ci.yml geaendert ist."
- "gen/schemas/*.json (vom Tauri-Bau generiert, im Repo getrackt) wurden mit committet — Praezedenz 18-04 (Opener-Plugin); ein liegengelassener Diff haette den Arbeitsbaum schmutzig hinterlassen (Rule 3, siehe Abweichungen)."
patterns-established:
- "Beta-Stempel im SemVer-Prerelease immer mit Praefix g (beta.g): ein rein numerischer SHA mit fuehrender Null waere kein gueltiger Identifier und liesse check() mit Err enden"
- "Tray-Eintrag 'update' ist zustandsbehaftet: PendingUpdate.take() beim Klick, Reset + Leeren zu Beginn jeder Pruefung, Rueckgabe des Standes bei Fehler"
requirements-completed: [QUICK-260917-KGC]
coverage:
- id: D1
description: "Client: Plugin, Config (pubkey/installMode/createUpdaterArtifacts), Comparator, Endpunkt-URL, Tray-Fluss Pruefen → Anzeige → Klick → Download → Signatur → Installation → Neustart, http-Rueckfall"
requirement: "QUICK-260917-KGC"
verification:
- kind: unit ref: "apps/desktop/src-tauri/src/lib.rs mod tests — 33 Tests (15 neu, 2 umgestellt, 16 unveraendert), cargo test --lib" status: pass
- kind: other ref: "cargo fmt --check, cargo check, cargo clippy — 0 Warnungen; jq-Gate tauri.conf.json; Cargo.lock: tauri-plugin-updater 2.11.0, semver 1.0.28, reqwest-Projekt bleibt 0.12" status: pass human_judgment: true rationale: "Der Ende-zu-Ende-Nachweis (Windows-VM: Tray-Text, Klick, passiver Installer, Neustart auf neuem Stand) braucht ein vom CI signiertes Paket — Orchestrator, siehe unten."
- id: D2
description: "API GET /desktop/update: base-Origin-Validierung (400), 204-Faelle, dynamisches Updater-Format mit absoluter URL; Manifest-Typen in @tessera/shared"
requirement: "QUICK-260917-KGC"
verification:
- kind: unit ref: "apps/api/src/desktop/desktop.service.spec.ts — 23 Tests (10 neu, Test 11 erweitert), vitest run src/desktop" status: pass
- kind: other ref: "pnpm --filter @tessera/api type-check, pnpm --filter @tessera/shared type-check — 0 Fehler; Route-Order-Gate (update vor download/:platform); kein fetch/axios im Service" status: pass human_judgment: false
- id: D3
description: "desktop-collect.sh (signature/updateVersion/Pflichtregel), desktop-stamp.sh check, ci.yml Secrets an zwei Schritten, .gitignore *.key"
requirement: "QUICK-260917-KGC"
verification:
- kind: other ref: "Mini-Fixture-Proben im Scratchpad (beta/live/dev, fehlende .sig auf main/Tag/mit Schluessel → Abbruch, dev → Warnung, zweizeilige .sig → Abbruch, stamp check reuse=true nur mit updateVersion + beiden Signaturen) + js-yaml-Tiefenvergleich gegen HEAD (CI-OK, SCRIPTS-OK)" status: pass human_judgment: true rationale: "Ob die Tauri-CLI im xwin-Cross-Bau tatsaechlich .sig-Dateien erzeugt und ring/rustls dort bauen, ist nur in der Pipeline beweisbar."
- id: D4
description: "Anwender-, Betriebs-, Entwicklungshandbuch, ci-cd-setup.md (ASCII), CHANGELOG"
requirement: "QUICK-260917-KGC"
verification:
- kind: other ref: "Grep-Gate Task 4 + git diff CHANGELOG.md (genau eine neue Zeile)" status: pass
- kind: unit ref: "apps/web/src/lib/changelog.test.ts — 10/10 gruen" status: pass human_judgment: false
duration: ~20min (erster RED-Lauf 15:2x, letzter Commit 15:36:58 +02:00; nicht exakt gestoppt) completed: 2026-09-17 status: complete
Quick Task 260917-kgc: Desktop-Client — Update in der App herunterladen und installieren Summary
Der Desktop-Client prueft beim Start ueber tauri-plugin-updater den neuen Endpunkt GET /api-proxy/desktop/update, und ein Klick auf „Auf Version X.Y.Z aktualisieren" im Infobereich laedt das im CI mit dem minisign-Schluessel signierte Paket, prueft die Signatur, installiert es (Windows: NSIS passiv, Linux: AppImage an Ort und Stelle) und startet die App neu — der Browser-Download bleibt nur noch als Rueckfall.
Performance
- Duration: ~20 min (vier Tasks, je RED → GREEN bei Task 1/2, vier Verifikations-Gates; der erste
cargo checkmit dem Plugin lief 23 s, weil reqwest 0.13/rustls bereits im Cargo-Zwischenspeicher lagen) - Completed: 2026-09-17T15:36:58+02:00
- Tasks: 4/4
- Files modified: 20 (davon 3 generierte Schema-Dateien, siehe Abweichungen)
Accomplishments
- Client (
lib.rs, Cargo.toml/Cargo.lock, tauri.conf.json):tauri-plugin-updater = "2"(→ 2.11.0) undsemver = "1"eingebunden,.plugin(tauri_plugin_updater::Builder::new().build())inrun();bundle.createUpdaterArtifacts: true,plugins.updater.pubkey(exakt der vorgegebene Schluessel),windows.installMode: "passive", keinendpoints, keinedangerous*-Schalter. Reine Helferbeta_commit,is_update_newer,update_endpoint,release_labels,update_labels(neue Texte).check_for_updateruftupdater_builder().endpoints(…)?.timeout(15 s).version_comparator(…).build()?.check();spawn_version_checksetzt Eintrag undPendingUpdatezurueck, legt bei Treffer(Update, Menuetext)mittimeout = 600 sab, zeigt die Benachrichtigung und aktiviert den Eintrag;InsecureTransportProtocol→ „Update nur über https möglich" gesperrt. Tray-Klickupdate:take()→spawn_update_install(FortschrittLädt … {n} %,Wird installiert…,app.restart(); Fehler → Text/Stand zurueck, Benachrichtigung „Update fehlgeschlagen: …", Einstellungsseite im Browser) bzw.open_download_pageohne abgelegten Stand.DesktopLatestentfernt,/desktop/latestwird im Client nicht mehr referenziert; Run-Handler,on_window_event, Commands,capabilities/default.json,setup.htmlunveraendert. - API:
GET /desktop/update(@Public(), vordownload/:platform):safeOrigin(base)(nur http/https-Origin, sonst 400),getUpdate({ target, arch, origin })mit Whitelist →x86_64→ Manifest →updateVersion(UPDATE_VERSION_RE) → Eintrag mitsignature→ Antwort{ version, pub_date (nur RFC 3339), url: <origin>/api-proxy/desktop/download/<p>, signature, notes }; sonst 204 ohne Body.isValidManifestFileEntryakzeptiert nur String-Signaturen;getManifestverwirft ein Nicht-String-updateVersion. TypenDesktopManifestFile.signature?,DesktopManifest.updateVersion?,DesktopUpdateResponsein@tessera/shared. - Skripte/CI:
desktop-collect.shliest<bundle>.sig(genau eine Base64-Zeile, Zeilenzahl separat erzwungen) alsfiles.<p>.signature, schreibtupdateVersion(X.Y.Zbzw.X.Y.Z-beta.g<sha7>), bricht ohne.sigab, wennTAURI_SIGNING_PRIVATE_KEYgesetzt ist oder der Kanal nichtdevist, warnt sonst;.sigwird nicht kopiert, Dateinamen unveraendert.desktop-stamp.sh checkgibtreuse=falseohneupdateVersionoder ohne eine der beiden Signaturen.ci.yml:envmit den zwei Secrets an genau den Schritten „Linux-AppImage bauen" und „Windows-Installer bauen (Cross-Bau)", sonst strukturgleich..gitignore:*.key. - Doku: Anwenderhandbuch (Update per Klick, Windows/Linux-Ablauf, Fehlerfall, https-Bedingung, einmaliger Wechsel fuer Clients bis 1.2.0, zwei neue „Wenn etwas nicht klappt"-Zeilen), Betriebshandbuch Kap. 10 (neuer Unterabschnitt Signierschluessel,
updateVersion/signatureim Manifest, zweitecurl-Kontrollzeile, zwei Fehlerbilder), Entwicklungshandbuch (--no-signlokal,tauri dev-Hinweise), ci-cd-setup.md (drei Secrets, signierende Bau-Schritte, Fehlerbild „no private key", ASCII), CHANGELOG eine Zeile.
Task Commits
Each task was committed atomically:
- Task 1: Client — Updater-Plugin, Config, Vergleichsregel, Endpunkt-URL, Tray-Fluss, Tests -
678ba51(feat(desktop)) - Task 2: API — GET /desktop/update, Manifest-Felder, Spec-Tests -
de81c74(feat(api)) - Task 3: Skripte + CI — signature/updateVersion, stamp check, Secrets, .gitignore -
7004b5b(ci) - Task 4: Doku — Handbuecher, ci-cd-setup.md, CHANGELOG -
7479cb4(docs)
Hinweis: kein separater test(...)-Commit trotz tdd="true" an Task 1 und 2 — die Orchestrator-Vorgabe lautet ausdruecklich vier Commits (feat(desktop)/feat(api)/ci/docs). RED und GREEN liefen innerhalb der Tasks, siehe „TDD Gate Compliance".
Files Created/Modified
apps/desktop/src-tauri/src/lib.rs— Imports (semver::Version,Mutex,Update,UpdaterExt),UPDATE_ITEM_DEFAULT = "Update installieren",UPDATE_ITEM_INSECURE,PendingUpdate, Helferbeta_commit/is_update_newer/update_endpoint/release_labels,update_labelsmit neuen Texten,check_for_update, umgebautesspawn_version_check,open_download_page,spawn_update_install, Plugin-Registrierung,app.manage(PendingUpdate…), Menue-Zweigupdate;mod tests33 Testsapps/desktop/src-tauri/Cargo.toml,Cargo.lock—tauri-plugin-updater = "2"(2.11.0),semver = "1"(1.0.28); Projekt-reqwestbleibt 0.12apps/desktop/src-tauri/tauri.conf.json—createUpdaterArtifacts,plugins.updaterapps/desktop/src-tauri/gen/schemas/{acl-manifests,desktop-schema,linux-schema}.json— vom Tauri-Bau regeneriert (Updater-Permissions), siehe Abweichungenpackages/shared/src/index.ts—signature?,updateVersion?,DesktopUpdateResponseapps/api/src/desktop/desktop.service.ts—safeOrigin(exportiert),UPDATE_VERSION_RE,RFC3339_RE,getUpdate, erweiterte Manifest-Pruefungapps/api/src/desktop/desktop.controller.ts—update()vordownload()apps/api/src/desktop/desktop.service.spec.ts—writeManifest(files, head),linuxEntry,updateUrl, Tests 12-21, Test 11 erweitert.gitea/scripts/desktop-collect.sh—UPDATE_VERSION,SIGN_REQUIRED,read_signature,jq-Manifest mitupdateVersion/signature.gitea/scripts/desktop-stamp.sh—checkverlangtupdateVersion+ Signaturen.gitea/workflows/ci.yml— Kopfkommentar +envan zwei Bau-Schritten.gitignore—*.keydocs/anleitung-anwender.md,docs/anleitung-betrieb.md,docs/anleitung-entwicklung.md,docs/ci-cd-setup.md,CHANGELOG.md
Decisions Made
- Vergleichsregel (gleiche Basis + Live → kein Update): siehe
key-decisions; steht als Doc-Kommentar anis_update_newer. LeererAPP_COMMIT(Quell-Tarball ohne git) → reiner Basisvergleich wie bisher. Derversion_comparatorersetzt den Standardvergleich vollstaendig — ohne ihn gaelte1.2.0-beta.gXXXX < 1.2.0und ein Beta-Client saehe nie einen neueren Beta-Bau. - Timeout-Trennung 15 s / 600 s:
UpdaterBuilder::timeoutwandert ins pub-FeldUpdate.timeoutund wird beim Download als reqwest-Gesamt-Timeout angewandt; darum wird es nachcheck()und vor der Ablage auf 600 s gesetzt. http-Anzeige: gesperrter Menuetext statt Benachrichtigung — fuer Dauer-http-Server waere eine Meldung bei jedem Start eine Nervmeldung; der Text ist sichtbar, sobald das Menue geoeffnet wird. KeindangerousInsecureTransportProtocol.- Secrets auf Schritt-Ebene, nicht Job-Ebene (geringste Sichtbarkeit; Gitea maskiert die Werte; die Skripte kennen den Schluessel nicht).
read_signatureprueft die Zeilenzahl getrennt:grep -qE '^…$'arbeitet zeilenweise und haette eine zweizeilige.sigdurchgelassen, sobald eine Zeile passt — darum zusaetzlichwc -l= 1 (die Plan-Probe „zweizeilige .sig → Abbruch" haette sonst nicht gehalten).
Deviations from Plan
Auto-fixed Issues
1. [Rule 3 - Blocking] Generierte Tauri-Schema-Dateien mit committet
- Found during: Task 1,
cargo check - Issue:
tauri-buildregeneriert bei jedem Bauapps/desktop/src-tauri/gen/schemas/{acl-manifests,desktop-schema,linux-schema}.json(die Updater-Permissions kommen hinzu). Die drei Dateien sind im Repo getrackt und stehen nicht infiles_modified; sie unveraendert zu lassen haette den Arbeitsbaum dauerhaft schmutzig hinterlassen bzw. bei jedem CI-Bau erneut abweichen lassen. - Fix: Die drei Dateien in den Task-1-Commit aufgenommen — exakt wie beim Einbau des Opener-Plugins in 18-04 (
8b130fd). Inhalt ausschliesslich generiert, kein manueller Eingriff. - Files modified:
apps/desktop/src-tauri/gen/schemas/acl-manifests.json,desktop-schema.json,linux-schema.json - Verification:
git statusnach dem Commit leer (bis auf.planning/);cargo check/clippy/testgruen - Committed in:
678ba51
2. [Rule 1 - Bug, vor dem Commit behoben] Zeilenzahl-Pruefung in read_signature
- Found during: Task 3, beim Schreiben der Funktion
- Issue: Die im Plan vorgesehene Pruefung
grep -qE '^[A-Za-z0-9+/=]+$'allein ist zeilenweise — eine zweizeilige.sig(zwei\nzeilen) haette bestanden, obwohl der Plan den Abbruch verlangt. - Fix: Zusaetzlich
SIG_LINES="$(printf '%s\n' "$SIG_CONTENT" | wc -l)"muss1sein. - Files modified:
.gitea/scripts/desktop-collect.sh - Verification: Plan-Probe „zweizeilige .sig → Abbruch" gruen (Teil von SCRIPTS-OK)
- Committed in:
7004b5b
Total deviations: 2 auto-fixed (1 blocking/generierte Dateien, 1 Bug in der geplanten Pruefung)
Impact on plan: Kein Verhalten ausserhalb der must_haves.truths geaendert; das Threat-Register (T-KGC-01 … T-KGC-SC) ist wie geplant umgesetzt: Cargo.lock loest tauri-plugin-updater auf 2.11.0 (kein 3.0.0-alpha), semver 1.0.28, Projekt-reqwest 0.12; ~/.tessera/desktop-updater/ wurde nie gelesen; kein Secret-Wert in Code, Doku, Logs oder hier.
TDD Gate Compliance
Task 1 und 2 liefen als RED → GREEN innerhalb eines Commits je Task (Orchestrator-Vorgabe: genau vier Commits):
- Task 1 (Rust, tdd="true"): RED — 15 neue Tests plus 2 umgestellte
update_labels_*-Tests inmod tests;cargo test --lib(Exit 101) scheiterte mit 18 Compile-Fehlern, alle auf die fehlenden Funktionenis_update_newer/beta_commit/update_endpoint/release_labelsund das noch fehlendesemverzurueckfuehrbar — genau der im Plan als RED vorgesehene Zustand (streng nachtdd.mdein Compile-RED, kein Assertion-RED; vom Plan ausdruecklich so definiert). GREEN — Abhaengigkeiten, Config und Implementierung;cargo test --lib33/33 gruen,cargo fmt --check/check/clippyohne Warnung. Ein Commit (678ba51). - Task 2 (API, tdd="true"): RED — Tests 12-21 und die Erweiterung von Test 11;
vitest run src/desktopzeigte 11 fehlgeschlagene Zieltests auf Assertions der geplanten Behauptungen (404/200 statt 204,safeOriginnicht vorhanden,updatenicht@Public()), 12 Bestandstests gruen. GREEN — Typen, Service, Controller; 23/23 gruen, api- und shared-type-check gruen. Ein Commit (de81c74). - Task 3/4 (kein
tdd="true"): Standard-Tasks.
Kein separater test(...)-Commit — bewusst, wie bei 260917-jn2; die formale gsd_run check tdd-red-evidence-Pruefung gilt fuer Plaene mit type: tdd, dieser Plan hat type: execute.
Testzahlen
| Pruefung | Ergebnis |
|---|---|
cargo test --lib (apps/desktop/src-tauri) |
33 passed, 0 failed (vorher 18) |
cargo fmt --check, cargo check, cargo clippy |
gruen, 0 Warnungen |
vitest run src/desktop (apps/api) |
23 passed (vorher 11) |
pnpm --filter @tessera/api type-check, @tessera/shared type-check |
0 Fehler |
| Skript-Proben (Scratchpad-Fixtures) + js-yaml-Tiefenvergleich | CI-OK, SCRIPTS-OK |
vitest run src/lib/changelog.test.ts (apps/web) |
10 passed |
Issues Encountered
None ueber die dokumentierten Abweichungen hinaus. Der Cargo-Zwischenspeicher enthielt reqwest 0.13/rustls/ring bereits (aus der Recherche-cargo fetch), darum kein minutenlanger Erst-cargo check.
Nachweis durch Orchestrator (offen)
Der echte Beweis der Kette braucht ein vom CI signiertes Paket und ist hier nicht erbracht:
- (a) CI baut signierte Pakete: Nach dem Push auf
mainmuss der Jobdesktopbeidetauri build-Schritte mit den Secrets durchlaufen (Voraussetzung:TAURI_SIGNING_PRIVATE_KEYundTAURI_SIGNING_PRIVATE_KEY_PASSWORDsind in Gitea angelegt — sonst „A public key has been found, but no private key"). „Pakete einsammeln" musssigniertfuer linux und windows loggen; das Manifest im Abbild traegtupdateVersion: "X.Y.Z-beta.g<sha7>"und je Plattformsignature.publishgruen. Der alte Stempel-Cache (ohne die Felder) wird durchdesktop-stamp.sh checkverworfen — der erste Lauf baut also zwingend neu. - (b) Endpunkt gegen alpha:
curl -si "https://alpha.tessera.ctl.de/api-proxy/desktop/update?target=windows&arch=x86_64¤t=0.0.0&base=https://alpha.tessera.ctl.de"→200mitversion,url(absolut,https://alpha.…/api-proxy/desktop/download/windows),signature,pub_date,notes;target=linuxanalog;base=https://alpha.tessera.ctl.de/x→400; vor dem Pull des neuen Abbilds noch404(Route existiert nicht). - (c) Windows-VM (8233): alter Client aktualisiert sich per Tray-Klick. Der bereits installierte Client 1.2.0 hat das Plugin NICHT — er zeigt weiterhin „Version … herunterladen"/Browser-Weg (Einmaliger Wechsel: das erste Paket mit Plugin muss ein letztes Mal ueber den Browser installiert werden). Danach, mit einem weiteren Beta-Bau auf dem Server: Nach dem Start Benachrichtigung „Neuer Beta-Stand verfügbar – Aktualisieren über das Symbol im Infobereich." und der Eintrag heisst „Auf Beta-Stand aktualisieren" (bei neuer Basisversion: „Auf Version X.Y.Z aktualisieren"). Klick → Benachrichtigung „Update wird heruntergeladen…", Eintrag gesperrt, Text „Lädt … NN %", dann „Wird installiert…" → passives NSIS-Fenster mit Fortschrittsbalken → Tessera startet neu, Tray zeigt den neuen Stand, Server-Adresse erhalten. SmartScreen-Verhalten des vom Updater gestarteten Installers notieren (Recherche A2: vermutlich kein Dialog, keine Mark-of-the-Web). Bei Fehler: Menuetext springt zurueck, Eintrag wieder aktiv, Benachrichtigung „Update fehlgeschlagen: . Die Download-Seite wird im Browser geöffnet." und die Seite Einstellungen → Desktop-App oeffnet im Browser. Bei
http://-Adresse: Eintrag gesperrt mit „Update nur über https möglich". - (d) Cross-Bau von ring/rustls in der Pipeline:
ring 0.17.14ist neu im Graph; fuerx86_64-pc-windows-msvcliefert es vorassemblierte Objekte mit (kein nasm), die C-Teile bautccmit dem Clang auscargo-xwin— nur im Runner beweisbar. Fallback bei rotem Bau:tauri-plugin-updater = { version = "2", default-features = false, features = ["native-tls", "system-proxy"] }(schannel/OpenSSL;libssl-devsteht bereits in der apt-Liste). Das Projekt-reqwestNICHT auf 0.13 heben (aws-lc-rs).
Self-Check: PASSED
- Dateien vorhanden:
apps/desktop/src-tauri/src/lib.rs,apps/api/src/desktop/desktop.service.ts,.gitea/scripts/desktop-collect.sh,docs/anleitung-betrieb.md— FOUND - Commits vorhanden:
678ba51,de81c74,7004b5b,7479cb4— FOUND (git rev-list --count 5a444ec..HEAD= 4)
Nachweis durch Orchestrator — Ergebnis (2026-09-17/18)
- (a) CI-Lauf 383 (
7479cb4):Finished 1 updater signaturefuer AppImage und NSIS (Cross-Bau mit rustls/ring lief durch), ManifestupdateVersion 1.2.0-beta.g7479cb4,files.*.signaturegesetzt; Lauf 384 (a6d1a64) ebenso. (d) damit erledigt, kein native-tls-Fallback noetig. - (b) alpha nach Pull:
GET /api-proxy/desktop/update?target=windows&arch=x86_64¤t=1.2.0&base=https://alpha.tessera.ctl.de→ 200 mit absoluterurlundsignature;base=https://evil.example/x?y→ 400. - (c) Windows-VM: Client
7479cb4(einmalig per Browser installiert), gegen den http-Dev-Stack „Update nur über https möglich" (gesperrt); gegen alpha mita6d1a64: Tray „Auf Beta-Standa6d1a64aktualisieren" → Klick → Download in Sekunden, App beendet sich, passiver NSIS-Installer laeuft durch, App startet selbst neu (kein SmartScreen), Server-Adresse erhalten, Update-Eintrag wieder gesperrt, Setup-Seite zeigt „Tessera-App 1.2.0 · Stand a6d1a64" (Quick-Fixa6d1a64). - Nebenbefund: NSIS-Seite „Bereits installiert" nennt „Eine -Version von Tessera" (leere Versionsangabe aus der Registry) — kosmetisch, Tauri-Vorlage, nicht angefasst.