# @ng-eventually/polyfill One entry point. Most of what it publishes has the same signature as the future SDK — `ng`, `useShape`, `watchShape`, `docs`, `inbox`, `storeRegistry`, `readUnion` (+ types) — and is a drop-in for `@ng-org/web` / `@ng-org/orm`: as NextGraph matures it resolves to the real SDK (build alias removed) with no code change. **One call does not, and it is the whole of what you will delete:** `configure`. It exists because one shared wallet hosts every user; upstream, an application imports the SDK and each user opens their own wallet. `src/index.ts` groups it under a heading that says so. (`ensureIdentity` is a second in substance — the shared-wallet gate — but its call site survives: an application still awaits a session before it renders.) *(There were two entry points until 2026-08-07, `.` and `./polyfill`, and the second one WAS that list. One door is easier to import from and says less — hence the grouping, and hence `docs/api-contract.md`, whose export inventory a test keeps honest.)* Per-symbol, with the target signature and an epistemic label on every claim: [`docs/api-contract.md`](../../docs/api-contract.md). > **Reading is key possession, and the isolation here is still fake.** The cap surface > has the shape of the real model — you hold a document's `ReadCap` or you do not read > it, and there is no authorization list anywhere — but nothing is encrypted yet and > the stand-in key is a constant. Nothing this library does may be described as > "anonymous" or "private" until per-document encryption lands (P1b). ```ts import { // SDK-shaped — the real SDK replaces these in place. ensureIdentity, storeRegistry, inbox, readUnion, docs, // Polyfill-era — one call, and it is the whole of what goes away. configure, } from "@ng-eventually/polyfill"; configure({ ng: realNg, useShape: realUseShape, getSession, sharedWallet }); await ensureIdentity(); // who I am (returned), connection work awaited const doc = await storeRegistry.createEntityDoc("protected"); await docs.sparqlUpdate(sid, `INSERT DATA { … }`, doc); const subjects = await readUnion(await storeRegistry.listMyEntityDocs("protected")); ``` ## Principle — the polyfill compensates, it never extends **Its only reason to exist is to bridge a NextGraph implementation gap.** Every non-SDK surface must map to something NextGraph will provide natively, and must fall away at that point — no bespoke features, no observability, no convenience API that isn't strictly *"NextGraph will do this later"*. The test for any proposed addition: *does it compensate a real, exhibited gap?* If not, it belongs in the consumer application. And a compensation whose gap is not actually exhibited on the target broker is dead weight, not defensive code. Both halves are binding — **the surface AND the implementation** stay as close as possible to what NextGraph plans. The question to ask at every choice: *would this make a caller learn something it has to UNLEARN at migration?* If yes, it is a deviation, whatever it buys. What the polyfill adds, each emulated now and native later: - **Shared-wallet identity** — one wallet hosts every user, so the library fabricates *virtual users* and confines every access to the connected one (`emulated-verifier/reach.ts`). Upstream, each user opens their own wallet. - **Capability emulation** — per-identity cap possession plus a read filter over it: you read the documents whose cap you hold. There is no authorization list, because the real model has none. - **Inbox** — `post`, `postToDocument`, `share`, and the recipient's processing. The model is verified (an inbox is a keypair on one repo); no JS surface exists yet. Generic by construction: no application domain here. See [`examples/notebook`](../../examples/notebook) for an application written against it, which the e2e suite drives. ## How a document is reached — the three acts, and no others ```ts import { storeRegistry, inbox, readUnion } from "@ng-eventually/polyfill"; // 1. CREATE — you hold its cap, with nothing to declare. No identity parameter: a // session belongs to one user, exactly as the target's own `doc_create` assumes. const doc = await storeRegistry.createEntityDoc("protected"); // 2. GIVE TO READ — name the document and the person. The key is looked up and // sealed into a deposit; the recipient applies it by connecting, with nothing // to call. Irreversible: there is no revoking a key already handed out. await inbox.share(doc, "bob"); // 3. CIRCULATE THE REFERENCE — no call at all. Every reference this surface returns // is BARE: it names the document and grants nothing. If the document sits in a // PUBLIC store, the store serves its read cap to whoever asks, so the bare // reference is enough to read it — and if it does not, the reference still names // it and opens nothing. const publicDoc = await storeRegistry.createEntityDoc("public"); // …put `publicDoc` in a QR code, a message, another document. Nothing else to do. await readUnion([publicDoc]); // a stranger holding only this reads it ``` **The invariant behind all three: you never derive a cap from a bare reference.** You look it up in what you hold, you were given it, or a public store served it. A `did:ng:o:…` without `:r:` names a document and opens nothing — which is what makes confidentiality composable: a widely circulated document may point at a restricted one, and following the reference gets you a name, not a key. See [`docs/readcap-and-nuri-model.md`](../../docs/readcap-and-nuri-model.md) § 0. ## The types carry that invariant `Nuri` and `ReadCap` are **template literal types**, not `string` aliases: ```ts type Nuri = `did:ng:${string}` type ReadCap = `did:ng:${string}:r:${string}` ``` They are still strings — assignable to `string`, JSON-serializable, no wrapper — but the distinction is checked. A `ReadCap` goes wherever a `Nuri` is expected (a cap *is* a NURI with the key inside); the reverse does not compile. **Permissive in, precise out.** Public entries take `NuriLike` (`Nuri | string`) and validate at the door, so a value coming from storage, a URL or a form needs no narrowing and no cast on your side; what they *return* is a precise `Nuri`. The runtime checks stay regardless — a JavaScript caller never meets the compiler. ```ts const saved = localStorage.getItem("doc"); // string | null if (saved) await readUnion([saved]); // ✓ validated at the door ```