summary: Réaligner les inscriptions sur la vision initiale — objet participation auto-possédé (vérité) + Set curé cap-less sur l'événement + cap scellé aux connexions → compteur = refs distinctes, anonyme par défaut, personne ne désinscrit autrui. Supersede en partie l'Option-B (compteur muté + userId dans l'inbox). Dépend de l'alignement caps du polyfill.
---
# Brief (2026-07-20) — inscriptions par Set (objet-vérité + Set curé cap-less)
## Pourquoi
L'implémentation actuelle (Option-B, [[brief_2026-07-06_reactive-reads-and-attendance]]) dérive un `participantCount`**muté-en-place** depuis des marqueurs d'inbox qui portent le **`userId` en clair** → (1) compteur **falsifiable** (marqueurs non authentifiés), (2) **fuite d'identité** au créateur, indépendamment de toute connexion. La **vision initiale**, retrouvée : **présence anonyme par défaut**, seul un **connecté (= ami)** voit l'identité. Faisabilité : **seules les références cap-less sont confirmées** (cf. polyfill `docs/readcap-and-nuri-model.md`) ; le **compteur** repose encore sur des inconnues (keyless-fetch, dédup-vérifiée — voir Tensions). Ne pas surestimer.
## Modèle cible
- **Objet participation = la vérité**, auto-possédé (protected), mis à jour/supprimé par le **seul inscrit** → **personne ne désinscrit autrui**.
- **Set sur l'événement** (doc du créateur — lui seul l'écrit) de **références cap-less** aux participations. Le créateur **ajoute** (sur nudge d'inbox) et **nettoie** les périmées.
- **Compteur = nombre de références distinctes** (`Set.size`) — plus de compteur muté séparé.
- **Identité** résolue **seulement** par les détenteurs du cap = les **connexions** (à qui l'inscrit a scellé le cap-porteur) → anonyme au public **et** au créateur non-ami.
- **Désinscription** = l'inscrit supprime son objet **ET** dépose un **nudge « retire la réf X »** chez le créateur — car une suppression n'est **PAS** détectable sans clé (**VÉRIFIÉ P0, Q2** : broker append-only, tombstone chiffré). Le créateur retire la réf du Set sur le nudge (forgeable = accepté, hors-scope sécurité). Les connexions (qui ont le cap) peuvent aussi filtrer en relisant l'objet.
- **Validation d'existence — CONFIRMÉ (P0, Q1)** : les lectures broker ne sont pas cap-gatées → le créateur peut vérifier que l'objet **existe** (fetch des blocs chiffrés) avant d'ajouter → un join forgé **sans objet réel** est **rejeté**. *Nuances* : (a) marche avec l'overlay **inner** (ou outer exposé) — une **sonde e2e** confirmera lequel la réf porte ; (b) existence ≠ **dédup** (#3) et ≠ inviolabilité totale (référencer un objet réel *quelconque* passe la vérif).
## Ce qui change vs l'actuel
- **Retirer le `userId`** des marqueurs d'inbox → ne garder qu'un **nudge + référence cap-less**.
- **Compter par références distinctes**, pas par `userId`.
-`event.participantCount` muté-en-place **disparaît** au profit de `Set.size`.
- La **résolution d'identité** passe par la **lecture de l'objet** (cap), pas par le marqueur.
## Ce qui reste valable
- La **lecture réactive** + le **reconnect-lifecycle** (lire le Set réactivement, ré-armer à la reconnexion).
- Le **fix identité** déjà livré (résoudre participation→profil via la clé).
Un adversaire a réfuté les deux promesses phares. **Distinction clé** (le polyfill vise la *forme*, pas la *sécurité* — cf. son objectif) : les attaques par **forge** (compteur gonflé, `from:null`) relèvent de l'insécurité **acceptée** de l'émulation ; **mais** le trilemme ci-dessous est un problème de **MODÈLE** qui **survit au vrai crypto** — ce sont donc de vraies questions de conception, pas des nitpicks d'émulation. Sous les primitifs NextGraph, **anonyme + compté-juste (dédupliqué) + inviolable** ne tiennent PAS ensemble :
1.**« Inviolable » est faux.** Le fetch keyless (INFÉRÉ, absent de `caps.ts` : `canRead(doc,null)→false`) ne prouve que l'**existence** d'un NURI, jamais une participation **valide, distincte, de cet événement** — un attaquant référence n'importe quel NURI existant → compteur gonflé. Sans keyless-fetch, retour au nudge (`inbox.post` accepte `from:null`) → forge comme Option-B.
2.**Anonymat ⊥ inviolabilité (overlay).** Valider l'existence exige l'**overlay** ; overlay = `outer(store_id)`, **un seul store protected par identité** → stable sur TOUS les événements → un créateur voyant `:v:{overlay}` sur N événements **corrèle** « même participant partout » sans lire l'identité (**pseudonyme de traçage**). *Mitigation candidate : chaque participation dans son propre repo/store → overlay par-participation.*
3.**Dédup : base VÉRIFIÉE (`nextgraph-rs`), mais GATÉE sur la vérification.** Le fond était juste : les **commits SONT signés** par un `UserId` (clé technique **≠ le profil**), donc un id technique de dédup **existe** — pas besoin de pseudonyme applicatif. MAIS deux gaps : (a) **vérifier** la signature exige d'être **membre du repo** (`member_pubkey`) — le créateur n'est pas membre du store du participant, et le store-partagé-en-écriture n'existe pas ; (b) le **dépôt d'inbox n'est PAS authentifié** (sealed box anonyme ; un `from` nommé = clé de **profil** → fuite). → dédup-par-signature-vérifiée **pas directement atteignable** dans le flux actuel. **Piste** : participations dans un **store PAR-ÉVÉNEMENT** vérifiable par le créateur → le digest d'auteur (par-overlay, donc par-store) = un **pseudonyme par-événement** → dédup + **pas** de corrélation cross-événement + anonyme-profil. Reste à résoudre « le créateur peut vérifier » (membership). *(Le digest par-user redevient corrélable pour un membre disposant de la members map sur un store stable par-user → d'où l'intérêt du store par-événement.)*
4.**Leave-par-détection-de-suppression : IMPOSSIBLE keyless — VÉRIFIÉ (P0, Q2).** Broker append-only, tombstone chiffré → sans clé on voit « une activité », pas « une suppression ». **Résolu** par un **nudge de leave** (voir Modèle cible), pas une détection automatique de réf pendante.
De plus (revue caps polyfill) : dans l'**émulation actuelle sans crypto**, le contenu est en **clair** dans le wallet partagé (`sparqlQuery` bypass le filtre) → l'anonymat « cap-less ne peut pas lire » **n'est même pas applicable** tant que le polyfill ne fournit pas soit du vrai crypto, soit une projection read-model masquée.
**Ce qui tient (validé par l'adversaire)** : l'objet auto-possédé **tue le griefing** de la désinscription forgée d'autrui, et **retire la fuite** du userId-dans-le-marqueur. Vrais gains — mais ils n'exigent pas tout le pivot.
## Dépendances
- **Bloquant** : l'**émulation caps du polyfill** alignée — réfs cap-less + possession, **ET** (pour la dédup #3) **WriteCap=membership + stores par-événement à signature vérifiable**. Ces deux derniers **ne sont pas encore façonnés** par le brief polyfill → son **scope doit s'élargir**, sinon les deux briefs **ne composent pas** (constat adverse). Brief polyfill `2026-07-20-caps-emulation-alignment`.
- **Parké** : la **terminologie identité** (wallet/user/profil) — détermine « à quelle identité on scelle ».
- **INFÉRÉ** : le **fetch keyless** (existence sans lire) — à confirmer pour la version forte.
## Statut : direction cible, PAS un pivot immédiat
Vu les tensions ci-dessus, **ne pas retirer Option-B** tant que : (a) le **trilemme** n'est pas tranché (que sacrifie-t-on, ou quelle mitigation — pseudonyme par-événement signé ?) ; (b) l'**émulation caps du polyfill** est alignée (sinon l'anonymat n'est pas applicable) ; (c) la **terminologie identité** est clarifiée ; (d) le **fetch keyless** est **vérifié** (spike P0). Les fixes contenus déjà faits (id-space) restent ; le reconnect-lifecycle reste utile quel que soit le modèle.
## Questions ouvertes
- **Owner hors-ligne** : le Set ne bouge pas tant que le créateur n'a pas matérialisé (accepté V1 ; futur service curateur).
- **Dédup** : « une participation = un objet par user par événement » à garantir (sinon un user avec 2 objets = compté 2×).
- **Anonymat-hôte** : par défaut le créateur ne détient pas le cap (sauf ami) → il ne voit pas l'identité. Cohérent avec l'arbitrage discuté.
Liens : [[brief_2026-07-06_reactive-reads-and-attendance]] (superseded en partie), [[caveat_participation-deletion]], app-security ([[brief_2026-05-18_authorization-matrix]], [[knowledge_trust-model]]), polyfill `readcap-and-nuri-model.md` + brief caps.
summary: Tout dysfonctionnement de NextGraph lui-même (cœur/broker/verifier ou primitif SDK qui se comporte mal — PAS un bug app ni un câblage polyfill) → créer un rapport .md dans le bug-inbox NextGraph partagé `../../nextgraph/orm-tests/INBOX/`
---
# Règle : consigner tout dysfonctionnement NextGraph dans le bug-inbox partagé
Quand tu identifies un **dysfonctionnement de NextGraph lui-même** — le cœur/broker/verifier, ou un primitif SDK (`doc_subscribe`, `sparql_update`, socket, reconnexion, ouverture de repo…) qui se comporte mal — **crée un rapport `.md`** dans le bug-inbox NextGraph partagé :
`../../nextgraph/orm-tests/INBOX/` (depuis la racine de ce repo) — le repo frère `nextgraph/orm-tests` (tests d'intégration ORM contre un vrai broker, avec `tests/standalone/` pour les repros).
## Ce qui QUALIFIE (et ce qui ne qualifie pas)
- **Oui** : un primitif NextGraph a le mauvais comportement — socket qui meurt (`SerializationError`), pas de reconnexion automatique, `doc_subscribe` qui ne délivre pas / est lent, cold-open de repo lent, écriture non durable côté broker.
- **Non** : un bug de l'**app** (effet React mal câblé, gating d'un effet) ou un **câblage du polyfill** (mauvais NURI, souscription non ré-armée). Ceux-là se corrigent **chez nous** — ils ne vont PAS dans l'inbox NextGraph. La distinction est cruciale : d'abord prouver que c'est le primitif qui faute (idéalement par un test), pas notre intégration. Cf. [[rule_app-uses-sdk-surface-only]].
## Format du rapport
Nom : `YYYY-MM-DD-<slug>.md`. Contenu : symptôme, **preuve verbatim** (logs / mesures), repro (idéalement un script standalone — `orm-tests/tests/standalone/`), attendu vs observé, pointeurs source (marqués « à re-vérifier » si non revérifiés), sévérité + statut. Un post-mortem plus complet peut vivre côté polyfill/Festipod ; l'inbox reçoit le **rapport actionnable pour les mainteneurs NextGraph**.
## Pourquoi
Séparer proprement « bug NextGraph » (remonte à l'upstream via ce bug-inbox) de « à corriger chez nous » (app/polyfill), et ne **rien perdre** : chaque dysfonctionnement identifié laisse une trace triable. Cf. [[knowledge_nextgraph-stack]].
- [ ] revoir l'implémentation des ReadCap et WriteCap, aligner avec NextGraph et vérifier que le polyfill enforce bien la logique de droits d'accès en attendant que cela soit implémenté
- [ ] clarifier la terminologie identité NextGraph (wallet = liste de clés ; user = données + username ; profils = identités contenues dans le user) et réconcilier avec decision_2026-07-06 (« identifiant = wallet »), decision_2026-07-20 (« username dans le profil ») et le modèle principal/identity du polyfill
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.