docs: une adresse d'inbox est TRANSMISE en amont, nous la PUBLIONS

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.
This commit is contained in:
Sylvain Duchesne
2026-08-03 18:03:03 +02:00
parent 2406afec8b
commit e5779ca850
2 changed files with 36 additions and 2 deletions
+21
View File
@@ -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<PubKey, RepoId>` 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:<inbox_id>`. Content: the `InboxMsgContent` enum (`ContactDetails`,
`DialogRequest`, `Link`, `Patch`, `ServiceRequest`, `ExtRequest`,
+15 -2
View File
@@ -1139,8 +1139,21 @@ export async function openDocumentInbox(doc: Nuri): Promise<Nuri> {
// 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 " +