bea9f51d91
This library presents a mature-NextGraph SDK face to consumers while compensating for the current SDK's gaps via a shared-wallet simulation. It therefore OWNS all current-state + simulation knowledge — moved here out of the Festipod app repo, which must treat this library as a finished SDK. New docs/: - nextgraph-current-state.md — what the current SDK/broker do and don't expose (5 store types, document=repo, per-document ReadCap, inbox not exposed, iframe RPC proxy, mono-user/no-global-data, wallet import constraint). Keeps the nextgraph-rs source pointers. - simulation.md — how the lib emulates the mature behaviour on one shared wallet (shim, store!=document two axes, docCreate→private store, RepoNotFound scope rule, @ng-org double-proxy DataCloneError, emulated ReadCap/inbox/curator). - decisions/ — the current-SDK ADRs (private-store-nuri-scope, sparql-delete, shared-wallet-login, discovery mechanism). - fork-inbox-fallback.md — the Rust-patch/self-host route not taken. - migration-guide.md — the checklist for when real NextGraph matures. README: boundary framing from the lib's side + docs/ index; replaced the stale "scaffold/stubbed" status with the actually-implemented mechanisms per source. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
101 lines
5.5 KiB
Markdown
101 lines
5.5 KiB
Markdown
# 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/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.
|