feat(android): Gitea read path verified on device

Against Zaki/pm_sync on Gitea 1.26.1, container uploaded through the web UI:
HTTP 200 in 1079 ms, decrypted, cache written with the sha; the next Sync finds
the same sha and skips the derivation entirely. That was the point of the ETag
on the PHP endpoint and it survives the change of transport.

One log line was lying: it announced If-None-Match, which is never sent to
Gitea - the API does not know that header, and the comparison happens on the
sha after reception. Announcing it sent you looking for a 304 from the server
that will never come. It now says "known sha" on that transport.

The plan records the two frictions met on the way, both already fixed: settings
kept only after a successful decryption (so a wrong password lost the URL and
the token every time), and the launch refresh being silent and using the
password stored with the vault rather than the field.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
r-zakarya
2026-08-17 14:09:36 +01:00
parent c2f7f47eba
commit 6cab9b7c0a
2 changed files with 31 additions and 6 deletions
+24 -5
View File
@@ -431,7 +431,7 @@ purge. Ça ne l'est pas sur GitHub, où le chiffré part chez un tiers, répliqu
et sauvegardé, avec un dépôt rendu public par erreur pour scénario du pire : et sauvegardé, avec un dépôt rendu public par erreur pour scénario du pire :
attaquable hors ligne, par n'importe qui, pour toujours. attaquable hors ligne, par n'importe qui, pour toujours.
### État : la lecture est écrite, elle n'a jamais parlé à un vrai dépôt ### État : la lecture fonctionne contre un vrai dépôt — 2026-08-17
[PMA.SyncClient.pas](PMA.SyncClient.pas) parle les deux transports, **reconnus à [PMA.SyncClient.pas](PMA.SyncClient.pas) parle les deux transports, **reconnus à
l'URL** : `/api/v1/repos/` → Gitea, sinon l'endpoint PHP. Le PHP est gardé le l'URL** : `/api/v1/repos/` → Gitea, sinon l'endpoint PHP. Le PHP est gardé le
@@ -446,10 +446,29 @@ retéléchargé, mais la dérivation de clé, elle, est bien épargnée. Le cham
`ETag` du résultat porte le `sha` : l'appelant n'a pas à savoir lequel des deux `ETag` du résultat porte le `sha` : l'appelant n'a pas à savoir lequel des deux
transports lui répond. transports lui répond.
**Rien de tout cela n'a encore atteint un vrai dépôt.** Il faut, dans cet ordre : **Vérifié sur téléphone** contre `Zaki/pm_sync` (Gitea 1.26.1), conteneur déposé
un dépôt privé, un fichier `vault-sync.json` publié par le poste Windows, un par l''interface web :
jeton limité à ce dépôt, puis l'URL de contenu complète dans *Server settings*.
Tant que le poste publie sur `sync.php`, le téléphone continue de lire là. ```
GET .../api/v1/repos/Zaki/pm_sync/contents/vault-sync.json
HTTP 200 in 1079 ms
304: unchanged, vault served from cache
4 entries, 2 in trash
```
Le premier appel télécharge, déchiffre et écrit le cache avec le `sha` ; le
second retrouve le même `sha` et épargne la dérivation. Le conteneur d''export
porte la corbeille, d''où les 2 en corbeille — un instantané de sync n''en aurait
aucune.
Deux frottements rencontrés en chemin, tous deux corrigés :
- les coordonnées n''étaient enregistrées qu''après un **déchiffrement** réussi,
donc un mot de passe faux faisait perdre l''URL et le jeton à chaque essai. Un
`200` prouve l''adresse et le jeton ; c''est là qu''elles sont retenues.
- le rafraîchissement au lancement est **silencieux** et utilise le mot de passe
rangé avec le coffre, pas le champ. Changer de conteneur demande donc un
*Sync* explicite au moins une fois.
**Reste à faire** : le PUT (§4 seulement — la lecture n'écrit rien), la même **Reste à faire** : le PUT (§4 seulement — la lecture n'écrit rien), la même
bascule côté poste Windows en remplacement de WebDAV, et une purge périodique de bascule côté poste Windows en remplacement de WebDAV, et une purge périodique de
+7 -1
View File
@@ -784,7 +784,13 @@ begin
try try
try try
Log('GET ' + AUrl); Log('GET ' + AUrl);
if FETag <> '' then Log(' If-None-Match: ' + FETag); // NOMMER CE QUI EST REELLEMENT ENVOYE. Sur Gitea aucun If-None-Match ne
// part - l'API ne le connait pas, et la comparaison se fait sur le sha
// apres reception. Annoncer l'en-tete faisait chercher un 304 du serveur
// qui ne viendra jamais.
if FETag <> '' then
if LClient.IsGitea then Log(' known sha: ' + FETag)
else Log(' If-None-Match: ' + FETag);
LSw := TStopwatch.StartNew; LSw := TStopwatch.StartNew;
LRes := LClient.Fetch(FETag); LRes := LClient.Fetch(FETag);
LSw.Stop; LSw.Stop;