Add a generic discovery-index surface: submitToIndex(ref) deposits a reference into the index document's inbox; readIndex() returns the materialized entries. A reserved special account (@index) owns the index document; deposits flow through the emulated inbox and are materialized by the emulated curator (the dedup/ moderation point). This replaces cross-account fan-out as the discovery path and is more faithful to the target (a single owned index fed via its inbox). Generic (the consumer supplies the reference to index). 79 tests pass; tsc rc=0. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ng-eventually
A generic polyfill layer over the NextGraph 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/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— 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/decisions/— historical current-SDK ADRs (private-store scope, SPARQL delete, shared-wallet login, discovery mechanism).docs/fork-inbox-fallback.md— the Rust-patch / self-host inbox path NOT taken (kept as fallback).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) anduseShape. The real SDK is injected viaconfigure()(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
inboxnamespace 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 injectedngdirectly (avoids the@ng-orgdouble-proxyDataCloneError). - Emulated ReadCaps —
caps.ts(CapRegistry, per-document) + read filterread-filter.ts(reactive-setProxyview), applied byuse-shape.tsonly when a policy is declared. - Write guard —
ng-proxy.ts(sparql_updateoverride, 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 for what each piece does and
docs/migration-guide.md for what is removed at
migration.