Skip to content

[BROUILLON] NOESIS 13.6 — sauvegarde Supabase : phase A compatible, anti-contenu périmé, historique journalier - #35

Merged
iPoower merged 6 commits into
mainfrom
release/noesis-13-6-reconversion
Oct 29, 2026
Merged

iPoower merged 6 commits into
mainfrom
release/noesis-13-6-reconversion

Conversation

@iPoower

@iPoower iPoower commented Oct 28, 2026

Copy link
Copy Markdown
Owner

[BROUILLON] NOESIS 13.6 — sauvegarde Supabase : anti-rejeu, historique journalier, statut honnête

Ne pas fusionner. PR brouillon de certification. Base : main 0edcf3fe1728 ; tête : 5621f98 (6 commits).

Changements

  • Déploiement en deux phases du format lié. La phase A est livrée : lecture des formats lié et hérité, écriture du format hérité. Preuve exécutable : le client de production 0edcf3f peut écraser une tête liée qu'il ne sait pas lire (C3b). La phase B (BOUND_WRITES=true, une ligne) n'intervient qu'après mise à jour de tous les appareils.
  • Correctif X1 : la tête distante n'est jamais associée au contenu déchiffré d'une autre révision, et « Récupérer le cloud » re-déchiffre la version actuelle.
  • Chiffré lié au compte et à la révision (actif en phase B) (AAD progress-v2|compte|révision). La forme de l'enveloppe est inchangée, la contrainte SQL intacte. Les anciennes enveloppes sont lues jusqu'au premier accusé lié, puis refusées définitivement.
  • Migration 20261028120000_history_daily_retention.sql préparée, non appliquée : 20 dernières révisions + la dernière de chaque jour sur 30 jours. Idempotente ; RLS et droits inchangés.
  • Statut « sauvegarde confirmée » seulement pour la révision accusée ; bouton « Sauvegarder maintenant » (même boucle).
  • Tests des déclencheurs de reprise : online, visibilitychange, pageshow, expiration de session.

Preuves locales (SHA exact)

  • Rollback 12/12 (cas liés exécutés en phase B ; X1 ; phase A face à l'API 0edcf3f) ; preuve avec le vrai client 0edcf3f : phase A 2/2, phase B (X1 corrigé ; C3b justifie la règle) ; historique PGlite 11 groupes ; sync 32/32 ; final-mile 14 ; restauration atomique 16.
  • Chromium : mobile 31, learning 21, SW 14, OAuth 5, final-mile 13 ; Pages 22 ; npm audit 0.

Déploiement futur

Phase A : compatible avec les appareils non mis à jour. Phase B : seulement après mise à jour de tous les appareils, sur GO séparé.


Generated by Claude Code

Add offline controller cases for the 'online', 'visibilitychange' and
'pageshow' resume listeners and for session expiry during read/write.
These triggers existed in cloud-ui.js but had no test; a mutation that
removes the online/visibilitychange listeners now fails 2 cases.
Synthetic data only, no network.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E5jwZntqAvJaRThsuEjgja
…k resistance)

A malicious or compromised server could serve an OLD progress ciphertext
under a HIGHER revision; a non-dirty device decrypted and applied it
silently because the AES-GCM AAD was a constant.

- New progress envelopes keep the SQL-pinned v1 shape (no migration) but
  use AAD 'reconversion-control-progress-v2|<user_id>|<target revision>'.
- openProgress() verifies the row's own user/revision; one PBKDF2
  derivation, then bound AAD, then (optionally) legacy constant AAD.
- Legacy envelopes stay readable until this device acknowledges a bound
  envelope for the account; then refusal is sticky (meta.bound=true in the
  per-user journal). A refused legacy head becomes an explicit conflict:
  only 'send this device' proceeds, after preserving the legacy copy.
- Preserve-only copies and explicit history recovery still read legacy.
- Session envelopes unchanged.

Red: tests/cloud-rollback.cjs 3/9 on 0edcf3f (replay, touch-replay,
cross-account, sticky, push cases failed). Green: 9/9. Test helpers that
decrypted uploaded rows with the legacy API now use the bound API.
Mixed-version: old main client cannot open bound envelopes and never
writes over them (conditional PATCH + mandatory preserve before push).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E5jwZntqAvJaRThsuEjgja
…r day, 30 days)

History eviction kept only the last 20 revisions per user, so a burst of
25 quick edits evicted every earlier day. Prepared (NOT applied) migration
20261028120000_history_daily_retention.sql replaces only the eviction rule
of the private trigger function (CREATE OR REPLACE, idempotent, guarded).
RLS policies, grants, CHECK constraints, +1 revision rule unchanged.

Red: cloud-history.cjs without the migration -> retention assertion fails
(revisions 3 and 4 from earlier days evicted). Green: 11/11 groups,
including policies/grants snapshot equality, double application, and
clients still unable to delete/update/insert history (42501).

Client history listing raised from 20 to 50 rows (20 + 30 days) so the
retained daily versions are reachable; red 20 != 50, green 14/14.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E5jwZntqAvJaRThsuEjgja
- Header label said 'LOCAL + CLOUD CHIFFRÉ' whenever sync mode was active,
  even with a pending/failed upload. It now says 'sauvegarde confirmée
  (révision N)' only when the current snapshot equals the acknowledged
  revision with no outbox, else 'LOCAL · sauvegarde cloud en attente'.
- Active-mode button renamed 'Sauvegarder maintenant'; same #rc-sync-now
  id and same tick() pipeline (no second loop).
- Session expiry: retries no longer degrade to 'Non connecté'; status keeps
  asking to reconnect and states unsent changes are not yet in the cloud.
- Intro lists what is sent and what stays local (display preferences,
  recovery copies); locked mode says edits stay local until unlock.

Red: 4/32 UI cases failed on previous commit. Green: 32/32.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E5jwZntqAvJaRThsuEjgja
…s du format lié

- X1 (preuve exécutable NOESIS 13.6) : inspect() associait la nouvelle tête distante au contenu
  déchiffré de la précédente quand le déchiffrement échouait ; « Récupérer le cloud » l'appliquait
  sous le nouveau numéro, puis la saisie suivante écrasait la tête. remoteData est effacé dès que la
  tête change, et chooseCloud re-déchiffre la version actuelle avant d'appliquer.
- C3b : l'ancien client de production (0edcf3f) a le même défaut et peut écraser une tête LIÉE
  qu'il ne sait pas lire. Phase A livrée (BOUND_WRITES=false) : lecture des formats lié et hérité,
  écriture du format hérité, lisible par les anciens appareils. Phase B (une ligne) seulement après
  mise à jour de tous les appareils.
- Tests : cloud-rollback 12/12 (cas existants exécutés en phase B, + X1, + 2 cas phase A).
…ocumentation du déploiement en deux phases

Les aides qui décodent les lignes envoyées acceptent les formats lié et hérité (legacy:true) ;
les assertions sont inchangées. Les tests qui scellent explicitement au format lié restent liés.
@iPoower
iPoower marked this pull request as ready for review October 29, 2026 07:45
@iPoower
iPoower merged commit eb5e7ea into main Oct 29, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants