diff --git a/docs/briefs/2026-08-03-document-inbox-addressing.md b/docs/briefs/2026-08-03-document-inbox-addressing.md new file mode 100644 index 0000000..f401aaf --- /dev/null +++ b/docs/briefs/2026-08-03-document-inbox-addressing.md @@ -0,0 +1,46 @@ +# 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 `Event` shape starts carrying a technical address, and the domain model encodes a temporary state of the library. +- **It costs.** `documentInbox` *creates 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 `@data` run 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.