The local vault now opens by itself at launch and the GET runs behind it, silently. A 304 shows nothing; a network failure shows nothing either - offline is a normal state, and an alert on every launch only teaches people to dismiss alerts. Only a new snapshot manifests, by refreshing the list. That silence is what puts the sync password inside the Keystore-encrypted file next to the vault: without it a new snapshot cannot be opened, so every edit made on the desktop would raise a password prompt at launch. The file already holds every password in plaintext; this one adds almost nothing to its exposure. A typed password still wins over the stored one, which is how you fix one that no longer works. Adopting a snapshot while the app is already open rebuilds the list in place instead of navigating - mandatory even from the detail screen, since list rows carry indices into the old vault and a tap would open the wrong entry. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
8.7 KiB
Client Android — ce qu'il reste à faire
État au 2026-08-15. Le client lit l'instantané et l'affiche ; il n'écrit rien. Ce document liste le reste, dans l'ordre où je le ferais.
Voir README.md pour l'architecture et les décisions déjà prises.
0. Ce que l'instantané contient déjà
À vérifier avant de réclamer quoi que ce soit au poste Windows : la plupart des manques côté téléphone ne demandent aucun changement côté serveur.
| Champ | Dans l'instantané | Lu par le client |
|---|---|---|
uuid, title, site, username, password |
oui | oui |
folder, tags, kind |
oui | oui |
totp_secret |
oui | oui |
custom_fields (label/value/is_secret) |
oui | oui |
template (carte, clé SSH, serveur…) |
oui | non |
icon_b64, icon_custom |
oui | non |
attachments (avec content_b64) |
oui | non |
created_at, updated_at |
oui | non |
folders (liste, couleur, icône) |
oui | non |
avatar_b64, username (compte) |
oui | non |
tombstones |
oui | non |
favorite, pinned |
non — device-local par conception | — |
| corbeille | non — voyage dans les sauvegardes, pas la sync | — |
Autrement dit : dossiers, icônes, pièces jointes et horodatages sont déjà là, il ne manque que la lecture. Seules les favoris/épinglés et la corbeille demanderaient une décision côté Windows — et pour les deux, la décision actuelle est délibérée.
1. À vérifier sur téléphone — aucun code
L'émulateur n'a ni empreinte ni écran de verrouillage, donc PMA.Store est
retombé sur la clé sans contrainte à chaque essai. Ces trois points n'ont
jamais été exercés :
- Création de la clé sous authentification —
SaveVault(…, True)doit écrire un en-tête à2, et le journal doit dire « protégé par biométrie/code » et non « AUCUNE protection ». - Ouverture —
VaultNeedsAuth→ invite système → déchiffrement dans les 30 s de validité de la clé. Au-delà,LoadVaultéchoue sans effacer le cache : la garde surUserNotAuthenticatedest écrite mais jamais éprouvée. - Cache d'un lancement à l'autre — fermer, rouvrir, Ouvrir le coffre : ni réseau ni mot de passe, et 0 ms de dérivation.
Un quatrième cas mérite d'être provoqué exprès : enrôler une nouvelle
empreinte invalide la clé définitivement
(KeyPermanentlyInvalidatedException). Le cache doit alors être effacé et
retéléchargé, pas rester illisible. C'est l'autre branche de la même garde.
2. Durcissement — petit, à faire avant tout usage réel
Par ordre de gravité, pas de taille.
android:allowBackup="false"dans AndroidManifest.template.xml. Une ligne. Aujourd'hui absente : la sauvegarde Google peut donc emportervault.bin. Le fichier est chiffré par une clé qui ne quitte pas la puce, donc ce qui sort est inexploitable — mais il sort, et le README prescrit déjà l'inverse.- Retirer
usesCleartextTraffic="true"du même fichier, et servir l'endpoint en HTTPS avec un vrai certificat. L'attribut est marqué TEST-ONLY dans le manifeste ; le jeton Bearer circule à chaque appel. FLAG_SECUREsur la fenêtre. On masque déjà l'écran avant la capture des applications récentes, mais rien n'empêche une capture d'écran manuelle.getWindow().setFlagspar JNI, quelques lignes.- Marquer le presse-papiers sensible — depuis Android 13 le système
affiche un aperçu de ce qui vient d'être copié, mot de passe compris.
ClipDescription+ extraandroid.content.extra.IS_SENSITIVE. - Sortir l'URL et le jeton du code source. Ce sont encore des constantes. Le mot de passe de sync, lui, est déjà rangé dans le fichier chiffré par le Keystore ; reste à en faire autant pour les deux autres, et à supprimer le préremplissage depuis PMA.LocalConfig.inc.
- Verrouillage sur inactivité au premier plan. Aujourd'hui seul le passage en arrière-plan verrouille ; le poste Windows a en plus une minuterie.
3. Compléter la lecture — moyen
Rangé par ce que ça apporte réellement sur un téléphone.
- Pièces jointes — elles sont dans l'instantané, personne ne les lit.
Lister nom/taille par entrée, et enregistrer via le sélecteur système.
Attention à la mémoire :
content_b64est déjà décodé en RAM avec tout le reste de l'instantané, un coffre avec des PDF pèse lourd. - Filtres dossier et étiquette.
TVault.Searchne prend qu'une requête texte ; ajouter deux paramètres facultatifs et une rangée de puces au-dessus de la liste. Les dossiers viennent du tableaufolders. - Icônes (
icon_b64) en tête de ligne, à la place des initiales. Purement cosmétique, mais c'est ce qui rend une liste balayable. template— afficher le sous-type (carte, clé SSH…) comme le fait le poste Windows, et adapter les intitulés d'une carte bancaire.- Tri. L'ordre actuel est celui de l'instantané. Par nom, au minimum.
TListViewà la place deTListBoxle jour où la reconstruction à chaque frappe se voit. Marquéponytail:dans le code, pas avant.
4. Le chemin d'écriture — le gros morceau
C'est ce qui ferait du téléphone un second appareil plutôt qu'une consultation. À ne pas commencer avant que le reste soit stable : une erreur ici perd des données, alors qu'une erreur en lecture n'affiche qu'une ligne de travers.
La contrainte qui gouverne tout le reste
Le client doit republier ce qu'il n'a pas compris, à l'identique.
TVault ne lit qu'un sous-ensemble des champs. S'il resérialise le coffre à
partir de ce qu'il a lu, un envoi depuis le téléphone efface silencieusement
icon_b64, template, attachments, created_at, l'avatar, les
tombstones — et le poste Windows adopterait cette version au prochain tirage,
puisque la fusion est un dernier-écrit-gagne sur updated_at.
Donc : garder le TJSONObject d'origine et ne modifier en place que ce que
l'utilisateur a changé. C'est une contrainte de conception, pas une
optimisation — elle décide de la forme de TVault avant la première ligne
d'interface d'édition.
Dans l'ordre
updated_atdansTVaultEntry. Rien n'est arbitrable sans lui. À faire même si l'écriture est repoussée : la lecture peut déjà afficher « modifié le… ».- AES-GCM en chiffrement. On n'a que le déchiffrement, des deux côtés
(JNI et CNG). Il faut aussi un sel de 32 octets et un IV de 12 octets tirés
d'un générateur cryptographique —
SecureRandomcôté Android,BCryptGenRandomcôté Windows. JamaisRandom. PUTdans sync.php, qui répond aujourd'hui 405. Même authentification,If-Matchobligatoire, 412 si l'ETag ne correspond plus, écriture atomique (fichier temporaire puis renommage — sans quoi une coupure laisse un conteneur tronqué, donc un coffre perdu), et un plafond de taille.- La fusion, calquée sur
applyRemoteSnapshotcôté JS : dernier-écrit- gagne paruuidsurupdated_at, tombstones qui propagent les suppressions, et l'arbitrage de résurrection (une entrée modifiée après sa date de suppression survit à la tombstone). - L'interface d'édition. Modifier un champ, marquer l'entrée sale, bumper
updated_aten UTC (toISOStringcôté JS,NowUTCcôté Delphi — mélanger heure locale et UTC a déjà décalé l'arbitrage une fois). - Le conflit 412 — retirer, refusionner, renvoyer, avec un nombre d'essais borné. Le poste Windows fait exactement cela, en trois essais.
Ce qu'on peut s'épargner
La création et la suppression d'entrées peuvent attendre : modifier un mot de passe depuis le téléphone couvre l'essentiel du besoin, et n'exige ni tombstones ni génération d'uuid.
5. Empaquetage
- Icône, nom d'application, numéro de version.
- Signature avec un magasin de clés dédié, pas la clé de débogage.
- Revue des permissions :
INTERNETsuffit. - Retirer le journal de diagnostic de l'écran de déverrouillage, ou le mettre derrière un appui long.
- Le mot de passe préchargé depuis
PMA.LocalConfig.incdoit disparaître.
Ordre proposé
- Vérifier §1 sur téléphone — c'est ce qui valide le travail déjà fait.
- §2 items 1 et 2 : deux lignes de manifeste, et le risque le plus concret.
- §3 items 1 à 3 : pièces jointes, filtres, icônes. Le client devient réellement utilisable au quotidien.
- §4 étape 1 seule (
updated_at), puis décider si l'écriture vaut son coût.