diff --git a/README.md b/README.md index 0368572..0e51bd5 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,7 @@ is needed), and how this lib emulates it today. | Three native stores per identity | Places entities by scope `public` / `protected` / `private` | The identity's three real native stores hold the entity documents | Not-yet-implemented: `doc_create`/ORM can target only the private (and protected) native store today; a `public`/arbitrary `StoreRepo` is not JS-constructible | Three emulated scope-index documents per account — each "store" is an index doc listing its entity-doc NURIs; all physically live in the one shared private store, and scope is a logical label | | Per-document read isolation | Nothing to declare: creating a document records its cap on its store, and its creator holds it. Reading is `capFor(doc)` — you hold the key or you do not read | The broker/verifier delivers only documents the wallet holds a ReadCap for; accessing a document without the cap yields an empty result in a union read (a targeted read of an unheld repo errors with `RepoNotFound`) | The model itself is the point: reading is key possession, and there is no read-ACL to introspect — a client cannot ask "may this identity read this doc?" because that question does not exist upstream | Caps recorded per identity: `AddRepo` on the store's emulated Store branch for documents it creates, `AddLink` on its User branch for caps received; `caps.ts` caches them for the session. A read filter (`read-filter.ts`) plus the boundary (`reach.ts`) keep only documents whose cap is held. The cap value is the stand-in `OK` — enforcement is P1b | | Directed read sharing | Owns the relationship concept ("who is connected to whom") itself, and on acceptance shares one document's cap to the other's inbox (`shareCap(cap, theirInbox)`) | The cap sealed to the recipient's inbox key (`ContactDetails.read_cap`), opened by their own verifier while processing the inbox | Not-yet-implemented — a **gap, not a disagreement**: the field exists but the message construction is `unimplemented!()`, its only caller passes "without read_cap", and the receiver discards the cap. The shape is right; the implementation is absent | `shareCap` deposits the cap into the recipient's inbox document; the recipient's existing `inbox.watch` absorbs it into what they hold. No "receive" operation, and no principal is ever named to the registry | -| Inbox (registration notifications) | `inbox.post` / `read` / `watch` | A message is sealed to the recipient's key and queued in their inbox; the recipient's own verifier unseals and applies each queued message inline while processing the inbox | Not-yet-implemented: the sender-side seal-into-inbox call (`inbox_post_link`) is proposed/future, not exposed in the JS SDK | Deposits written as RDF into an inbox document via SPARQL; `read`/`watch` read the deposits back — an in-lib stand-in for the recipient's own inbox processing | +| Inbox (registration notifications) | `inbox.post` / `read` / `watch` | A message is sealed to the recipient's key and queued in their inbox; the recipient's own verifier unseals and applies each queued message inline while processing the inbox | Not reachable from JS: the verifier has no `InboxPost` arm, and no `inbox` method exists in `@ng-org/web`. (`inbox_post_link` is OUR proposed name from [`docs/fork-inbox-fallback.md`](docs/fork-inbox-fallback.md), not an announced NextGraph API — no such symbol exists in `nextgraph-rs`.) | Deposits written as RDF into an inbox document via SPARQL; `read`/`watch` read the deposits back — an in-lib stand-in for the recipient's own inbox processing | | ~~Discovery of all public events~~ **REMOVED 2026-07-30** | Circulates the link itself — into inboxes, or into a document the reader already holds | **There is no discovery.** You cannot discover, you can only follow links: publishing = place the data in your public store **and** circulate the link, seen only by those who received it (a foundation of local-first) | Not a gap to be filled — a global index is not a NextGraph shape, and it would pool data across wallets | Nothing. `discovery.ts` and its global index were removed: they emulated a capability the target will never have. See [`docs/readcap-and-nuri-model.md`](docs/readcap-and-nuri-model.md) §4ter-bis | | Reads / listing | Lists the documents it needs, by scope, and reads them | Native per-wallet reads over the real per-identity stores | Bug/perf: an anchorless union query spans every named graph in the session store, which on a shared / accumulating wallet is O(wallet size) and stalls | A bounded, by-need set of per-doc anchored `sparql_query`s (each anchored to one repo's default graph), independent of wallet size | | Reactivity | Lists update on change | Native reactive reads | Not-yet-implemented: there is no reactive union query across graphs | Re-query the bounded per-doc anchored set on a lightweight change signal (`doc_subscribe` / ORM on an already-opened single store) | diff --git a/docs/briefs/2026-07-27-p1a-cap-surface.md b/docs/briefs/2026-07-27-p1a-cap-surface.md index 382ad69..cfe47b1 100644 --- a/docs/briefs/2026-07-27-p1a-cap-surface.md +++ b/docs/briefs/2026-07-27-p1a-cap-surface.md @@ -182,7 +182,7 @@ Not started. It changes the consumer contract in the right direction (one less o - **`open(nuri, scope)` was kept** (it is in neither the §6 table nor the removals) as the single "this document is mine, in this scope" act — `mint` for protected/private, `publishRepoLink` for public. It no longer touches write caps: arming that guard would be enforcement this batch does not do. - **`grantWrite` / `canWrite` were left exactly as they were** (an authorization list, decorative, guard bypassed by every internal writer) and now have to be called explicitly — `open` used to set them as a side effect. They belong to P1b. -- **`shareCap` is implemented in `inbox.ts`** and re-exported from `/polyfill`, so it is reachable both as `inbox.shareCap` (SDK-identical entry, via `export * as inbox`) and from the polyfill surface. Deliberate: sharing a cap **is** an inbox deposit upstream, and at migration this call becomes `inbox_post_link` — a real SDK method — so hiding it from the SDK entry would have been the less faithful choice. §7's boundary holds where it matters: the registry, `capFor` and `CapRegistry` stay on the polyfill side, and every signature is a plain string. +- **`shareCap` is implemented in `inbox.ts`** and re-exported from `/polyfill`, so it is reachable both as `inbox.shareCap` (SDK-identical entry, via `export * as inbox`) and from the polyfill surface. Deliberate: sharing a cap **is** an inbox deposit upstream, and at migration this call becomes the native sealed deposit — a real SDK method, whatever it ends up being called (`inbox_post_link` was our own proposed name, not an announced API) — so hiding it from the SDK entry would have been the less faithful choice. §7's boundary holds where it matters: the registry, `capFor` and `CapRegistry` stay on the polyfill side, and every signature is a plain string. - **The stand-in key is the constant `OK`** (`nuri.ts`; it was a derived FNV-1a digest until the PO simplified it on 2026-07-30). The only question the emulation answers is *do I hold this cap or not*, so the value says that and nothing more — a digest merely looked like a key. Possession is a shape here, not a protection; P1b replaces the constant with a real key. - **`resetCaps()` clears in place** rather than rebuilding the registry, so a `watchShape` subscribed to the change signal does not end up holding a listener on an orphaned instance. - **The scope-index feed is holder-scoped** (`fileOwnCaps` compares through the shim key): the cross-account fan-out `listEntityDocs` files nothing, because other accounts' caps are emphatically not ours to hold. diff --git a/docs/migration-guide.md b/docs/migration-guide.md index 71fa902..d18db03 100644 --- a/docs/migration-guide.md +++ b/docs/migration-guide.md @@ -27,7 +27,7 @@ this step swaps the *emulated* key for the real one, not the model: `r:{base64url(serde_bare(ObjectRef))}`. It is **one function** (`mintCap`), because every path now READS a stored cap instead of recomputing one. `hasReadCap` / `targetOf` stay meaningful: the `r:` discriminant is upstream grammar, not ours. -- `shareCap(cap, toInbox)` becomes the native sealed delivery (`inbox_post_link` +- `shareCap(cap, toInbox)` becomes the native sealed delivery (whatever the SDK ends up naming it — see the note below and `ContactDetails.read_cap`), and `inbox.read`'s inline absorption becomes the recipient's own verifier applying queued messages. **The consumer's call does not change.** @@ -73,7 +73,7 @@ per-user wallets replace the shared one. ### 4. Real inbox → drop the in-lib read emulation Replace the emulated `inbox.ts` deposit (`docs.sparqlUpdate` into a shared-wallet -document) with the native `inbox_post_link` (proposed/future). On the read side the +document) with the native sealed deposit, once one is exposed to JS. **Its name and shape are NOT known**: no inbox method exists in `@ng-org/web`, the verifier has no `InboxPost` arm, and `inbox_post_link` is OUR proposed name (`fork-inbox-fallback.md`), not an announced API. On the read side the recipient's own verifier unseals each queued sealed message and applies it inline when it processes its inbox — there is no separate curator to build; the in-lib read emulation simply goes away. *(There is no global index to replace the cross-account fan-out: both were removed on 2026-07-30 — you cannot discover in NextGraph, you follow links.)* diff --git a/docs/nextgraph-current-state.md b/docs/nextgraph-current-state.md index 684ab21..fc3b36c 100644 --- a/docs/nextgraph-current-state.md +++ b/docs/nextgraph-current-state.md @@ -151,8 +151,11 @@ it processes its inbox — there is no separate curator or materialization servi `QrCodeProfile(Import)`, `Header`, `Create`, `FilePut`). Sending an `InboxPost` triggers nothing. - Building an `InboxPost` requires crypto sealing on the Rust side; no wasm - helper exposes it. A high-level `inbox_post_link` is a proposed/future API, not - yet present. + helper exposes it, and **no `inbox` method exists in `@ng-org/web` at all**. + *(`inbox_post_link`, named across this repo's docs, is OUR proposed name from + [`fork-inbox-fallback.md`](./fork-inbox-fallback.md) — grep `nextgraph-rs` and it + is nowhere. Nothing is announced about the eventual JS surface for inboxes: its + name and shape are unknown, not merely unimplemented.)* - Inbox deposit is only triggered internally by `QrCodeProfileImport` (`post_to_inbox(new_contact_details)`) and `social_query_start` (contact propagation via inbox). diff --git a/docs/simulation.md b/docs/simulation.md index 2a94a2b..38bfba8 100644 --- a/docs/simulation.md +++ b/docs/simulation.md @@ -464,7 +464,7 @@ fork the broker ([`fork-inbox-fallback.md`](./fork-inbox-fallback.md)), the lib emulates the inbox on the shared wallet: - **Target vs polyfill.** In the target, `post` seals a reference into the owner's - native inbox (`inbox_post_link(...)`, a proposed/future API) and the recipient's + native inbox — through a JS call that **does not exist and is not announced** — and the recipient's own verifier unseals each queued message and applies it inline when it processes its inbox — there is no separate curator or materialization process. Here, everything is readable, so the lib emulates the read side in-lib. @@ -507,9 +507,10 @@ At migration this guard disappears into cryptography: an inbox is sealed to its owner's key. The module knows no domain — the consumer application supplies the inbox document -NURI and interprets `payload`. At migration `post` becomes the native -`inbox_post_link` (proposed/future) and the read side is served by the recipient's -own verifier unsealing queued messages inline. +NURI and interprets `payload`. At migration `post` becomes the native sealed +deposit — **whose JS name and signature are not known**, since none is exposed or +announced — and the read side is served by the recipient's own verifier unsealing +queued messages inline. The inbox + watcher is the one deposit/read mechanism a consumer reuses for its own purposes — a registration/deposit, a cap delivery (`shareCap`), a link handed to someone — same `post` API, same watcher. diff --git a/packages/client/docs/sdk-reference.md b/packages/client/docs/sdk-reference.md index 53ab113..4a711a6 100644 --- a/packages/client/docs/sdk-reference.md +++ b/packages/client/docs/sdk-reference.md @@ -297,7 +297,7 @@ from the reactive contract: deposit), so `inbox.watch` ([`../src/inbox.ts`](../src/inbox.ts)) **polls** via `setInterval` (default 1s) instead of subscribing. The finished contract is push (the broker already routes the inbox natively); these become subscriptions when the - sealed-inbox path (`inbox_post_link`) lands. + sealed-inbox path is exposed to JS (no such method exists today). 3. **No cross-wallet / on-demand repo open.** There is no JS primitive to sync an *unknown* repo by NURI+ReadCap today (`load_repo_from_read_cap` is `pub(crate)`, diff --git a/packages/client/src/inbox.ts b/packages/client/src/inbox.ts index 13bbd5c..e658649 100644 --- a/packages/client/src/inbox.ts +++ b/packages/client/src/inbox.ts @@ -10,15 +10,17 @@ * In real NextGraph, a message is sealed to the recipient's key and queued into * their inbox; the recipient's own verifier unseals each queued message and * applies it inline as it processes the inbox — there is no separate curator - * process. A future `inbox_post_link` is the intended way to seal a link into an - * inbox from the sender side; it is not exposed yet. + * process. There is NO sender-side JS call for this today: the verifier has no + * `InboxPost` arm and `@ng-org/web` exposes no inbox method at all. (`inbox_post_link`, + * named elsewhere in these docs, is OUR proposal from `docs/fork-inbox-fallback.md` — + * no such symbol exists in `nextgraph-rs`. Do not cite it as a planned API.) * * Here, on one shared wallet where everything is readable, both sides run in-lib: * - `post` appends a deposit `{ from, payload, ts }` as RDF into the inbox * document (in the shared wallet) via the `docs.sparqlUpdate` primitive; * - `read` / `watch` read the deposits back via `docs.sparqlQuery` and expose * them. This in-lib read stands in for the recipient's own inbox processing - * until the sealed-inbox path (`inbox_post_link`) is available. + * until a sealed-inbox path is exposed to JS. * * All NextGraph I/O routes through the `docs` primitives (the real injected `ng`, * never `makeNg`), so this module imports no `@ng-org` package. diff --git a/packages/client/src/ng-proxy.ts b/packages/client/src/ng-proxy.ts index 27879a9..ae39889 100644 --- a/packages/client/src/ng-proxy.ts +++ b/packages/client/src/ng-proxy.ts @@ -48,7 +48,7 @@ export function makeNg(): Record { }; } - // TODO(anticipated API): inbox_post_link + capability operations — expose + // TODO(anticipated API): a sealed inbox deposit + capability operations — expose // here with their anticipated signatures, emulated for now. // Everything else: passthrough to the real SDK, unchanged.