# ng-eventually A **generic polyfill layer** over the [NextGraph](https://nextgraph.org) JS SDK. NextGraph's JS SDK does not yet expose cross-wallet reads, capabilities, inboxes or group stores. `ng-eventually` lets an app behave as if those existed today, by emulating them on top of a **single shared wallet / broker**. It is **generic**: it contains **no application domain** — the consumer injects its shapes and the *acts* of granting access. The name: *eventually* NextGraph will ship these features; until then this layer fills the gap (and nods at eventual consistency / events). ## The boundary — mature face out, compensation in The asymmetry is the whole point. **Consumers write SDK-shaped code as if NextGraph were finished**: per-entity documents in public/protected/private stores, capabilities, inboxes. This library **owns all the current-state NextGraph knowledge and the simulation** that fabricates that mature face — a **shared-wallet** emulation — so the application never sees it. When NextGraph matures, **only this library changes**; the consumer's code does not. Docs (this library's own engineering doctrine, under [`docs/`](./docs/)): - [`docs/nextgraph-current-state.md`](./docs/nextgraph-current-state.md) — the authoritative reference on what the CURRENT SDK/broker do and do NOT expose (the ground truth every polyfill compensates for). - [`docs/simulation.md`](./docs/simulation.md) — how this lib emulates the mature behaviour on ONE shared wallet (shim, per-document ReadCaps, emulated inbox+curator, write guard, faux login, the two axes, the double-proxy constraint). - [`docs/read-model.md`](./docs/read-model.md) — the READ MODEL the polyfill implements: events via the global index, everything else by following a shared graph; listing via a one-shot union `sparql_query` (never the ORM fan-out, which hangs); reactivity via re-query on a change signal. - [`docs/decisions/`](./docs/decisions/) — historical current-SDK ADRs (private-store scope, SPARQL delete, shared-wallet login, discovery mechanism). - [`docs/fork-inbox-fallback.md`](./docs/fork-inbox-fallback.md) — the Rust-patch / self-host inbox path NOT taken (kept as fallback). - [`docs/migration-guide.md`](./docs/migration-guide.md) — the checklist for when real NextGraph matures. ## Packages | Package | Role | At migration | |---|---|---| | **`@ng-eventually/client`** | **SDK-identical** wrapper the app imports instead of `@ng-org/web` / `@ng-org/orm`. Adds the polyfills the broker/verifier will do natively (shared-wallet login, capability enforcement, anticipated cap/inbox methods). | Disappears: the app points back at the real SDK (build alias removed). | > **A global-index curator package is deferred.** NextGraph is **mono-user with > no global data** (apps/services see only what the user shares; there is no > multi-user backend). A global index would come from a **singleton app** > (a global document administered by the developer) — **not implemented and > uncertain**, and simpler paths may exist. So no second package for now; it will > be (re)introduced once the global-index mechanism is decided. The curator must > never be bundled in the client → it will be a separate package when it lands. ## Design principle The application code is written **as if the target NextGraph existed**. All compensation lives here, *beside* the app. Migration = remove this layer; the app code (SDK-shaped) is unchanged. - **SDK-identical surface**: the client wraps the real `ng` (a Proxy that forwards everything and overrides only what must be emulated) and `useShape`. The real SDK is **injected** via `configure()` (no hard import → build-alias safe + testable). - **Authorization = emulated capabilities**: documents carry grants; the client enforces them generically (read filter + write guard). The app *attaches* grants via cap operations — same as it will in the target. No policy is injected. - **Inbox**: the client `inbox` namespace deposits (`post`) and, in the shared-wallet emulation, also plays the curator inline (`read` / `materialize` / `watch`). At migration the read side moves to a separate curator package, deferred with the global-index mechanism — see the note above. - **Tests** of the polyfill (against a real broker) live **in this repo**, so the consuming app can test its features against a mocked, clean API. ## Status **Implemented.** The polyfill mechanisms are wired against a real broker, not stubbed: - **Shared-wallet shim** — `store-registry.ts` (`(account, scope) → document NURI`, `createEntityDoc` / `listEntityDocs` + per-scope index, cross-device via the RDF shim anchored in the private store). - **Document / SPARQL primitive** — `docs.ts`, calling the real injected `ng` directly (avoids the `@ng-org` double-proxy `DataCloneError`). - **Emulated ReadCaps** — `caps.ts` (`CapRegistry`, per-document) + read filter `read-filter.ts` (reactive-set `Proxy` view), applied by `use-shape.ts` only when a policy is declared. - **Write guard** — `ng-proxy.ts` (`sparql_update` override, emulated write cap). - **Inbox** — `inbox.ts` (`post` / `read` / `materialize` / `watch`, emulated curator inline). - **Isolation** — `isolation.ts` (pure social-visibility matrix, distinct axis from ReadCaps). - **Accounts** — `accounts.ts` (faux username login, injected storage). - **SPARQL hardening** — `sparql.ts` (`escapeLiteral` / `escapeIri` / `assertNuri`). Remaining `TODO` markers are narrow: the shared-wallet credential passthrough in the `login`/`session_start` proxy branch, and the anticipated cap/inbox SDK signatures to reconcile if the official API differs. See [`docs/simulation.md`](./docs/simulation.md) for what each piece does and [`docs/migration-guide.md`](./docs/migration-guide.md) for what is removed at migration.