Ng eventually #1
@@ -17,7 +17,8 @@ Ce brief porte cette analyse. Il alimentera la décision finale sur la structure
|
||||
|
||||
### Acteurs (tous authentifiés)
|
||||
|
||||
- `Self` — propriétaire de la donnée (varie par type : auteur d'un message, titulaire d'un profil…)
|
||||
- `Alice` — l'utilisateur dont on adopte le point de vue ; propriétaire de la donnée en focus (varie par type : auteur d'un message, titulaire d'un profil, inscrit à un PdR, hôte d'un PdR…)
|
||||
- `Bob` — un autre utilisateur, second protagoniste utilisé pour les relations bilatérales (connexion à Alice, etc.)
|
||||
- `D` — Déclarant d'un événement (celui qui a inséré la référence dans Festipod ; pas l'organisateur réel)
|
||||
- `H` — Hôte d'un point de rencontre (celui qui l'a créé)
|
||||
- `I` — Inscrit à un point de rencontre
|
||||
@@ -41,7 +42,18 @@ Ce brief porte cette analyse. Il alimentera la décision finale sur la structure
|
||||
- **Tous les utilisateurs sont authentifiés.** Pas d'accès anonyme.
|
||||
- **Points de rencontre publics universels.** Tout utilisateur peut lire et s'abonner.
|
||||
- **Création de point de rencontre ouverte à tous.** Pas de prérequis (adhésion, invitation).
|
||||
- **Hôte = détenteur technique des droits d'écriture** sur un point de rencontre. À ce stade : 1 hôte par PdR, celui qui l'a créé.
|
||||
- **Hôte = détenteur technique des droits d'écriture** sur un point de rencontre. À ce stade : 1 hôte par PdR, celui qui l'a créé. Le fait d'être hôte est public (l'offre n'a de sens que si on sait qui la fait).
|
||||
- **Informations personnelles = réservées au réseau.** Toute donnée qualifiée de « personnelle » n'est visible qu'à l'utilisateur titulaire et à ses connexions. Inclut explicitement :
|
||||
- les participations à un événement ou un point de rencontre,
|
||||
- l'intégralité du profil d'un utilisateur,
|
||||
- la liste de connexions d'un utilisateur,
|
||||
- et par extension, tout état déclaratif dont la divulgation à des tiers serait une fuite de vie privée.
|
||||
Le statut « public » (PdR, événement) et le statut « personnel » (profil, participations, liste de connexions) coexistent au sein du même utilisateur.
|
||||
- **Connexion bilatérale.** Une connexion (« lien d'amitié ») n'existe qu'après acceptation par les deux côtés. Modélisée en deux objets : `DemandeDeConnexion` (unilatérale, transitoire) et `Connexion` (bilatérale, persistante).
|
||||
- **Notification d'inscription via l'inbox NextGraph du PdR.** L'acte « s'inscrire à un PdR » est composite : (a) écriture d'un objet `Inscription` dans le `protected_store` de l'inscrit, et (b) dépôt d'un lien (DID cap) pointant vers cet objet dans l'**inbox** du document PdR. L'inbox est un primitive natif de chaque document NextGraph (cf. doc protocole : *« each document has an inbox, which is used in this case to drop the link »*). L'identification du sender côté hôte se fait par résolution du DID contre le graphe de connexions de l'hôte :
|
||||
- si l'inscrit est connexion de l'hôte → l'hôte a la capability pour résoudre le lien, voit l'inscription complète (identité + éventuel message) ;
|
||||
- sinon → le lien reste opaque, l'hôte voit *« quelqu'un (DID …) s'est inscrit »* sans pouvoir aller plus loin.
|
||||
L'anonymat partiel est ainsi natif aux capabilities, pas une logique applicative.
|
||||
- **Adhésion à une communauté : hors périmètre actuel.** Le rôle « Membre de communauté » n'est pas analysé ici.
|
||||
- **Suivi de communauté ou d'utilisateur : hors périmètre actuel.** À reprendre quand la fonctionnalité de discovery par abonnement sera traitée.
|
||||
|
||||
@@ -49,7 +61,7 @@ Ce brief porte cette analyse. Il alimentera la décision finale sur la structure
|
||||
|
||||
### Point de rencontre
|
||||
|
||||
| Verbe | Self (= Hôte) | I (autre inscrit) | D (déclarant de l'événement parent) | U (utilisateur lambda) |
|
||||
| Verbe | Alice (= Hôte) | I (autre inscrit) | D (déclarant de l'événement parent) | U (utilisateur lambda) |
|
||||
|---|---|---|---|---|
|
||||
| créer | ✓ (l'acte de créer rend l'utilisateur hôte) | — | ✗ | ✓ (l'acte le rend hôte) |
|
||||
| lire | ✓ | ✓ | ✓ | ✓ |
|
||||
@@ -63,24 +75,30 @@ Ce brief porte cette analyse. Il alimentera la décision finale sur la structure
|
||||
|
||||
### Inscription à un point de rencontre
|
||||
|
||||
L'objet « Inscription » lie un utilisateur et un point de rencontre. Représente l'engagement à participer.
|
||||
L'objet `Inscription` lie un utilisateur et un point de rencontre. Représente l'engagement à participer. **Donnée personnelle** — visible uniquement par l'inscrit et ses connexions.
|
||||
|
||||
| Verbe | Self (l'inscrit) | H (hôte du PdR) | I (autre inscrit au même PdR) | U (utilisateur lambda) |
|
||||
|---|---|---|---|---|
|
||||
| créer | ✓ (s'inscrire) | ✗ | ✗ | ✓ (l'acte le rend inscrit) |
|
||||
| lire | ✓ | ✓ | ? **à trancher** | ? **à trancher** |
|
||||
| s'abonner | ✓ | ✓ | ? **à trancher** | ? **à trancher** |
|
||||
| modifier | ? **à trancher** (selon les champs modifiables) | ✗ | ✗ | ✗ |
|
||||
| supprimer | ✓ (se désinscrire) | ? **à trancher** (modération ? blacklist ?) | ✗ | ✗ |
|
||||
**L'acte de créer une inscription est composite** (cf. décision cadre sur l'inbox) :
|
||||
- (a) écriture de l'objet `Inscription` dans le `protected_store` de l'inscrit,
|
||||
- (b) dépôt d'un lien (DID cap) pointant vers cet objet dans l'**inbox du document PdR**.
|
||||
|
||||
**Questions ouvertes :**
|
||||
- **Visibilité de la liste des inscrits.** Cohérent avec « tout est public » : tous les utilisateurs voient qui s'est inscrit. Mais à confirmer — y a-t-il un cas où on veut cacher la liste (PdR à inscription confidentielle) ?
|
||||
- **Champs modifiables d'une inscription.** Booléen seul, ou champs additionnels (commentaire, statut "peut-être", nombre d'accompagnants) ?
|
||||
- **Modération par l'hôte.** L'hôte peut-il désinscrire un inscrit (= blacklist) ?
|
||||
| Verbe | Alice (l'inscrite) | C (connexion d'Alice) | H (hôte du PdR) | I (autre inscrit) | U (utilisateur lambda) |
|
||||
|---|---|---|---|---|---|
|
||||
| créer (= acte composite (a)+(b)) | ✓ | — | ✗ | ✗ | ✓ (l'acte fait d'Alice l'inscrite) |
|
||||
| lire le contenu de l'inscription | ✓ | ✓ | cond : ✓ si H ∈ connexions(Alice) ; sinon voit le lien dans l'inbox sans pouvoir le résoudre | cond : ✓ si I ∈ connexions(Alice) | ✗ |
|
||||
| s'abonner | ✓ | ✓ | cond (idem) | cond (idem) | ✗ |
|
||||
| lire l'inbox du PdR (entrées brutes, sans résolution) | — | — | ✓ | ✗ | ✗ |
|
||||
| modifier | ? **à trancher** (selon champs) | ✗ | ✗ | ✗ | ✗ |
|
||||
| supprimer | ✓ (se désinscrire ; doit aussi retirer le lien de l'inbox du PdR si possible) | ✗ | cond : ✓ uniquement modération de l'inbox (refuser / retirer le lien) ; ne supprime pas l'objet `Inscription` de Bob | ✗ | ✗ |
|
||||
|
||||
**Visibilité hôte : résolue.** Combinée à l'inbox NextGraph, la mécanique donne *« inscription identifiée si l'hôte est connecté à l'inscrit, anonyme sinon »* — natif via les capabilities, pas de logique applicative à ajouter. Plus de question ouverte sur ce point.
|
||||
|
||||
**Questions ouvertes restantes :**
|
||||
- **Champs modifiables d'une inscription.** Booléen seul, ou champs additionnels (commentaire, statut « peut-être », nombre d'accompagnants) ?
|
||||
- **Suppression côté inbox.** Quand Alice se désinscrit, peut-elle retirer le lien qu'elle avait déposé dans l'inbox d'un document qu'elle ne contrôle pas ? À vérifier dans le mécanisme protocolaire NextGraph — soit le déposant garde un droit de retrait sur ses propres dépôts, soit l'hôte doit faire le ménage. À creuser avec la doc protocole quand le sujet sera repris.
|
||||
|
||||
### Événement
|
||||
|
||||
| Verbe | Self (= D, déclarant) | H (hôte d'un PdR greffé) | U (utilisateur lambda) |
|
||||
| Verbe | Alice (= D, déclarant) | H (hôte d'un PdR greffé) | U (utilisateur lambda) |
|
||||
|---|---|---|---|
|
||||
| créer | ✓ (l'acte rend déclarant) | — | ✓ (l'acte le rend déclarant) |
|
||||
| lire | ✓ | ✓ | ✓ |
|
||||
@@ -94,36 +112,57 @@ L'objet « Inscription » lie un utilisateur et un point de rencontre. Représen
|
||||
|
||||
### Profil utilisateur
|
||||
|
||||
À déterminer : un seul objet ou split public/privé ?
|
||||
**Rien dans le profil n'est public.** Le profil se divise en deux périmètres seulement :
|
||||
|
||||
| Verbe | Self | C (connexion) | U (utilisateur lambda) |
|
||||
- **Profil réseau** — visible par Alice et ses connexions (tout ce qui décrit l'utilisateur : nom d'affichage, avatar, bio, ville, intérêts…).
|
||||
- **Profil privé** — visible par Alice seule (paramètres, email, préférences notifications, langue, etc.).
|
||||
|
||||
| Verbe | Alice | C (connexion) | U (utilisateur lambda) |
|
||||
|---|---|---|---|
|
||||
| créer | ✓ (à l'inscription) | — | — |
|
||||
| lire (partie publique) | ✓ | ✓ | ? **à trancher** |
|
||||
| lire (partie privée) | ✓ | ? **à trancher** | ✗ |
|
||||
| s'abonner | ✓ | ? | ? |
|
||||
| lire — *profil réseau* | ✓ | ✓ | ✗ |
|
||||
| lire — *profil privé* | ✓ | ✗ | ✗ |
|
||||
| s'abonner | ✓ | ✓ (réseau) | ✗ |
|
||||
| modifier | ✓ | ✗ | ✗ |
|
||||
| supprimer | ✓ (auto-destruction du compte) | ✗ | ✗ |
|
||||
| supprimer (compte) | ✓ | ✗ | ✗ |
|
||||
|
||||
**Questions ouvertes :**
|
||||
- **Split public/privé ?** Le profil contient-il des champs réservés aux connexions ou à l'utilisateur seul (préférences, paramètres, email) ?
|
||||
- **Profil entièrement public ?** Cohérent avec « points de rencontre publics » : un visiteur peut voir le profil de l'hôte d'un PdR. Mais le détail (bio, photos, ville…) ?
|
||||
**Questions ouvertes — tension à résoudre :**
|
||||
|
||||
Cette décision crée une **tension forte** avec la visibilité publique des points de rencontre. Un PdR est lisible par tous, mais son hôte ne devrait *pas* être identifiable par un utilisateur lambda. Comment un visiteur perçoit l'hôte d'un PdR ?
|
||||
|
||||
Trois positions possibles :
|
||||
|
||||
- (i) **Pseudonyme par DID seul.** Un lambda voit « hôte : `did:ng:…123` » sans nom ni avatar. Le nom et l'avatar se résolvent uniquement si le visiteur est une connexion de l'hôte.
|
||||
- (ii) **Identité dénormalisée dans l'offre.** L'hôte choisit, au moment de créer le PdR, quels éléments d'identité il *accepte* d'exposer dans cette offre publique (par ex. juste un prénom et une photo). Ces données vivent dans l'objet PdR, pas dans le profil. Le profil reste fermé, mais l'utilisateur consent à publier une « carte de visite » par PdR. Distinction conceptuelle nette : *publier sous un visage choisi* ≠ *exposer son profil*.
|
||||
- (iii) **Anonymat de l'hôte.** Le PdR est offert sans identité visible publiquement ; un lambda voit « un PdR à tel endroit, telle heure » sans savoir qui héberge. Identité révélée seulement aux connexions.
|
||||
|
||||
À trancher — c'est la pièce manquante pour que la matrice soit cohérente.
|
||||
|
||||
**Autres questions ouvertes :**
|
||||
- **Composition exacte de chaque périmètre.** Champ par champ (bio → réseau ? ville → réseau ? URL personnelle → privé ?). Sous-tableau à faire quand la liste sera arrêtée.
|
||||
- **Le username.** S'il sert d'identifiant stable de connexion ou de découverte, il est *de facto* visible aux personnes qui le connaissent déjà. Public, réseau, ou supprimé du modèle ?
|
||||
|
||||
### Connexion (lien d'amitié)
|
||||
|
||||
| Verbe | Self (A, demandeur) | Other (B, l'autre côté de la connexion) | U (utilisateur lambda) |
|
||||
|---|---|---|---|
|
||||
| créer (demande) | ✓ | — | — |
|
||||
| accepter | — | ✓ | ✗ |
|
||||
| lire (sa propre liste d'amis) | ✓ | — | — |
|
||||
| lire (la liste d'amis d'un autre) | — | — | ? **à trancher** |
|
||||
| s'abonner (à sa liste) | ✓ | — | — |
|
||||
| modifier | — | — | — |
|
||||
| supprimer (rompre la connexion) | ✓ | ✓ | ✗ |
|
||||
**La connexion est bilatérale** : les deux utilisateurs doivent accepter pour qu'elle existe. Deux objets distincts en découlent :
|
||||
|
||||
- `DemandeDeConnexion` — unilatérale, créée par l'initiateur, en attente d'acceptation par le destinataire.
|
||||
- `Connexion` — bilatérale, persistante, créée à l'acceptation. C'est cet objet qui ouvre l'accès aux données personnelles des deux côtés.
|
||||
|
||||
La liste de connexions d'Alice est une **donnée personnelle** (même principe que les participations) : visible à Alice et aux connexions d'Alice, pas au monde.
|
||||
|
||||
| Verbe | Alice (initiatrice) | Bob (l'autre côté de la connexion) | C (autre connexion d'Alice) | U (utilisateur lambda) |
|
||||
|---|---|---|---|---|
|
||||
| créer la demande de connexion | ✓ | — | — | — |
|
||||
| accepter la demande | — | ✓ | — | ✗ |
|
||||
| lire la liste de connexions d'Alice | ✓ | ✓ | ✓ | ✗ |
|
||||
| s'abonner à la liste de connexions d'Alice | ✓ | ✓ | ✓ | ✗ |
|
||||
| modifier | — | — | — | — |
|
||||
| supprimer (rompre la connexion Alice↔Bob) | ✓ | ✓ | ✗ | ✗ |
|
||||
|
||||
**Questions ouvertes :**
|
||||
- **Bilatérale ou unilatérale ?** Le concept « connexion / ami » suggère bilatérale (les deux acceptent). À confirmer ; si oui, il y a deux objets distincts : `DemandeDeConnexion` (unilatérale) et `Connexion` (bilatérale).
|
||||
- **Visibilité de la liste d'amis.** Une connexion est-elle observable par des tiers ? « Marie est connectée à Bob » est-il public, restreint, ou privé ?
|
||||
- **Granularité de visibilité côté Bob.** Bob voit-il *toute* la liste de connexions d'Alice (au même titre que les autres connexions), ou seulement le lien Alice↔Bob ? Conséquence du principe « personnel = réseau » : Bob, étant connexion d'Alice, accède au même périmètre que les autres connexions — donc toute la liste.
|
||||
- **Découvrabilité réciproque des connexions « amis d'amis ».** Si Alice est connectée à Bob et Bob à Carole, Alice peut-elle voir que Bob est connecté à Carole ? Conséquence du principe : non, sauf si Carole est aussi connectée directement à Alice. À confirmer pour les besoins de découverte (« amis d'amis »).
|
||||
|
||||
## Hors périmètre actuel
|
||||
|
||||
@@ -164,9 +203,69 @@ Schéma prévu :
|
||||
|
||||
## Partitions naturelles dérivées
|
||||
|
||||
*À remplir une fois la matrice + l'inventaire stabilisés.*
|
||||
Heuristique : on regroupe dans un même store les données qui (a) partagent leur cellule d'autorisation pour les verbes d'écriture, *et* (b) sont accédées ensemble dans la majorité des requêtes.
|
||||
|
||||
Heuristique de dérivation : on regroupe dans un même store les données qui (a) partagent leur cellule d'autorisation pour les verbes d'écriture, et (b) sont accédées ensemble dans la majorité des requêtes (pour éviter de multiplier les abonnements).
|
||||
À partir des seuls points validés (les questions ouvertes seront tranchées plus tard), trois périmètres distincts émergent. **Ces trois périmètres correspondent presque parfaitement aux trois stores NextGraph par défaut d'un utilisateur.**
|
||||
|
||||
### Trois périmètres par utilisateur
|
||||
|
||||
| Périmètre | Écriture | Lecture | Données qui y vivent (validées) |
|
||||
|---|---|---|---|
|
||||
| **Public** | Alice seule (titulaire) | Tous les utilisateurs authentifiés | PdR dont Alice est hôte ; événements qu'Alice a déclarés *(sous réserve du modèle d'écriture événement, à trancher)* |
|
||||
| **Réseau / personnel** | Alice seule | Alice + connexions d'Alice | Profil réseau d'Alice ; participations d'Alice à des PdR ; index de la liste des connexions d'Alice |
|
||||
| **Privé** | Alice seule | Alice seule | Profil privé d'Alice (paramètres, email, préférences) |
|
||||
|
||||
### Mapping aux stores NextGraph natifs
|
||||
|
||||
- **Périmètre public ↔ `public_store` d'Alice.** Définition NextGraph : *« everyone can read; only you write »*. Match exact.
|
||||
- **Périmètre réseau ↔ `protected_store` d'Alice.** Définition NextGraph : *« share data with other users, but they will need a special link and permission »* et *« functions as a protected social profile »*. C'est précisément le périmètre « réseau » du modèle Festipod.
|
||||
- **Périmètre privé ↔ `private_store` d'Alice.** Définition NextGraph : *« only you have access to »*. Match exact.
|
||||
|
||||
### Cas particulier : la Connexion bilatérale
|
||||
|
||||
Une `Connexion` Alice↔Bob est une donnée à *deux* écrivains (Alice et Bob peuvent tous deux la rompre, mutuellement la voir, etc.). Elle ne tient dans aucun store individuel d'un seul utilisateur. NextGraph dispose d'un primitive natif pour ce cas : le **Dialog store** *(« A two-person-only store for direct messages and shared content between individual users »)*.
|
||||
|
||||
Modèle dérivé :
|
||||
|
||||
- **Une `Connexion` Alice↔Bob = un Dialog store** entre Alice et Bob, contenant l'objet `Connexion` et — naturellement — la matière à conversation/messagerie directe future.
|
||||
- **L'index « toutes les connexions d'Alice »** vit dans le `protected_store` d'Alice et liste les NURIs des Dialog stores auxquels elle participe.
|
||||
- La **`DemandeDeConnexion`** (transitoire, asymétrique avant acceptation) peut vivre :
|
||||
- soit dans le Dialog store provisoire créé dès l'envoi de la demande (qui devient une Connexion à l'acceptation),
|
||||
- soit dans un objet à part dans le `public_store` du destinataire (« boîte de réception » publique des demandes). À trancher selon la mécanique d'invitation que NextGraph permettra côté SDK.
|
||||
|
||||
### Inbox du document PdR
|
||||
|
||||
Le document PdR (qui vit dans le `public_store` de l'hôte) dispose nativement d'une **inbox** (primitive NextGraph, présente sur tout document). Elle est utilisée pour :
|
||||
|
||||
- recevoir les **dépôts d'inscription** (liens DID cap pointant vers l'objet `Inscription` chez chaque inscrit) ;
|
||||
- potentiellement, plus tard, recevoir des commentaires ou d'autres signaux non-éditeurs sur le PdR.
|
||||
|
||||
L'inbox **n'est pas un store séparé**, c'est un attribut du document PdR. Pas d'impact sur la dérivation des partitions.
|
||||
|
||||
### Ce qui ne demande aucun Group store
|
||||
|
||||
Sur le périmètre actuellement validé, **aucune donnée ne demande de Group store**. Toutes les autorisations validées (PdR + inbox, profil, participations, connexions) tiennent dans la combinaison :
|
||||
|
||||
- 3 stores natifs par utilisateur : `public_store` + `protected_store` + `private_store`,
|
||||
- Dialog stores pour les connexions bilatérales,
|
||||
- inboxes natives sur les documents PdR.
|
||||
|
||||
Les Group stores ne deviennent nécessaires que si :
|
||||
|
||||
- le modèle d'écriture événement choisi est « wiki » (plusieurs écrivains sur la même référence événement) ;
|
||||
- ou les communautés / suivi / collaboration multi-hôte sortent du hors-périmètre actuel.
|
||||
|
||||
### Implications pour le brief `multi-store-refactor`
|
||||
|
||||
Le [brief multi-store-refactor](./multi-store-refactor.md) propose une structure à 4 niveaux de Group stores (index communautaire / communauté / event / meeting point). **Cette analyse, sur la base des seules décisions validées, dérive une structure différente** : 3 stores natifs par utilisateur + Dialog stores pour les connexions, sans aucun Group store nécessaire.
|
||||
|
||||
L'écart vient du fait que les concepts qui justifient les Group stores (communautés, collaboration multi-utilisateurs sur un même objet) ont été mis hors périmètre. Quand ils reviendront, des Group stores apparaîtront dans la cible — mais probablement pas selon la hiérarchie initiale, qui sera elle aussi à ré-évaluer à partir d'une matrice étendue.
|
||||
|
||||
### Données restant suspendues aux questions ouvertes
|
||||
|
||||
- **Événement (où vit-il, qui le détient)** dépend du modèle d'écriture (propriétaire / wiki / immuable). Si propriétaire ou immuable : `public_store` du déclarant. Si wiki : nécessite un Group store ou une indirection par une référence externe canonique.
|
||||
- **Identité visible de l'hôte d'un PdR aux yeux d'un lambda** influence la structure du PdR lui-même (option ii « carte de visite dénormalisée » ajoute des champs dans l'objet PdR ; options i et iii ne changent rien). Pas d'impact sur la partition.
|
||||
- **Champs modifiables d'une inscription** : impact mineur sur la structure ; juste sur le schéma de l'objet `Inscription`.
|
||||
|
||||
## See Also
|
||||
|
||||
|
||||
@@ -0,0 +1,136 @@
|
||||
# Forker NextGraph pour exposer l'inbox au SDK JS
|
||||
|
||||
**Status:** Incubating — aucun travail démarré
|
||||
**Last updated:** 2026-05-21
|
||||
|
||||
## Context
|
||||
|
||||
Festipod doit notifier l'hôte d'un point de rencontre quand quelqu'un s'inscrit, avec **identification si connexion / anonyme sinon** (voir la décision cadre inbox dans [authorization-matrix](./authorization-matrix.md)). L'**inbox** NextGraph est le mécanisme natif idéal — le champ `from` optionnel donne l'anonymat gratuitement — **mais elle n'est pas exposée au SDK JS** (voir [nextgraph-stores-permissions §Inbox](../knowledge/nextgraph-stores-permissions.md)).
|
||||
|
||||
Ce brief évalue l'option de **forker / patcher `nextgraph-rs`** pour l'exposer. Travail non démarré.
|
||||
|
||||
### Posture stratégique (cadrée par l'utilisateur)
|
||||
|
||||
Le fork est **explicitement temporaire et non destiné à être intégré upstream**. Hypothèse de travail : les développeurs de NextGraph finiront par exposer leur **propre** solution d'inbox au SDK JS, **possiblement différente** de notre patch. Quand elle arrivera, on **abandonnera notre fork et on adaptera Festipod à leur solution**.
|
||||
|
||||
Conséquences tant que leur solution n'est pas là :
|
||||
|
||||
- **Maintenir le fork à jour** (rebase régulier sur `upstream/main`, qui bouge vite en `0.1.2-alpha`).
|
||||
- **Déployer le broker (et le ng-app) depuis le fork**, pas depuis les binaires officiels — c'est notre build patché qui doit tourner.
|
||||
- **Surveiller l'upstream** pour détecter l'arrivée de leur API inbox et basculer dès que possible (réduit la dette de maintenance).
|
||||
|
||||
On ne cherche donc **pas** à faire accepter une PR (ce n'est pas le but) ; on assume un fork jetable en attendant.
|
||||
|
||||
## What We Know
|
||||
|
||||
Le travail s'étend sur **trois couches**, pas une :
|
||||
|
||||
1. **Fork SDK** — patch Rust (moteur) + paquets JS clients patchés.
|
||||
2. **Auto-hébergement** — `ngd` + ng-app déployés depuis le fork (Coolify).
|
||||
3. **Intégration dans Festipod** — l'app doit *utiliser* ces libs : appeler l'écriture inbox au bon endroit, modéliser et lire les notifications, câbler le tout.
|
||||
|
||||
Les trois sections ci-dessous les détaillent.
|
||||
|
||||
### Couche 1 — Le patch Rust : 4 fichiers, tous côté moteur (broker vanilla)
|
||||
|
||||
1. **`engine/net/src/types.rs`** — `InboxMsgContent::Link` est aujourd'hui une variante **unit** (stub). Lui donner un payload, ou ajouter une variante (ex. `Notification`) portant le NURI du PdR + un lien vers l'`Inscription`. Ajouter un builder `InboxPost::new_link(...)` calqué sur `new_contact_details` (≈ ligne 3772). `from = None` → anonymat.
|
||||
2. **`engine/verifier/src/request_processor.rs`** — ajouter le bras de commande manquant. Le dispatch n'a **pas** de bras `InboxPost` ; commandes traitées : `OrmStart(Discrete)`, `Fetch`, `FileGet`, `OrmUpdate`, `OrmDiscreteUpdate`, `SocialQueryStart`, `QrCodeProfile(Import)`, `Header`, `Create`, `FilePut`. Idéalement une commande haut-niveau (`NotifyInbox`) qui construit le post côté Rust (garde le scellement crypto en Rust). Calquer sur le bras `SocialQueryStart`.
|
||||
3. **`sdk/js/lib-wasm/src/lib.rs`** — exposer `pub async fn inbox_post_link(session_id, to_inbox_nuri, to_profile_nuri, link, anonymous)`, calqué sur `social_query_start` (prend des NURI string, construit l'`AppRequest`, appelle `local_broker::app_request`).
|
||||
4. **`engine/verifier/src/inbox_processor.rs`** (`process_inbox`) — ajouter le bras de réception qui **matérialise** le message reçu en document dans le store de l'hôte (calquer sur le handler `ContactDetails` qui crée un doc `social:contact`). L'app lit ensuite via ORM/SPARQL — pas de nouvelle API de lecture d'inbox.
|
||||
|
||||
**Résolution d'identité** (connu / anonyme) : tombe gratuitement via SPARQL côté app (JOIN du NURI d'inbox émetteur contre les docs `social:contact`, qui stockent les NURI d'inbox). Probablement zéro Rust supplémentaire.
|
||||
|
||||
**Découverte de l'inbox de l'hôte** : l'inscrit a besoin du NURI d'inbox du `public_store` de l'hôte ; à embarquer dans le doc PdR ou le profil public (le flux QR-code de partage de profil porte déjà cette info).
|
||||
|
||||
### Couche 2 — Déploiement (depuis le fork)
|
||||
|
||||
Détail du modèle dans [nextgraph-integration-model](../knowledge/nextgraph-integration-model.md). Le verifier patché tourne **dans l'iframe ng-app** → il faut **construire et auto-héberger, depuis le fork, le `ngd` + le ng-app** (`app/nextgraph`), puis rebuilder le `@ng-org/web` de Festipod avec `NG_REDIR_SERVER` / `NG_DEV*` pointant sur ce ng-app auto-hébergé. **Aucune réécriture de l'intégration Festipod** (elle reste iframe).
|
||||
|
||||
Précision : le *routage* inbox du broker est déjà natif (un `ngd` officiel routerait l'inbox). Mais comme on auto-héberge de toute façon le ng-app patché (qui embarque le verifier patché), **on déploie toute la stack depuis le fork** — un seul arbre source à maintenir, build cohérent, pas de mélange binaires-officiels / fork.
|
||||
|
||||
- **Local** : `ngd` + ng-app buildés depuis le fork (DEV.md « first run ») ; Festipod buildé avec `NG_DEV` / `NG_DEV_LOCAL_BROKER`.
|
||||
- **Serveur de test** : `ngd` + ng-app du fork déployés sur notre domaine ; Festipod buildé avec `NG_REDIR_SERVER=notre-domaine`.
|
||||
|
||||
### Hébergement sur Coolify
|
||||
|
||||
Auto-héberger = **3 pièces web** derrière notre domaine (détails pérennes dans [nextgraph-integration-model](../knowledge/nextgraph-integration-model.md)) :
|
||||
|
||||
1. **`ngd`** — démon WebSocket **stateful**. Sur Coolify : conteneur avec **volume persistant** pour `--base-path` (RocksDB + clés + PeerId — à ne jamais wiper entre redéploiements), lancé en mode `--domain` derrière le **Traefik de Coolify** (TLS terminé, X-Forwarded-For). Build : pas de Dockerfile officiel utilisable (les 3 fournis sont cassés) → **écrire notre propre Dockerfile multi-stage Rust** (RocksDB exige llvm/clang). Premier démarrage **interactif** (lien d'invitation pour le wallet admin) → à scripter via `ngcli` ou à faire une fois à la main puis persister dans le volume.
|
||||
2. **ng-app** (le frontend iframe, embarquant le wasm patché) — **build statique** (`pnpm webfilebuild`, nécessite pnpm + wasm-pack). Servi comme site statique (buildpack static Coolify ou conteneur nginx).
|
||||
3. **Routage** : un même domaine doit servir le **statique du ng-app** ET proxifier le **WebSocket vers ngd** (le broker ne sert pas de statique). À configurer dans Coolify (routes/domaines).
|
||||
|
||||
Plus **Festipod** lui-même (app Bun → le skill `coolify-hosting` s'applique pour CELLE-CI, mais pas pour le `ngd` Rust).
|
||||
|
||||
**Drivers de complexité** : build Rust+RocksDB sans Dockerfile prêt, conteneur stateful à volume critique, premier-run interactif, et le double-service (statique + WS) sur un domaine. → ops **modéré-à-conséquent**, surtout au premier montage.
|
||||
|
||||
### Couche 1 (libs JS) — Gestion des libs npm clientes
|
||||
|
||||
**On maintient des versions patchées des paquets clients, pas seulement le wasm.** Le fait que les 3 maillons JS soient génériques (proxy `@ng-org/web` → `call_sdk` d'api-web → `Reflect.apply` du worker, cf. [knowledge](../knowledge/nextgraph-integration-model.md)) permet *techniquement* d'atteindre une nouvelle méthode wasm d'écriture sans toucher au JS — mais c'est un **hack** (non typé, non documenté, fragile) qu'on ne retient que comme test rapide, pas comme plan.
|
||||
|
||||
Ce qu'il faut réellement modifier :
|
||||
|
||||
- **`@ng-org/web`** — modifié de toute façon (URL broker, voir ci-dessus) → y ajouter `inbox_post_link` dans la **surface d'API typée + les `.d.ts`**, plutôt qu'un appel string casté.
|
||||
- **Méthodes streamées (cas obligatoire)** — si on lit un jour l'inbox en *flux* (au lieu du doc matérialisé lu via ORM/SPARQL), il faut une entrée dans la table de streaming **des deux côtés** : `E` dans `@ng-org/web` et `streamed_api` dans api-web. Pour la seule **écriture** (requête/réponse), pas nécessaire.
|
||||
- **`@ng-org/orm`** — à modifier **si** on intègre l'écriture inbox au flux ORM (helper, ou couplage écriture `Inscription` + post inbox). Si on appelle `ng.inbox_post_link` directement à côté de l'ORM, pas nécessaire.
|
||||
- **`@ng-org/alien-deepsignals`, `@ng-org/shex-orm`** — a priori inchangés (sans rapport avec l'inbox).
|
||||
|
||||
Donc on porte un **fork JS** (au moins `@ng-org/web`, possiblement `@ng-org/orm`) en parallèle du fork Rust.
|
||||
|
||||
#### Comment Festipod obtient ces libs custom — l'outillage existe déjà
|
||||
|
||||
Le script **`scripts/build-ng-packages.sh`** (alias `bun run build:ng`) fait exactement ça depuis le fork local :
|
||||
|
||||
1. Build des 4 paquets (`alien-deepsignals`, `shex-orm`, `web`, `orm`) depuis `$NEXTGRAPH_RS/sdk/js/*` (défaut `NEXTGRAPH_RS=../../nextgraph/nextgraph-rs`).
|
||||
2. `pnpm pack` → `.tgz` dans `.ng-tarballs/`.
|
||||
3. `bun add .ng-tarballs/ng-org-*.tgz` → **réécrit `package.json`** pour pointer chaque dep vers le tarball local au lieu du registre.
|
||||
|
||||
C'est le **pattern d'origine du projet** : le commit `fd6d408` (« install from npm instead of local tarballs ») l'a abandonné quand les alphas ont été publiées sur npm (suppression de `.ng-tarballs/`). Pour repasser au custom : **réactiver `bun run build:ng`** (le script est toujours présent).
|
||||
|
||||
Nuances :
|
||||
- **`@ng-org/web` est un proxy TS pur (sans wasm)** — le script crée un *stub* `lib-wasm`. Le tarball porte donc l'**API inbox typée + l'URL broker bakée au build**, mais **pas** le wasm (qui vit dans le ng-app auto-hébergé, couche 2).
|
||||
- **Fork temporaire** : le script fait `git pull --ff-only` sur `nextgraph-rs` → le pointer sur notre **branche patchée** (ou retirer le pull) pour builder le fork, pas l'upstream.
|
||||
- **Option complémentaire (rec.)** : patcher `@ng-org/web` pour lire l'URL broker au **runtime** (env/global), pour éviter de rebuilder le tarball à chaque changement de domaine (local/test/prod).
|
||||
|
||||
Flux complet à chaque rebase : patcher le fork → `bun run build:ng` (rebuild tarballs + repointe `package.json`) → `bun install`. Les libs non touchées peuvent rester sur les versions npm publiées.
|
||||
|
||||
### Couche 3 — Intégration dans Festipod
|
||||
|
||||
Exposer la méthode ne suffit pas : le code de l'app doit l'**utiliser**. Plusieurs chantiers, dont certains préexistent à l'inbox (l'app n'est pas encore prête côté données) :
|
||||
|
||||
- **Modéliser le point de rencontre.** Les SHEX (`src/shared/shapes/shex/festipodShapes.shex`) ne définissent que `Event`, `UserProfile`, `Participation` — **pas de `MeetingPoint`** (aujourd'hui local-only), ni d'entité « notification d'inscription ». Ajouter les shapes + `bun run build:orm`.
|
||||
- **Implémenter l'inscription (aujourd'hui un no-op).** Dans `src/shared/context/FestipodDataContext.tsx`, `joinEvent`/`leaveEvent` sont des `console.log('… (local, no-op)')`. Le vrai flux d'inscription à un PdR doit : (a) écrire l'`Inscription` dans le `protected_store` de l'inscrit (ORM, via le multi-store — voir [multi-store-refactor](./multi-store-refactor.md)), **et** (b) appeler `ng.inbox_post_link(...)` pour notifier l'inbox du PdR de l'hôte.
|
||||
- **Porter le NURI d'inbox de l'hôte sur le doc PdR** (ou via lookup profil) pour que l'inscrit puisse cibler l'inbox.
|
||||
- **Lire et résoudre les notifications côté hôte.** `getEventParticipants` / l'écran liste des inscrits doit lire les docs « notification » matérialisés (ORM/SPARQL) et faire le JOIN identité contre les contacts (`social:contact`). UI à prévoir : « N inscrits dont X identifiés ».
|
||||
- **Câblage session** : l'appel direct `ng.inbox_post_link` passe par le `ng`/session de `src/shared/utils/ngSession.ts`.
|
||||
|
||||
**Dépendances** : cette couche présuppose (1) le fork SDK livré et (2) le [refactor multi-store](./multi-store-refactor.md) (les inscriptions vivent dans le `protected_store`, pas le store unique actuel).
|
||||
|
||||
**Surface jetable** : quand NextGraph livrera sa propre API inbox (possiblement différente), il faudra migrer **aussi** ces points d'appel Festipod (l'appel `inbox_post_link`, la shape notification, la logique de lecture/résolution) — pas seulement les libs.
|
||||
|
||||
## Open Questions
|
||||
|
||||
- Commande haut-niveau (`NotifyInbox`) vs `InboxPost` brut dans `request_processor` ? (haut-niveau préféré : garde la crypto en Rust)
|
||||
- Où sourcer le NURI d'inbox de l'hôte (champ du doc PdR vs lookup profil) ?
|
||||
- Forme de la matérialisation côté réception (quels triples pour une notification d'inscription) ?
|
||||
- Suppression côté inbox : un déposant peut-il retirer son propre dépôt d'un doc qu'il ne contrôle pas ? (déjà noté en question résiduelle dans [authorization-matrix](./authorization-matrix.md))
|
||||
- Cadence de rebase du fork sur `upstream/main` : à chaque alpha, ou par jalons ? (arbitrer coût de maintenance vs dérive)
|
||||
- Critère de bascule : à quel signal upstream considère-t-on leur solution inbox « adoptable » et démarre-t-on la migration ?
|
||||
- `@ng-org/web` : patch runtime (build unique, multi-env) vs tarball local par domaine ? (le patch runtime est recommandé mais ajoute une ligne au fork à maintenir)
|
||||
- `ngd` sur Coolify : comment automatiser le premier-run (création du wallet admin via `ngcli`) pour un déploiement reproductible vs one-shot manuel persisté dans le volume ?
|
||||
- Faut-il un seul service Coolify (reverse-proxy maison servant statique + WS) ou deux services (static ng-app + ngd) avec routage de domaine Coolify ?
|
||||
|
||||
## Possible Approaches
|
||||
|
||||
Posture retenue (voir Context) : **fork temporaire auto-hébergé**, abandonné dès que NextGraph expose sa propre solution.
|
||||
|
||||
- **A. Fork temporaire + auto-hébergement (retenu comme stopgap)** — patch des 4 fichiers, build et déploiement de `ngd` + ng-app depuis le fork. Vrai inbox, anonymat natif, livrable sans attendre l'upstream. Coût : maintenir le fork rebasé + héberger la stack. Jetable : on migrera vers la solution officielle quand elle sortira.
|
||||
- **B. Contribution upstream — écartée comme objectif.** On ne vise pas à faire accepter une PR ; on attend plutôt la solution propre des développeurs NextGraph (qui sera possiblement différente) et on s'y adaptera. (Rien n'interdit de signaler le besoin à l'auteur, mais ce n'est pas le plan.)
|
||||
- **C. Pas de patch, détourner `social_query_start` (déjà exposé)** — repli si l'auto-hébergement n'est pas souhaité à court terme. Livrable tout de suite mais limité aux **contacts** : pas de notification anonyme vers un hôte non-connecté.
|
||||
|
||||
## Starting Points
|
||||
|
||||
- [nextgraph-integration-model](../knowledge/nextgraph-integration-model.md) — modèle d'intégration/déploiement
|
||||
- [nextgraph-stores-permissions](../knowledge/nextgraph-stores-permissions.md) — inbox au protocole, exposition SDK, chemin du repo local
|
||||
- [authorization-matrix](./authorization-matrix.md) — la décision cadre inbox que ce patch sert
|
||||
- Repo local `nextgraph-rs` : `sdk/js/lib-wasm/src/lib.rs`, `engine/verifier/src/{request_processor,inbox_processor}.rs`, `engine/net/src/types.rs`
|
||||
- Remotes du repo local : `origin` = `git.nextgraph.org/slaivyn/nextgraph-rs` (fork perso, déjà en place pour pousser un patch), `upstream` = `git.nextgraph.org/NextGraph/nextgraph-rs` (officiel, pour PR / rebase).
|
||||
@@ -31,6 +31,8 @@ Entités impactées (toutes mélangées dans le même store aujourd'hui) :
|
||||
|
||||
### Modèle cible proposé
|
||||
|
||||
> **Note (2026-05-19)** : la [matrice d'autorisations](./authorization-matrix.md) a depuis dérivé, à partir des seuls points validés, une structure différente — 3 stores natifs par utilisateur (`public_store` + `protected_store` + `private_store`) + Dialog stores pour les connexions bilatérales, sans Group store dans le périmètre actuel. La structure à 4 niveaux ci-dessous reste pertinente pour le périmètre élargi (communautés, collaboration multi-hôte), qui est aujourd'hui hors périmètre. À reconcilier au moment de l'exécution.
|
||||
|
||||
Structure hiérarchique en **4 niveaux de Group stores** (pas de private/public pour le métier collaboratif — tout en Group) :
|
||||
|
||||
```
|
||||
@@ -72,7 +74,12 @@ Mapping entités → store cible :
|
||||
|
||||
### Contrainte SDK bloquante
|
||||
|
||||
La création de Group stores et la gestion des invitations/permissions **ne sont pas exposées dans le SDK `@ng-org/web` actuel** (version `0.1.2-alpha.11`). Les méthodes disponibles : `doc_create`, `doc_subscribe`, `sparql_query/update`, `orm_start_*`, `file_get`, `app_request_stream`. Aucune méthode `share_doc`, `invite_user`, `create_group_store`, `accept_invite`. La doc NextGraph annonce qu'*« An API will be provided for permission manipulation »* — pas de date.
|
||||
Plusieurs primitives présentes au niveau protocole NextGraph **ne sont pas exposées dans le SDK `@ng-org/web` actuel** (vérifié en `0.1.2-alpha.13` = `upstream/main` au 2026-05-21, version installée dans Festipod). Méthodes disponibles : `doc_create`, `doc_subscribe`, `sparql_query/update`, `orm_start_*`, `file_get`, `app_request_stream`. Absents du SDK alors qu'existant côté protocole :
|
||||
|
||||
- création de Group stores et gestion des invitations/permissions (`share_doc`, `invite_user`, `create_group_store`, `accept_invite`) ;
|
||||
- **dépôt et lecture de l'inbox d'un document** (cf. [matrice d'autorisations](./authorization-matrix.md) — l'inbox est le mécanisme natif retenu pour la notification d'inscription au PdR). À noter que `app_request_stream` est la méthode générique la plus susceptible de porter ce mécanisme une fois exposé, à confirmer en lisant le code Rust du broker.
|
||||
|
||||
La doc NextGraph annonce qu'*« An API will be provided for permission manipulation »* — pas de date.
|
||||
|
||||
**Implication :** le refactor *structurel* (passer d'un store unique à un système de stores par entité) peut commencer sans attendre cette API, en utilisant des placeholders (par ex. continuer à pointer vers `private_store_id` pour les Group stores qui ne peuvent pas encore exister). Mais l'**aboutissement complet** (vrai multi-user, partage entre wallets distincts) dépend de l'arrivée de l'API SDK ou d'un contournement (fork du wallet, accès Rust direct, etc.).
|
||||
|
||||
|
||||
@@ -72,7 +72,7 @@ See [decision record](../decisions/2026-03-17-1800-sparql-delete-for-orm-objects
|
||||
- `src/shared/utils/ngGraph.ts` — `ensureGraphNuri()` returns `@graph` for entity creation
|
||||
- `src/shared/utils/ngBootstrap.ts` — Seeds test data using `ensureGraphNuri()` for `@graph`
|
||||
|
||||
See [decision record](.project/decisions/2026-03-17-1600-private-store-nuri-scope.md) for why.
|
||||
See [decision record](../decisions/2026-03-17-1600-private-store-nuri-scope.md) for why.
|
||||
|
||||
## Context Providers
|
||||
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
# Modèle d'intégration et de déploiement NextGraph
|
||||
|
||||
Comment une app web tierce s'intègre à NextGraph, et où tourne le moteur (verifier).
|
||||
|
||||
## Overview
|
||||
|
||||
NextGraph s'utilise depuis une app web via un **proxy iframe** (`@ng-org/web`) : l'app tierce ne contient pas le moteur, elle délègue à un ng-app hébergé (par défaut `nextgraph.net`) qui exécute le moteur dans une iframe. Comprendre ce découpage est nécessaire pour savoir ce qu'on peut modifier sans auto-héberger. Vérifié dans `nextgraph-rs` le 2026-05-21 (voir [chemin du repo local](./nextgraph-stores-permissions.md#code-source-local)).
|
||||
|
||||
## Les paquets JS
|
||||
|
||||
- **`@ng-org/web`** — paquet **publié**. Proxy postMessage léger (aucun wasm embarqué). C'est **le** chemin d'intégration d'une app web tierce. `@ng-org/orm` et tous les exemples officiels (expense-tracker…) en dépendent. **Festipod l'utilise.**
|
||||
- **`@ng-org/api-web`** — paquet **privé** (`"private": true`, non publié). Moteur navigateur complet : charge `@ng-org/lib-wasm` dans un Web Worker (`?worker&inline`), utilise `sessionStorage`/`Worker`. Consommé uniquement par `app/nextgraph` (le frontend ng-app) et `engine/broker/auth`. C'est le moteur **interne** de l'app NextGraph, **pas** une cible d'intégration tierce.
|
||||
- **`@ng-org/lib-wasm`** — le moteur compilé en wasm (contient le verifier via la dépendance `nextgraph` / `local_broker`). Source : `sdk/js/lib-wasm/`.
|
||||
- **`nextgraph`** (npm) — l'API **NodeJS** (build `pkg-node` de lib-wasm).
|
||||
- **`@ng-org/orm`** — l'ORM réactif (`useShape`…), bâti sur `@ng-org/web`.
|
||||
|
||||
## Où tourne le verifier
|
||||
|
||||
Dans le modèle web standard (iframe), le verifier tourne **dans l'iframe** : `app/nextgraph` charge `api-web` → `lib-wasm` dans un Web Worker, côté navigateur. Le broker (`ngd`) ne fait que **le transport et le stockage**.
|
||||
|
||||
**Conséquence** : modifier la logique du verifier (ex. `request_processor`, `inbox_processor`) = reconstruire le **ng-app**, pas le broker.
|
||||
|
||||
## Le modèle iframe (intégration tierce)
|
||||
|
||||
- `@ng-org/web` redirige vers le ng-app hébergé, qui recharge l'app tierce dans une iframe après authentification, puis relaie les appels par `postMessage`.
|
||||
- **Reciblable au build** via variables d'env (fichier `sdk/js/web/src/index.ts`) :
|
||||
|
||||
| Variable | Cible |
|
||||
|---|---|
|
||||
| `NG_REDIR_SERVER` | défaut `nextgraph.net` |
|
||||
| `NG_DEV3` | `127.0.0.1:3033` |
|
||||
| `NG_DEV` | `localhost:14402` (redir) / `14404` (origin) |
|
||||
| `NG_DEV_LOCAL_BROKER` | `localhost:1421` |
|
||||
|
||||
Une app tierce peut donc pointer `@ng-org/web` vers un ng-app **auto-hébergé** sans changer son code, juste en rebuildant avec ces variables.
|
||||
|
||||
## Build pipeline lib-wasm
|
||||
|
||||
Scripts cargo dans `sdk/js/lib-wasm/Cargo.toml` (`[package.metadata.scripts]`) :
|
||||
|
||||
- `web` / `webdev` — `wasm-pack build --target web`
|
||||
- `node` / `nodedev` — `wasm-pack build -t nodejs`
|
||||
- `app` / `appdev` — `wasm-pack build --target bundler`
|
||||
|
||||
Post-traités par `prepare-web.js` / `prepare-node.js`.
|
||||
|
||||
## Plomberie proxy ↔ iframe ↔ worker (générique)
|
||||
|
||||
Le chemin d'appel d'une méthode du moteur est **entièrement générique** — aucune allowlist :
|
||||
|
||||
- `@ng-org/web` (proxy) : un `Proxy` JS qui relaie *n'importe quel* nom de méthode à l'iframe par `postMessage` (`apply` → `postMessage({method, args})`). Seules les méthodes *streamées* ont une entrée dans une table interne (positions d'arguments) ; les autres passent en simple requête/réponse.
|
||||
- `app/nextgraph` → `api-web/wasm-worker.js` : dispatch générique `Reflect.apply(ng[method], null, args)` (la table `mapping` est commentée/inutilisée).
|
||||
|
||||
**Conséquence** : une nouvelle fonction wasm en **requête/réponse simple** est *atteignable* de bout en bout via ce forwarding générique sans modifier le JS. Mais c'est un mécanisme de relais, **pas un substitut à une API typée** : l'appeler ainsi est un appel string non typé/non documenté (hack de test). Pour une intégration propre, on ajoute la méthode à la surface d'API du paquet (`@ng-org/web`) et à ses `.d.ts`, et éventuellement à `@ng-org/orm` (qui, lui, n'est **pas** un forwarder générique).
|
||||
|
||||
Cas **streamé** : une méthode en flux exige une entrée dans la table de streaming **des deux côtés** — `E` dans `@ng-org/web` (`ngweb.js`) **et** `streamed_api` dans `api-web/main.ts`. (Méthodes streamées actuelles : `doc_subscribe`, `orm_start_graph`, `orm_start_discrete`, `file_get`, `app_request_stream`.)
|
||||
|
||||
## Ciblage du broker : build-time uniquement
|
||||
|
||||
La cible (broker/ng-app) est figée **au build** de `@ng-org/web` via `import.meta.env` (`sdk/js/web/src/index.ts`) — **pas d'override runtime**, et `init()` ne prend pas d'URL de broker. Pour pointer une app vers un ng-app auto-hébergé, il faut donc **rebuilder `@ng-org/web`** avec `NG_REDIR_SERVER`/`NG_DEV*` (paquet en TypeScript pur, sans wasm → build trivial).
|
||||
|
||||
## Le broker (ngd)
|
||||
|
||||
- Supporte déjà nativement l'inbox (`inbox_post`, `inbox_register`, `inbox_pop_for_user` dans `engine/net/src/server_broker.rs`). Un `ngd` standard routerait l'inbox — aucun patch broker nécessaire.
|
||||
- C'est un démon **WebSocket** (`async-tungstenite`), **stateful** : stockage RocksDB sous `--base-path`, identité de pair (PeerId) persistée. Le volume est critique (clés + données chiffrées des users).
|
||||
- CLI (`bin/ngd/src/cli.rs`) : `--local PORT`, et surtout `--domain DOMAIN:PORT,LOCAL_PORT` = mode « derrière reverse-proxy TLS-terminé qui envoie X-Forwarded-For » (adapté à Traefik/Coolify).
|
||||
- **Ne sert pas de fichiers statiques** : pas de `ServeDir`/HTTP statique dans le crate. Le **ng-app frontend est un déploiement statique séparé** (`pnpm webfilebuild`). En prod, un reverse-proxy sert le statique du ng-app et proxy le WebSocket vers ngd sur un même domaine.
|
||||
- Premier démarrage **interactif** : ngd émet un lien d'invitation pour créer le wallet admin (cf. DEV.md « first run »). Wrinkle pour un déploiement conteneurisé headless.
|
||||
- Les Dockerfiles officiels (`bin/ngd/docker/Dockerfile.{alpine,fedora,ubuntu}`) sont **incomplets/cassés** (chemins obsolètes, échec de link llvm/clang documenté en commentaire) — pas de build conteneur turnkey.
|
||||
|
||||
## See Also
|
||||
|
||||
- [Stores NextGraph et droits d'accès](./nextgraph-stores-permissions.md) — stores, permissions, inbox au protocole, chemin du repo local
|
||||
- [Data Layer](./data-layer.md) — usage actuel côté Festipod (auto-init iframe conditionnel)
|
||||
- [Brief : forker NextGraph pour l'inbox](../briefs/fork-nextgraph-inbox.md) — consommateur de cette fiche
|
||||
@@ -0,0 +1,108 @@
|
||||
# Stores NextGraph et droits d'accès
|
||||
|
||||
Fiche de référence des 5 types de stores NextGraph et de leurs droits de lecture/écriture.
|
||||
|
||||
## Overview
|
||||
|
||||
Décrit les primitives de stockage et de permission de NextGraph (système externe, pas le code de Festipod). Sert de socle aux briefs [multi-store-refactor](../briefs/multi-store-refactor.md) et [authorization-matrix](../briefs/authorization-matrix.md), qui dérivent la structure de données cible de Festipod à partir de ces primitives.
|
||||
|
||||
Source : doc NextGraph officielle — [Documents & Stores](https://docs.nextgraph.org/en/documents/) et [Getting started](https://docs.nextgraph.org/en/getting-started/), vérifiée le 2026-05-21.
|
||||
|
||||
## Code source local
|
||||
|
||||
Le repo `nextgraph-rs` est cloné localement à **`../../nextgraph/nextgraph-rs`** (relatif à la racine du projet, soit `/home/sylvain/projects/nextgraph/nextgraph-rs`). À consulter pour vérifier ce qui est réellement exposé au protocole/SDK plutôt que de se fier à la doc. Points d'entrée utiles :
|
||||
|
||||
- `sdk/js/lib-wasm/src/lib.rs` — l'API wasm effectivement exposée au JS (`@ng-org/web` n'est qu'un proxy postMessage vers ces fonctions).
|
||||
- `engine/net/src/app_protocol.rs` — l'enum `AppRequestCommandV0` (commandes de l'app protocol) et `NuriV0` (formats de NURI).
|
||||
- `engine/verifier/src/request_processor.rs` — le dispatch effectif des commandes `app_request` (la vérité sur ce qui est *traité*, pas seulement déclaré).
|
||||
- `engine/net/src/types.rs` — types inbox (`InboxPost`, `InboxMsg`, `InboxMsgContent`).
|
||||
- `engine/verifier/src/inbox_processor.rs` — traitement des messages d'inbox.
|
||||
|
||||
## Les 5 types de stores
|
||||
|
||||
| Store | Lecture | Écriture | Création |
|
||||
|---|---|---|---|
|
||||
| **Private** | Titulaire seul | Titulaire seul | Par défaut |
|
||||
| **Protected** | Titulaire + utilisateurs disposant d'un lien + permission (capability) | Titulaire + collaborateurs permissionnés | Par défaut |
|
||||
| **Public** | Tout le monde, sans capability | Titulaire seul | Par défaut |
|
||||
| **Group** | Membres du groupe | Membres du groupe (collaboratif) | À la demande |
|
||||
| **Dialog** | Les deux utilisateurs uniquement | Les deux utilisateurs uniquement | À la demande |
|
||||
|
||||
### Citations doc (verbatim)
|
||||
|
||||
- **Private** — *« this is a place where you put only private and personal information that only you have access to »*, *« It is not possible to share the documents of your private store with anybody else »*.
|
||||
- **Protected** — *« a space where you can share data, documents, and media with other users, but they will need a special link and permission in order to access them »* ; fait office de *« protected social profile »*.
|
||||
- **Public** — *« equivalent to your website, blog, or public profile on social networks … that you want everybody to have access to, without the need for special permissions »*.
|
||||
- **Group** — *« each Group is a separate Store … you can configure the store so that all the documents included in this store, inherit the permissions of the store »*.
|
||||
- **Dialog** — *« hold all the data you exchange with another user (and only with that other user) … You cannot add more users to this store »*.
|
||||
|
||||
### Stores par défaut vs à la demande
|
||||
|
||||
Tout wallet utilisateur dispose d'office des **3 stores** private / protected / public. Ils sont exposés dans la session du SDK sous `private_store_id`, `protected_store_id`, `public_store_id`. Les **Group** et **Dialog** stores se créent à la demande.
|
||||
|
||||
## Concepts transverses
|
||||
|
||||
### Document vs Repo
|
||||
|
||||
- *« A Repo is basically the equivalent of an E2EE group for one and only one Document. »*
|
||||
- **1 document = 1 repo.** Le repo détient les commits (changements) **et** les permissions du document.
|
||||
- Identifiant du repo : `did:ng:o:<RepoID>` (RepoID de 44 caractères).
|
||||
- Un **store** est lui-même un document spécial qui regroupe et permissionne d'autres documents.
|
||||
|
||||
### Granularité des permissions
|
||||
|
||||
- **Écriture** : gérée au niveau du **Document (repo)**, pas de la branche ni du bloc — *« Write permissions are managed at the level of the Document, not at the level of the branch or block »*.
|
||||
- **Lecture** : peut être plus fine, **par bloc ou par branche** — *« Read permissions can be by block or branch »*.
|
||||
- **Héritage** : un store (notamment Group) peut être configuré pour que tous les documents qu'il contient héritent des permissions du store.
|
||||
|
||||
### Capability / Nuri
|
||||
|
||||
- Le partage se fait en transmettant un **Nuri** qui embarque la capability cryptographique (lecture et/ou écriture). Pas d'ACL centralisée : la possession du Nuri = le droit.
|
||||
- *« adding permissions can be done offline »* — l'ajout de permission est asynchrone.
|
||||
- *« removing permissions is a synchronous operation that requires a SyncSignature »* — le retrait est synchrone et nécessite une SyncSignature.
|
||||
|
||||
### Inbox
|
||||
|
||||
- **Chaque document a une inbox native.** Un non-éditeur (sans capability d'écriture) peut y **déposer un lien (DID cap)** sans être invité comme éditeur.
|
||||
- Le propriétaire **modère** : accepter / rejeter / retirer.
|
||||
- Citation : *« each document has an inbox, which is used in this case to drop the link »*.
|
||||
- C'est le mécanisme retenu par Festipod pour la notification d'inscription à un point de rencontre (voir [authorization-matrix](../briefs/authorization-matrix.md)).
|
||||
|
||||
#### Modèle inbox au protocole (vérifié dans `nextgraph-rs`, 2026-05-21)
|
||||
|
||||
- NURI d'inbox : `did:ng:d:<inbox_id>`.
|
||||
- Contenu : enum `InboxMsgContent` avec les variantes `ContactDetails`, `DialogRequest`, **`Link`**, `Patch`, `ServiceRequest`, `ExtRequest`, `RemoteQuery`, `SocialQuery` (`Comment`, `Transaction`, `BackLink` encore en TODO).
|
||||
- Le message est **scellé** (`crypto_box::seal`) vers la pubkey de l'inbox destinataire → seul le titulaire de l'inbox déchiffre.
|
||||
- Le champ `from` est **optionnel** → l'expéditeur peut être **anonyme** (pas de signature, pas de `from_inbox`). C'est exactement le « identifié si connu, anonyme sinon » voulu par Festipod, **natif au protocole**.
|
||||
|
||||
#### Exposition côté SDK JS : l'inbox n'est PAS utilisable directement
|
||||
|
||||
Investigation dans `lib-wasm` + `request_processor.rs` :
|
||||
|
||||
- `app_request(request)` est exposé au JS, et l'enum `AppRequestCommandV0::InboxPost` + le constructeur `AppRequest::inbox_post()` existent.
|
||||
- **MAIS** le `request_processor` du verifier (qui traite les `app_request`) **n'a aucun bras `InboxPost`**. Commandes réellement traitées : `OrmStart`, `OrmStartDiscrete`, `Fetch`, `FileGet`, `OrmUpdate`, `OrmDiscreteUpdate`, `SocialQueryStart`, `QrCodeProfile`, `QrCodeProfileImport`, `Header`, `Create`, `FilePut`. Envoyer un `InboxPost` via `app_request` ne déclenche donc rien.
|
||||
- En plus, construire un `InboxPost` exige le scellement crypto côté Rust ; **aucun helper wasm** n'expose cette construction.
|
||||
- Le dépôt en inbox n'est déclenché qu'**en interne** par deux features, elles exposées au JS :
|
||||
- `QrCodeProfileImport` → `post_to_inbox(InboxPost::new_contact_details(...))` (échange de contact) ;
|
||||
- `social_query_start(...)` → propagation de requête sociale via les inbox des **contacts**.
|
||||
|
||||
**Conséquence** : pas de moyen propre, aujourd'hui, de faire un « drop a Link » arbitraire dans l'inbox d'un PdR depuis le SDK JS. Il faudrait, dans `nextgraph-rs`, soit exposer un helper `inbox_post_link(...)` dans `lib-wasm` **et** ajouter le bras `InboxPost` au `request_processor`, soit détourner `social_query`.
|
||||
|
||||
**Piste connexe — `social_query_start`** : EST exposé au JS. C'est une requête fédérée sur le graphe social (propagée via inbox jusqu'à `degree` sauts), pertinente pour « qui dans mon réseau participe à X » et pour la découverte. Limite : ne touche que les **contacts**, donc ne couvre pas la notification anonyme vers un hôte non-connecté.
|
||||
|
||||
## Limites du SDK JS
|
||||
|
||||
Le SDK `@ng-org/web` (vérifié en `0.1.2-alpha.13`, soit `upstream/main` au 2026-05-21 — la version installée dans Festipod) **n'expose pas** les primitives suivantes, pourtant présentes au niveau protocole :
|
||||
|
||||
- création de Group / Dialog store ;
|
||||
- partage de capability (transmission de Nuri avec droits) ;
|
||||
- manipulation de permissions (ajout / retrait) ;
|
||||
- dépôt et lecture d'inbox.
|
||||
|
||||
Méthodes JS effectivement disponibles : `doc_create`, `doc_subscribe`, `sparql_query`, `sparql_update`, `orm_start_graph`, `orm_start_discrete`, `graph_orm_update`, `discrete_orm_update`, `file_get`, `app_request_stream`. La doc annonce qu'*« An API will be provided for permission manipulation »* (sans date). Détail dans [multi-store-refactor §Contrainte SDK](../briefs/multi-store-refactor.md).
|
||||
|
||||
## See Also
|
||||
|
||||
- [Brief : refactor multi-store](../briefs/multi-store-refactor.md) — consommateur de cette fiche
|
||||
- [Brief : matrice d'autorisations](../briefs/authorization-matrix.md) — dérive la structure de stores Festipod
|
||||
- [Knowledge : data layer](./data-layer.md) — état actuel mono-store de l'app
|
||||
@@ -68,8 +68,11 @@ bun run build:orm # Regenerate ORM from SHEX shapes
|
||||
- [Test Layer Contracts](.project/knowledge/test-layer-contracts.md) — what each of `@ui`/`@data`/`@e2e` is allowed to test
|
||||
- [Screens](.project/knowledge/screens.md) — screen inventory, registry, sketchy components
|
||||
- [Data-Layer Testing](.project/knowledge/data-layer-testing.md) — real broker testing, wallet setup, Playwright harness, e2e layer
|
||||
- [Stores NextGraph et droits d'accès](.project/knowledge/nextgraph-stores-permissions.md) — fiche de référence des 5 types de stores et de leurs permissions
|
||||
- [Modèle d'intégration NextGraph](.project/knowledge/nextgraph-integration-model.md) — paquets JS, modèle iframe, où tourne le verifier, reciblage du broker
|
||||
|
||||
## Briefs (work not yet started)
|
||||
|
||||
- [Multi-store refactor](.project/briefs/multi-store-refactor.md) — passer du mono-store actuel à une structure de Group stores par communauté/event/RDV (prérequis multi-user)
|
||||
- [Matrice d'autorisations et requêtes](.project/briefs/authorization-matrix.md) — analyse qui doit guider la structure de stores cible
|
||||
- [Forker NextGraph pour l'inbox](.project/briefs/fork-nextgraph-inbox.md) — patcher nextgraph-rs pour exposer l'inbox au SDK JS (notification d'inscription)
|
||||
|
||||
Reference in New Issue
Block a user