docs: ce que NextGraph offre vraiment autour des inbox, et ce qui n'est qu'un nom réservé

This commit is contained in:
Sylvain Duchesne
2026-08-20 10:30:31 +02:00
parent fafac38907
commit c507e79f8a
3 changed files with 54 additions and 1 deletions
@@ -0,0 +1,47 @@
---
type: knowledge
summary: What NextGraph actually offers around inboxes — a document may be given one, nothing above the engine exposes them, and most message variants are reserved names carrying nothing
last_checked: 2026-08-17
---
# What an inbox is upstream
Read at the source in `nextgraph-rs`. Every claim here points at a symbol rather than a line, so it stays verifiable by `git grep` after the code moves.
## A document may have an inbox, and must be given one
`Repo` upstream **is** a document — not a store. Its `inbox` field is an `Option<PrivKey>`: at most one inbox per document, and a document has none until something creates it. The creating act is an **`AddInboxCap` commit**, verified and applied like any other — see `update_inbox_cap_v0` in the verifier and the `CommitVerifier for AddInboxCap` impl.
The automatic creation inside `complete_site_store` is narrower than it looks: it gives an inbox to the site's **non-private stores** only. Reading that as *"only stores can have inboxes"* is a mistake this project has now made twice — the site path is one caller of a general capability, not the rule.
So an inbox on an ordinary document is **aligned with the target**, not an emulation of something absent.
## Nothing above the engine exposes any of it
Run the cascade for *"post, observe, or process an inbox message"* and every level above the engine is silent. The ORM has no occurrence of `inbox` at all; neither the web API nor the wasm binding exposes one — the binding's single mention is an unused import — and there is no generic `app_request` escape hatch, since every command carries its own dedicated wrapper.
Anything built here therefore aligns on the **engine's model**, because levels 2 and 3 answer nothing.
## Most message variants are reserved names, not shapes
`InboxMsgContent` declares eight variants. `ContactDetails` and `SocialQuery` carry data and have processors. **`Link`, `Patch`, `ServiceRequest`, `ExtRequest`, `RemoteQuery` and `DialogRequest` carry nothing at all** — they are bare unit variants, so they cannot hold a reference, name a servicer, or describe an operation.
That distinction decides how much they bind us, and it is why the provenance taxonomy in [[rule_no-divergence-from-nextgraph]] separates a reserved name from a declared shape.
## Processing is closed, and so is posting
`process_inbox` is `pub(crate)`, its match ends in `NotImplemented`, and its only caller is `Verifier::inbox`. There is no trait, registry or hook: **nothing outside the engine can contribute a processor** for a message type.
Posting is no better wired. `AppRequestCommandV0::InboxPost` is declared with its constructor and has **no handler arm** — the identifier appears nowhere else in the tree.
## A message waits for its recipient, and there is no delegate
An inbox's readers are `UserId`s, and messages queue until that user comes online. The only *"someone else runs it"* notion upstream is `Headless(Credentials)` — a server-side verifier holding a **user's own** credentials, which is that user running elsewhere, not a service delegated over someone's inbox.
## What follows for us
The **container** is settled by the target: a document may be given an inbox, one at most. Build on that freely.
The **content** of a deposit and **who processes it, when** have no upstream answer — the variants that would carry them are empty, and the processor set admits nothing from outside. So these are ours to choose, and a choice here is a bet that must be named as one rather than presented as alignment.
The standing choice, deliberate and provisional: **a deposit is a reference to a document**. It is enough for what is built on it today, and it is expected to evolve — most plausibly toward whatever shape the target eventually gives its own variants.