Files
ng-eventually/.project/concepts/app-contract/knowledge_what-an-inbox-is-upstream.md

3.8 KiB

type, summary, last_checked
type summary last_checked
knowledge 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 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 UserIds, 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.