From e5779ca8506813e16ac06a090e14e7f8d84633c7 Mon Sep 17 00:00:00 2001 From: Sylvain Duchesne Date: Mon, 3 Aug 2026 18:03:03 +0200 Subject: [PATCH] docs: une adresse d'inbox est TRANSMISE en amont, nous la PUBLIONS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Assertion fausse retirée de `openDocumentInbox` : « en amont, l'acte équivalent est le propriétaire qui commite AddInboxCap avec la clé du repo — personne d'autre ne le peut ». Personne d'autre ne le peut est inventé. Ce commit atterrit sur la branche User de CELUI QUI LE FAIT, donc n'importe qui peut en écrire un nommant le repo de n'importe qui. Le moteur ne pose aucune garde là-dessus. Ce qui protège en amont n'est pas une garde, c'est le mode de circulation : `inboxes: PubKey → RepoId` est une table du Verifier (`verifier.rs:105`), reconstruite vide à chaque session — l'association inbox→repo est LOCALE, pas publiée. Un déposant apprend une pubkey parce qu'on la lui a ENVOYÉE : dans un `ContactDetails` (`contact.inbox`) ou par le QR de profil. Une paire forgée n'atteint personne, faute que quiconque en ait été informé. D'où une divergence à assumer et non à maquiller : nous PUBLIONS l'adresse sur le document, seul moyen qu'un tiers la trouve dans une émulation sans canal de messages. Cela crée un vecteur que le moteur n'a pas — qui peut écrire le document peut rediriger ses dépôts — et c'est ce que la garde `ownsDocument` compense. Elle compense NOTRE conception ; elle ne reproduit aucune règle amont. Manquait aussi dans la carte des inbox dressée juste avant : elle disait qui A une inbox, et omettait comment l'adresse circule — la dimension dont tout le reste dépend. --- docs/nextgraph-current-state.md | 21 +++++++++++++++++++++ packages/client/src/store-registry.ts | 17 +++++++++++++++-- 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/docs/nextgraph-current-state.md b/docs/nextgraph-current-state.md index 0d0a2c4..9127d18 100644 --- a/docs/nextgraph-current-state.md +++ b/docs/nextgraph-current-state.md @@ -135,6 +135,27 @@ So a per-document inbox is **not an anticipation**: it is an engine capability t code path exercises automatically and that no level-2 or level-3 API exposes. This lib implements it aligned on the engine's model. +**An inbox address is TRANSMITTED, never published — and nothing in the engine says who +may open one.** Two facts that decide more than they look: + +- `inboxes: HashMap` is a field of the **Verifier** + (`engine/verifier/src/verifier.rs:105`), rebuilt empty on each construction (`:520`, + `:2820`). The inbox → repo association is **local to a session**, not a published + fact. A depositor learns a pubkey because it was **sent** to them — in a + `ContactDetails` message (`contact.inbox`) or through a profile QR code; the reply + path reads its own `repo.inbox` to include it (`request_processor.rs:736-750`). +- There is therefore **no engine guard on who opens an inbox for a repo**. + `AddInboxCap` lands on the committer's OWN User branch, so anyone may write one naming + anyone's repo. It simply reaches nobody: no one was told that pubkey means that + document. + +*Consequence for this lib, and it is a real divergence:* we **publish** the address on +the document (its Header branch) because that is the only way a third party can find it +in an emulation with no message channel. That creates a vector the engine does not have +— whoever can write the document can redirect its deposits — so `openDocumentInbox` +guards on ownership. That guard compensates OUR design; it does not mirror an upstream +rule. Do not cite it as one. + A non-editor can deposit into an inbox without being invited as an editor; the owner moderates. NURI: `did:ng:d:`. Content: the `InboxMsgContent` enum (`ContactDetails`, `DialogRequest`, `Link`, `Patch`, `ServiceRequest`, `ExtRequest`, diff --git a/packages/client/src/store-registry.ts b/packages/client/src/store-registry.ts index e8172fa..b570e2a 100644 --- a/packages/client/src/store-registry.ts +++ b/packages/client/src/store-registry.ts @@ -1139,8 +1139,21 @@ export async function openDocumentInbox(doc: Nuri): Promise { // OWNERSHIP is the criterion, and holding a cap is NOT ownership — a cap can be // received. Opening an inbox is what PUBLISHES this document's address, so a // non-owner doing it would route the owner's deposits to itself, silently, on a - // document it merely reads. Upstream the equivalent act is the owner committing - // `AddInboxCap` with the repo's own key; nobody else can. + // document it merely reads. + // + // **This guard compensates OUR design, not an upstream constraint** — an earlier + // comment here claimed "upstream only the owner can commit `AddInboxCap`", which is + // false: that commit lands on the committer's OWN User branch, so anyone may write + // one naming anyone's repo. What protects upstream is that an inbox address is never + // PUBLISHED — it is TRANSMITTED (in a `ContactDetails` message, or a profile QR + // code), and `inboxes: PubKey → RepoId` is a per-verifier local table + // (`engine/verifier/src/verifier.rs:105`, rebuilt empty each session). A forged pair + // reaches nobody, because nobody was told about it. + // + // We publish instead of transmitting — the only way a third party can find the + // address at all here — which creates a vector upstream does not have: whoever can + // write the document can redirect its deposits. Hence this guard. It is a real + // divergence, deliberately taken; see `docs/briefs/2026-08-03-document-inbox-addressing.md`. if (!(await ownsDocument(doc))) { throw new Error( "[ng-eventually] openDocumentInbox: refused — you may only open an inbox on a document " +