From 94173fd3d3cd567698eb9dc225759c620a3a41a4 Mon Sep 17 00:00:00 2001 From: Sylvain Duchesne Date: Thu, 6 Aug 2026 16:25:33 +0200 Subject: [PATCH] =?UTF-8?q?docs:=20consigner=20l'=C3=A9cart=20sur=20l'inbo?= =?UTF-8?q?x=20choisie=20par=20`share`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `inbox.share` dépose toujours sur l'inbox PROTÉGÉE du destinataire. En amont le choix n'est pas fixe : un enregistrement de contact prend son inbox du PROFIL par lequel la personne a été atteinte — `a_or_b = if details.profile.is_public() { "site" } else { "protected" }` (`inbox_processor.rs:787`, écrit `ng:site_inbox` ou `ng:protected_inbox` en `:823-824`). Mineur aujourd'hui, mais ça aplatit une distinction du modèle, et ce sera faux le jour où une application partagera avec quelqu'un rencontré par son profil public. Consigné plutôt que corrigé : le correctif demande une notion que rien n'a établi ici. Ce qui EST vérifié se limite à un wallet qui contient des `sites`, un `SiteV0` avec `id: PubKey`, `name`, `site_type` et trois stores. L'enum `Identity`, qui nommerait le reste, est entièrement COMMENTÉ en amont (`engine/repo/src/types.rs:586-595`) — il n'y a donc aucun modèle de profil à lire, et rien à supposer. Rappelé au passage, parce que la question s'est posée : le store privé n'a aucune inbox — `new_store_default` n'en attache une que `if !private` — d'où `InboxScope`, qui rend « l'inbox privée » inécrivable plutôt que simplement vide. --- docs/api-contract.md | 2 ++ packages/client/src/surface/inbox.ts | 24 +++++++++++++++++++++++- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/docs/api-contract.md b/docs/api-contract.md index 7c099a4..47b1a5f 100644 --- a/docs/api-contract.md +++ b/docs/api-contract.md @@ -390,6 +390,8 @@ Consequences per function: --- +> **Known divergence, low impact today — `inbox.share` always deposits on the recipient's PROTECTED inbox.** Upstream the choice follows the profile through which the person was reached (`a_or_b = if details.profile.is_public() { "site" } else { "protected" }`, `engine/verifier/src/inbox_processor.rs:787`). This library has no notion of "which profile I know this person by", so it picks one. It flattens a distinction the model makes; it will be wrong the day an application shares with someone met through a public profile. Recorded rather than fixed, because the fix needs a notion nothing here has established — note that the `Identity` enum that would name it is entirely commented out upstream (`engine/repo/src/types.rs:586-595`), so there is no profile model to read yet. + ## 10. Capabilities — possession, not ACL ### Today diff --git a/packages/client/src/surface/inbox.ts b/packages/client/src/surface/inbox.ts index d908f6c..59b5cf8 100644 --- a/packages/client/src/surface/inbox.ts +++ b/packages/client/src/surface/inbox.ts @@ -296,7 +296,29 @@ export async function share(doc: NuriLike, toUser: string): Promise { `for it. A cap is looked up in what you hold, or it was delivered to you: ${JSON.stringify(target)}`, ); } - // Protected, because directed sharing is not a public announcement. + // ── KNOWN DIVERGENCE: the protected inbox is hard-coded here ────────────── + // Upstream the choice is not fixed. A contact record picks its inbox from the PROFILE + // through which the person was reached: `a_or_b = if details.profile.is_public() + // { "site" } else { "protected" }` (`engine/verifier/src/inbox_processor.rs:787`, + // written as `ng:site_inbox` vs `ng:protected_inbox` at `:823-824`). Reach someone by + // their public profile and the deposit goes to their public store's inbox; by their + // protected profile, to the protected one. + // + // This library has no notion of "the profile by which I know this person", so it + // always uses the protected one. Minor today — a consumer names a user and gets one + // answer — but it flattens a distinction the model makes, and the day an application + // shares with someone met through a public profile, this picks the wrong inbox. + // + // Not fixable in isolation: it needs a notion this library does not have, and about + // which nothing has been established here. What IS verified: a wallet holds `sites`, + // a `SiteV0` has an `id: PubKey`, a `name`, a `site_type` (Individual | Org) and three + // stores (`engine/verifier/src/site.rs:23-40`); the `Identity` enum that would name + // the rest is entirely COMMENTED OUT upstream (`engine/repo/src/types.rs:586-595`). + // Do not build on an assumed profile model — there is none to read yet. + // + // (The private store has no inbox at all — `new_store_default` attaches one only + // `if !private`, `verifier.rs:2994` — hence `InboxScope`, which makes "the private + // inbox" unwritable rather than merely empty.) await post(await userInbox(toUser, "protected"), { payload: { kind: LINK_KIND, cap } }); }