0eb25286c8
Deux mouvements de surface, aucun changement de comportement. **`packages/client` → `packages/sdk`, `@ng-eventually/client` → `@ng-eventually/sdk`.** « client » ne disait rien : ce paquet EST le SDK que l'application appelle, et c'est tout ce qu'elle appelle. L'ancien nom reste comme mot-clé de recherche dans `docs/source-layout-by-fate.md` et le tableau des paquets du README. **Une seule entrée.** L'entrée `./polyfill` disparaît ; ses symboles applicatifs — `configure`, `configureStoreRegistry`, `setCurrentUser`, `connectedUser` et leurs types — vivent dans un bloc `POLYFILL-ERA` de `src/index.ts`. Ce que la seconde porte portait mérite d'être nommé avant d'être retiré : *ce qu'on importe de ce chemin est exactement ce qu'on supprimera à la migration*. Une seule porte perd ce signal — rien à la ligne d'import ne distingue `configure`, qui part, de `docs`, que le vrai SDK remplace sur place. Trois choses le portent désormais : le bloc lui-même, l'inventaire d'exports de `docs/api-contract.md` (épinglé par `test/vocabulary.test.ts`, donc il ne peut pas rancir en silence), et le contrôle de vocabulaire sur les noms publiés. **Six symboles quittent la surface au passage**, et la fusion est ce qui a rendu le choix visible plutôt qu'hérité : - `getConfig` / `getStoreRegistryDeps` — câblage interne, atteint par `shared-wallet/bootstrap` ; - `resetConfig` / `resetStoreRegistry` / `resetCaps` — remises à zéro de test, atteintes par leur chemin interne, ce qui est leur raison d'être ; - le `share` direct — `inbox.share` a toujours été la même fonction, et la publier deux fois brouillait la frontière qu'elle servait à marquer. Corrections d'affirmations fausses trouvées en chemin : le contrat annonçait `isNuri` / `hasReadCap` sur la porte SDK alors qu'ils ne sont plus exportés depuis le passage au permissif en entrée (`NuriLike` validé à la porte) ; le README du paquet documentait `capFor`, `shareCap`, `getCaps` et `publishRepoLink`, dont aucun n'existe ; et le README de l'app d'exemple affirmait que la suite e2e la pilote, ce qui reste à faire. 179 tests unitaires, typecheck bibliothèque / exemple / harnais, e2e 42/42 contre le broker en ligne — mesuré une fois après le renommage, une fois après la fusion.
122 lines
7.9 KiB
Markdown
122 lines
7.9 KiB
Markdown
# Migration guide — when real NextGraph matures
|
|
|
|
The whole point of this library: the consumer application already writes SDK-shaped
|
|
code, so when NextGraph ships cross-wallet reads, capabilities and inboxes, only this
|
|
lib changes. The consumer application's code does not change. This is the checklist.
|
|
|
|
## Guiding invariant
|
|
|
|
Every emulated piece has a 1:1 image in the real infra. Migration = swap the
|
|
emulation for the real primitive, remove the scaffold. If a piece of the emulation
|
|
has no clear target image, that is a drift signal (see
|
|
[`simulation.md`](./simulation.md)).
|
|
|
|
## Checklist
|
|
|
|
### 1. Emulated ReadCaps → real capabilities
|
|
The shape is already the target's (P1a): a `ReadCap` is the document's key, a
|
|
each identity holds a set of caps, and there is no read-ACL anywhere. So
|
|
this step swaps the *emulated* key for the real one, not the model:
|
|
|
|
- `emulated-verifier/caps.ts`'s per-identity record becomes the verifier's own local user storage —
|
|
it was always the cache, not the register. The two durable registers we emulate
|
|
(`readCap` on the store's Store branch, `link` on its User branch) become the real
|
|
`AddRepo` / `AddLink` commits. Remove the emulation; the wallet and the branches
|
|
already hold them.
|
|
- `nuri.ts`'s stand-in cap value — the constant `OK` — becomes the real
|
|
`r:{base64url(serde_bare(ObjectRef))}`. It is **one function** (`mintCap`), because
|
|
every path now READS a stored cap instead of recomputing one. `hasReadCap` /
|
|
`targetOf` stay meaningful: the `r:` discriminant is upstream grammar, not ours.
|
|
- `inbox.share(doc, toUser)` becomes the native sealed delivery (whatever the SDK ends up naming it — see the note below
|
|
and `ContactDetails.read_cap`), and `inbox.read`'s inline absorption becomes the
|
|
recipient's own verifier applying queued messages. **The consumer's call does not
|
|
change.**
|
|
- `caps.markInPublicStore` and the whole of `emulated-verifier/public-store.ts` disappear: which store a document sits in stops being a fact we record, and serving a public store's repos becomes the broker's job (`expose_outer`, the ReadCap downloaded from the outer overlay — `PublicRepoLinkV0`, `engine/net/src/types.rs:5098`). Nothing an application calls changes: it circulates bare references now, and will still.
|
|
- `assertMayWrite` goes with it — refusing a write on a cap the public store served is a stand-in for the write cap this emulation does not have.
|
|
- The read filter (`emulated-verifier/read-filter.ts`) and the possession gate in
|
|
`read-model.readUnion` are then dead code — the broker only delivers documents
|
|
whose cap the wallet holds. Remove them.
|
|
- The write guard (`surface/ng-proxy.ts` `sparql_update` override) is a separate axis and
|
|
is decorative today (every internal writer bypasses the proxy); it belongs to the
|
|
P1b batch, not here.
|
|
|
|
The access unit is already the document (`@graph`), matching the native per-repo cap
|
|
model, so this is a key-material step, not a reshape.
|
|
|
|
### 2. Place documents in real native stores
|
|
Today `docCreate(..., undefined)` writes every document into the shared wallet's
|
|
private store, and the `public|protected|private` scope is a logical label
|
|
in the shim (see the two-axes section in [`simulation.md`](./simulation.md)).
|
|
|
|
- `doc_create` cannot target a non-private native store today — verified:
|
|
`StoreRepo` is not constructible from the WEB build of the SDK, so there is no way to pass
|
|
a public/protected store as the create destination (`docCreate`'s trailing
|
|
`store` arg is left `undefined` → private store). The private store works only
|
|
because it opens without `RepoNotFound`.
|
|
- When the SDK lets you construct/target a native store, the migration adds a
|
|
`getNativeStore(scope)`-style resolver returning the real store to pass as the
|
|
`docCreate` destination, so the logical scope label becomes a real store
|
|
placement. (No such helper exists yet — it is blocked on the SDK gap above.)
|
|
- At that point `shared-wallet/account-registry.ts` maps `(account, scope)` to the user's real
|
|
store NURI instead of a document in the shared wallet; the per-scope index
|
|
document (the store-container emulation) is replaced by the store itself. The
|
|
surface facing the consumer application (`createEntityDoc`, `listMyEntityDocs`,
|
|
resolvers) is designed to survive that swap unchanged.
|
|
|
|
### 3. Drop the resolver / shim
|
|
The `sharedWalletShim` (account → 3 scope-document NURIs, held in a subscribable
|
|
doc-shim reached via a write-once pointer in the store-root — see
|
|
[`nextgraph-current-state.md`](./nextgraph-current-state.md) § *The pointer → doc-shim
|
|
indirection*) has no target equivalent — the target has no central directory. Remove
|
|
it entirely: `shared-wallet/account-registry.ts`, `configureStoreRegistry`, the pointer + doc-shim
|
|
resolution, and the `pointerGuard` dep. Cross-wallet reads replace the fan-out;
|
|
per-user wallets replace the shared one.
|
|
|
|
### 4. Real inbox → drop the in-lib read emulation
|
|
Replace the emulated `inbox.ts` deposit (`docs.sparqlUpdate` into a shared-wallet
|
|
document) with the native sealed deposit, once one is exposed to JS. **Its name and shape are NOT known**: no inbox method exists in `@ng-org/web`, the verifier has no `InboxPost` arm, and `inbox_post_link` is OUR proposed name (`fork-inbox-fallback.md`), not an announced API. On the read side the
|
|
recipient's own verifier unseals each queued sealed message and applies it inline
|
|
when it processes its inbox — there is no separate curator to build; the in-lib read
|
|
emulation simply goes away. *(There is no global index to replace the cross-account fan-out: both were removed on 2026-07-30 — you cannot discover in NextGraph, you follow links.)*
|
|
|
|
### 5. Retire the identity store → real per-user login
|
|
Remove `shared-wallet/virtualUsers.ts` (the `IdentityStore` that persists the identity id in
|
|
`localStorage`) and the app-level "Connexion" screen. The technical broker gate
|
|
becomes the real per-user login
|
|
(see [`decisions/shared-wallet-login-flow.md`](./decisions/shared-wallet-login-flow.md)).
|
|
The flow shape ("broker redirect → app") does not change.
|
|
|
|
### 6. ~~Drop the isolation scaffold~~ — already gone
|
|
`isolation.ts` (the old application-visibility filter) was deleted from the library;
|
|
nothing remains to remove at migration. Kept as a numbered step so the following
|
|
numbers stay stable across references.
|
|
|
|
### 7. Remove the build alias — the client becomes the real SDK
|
|
The consumer application imports `@ng-org/web` / `@ng-org/orm` resolved to this lib
|
|
via a build alias during the polyfill period. Removing the alias makes those imports
|
|
resolve to the real SDK — the `ng`/`useShape`/`inbox` surface is SDK-identical, so
|
|
no consumer code changes. The one non-SDK call — `configure(...)` /
|
|
the POLYFILL-ERA block of `@ng-eventually/sdk` — is deleted. The lib itself disappears.
|
|
|
|
## The one break already taken: `declareConnections`
|
|
|
|
P1a broke the consumer once, deliberately and early, so that migration would not.
|
|
The old surface was an ACL held in memory, which forced the consumer to re-declare
|
|
every grant on every session (`declareConnections`). That call **disappears**: with
|
|
delivered caps the grant moves to the moment a connection is *accepted* — one
|
|
`inbox.share(doc, toUser)` per document shared — and it persists, because
|
|
the delivery lives in the recipient's inbox rather than in a map that empties at
|
|
reload. There is no analogue of `protectedDocsOf` + the re-derivation loop.
|
|
|
|
This is a consumer **re-architecture**, not an API swap, and it is the price of
|
|
being coded against a model that will exist. Nothing else about the migration below
|
|
touches consumer code.
|
|
|
|
## What does not change
|
|
|
|
The consumer application's code. Shapes, screens, the *acts* of sharing
|
|
access, entity→scope mapping, the relationship graph — all injected, all untouched.
|
|
Migration is entirely inside this library plus removing the alias + the bootstrap
|
|
call. That asymmetry — a mature SDK face outward, all compensation inward — is the
|
|
library's reason to exist.
|