kazeia/docs/RELEASE_SIGNING.md

7.0 KiB

Signature release Kazeia — gestion du keystore

Résumé en 3 lignes : les deux APK (patient + admin) sont signées en release avec une seule clé : /opt/Kazeia/keystore/kazeia-release.jks. Si cette clé est perdue, aucune mise à jour in-place n'est plus possible (désinstallation forcée = perte des données patient). → Sauvegarder le dossier keystore/ hors machine, maintenant.


1. Ce qui existe

Fichier Rôle
/opt/Kazeia/keystore/kazeia-release.jks Keystore (RSA 4096, alias kazeia, valide jusqu'en 2056)
/opt/Kazeia/keystore/credentials.properties Chemin + mots de passe, lu par Gradle (chmod 600)
docs/RELEASE_SIGNING.md Ce document

Empreinte du certificat (référence pour vérifier qu'on signe avec la bonne clé) :

SHA256: 4B:40:8D:9D:40:09:FD:68:F7:FD:5B:E0:BD:64:2B:D3:EE:B8:F8:BD:7A:84:D3:9C:84:90:B8:63:21:AF:E4:90

Le dossier keystore/ est VERSIONNÉ dans le dépôt git (décision 2026-06-11 : le dépôt git.kazeia.com est privé, auto-hébergé, équipe restreinte → le git sert de sauvegarde de la clé). Conséquences à garder en tête :

  • accès au dépôt = capacité de signer des mises à jour Kazeia ;
  • la clé reste dans l'historique git pour toujours ;
  • si le dépôt doit un jour être ouvert/partagé plus largement → rotation de clé AVANT (§7).

2. Comment c'est câblé

app/build.gradle.kts et app-admin/build.gradle.kts lisent /opt/Kazeia/keystore/credentials.properties :

  • PrésentassembleRelease signe avec la clé release.
  • Absent (autre machine, CI sans secrets) → le build ne casse pas : release signé en debug avec un warning ⚠ keystore release absent. Ces APK de repli ne doivent JAMAIS être distribuées.

Une seule clé pour les deux apps (patient com.kazeia + admin com.kazeia.admin) : même identité → permettra de protéger le ContentProvider par une signature-permission (prévu au plan admin, pas encore fait).

3. Construire et distribuer une release

cd /opt/Kazeia/kazeia-android

# 1) Bumper versionCode (OBLIGATOIRE sinon l'OTA ne se déclenche pas)
#    app/build.gradle.kts        : versionCode = N+1
#    (app-admin pareil si distribuée)

# 2) Build
./gradlew :app:assembleRelease            # → app/build/outputs/apk/release/app-release.apk

# 3) Vérifier la signature (DOIT afficher l'empreinte SHA256 ci-dessus)
/opt/Kazeia/android-sdk/build-tools/*/apksigner verify --print-certs \
    app/build/outputs/apk/release/app-release.apk | grep SHA-256

# 4) Publier via le catalogue Nextcloud
cp app/build/outputs/apk/release/app-release.apk /opt/Kazeia/kazeia-dist/apk/kazeia.apk
#    Éditer kazeia-dist/catalog.spec.json : app.version_code = N+1, bump catalog_version
python3 /opt/Kazeia/kazeia-dist-tools/make_catalog.py --dist-root /opt/Kazeia/kazeia-dist --upload

Les tablettes voient catalog.app.version_code > BuildConfig.VERSION_CODE → téléchargent, vérifient le SHA-256, et proposent l'installation (dialogue système).

4. SAUVEGARDE — le point qui ne pardonne pas

La clé EST l'identité de l'app. Android n'accepte une mise à jour que si elle est signée par la même clé que la version installée.

Sauvegarde principale : le dépôt git privé (keystore/ est versionné, cf §1). Une panne de la machine de build ne coûte donc plus la clé — tant que git.kazeia.com est vivant.

Recommandé en plus (le git et la machine de build peuvent partager un même sinistre — serveur et poste au même endroit) : une copie hors-ligne occasionnelle :

tar czf kazeia-keystore-$(date +%Y%m%d).tar.gz -C /opt/Kazeia keystore/   # → USB / coffre

Sur une nouvelle machine de build : git clone apporte le code, le keystore ET la définition des jniLibs (manifest). Mais les binaires jniLibs (474 Mo, gitignorés) doivent être récupérés à part :

# 1) code + keystore + manifest
git clone https://git.kazeia.com/Kazeia/kazeia.git /opt/Kazeia && cd /opt/Kazeia
chmod 600 keystore/credentials.properties
# 2) jniLibs (tarball privé sur Nextcloud, auth requise)
curl -u <user>:<pass> -o /tmp/jnilibs.tar.gz \
  "https://box.kazeia.com/remote.php/dav/files/kazeia/soft/build-artifacts/kazeia-jnilibs-20260611.tar.gz"
bash kazeia-android/scripts/jnilibs.sh import /tmp/jnilibs.tar.gz   # extrait + verify
# 3) build
cd kazeia-android && ./gradlew :app:assembleRelease

Le tarball jniLibs vit dans soft/build-artifacts/ (dossier Nextcloud auth-protégé, NON référencé par le catalogue → jamais servi à l'app). À régénérer après tout changement de libs : jnilibs.sh export → re-uploader. La version (date dans le nom) doit correspondre au jnilibs.MANIFEST.sha256 du commit.

5. Scénarios de panne

Scénario Conséquence Procédure
Keystore perdu (aucune sauvegarde) Plus AUCUNE mise à jour in-place possible Générer une nouvelle clé, désinstaller/réinstaller sur chaque tablette → perte des données patient (SQLCipher lié à l'install). Exporter l'historique via l'admin AVANT si possible. À éviter à tout prix → §4.
Mot de passe perdu (jks présent) Identique à la perte du keystore Aucun recouvrement possible sur un .jks. Le mot de passe est dans credentials.properties — sauvegardé AVEC le .jks.
Machine de build morte (sauvegarde OK) Aucun impact Restaurer keystore/ sur la nouvelle machine (§4).
Clé compromise (fuite) Un tiers peut signer des mises à jour Distribution étant privée (Nextcloud authentifié), changer aussi les identifiants WebDAV ; planifier une migration de clé (réinstallation contrôlée).

6. Migration des tablettes existantes (one-shot)

Les installs actuelles sont debug-signées → la première APK release sera refusée en mise à jour (mismatch). Une fois par tablette :

# 1) (si données à garder) export de l'historique via l'app admin
# 2) Désinstaller les builds debug
adb uninstall com.kazeia ; adb uninstall com.kazeia.admin
# 3) Installer les release
adb install app/build/outputs/apk/release/app-release.apk
adb install app-admin/build/outputs/apk/release/app-admin-release.apk
# 4) Re-provisionner (les modèles sur stockage externe/`/data/local/tmp` SURVIVENT à la
#    désinstallation ; profils/conversations/config in-app sont PERDUS → re-créer le profil)

Après cette migration, toutes les mises à jour passent par l'OTA catalogue, sans perte.

Le dev quotidien reste en debug (assembleDebug + adb install -r) : debug et release sont signés différemment, on ne peut pas écraser l'un par l'autre. La tablette de dev reste en debug ; les tablettes patient passent en release.

7. Rotation / clés multiples (futur)

  • Pas d'expiration avant 2056 ; pas de rotation préventive nécessaire pour une distribution privée.
  • Si un jour publication sur un store : Google Play App Signing prendra cette clé comme « upload key » et gérera la clé de signature côté store (le risque de perte disparaît).