Sylvain Duchesne 9951cd5223 feat(client): discovery via a global index (special @index account)
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>
2026-07-04 09:33:42 +02:00

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) 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 shimstore-registry.ts ((account, scope) → document NURI, createEntityDoc / listEntityDocs + per-scope index, cross-device via the RDF shim anchored in the private store).
  • Document / SPARQL primitivedocs.ts, calling the real injected ng directly (avoids the @ng-org double-proxy DataCloneError).
  • Emulated ReadCapscaps.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 guardng-proxy.ts (sparql_update override, emulated write cap).
  • Inboxinbox.ts (post / read / materialize / watch, emulated curator inline).
  • Isolationisolation.ts (pure social-visibility matrix, distinct axis from ReadCaps).
  • Accountsaccounts.ts (faux username login, injected storage).
  • SPARQL hardeningsparql.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.

S
Description
No description provided
Readme 375 KiB
Languages
TypeScript 100%