`documentInbox(doc)` answers "which inbox do I know for this document?" and mints a fresh one when the answer is none. A third party therefore never reaches the owner's inbox: they get one of their own, the owner never reads it, and the deposit vanishes with no error. That breaks the consumer's central act — signing up to someone else's event — and it blocks the directory document that has to replace discovery, since creators must deposit into a document they do not own. The consumer tried solving it on its side: resolve the address once at creation and publish it on the public document, so participants read it instead of deriving it. It works, and it is still the wrong place. It puts a technical address into a domain shape; it doubles document creation on every event, since resolving an inbox creates one (the @data suite went from 9m37 to 21m30, with sign-up scenarios timing out during setup on a FRESH profile — not wallet bloat); and the library already claims this job, having stated when per-document inboxes landed that draining them "is the library's job, not the app's". Addressing is the other half of that same job. The app-side change is reverted. No shape is prescribed here. Deriving the address from the document would cost nothing and match how the rest of the model works — an overlay is derived, a keyring is looked up, nothing is handed over out of band — with the read guard keeping the asymmetry where it belongs. Publishing it library-side would work too and leaves room to make it lazy. One question decides where this note ultimately belongs: is "a document has an inbox addressable by any holder" part of NextGraph's target model, or this library's own invention? Upstream, it is emulation to align; otherwise it is a design decision to take deliberately.
4.1 KiB
Brief — a document's inbox has to be addressable by anyone holding the document
Raised 2026-08-03, from the consumer side (Festipod), after an attempt to solve it in the app proved it does not belong there.
The problem, in one sentence
documentInbox(doc) answers "which inbox do I know for this document?" — and mints a fresh one when the answer is none. So a third party never reaches the owner's inbox: they get one of their own, which the owner never reads, and their deposit vanishes without an error.
Why that breaks the consumer
Festipod's central act is signing up to someone else's event. The participant deposits into the event's inbox; the owner drains it at their next connection and materializes the count. With addressing scoped to the caller, only the owner can deposit into their own event — every other sign-up is silently lost.
The same wall stands in front of the directory document the app needs to rebuild discovery: creators must deposit a reference into a document they do not own.
What was tried in the app, and why it was wrong
The consumer published the address on the document itself: resolve documentInbox(eventDoc) once at creation, write the NURI into the public event, and have participants read it from there instead of deriving it.
It works. It is still the wrong place, for three reasons — and the third is the one that settles it.
- It puts infrastructure in the domain. The
Eventshape starts carrying a technical address, and the domain model encodes a temporary state of the library. - It costs.
documentInboxcreates a document. Publishing the address at creation takes every event creation from one document to two — and document creation is a round-trip that does not overlap. Measured on the test suite: the@datarun went from 9m37 to 21m30, with sign-up scenarios timing out on their setup step (4 failures out of 7 on a fresh profile, so not test-wallet bloat). That cost belongs to whoever can amortize or defer it. The app can do neither, because it does not own the mechanism. - The library already claims this job. The commit that introduced per-document inboxes says: "Inboxes belong to someone — the user's own, plus one per document — and connecting a user drains them all; that is the library's job, not the app's." Draining was taken as the library's job. Addressing was left to the caller. Those two halves belong together.
The consumer's own doctrine says the same thing from the other side: when something does not work, the question is never how to work around it in the app, but what the library has to compensate. The app-side change has been reverted.
What is being asked
Given a document, any holder of that document should be able to name the inbox its owner reads — without owning it, and without being handed the address out of band.
How is the library's call. Two shapes come to mind, neither prescriptive:
- Make the address derivable from the document, so
documentInbox(doc)returns the same NURI for everyone, and only the owner can read it — the read guard already enforces that, and it is where the asymmetry belongs. - Or have the library publish the address itself, at document creation, somewhere it controls — so it stays out of the consumer's shapes and the library keeps the freedom to make it lazy.
The second keeps the current create-time cost unless it is deferred; the first has none, and matches how the rest of the model works — an overlay is derived, a keyring is looked up, nothing is handed over out of band.
Open question worth settling first
Is "a document has an inbox addressable by any holder" part of NextGraph's target model, or an invention of this library?
If it is upstream, this is emulation to align. If it is the library's own, it is a design decision to take deliberately — and the answer decides whether the note about it belongs in the shared NextGraph inbox as a gap, or here.
Consumer state meanwhile
Sign-ups across identities do not converge, and the app does not pretend otherwise. Nothing in Festipod works around it.