Sur cette page

← Toute la documentation

Canaux, publication et mises à jour

Mises à jour du jeu : le contrat de releases.json, canaux, signature, ce que fait le client.

Statut : canaux, publication et client de mise à jour dans le jeu. Décision de l'auteur (2026-10-10) : les zips sont envoyés à la main sur le serveur du site (prism.am, prism-admin release add), qui publie une liste publique releases.json ; les joueurs mettent à jour depuis le jeu, contre ce flux du site, jamais contre GitHub (le dépôt est privé : pas de jeton dans le client). Le flux signé, Velopack, OVH et la signature des exécutables restent des phases ultérieures (docs/etude-mises-a-jour.md, branche docs/autoupdate-study) ; ce qui suit décrit ce qui existe.

Deux canaux, deux installations

stable pbe
Fichier channel (à côté de prism.exe) stable ou absent pbe
Dossier de données %LOCALAPPDATA%\Prism\PrismNG\data (inchangé) %LOCALAPPDATA%\Prism\PrismNG-PBE\data
Base, replays, mods, skins, thèmes du joueur ceux du dossier de données ceux de son propre dossier
Profil WebView2 (localStorage : réglages, skinConfig, brouillons de l'Atelier) <données>\webview2 <données>\webview2 (du canal PBE)
Titres de fenêtre Prism, Prism — Auto… Prism PBE, Prism PBE — Auto…
Pastille aucune « PBE » dans l'en-tête du tiroir (Échap)
Instance unique (mutex Windows) Local\PrismNG-stable Local\PrismNG-pbe

Le fichier channel est lu une seule fois, au démarrage (apps/desktop/src/app/install.rs, appelé par startup.rs). Il tient en un mot (stable ou pbe, casse et espaces ignorés). Absent : stable, sans message. Illisible, trop long ou mot inconnu : stable et une ligne dans la console (PRISM: … is not usable). Le canal n'est jamais compilé : c'est ce qui permet de promouvoir les mêmes octets d'exécutable de PBE vers stable.

Les deux canaux tournent côte à côte. Une seconde instance du même canal ne démarre pas : elle ramène la fenêtre de la première au premier plan (recherche des fenêtres visibles dont le titre est celui du canal, dans un prism.exe), ou, si aucune fenêtre n'existe encore, affiche « Prism est déjà lancé » puis quitte. --write-ts-packages, --check-library, --bench-gameplay et les --smoke-* ne prennent jamais ce verrou.

Les variables PRISM_MODS_DIR et PRISM_SKINS_DIR fonctionnent comme avant. Réglages → À propos affiche le canal et la version (Cargo.toml, la même pour les zips PBE et stable d'un même build).

Profil WebView2 hors du dossier de l'exécutable

Avant, WebView2 écrivait prism.exe.WebView2 à côté de l'exe : un outil qui remplace le dossier de l'exe aurait effacé les réglages. Il est maintenant dans <données>\webview2 (app/profile.rs, WEBVIEW2_USER_DATA_FOLDER posé avant toute WebView). Au premier lancement, un ancien prism.exe.WebView2 est copié (jamais déplacé) vers <données>\webview2 :

  • seulement si la cible n'existe pas (une cible présente gagne toujours, même vide) ; idempotent ;
  • copie dans webview2.copying puis renommage : une copie interrompue ne laisse pas un faux profil ;
  • les fichiers verrou du navigateur (lockfile, LOCK) sont ignorés, tout autre fichier illisible est journalisé (PRISM: profile file … not copied) et ignoré ; le reste est copié ;
  • une variable WEBVIEW2_USER_DATA_FOLDER déjà définie est respectée (rien n'est migré).

Conséquence : supprimer le dossier de données remet maintenant aussi les réglages à zéro (avant, ils survivaient). Les diagnostics (--smoke-*, --bench-gameplay) gardent leur profil jetable dans le dossier temporaire.

Couper une release avec la CI GitHub (écartée pour l'instant)

Cette voie existe (.github/workflows/release.yml, jamais exécuté) mais n'est pas celle retenue : voir « Couper une release et la publier sur le site (sans CI : la voie retenue) ».

Tout passe par .github/workflows/release.yml (déclenché à la main : Actions → release → Run workflow, ou par un tag v*). Le workflow ne se lance jamais seul sur un push.

  1. Changer la version du workspace (Cargo.toml) si c'est une nouvelle version ; fusionner dans la branche à publier.
  2. PBE : lancer le workflow avec channel = pbe. Il construit, vérifie, empaquette Prism-<version>-pbe.<n>-windows-x64.zip (channel = pbe), et publie la release v<version>-pbe.<n> en prerelease avec SHA256SUMS.txt (case « draft » pour la créer en brouillon).
  3. Tester ce zip (décompresser, lancer prism.exe, pastille PBE et dossier PrismNG-PBE).
  4. Promouvoir sans recompiler (le chemin normal vers stable) :
gh release download v0.1.0-pbe.3 --pattern "Prism-*-pbe.3-windows-x64.zip" --dir dist
.\scripts\promote.ps1 -Zip dist\Prism-0.1.0-pbe.3-windows-x64.zip -OutDir dist\stable
gh release create v0.1.0 dist\stable\Prism-0.1.0-windows-x64.zip dist\stable\SHA256SUMS.txt --title "Prism v0.1.0" --notes-file dist\stable\NOTES.md

promote.ps1 refuse un zip qui n'est pas pbe, vérifie que prism.exe correspond au SHA-256 de RELEASE.txt, écrit channel = stable, met RELEASE.txt à jour (promoted-from), reconstruit le zip Prism-<version>-windows-x64.zip, contrôle que le SHA-256 de prism.exe est inchangé et écrit SHA256SUMS.txt. Écrire soi-même NOTES.md (ou utiliser --generate-notes). Le workflow accepte aussi channel = stable (reconstruction complète), à réserver au tout premier zip ou à un cas d'urgence.

Le zip contient : prism.exe, channel, RELEASE.txt (version, canal, commit, date, SHA-256 de l'exe), LISEZMOI.txt, mods\, skins\, themes\. Un zip stable garde son nom historique ; un zip PBE porte -pbe.<n>. En local : .\scripts\package.ps1 [-Channel pbe] [-Label pbe.3] [-SkipBuild].

Ce que le workflow vérifie (volontairement peu)

Compilation release verrouillée (--locked, runtime statique) puis : bun run check et les tests TypeScript purs (src/lib, src/menu, src/theme : parité des langues, réglages, menus, thèmes) ; cargo test --release -p themes -p skin -p modding (le contenu livré : thèmes, skins, mods) ; les paquets TypeScript commités doivent être ceux qu'écrit l'exe qu'on vient de construire. Les tests du crate desktop ne tournent pas là : ils recompileraient toute l'application en profil de test, presque aussi long que la compilation. La suite complète et les smokes (GPU, WebView2) restent dans le flux de l'intégrateur. Le workflow n'a encore jamais tourné : première exécution à surveiller.

Coût (estimation)

Dépôt privé, plan gratuit : 2 000 minutes par mois, les minutes Windows comptent double (facteur 2 du barème GitHub). Une compilation à froid sur windows-latest : 25 à 40 min [estimation, non mesurée] ; avec le cache cargo : 8 à 15 min [estimation]. Une release PBE coûterait donc de l'ordre de 20 à 80 minutes décomptées à froid, 16 à 30 avec cache : le plan gratuit n'en offre qu'une trentaine par mois à froid. Raisons de ne pas lancer le workflow à chaque fusion.

Le client de mise à jour (Réglages → À propos → Mises à jour)

Tout est dans le crate crates/updater (aucune dépendance nouvelle : ureq, ring déjà dans l'arbre) ; l'hôte (apps/desktop/src/app/updates.rs) route les commandes de la page et les états (updateState). Chaque étape tourne sur un fil à elle, jamais sur le fil de la fenêtre, jamais pendant une partie, un aperçu ou un replay (un téléchargement en cours s'arrête quand une partie démarre ; le fichier partiel est gardé et repris).

Étape Ce qui se passe
Vérifier un seul GET de la liste (https://prism.am/releases.json par défaut), sans identifiant, sans version, sans cookie (agent Prism-updater). Automatique une fois par jour au plus (20 s après l'interface prête, puis relecture horaire de l'échéance ; une tentative compte, un site en panne n'est pas interrogé toutes les heures), bouton « Rechercher » à la demande
Télécharger uniquement sur clic. Progression, reprise Range, taille et SHA-256 vérifiés contre la liste ; le fichier faux est supprimé
Installer sur clic « Redémarrer pour mettre à jour » (ou au prochain lancement si le paquet est prêt) : déplacement des fichiers avec sauvegarde, redémarrage

Réglages (visibles, Réglages → À propos) : updateCheck (activé par défaut : il ne fait que lire la liste), updateChannel (vide = le canal de cette installation, stable ou pbe), updateFeedUrl (vide = le site ; PRISM_UPDATE_FEED l'emporte). Aucun n'est exporté (.pvsettings.json). L'adresse du flux doit être en https (http seulement pour localhost/127.0.0.1), et toute adresse suivie (paquet, redirection) doit avoir le même schéma, le même hôte et le même port que le flux ; 3 redirections au plus, suivies par le client lui-même. Un site injoignable ou pas encore déployé donne seulement « Mises à jour indisponibles ».

Le flux releases.json (contrat du site)

Par version : channel, seq, build, publishedAt (ou date), url (relative au site ou absolue), sha256, size, notesMd (ou notes), notesEnMd, commit, semver, builtFromBuild, withdrawn / status: "withdrawn", signature (facultatif, voir plus bas). Les champs inconnus sont ignorés ; une entrée inutilisable est écartée seule. L'ordre d'un canal est seq, jamais build (libellé libre, jamais analysé). Même seq que l'installé : rien à faire. Un « dernier » plus ancien que l'installé (retiré) : jamais de rétrogradation. Un autre canal : sa dernière version est toujours proposée, avec l'avertissement que le dossier de données change (PrismNG ↔ PrismNG-PBE : le channel du paquet déplace l'installation).

Quelle version est installée

Le seq est attribué par l'outil du site après la construction du zip : le zip ne peut pas toujours le porter. Le jeu le déduit, de la source la plus sûre à la moins sûre :

  1. son propre enregistrement (update-state.json, à côté de l'exe), écrit quand ce client a installé la version, avec le SHA-256 de l'exe installé : il ne compte que tant que l'exe en cours a ce hash (un zip décompressé à la main par-dessus l'invalide) ;
  2. seq: dans RELEASE.txt si le paquetage l'a reçu (package.ps1 -Seq N) ; ignoré si la liste connaît ce seq avec un autre commit ;
  3. le commit de RELEASE.txt contre celui de la liste (préfixe, 7 à 40 hexadécimaux ; plusieurs correspondances : celle dont le build est l'étiquette du paquetage, sinon le plus petit seq) ;
  4. l'étiquette (label: = pbe.3) égale à un build.

Rien ne correspond : « Non reconnue ». La dernière version reste proposée, mais jamais installée sans clic. Pour que le jeu se reconnaisse toujours, publier avec --commit (celui de RELEASE.txt) ou donner -Seq au paquetage (voir la procédure plus bas).

Signature Ed25519 (emplacement de clé vide pour l'instant)

La liste peut porter, par version, signature : 128 hexadécimaux, signature Ed25519 des octets prism-update-v1\n<canal>\n<seq>\n<sha256 en minuscules>\n (le hash et non le fichier : vérifiable avant tout téléchargement, et le canal et l'ordre sont liés au paquet). crates/updater/src/signature.rs::TRUSTED_KEYS est vide : rien n'est vérifié et l'interface écrit « Non signée ». Dès qu'une clé publique (64 hexadécimaux) y est mise, une version sans signature ou à signature invalide est refusée (la dernière n'est pas remplacée par une plus ancienne signée). Plusieurs clés sont admises (rotation : une version qui embarque la suivante avant que l'ancienne parte). La clé privée n'est jamais dans ce dépôt.

Proposition pour l'outil d'administration du site (à implémenter par le worker du site ; rien n'a été modifié dans son dépôt) : prism-admin keys generate --out <fichier> (Ed25519, clé privée PKCS#8 hors dépôt et hors base, droits 0600, sauvegarde hors ligne ; affiche la clé publique en hexadécimal à coller dans TRUSTED_KEYS) ; prism-admin release add … --sign-key <fichier> calcule la signature du message ci-dessus et la range dans releases.json (champ signature) ; verify la revérifie ; regenerate la conserve (jamais re-signée pour un autre seq) ; release next-seq --channel pbe affiche le prochain seq pour package.ps1 -Seq. Vecteur de test (Ed25519 étant déterministe) : graine [7; 32], clé publique ea4a6c63e29c520abef5507b132ec5f9954776aebebe7b92421eea691446d22c, message (canal pbe, seq 7, hash ab×32) prism-update-v1\npbe\n7\nabababababababababababababababababababababababababababababababab\n, signature 281558931de70d1d028affffbe5b7941fcd0cb1150a5d9376e3102ce6c2322f6b743cd20ba601d65ab3370a3b2c08e45a2abe61ce800c88178b3e45525bbc000 (test the_signing_vector_published_for_the_site_verifies).

Installation, sauvegarde, retour arrière

Un prism.exe en cours se renomme sous Windows (il ne s'écrase ni ne se supprime ; prouvé par un test avec une copie d'un programme système en cours d'exécution). Tout reste à côté de l'exe, jamais dans le dossier de données (sauf update-check.json, l'heure de la dernière vérification) :

update-staging/download/…zip.partial   le zip pendant le téléchargement (repris)
update-staging/files/…                 le paquet décompressé (règles d'archive du téléchargeur : ni `..`, ni chemin absolu, ni lien) et contrôlé
update-staging/ready.json              écrit en dernier : le paquet est complet
update-backup/<seq>/files/…            chaque fichier remplacé, tel qu'il était (les deux dernières sauvegardes sont gardées)
update-backup/<seq>/manifest.json      remplacés / ajoutés
update-state.json                      version installée, compteur de lancements, dernier retour arrière

Le paquet doit contenir prism.exe (en-tête MZ) et un fichier channel égal au canal annoncé ; les noms update-* sont réservés. Politique des dossiers livrés (mods\, skins\, themes\) : un fichier du paquet remplace le même chemin (le contenu livré suit le jeu) et l'ancien est gardé dans update-backup/<seq>/ s'il diffère ; tout ce que le paquet ne contient pas n'est jamais touché ni supprimé (dossiers et fichiers ajoutés par le joueur) ; un fichier livré qu'une version supprime reste (le retirer demande le manifeste de hachage de la phase suivante). Les modifications sur place d'un fichier livré sont donc écrasées mais sauvegardées.

Au lancement, avant la fenêtre : le compteur de lancements d'une version fraîchement installée augmente ; l'interface prête (ready) le remet à zéro. Deux lancements de suite sans atteindre l'interface : au troisième, les fichiers sont remis (les fichiers fautifs vont dans update-backup/<seq>/failed/), l'ancienne version redémarre et Réglages → À propos le dit. Une installation interrompue (coupure, processus tué) est annulée au lancement suivant (le manifeste est écrit avant le premier déplacement). Le nouveau processus attend la fin de l'ancien (--after-update <pid>) avant de prendre le verrou d'instance et le profil WebView2. Limite : le garde-fou tourne dans le nouvel exe ; un exe qui ne se charge même pas ne peut pas se remettre lui-même (restaurer update-backup/<seq>/files/prism.exe). Le programme suppose que l'exe s'appelle prism.exe et que son dossier est inscriptible (pas dans Program Files : sinon « dossier non inscriptible »).

Couper une release et la publier sur le site (sans CI : la voie retenue)

  1. .\scripts\package.ps1 -Channel pbe -Label pbe.4 [-Seq N] (le seq ne sert qu'à la reconnaissance : release next-seq du site, si l'outil l'offre). RELEASE.txt porte la version, le canal, l'étiquette, le seq s'il est donné, le commit, la date et le SHA-256 de l'exe.
  2. Envoyer le zip sur le serveur ; prism-admin release add --channel pbe --build "PBE 0004" --zip … --commit <commit de RELEASE.txt> --notes …. Le jeu des joueurs voit la version au prochain contrôle (une fois par jour, ou « Rechercher »).
  3. Promouvoir sans recompiler : .\scripts\promote.ps1 -Zip <zip pbe> produit le zip stable (le seq: du PBE y est retiré : le site attribue celui de stable), puis le publier de la même façon avec --channel stable. Retirer une version : prism-admin release withdraw (elle disparaît de la liste ; un client qui l'avait vue ne la télécharge plus).

.github/workflows/release.yml reste dans le dépôt mais la décision de l'auteur est de ne pas utiliser la CI GitHub pour l'instant.

Ce qui n'est pas prouvé

Testé : tout le crate contre un vrai serveur HTTP local (flux, zip, hash faux, signature fausse ou absente, version retirée, téléchargement coupé puis repris, zip-slip, même seq, changement de canal, redirections vers un autre hôte), l'installation, le retour arrière, et le renommage d'un exécutable en cours. Non prouvé, seul un vrai passage sur le site le montre : le TLS et les en-têtes réels du site (Range, Content-Range, redirections de Caddy), la reconnaissance d'une version publiée avec les vrais champs, le redémarrage réel (nouveau processus, mutex, WebView2) et l'application au lancement. Aucun prism.exe n'a été lancé pour ce travail.

Suite

  1. Générer la clé de signature et la mettre dans TRUSTED_KEYS (décision de l'auteur : où vit la clé privée, sa sauvegarde, la rotation).
  2. Manifeste de hachage des dossiers livrés, « Dupliquer pour personnaliser », pierres tombales (retirer un fichier livré).
  3. Installeur (amorce WebView2), signature des exécutables, deltas, déploiement progressif.

Source dans le dépôt du jeu : docs/mises-a-jour.md