docs: l'overlay est store-scopé ; retrait de la fausse piste "membership"

readcap-and-nuri-model — nouvelle section sur l'OVERLAY, le concept qui
manquait à la référence. C'est l'espace réseau d'un STORE : deux formes
(outer = BLAKE3 public du store_id, calculable par tous ; inner = BLAKE3 keyed
par le ReadCapSecret, réservé aux détenteurs de la clé). Le `✌️` d'un NURI de
DOCUMENT porte l'overlay de son store — VÉRIFIÉ de bout en bout, avec une
contre-preuve mécanique : dans Store, get/put/del/has passent tous
`&self.overlay_id`, donc tous les documents d'un store partagent le namespace
de blocs et un overlay par-document est structurellement impossible.

Conséquence documentée, qui contraint tout modèle de présence anonyme : le
`✌️` est un pseudonyme stable et permanent de la personne, présent dans toute
référence cap-less vers n'importe lequel de ses documents protected. Le même
bit d'information sert à dédupliquer sans lire ET à tracer — indissociables.

vision — correction d'une forme fausse. Le document affirmait « écriture =
membership/permissions », en miroir de « lecture = possession de clé ». Faux :
il n'y a pas de notion d'appartenance dans le modèle, uniquement des clés et
des URLs. Toute forme en member/role/permission est une MAUVAISE forme.

brief caps — la section « périmètre élargi : WriteCap = membership » est
retirée, conservée barrée comme garde-fou, avec la leçon de méthode qui vaut
plus qu'elle : lire l'état courant de nextgraph-rs pour en DÉDUIRE la forme
cible est une erreur — le source contient de l'échafaudage inerte (AddMember,
PermissionV0, verify_sig jamais appelé hors tests). Le source sert à vérifier
un mécanisme, jamais à inférer une intention.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014GbGgNEHRejVKoREvFuDFg
This commit is contained in:
Sylvain Duchesne
2026-07-27 12:07:24 +02:00
parent f2c5b30527
commit ead5aececf
3 changed files with 161 additions and 7 deletions
+50 -2
View File
@@ -82,8 +82,56 @@ regexes `net/types.rs` :
- `did:ng:o:{repo}:c:{commit}:k:{clé}` (`RE_COMMIT` types.rs:73)
- liste `RE_OBJECTS` `…:[cj]:{id}:k:{clé}…:l:{locator}` (types.rs:64)
L'`overlay` est **dérivé publiquement** de l'id du repo (`OverlayId::outer(store_id)`,
`:259`) → id+overlay ne fuitent **aucun secret**.
Le segment `:v:` est l'**overlay**, qui a sa propre section ci-dessous — c'est le
point le plus lourd de conséquences pour les modèles de présence anonyme.
## 4bis. L'overlay est l'espace réseau d'un STORE — jamais d'un document
**L'overlay est l'unité d'adressage réseau d'un store.** Chez le broker, les blocs
sont rangés sous une clé `(overlay, block_id)`, et les pairs se synchronisent
*dans* un overlay. Deux formes par store :
| | Dérivation | Qui peut le calculer |
|---|---|---|
| **outer** | `OverlayId::outer(store_id)` = BLAKE3 **public** | tout le monde (le store_id suffit) |
| **inner** | `OverlayId::inner(store_id, readcap_secret)` = BLAKE3 **keyed** | seulement qui détient la clé de lecture du store |
Cohérent avec le reste du modèle : pas de rôle ni de liste, seulement « détiens-tu
la clé qui permet de dériver cet identifiant ». `outer` = le nom public du store,
`inner` = son nom privé.
**Le `:v:` d'un NURI de DOCUMENT porte l'overlay de son STORE** (VÉRIFIÉ, chaîne
lue de bout en bout) : `NuriV0::repo_graph_name(repo_id, overlay_id)` formate
`o:{repo_id}:v:{overlay_id}` ; dans `doc_create` la valeur injectée est
`store.outer_overlay()` — le store **contenant**, jamais le `repo_id`. Un `Repo` ne
porte **aucun** champ overlay (seulement `store: Arc<Store>`) ; c'est `Store` qui
porte `overlay_id`. **Contre-preuve mécanique** : dans `Store`, `get`/`put`/`del`/`has`
passent tous `&self.overlay_id` au block storage — tous les documents d'un store
partagent le namespace de blocs, donc un overlay par-document est structurellement
impossible.
### La conséquence à connaître : le `:v:` est un pseudonyme stable
**Tous les documents d'une même personne dans son store protected portent le MÊME
`:v:`** = `outer(protected_store_id)`. Donc une référence cap-less — précisément
celle qu'on utilise pour « nommer sans donner à lire » — **expose l'appartenance
au store**, c'est-à-dire un **identifiant pseudonyme stable et permanent de la
personne**. Le store_id lui-même ne fuit pas (BLAKE3 non inversible), donc ça ne
dit pas *qui* ; mais c'est un **handle constant**, le même partout et pour
toujours, corrélable par quiconque collecte des références cap-less.
**Le couplage qui en résulte, et qui contraint tout modèle de présence anonyme** :
ce même `:v:` est *simultanément* (a) ce qui permet de **dédupliquer** des
références sans les lire — deux références de même `:v:` viennent de la même
personne — et (b) ce qui permet de **tracer** cette personne d'un contexte à
l'autre. **C'est le même bit d'information.** On ne peut pas obtenir la dédup sans
concéder le traçage, ni supprimer le traçage sans perdre la dédup — sauf à changer
le découpage en stores, ce qui déplace le curseur mais ne supprime pas l'arbitrage.
*Nuances.* Le `:v:` du NURI est l'overlay **outer**, alors que le trafic
client↔broker et le stockage local utilisent l'**inner** — valeur différente, mais
tirée du store elle aussi, donc la propriété tient dans les deux cas. Un store
`Dialog` renvoie un `Inner`, toujours store-scopé.
INFÉRÉ : un détenteur **sans clé** peut vraisemblablement **récupérer les blocs
chiffrés** (avec l'overlay, toujours cap-less) → vérifier l'**existence** d'un doc