0832338201
Le modèle amont est explicite dans `PublicRepoLinkV0` : le lien ne porte AUCUN `read_cap`, et son commentaire dit pourquoi — *"The latest ReadCap of the branch will be downloaded from the outerOverlay, if the peer brokers listed below allow it […] the public site are served differently by brokers"* (engine/net/src/types.rs:5098). La clé n'est pas remise par un émetteur : elle est donnée par le réseau à qui la demande, parce que le broker a épinglé l'overlay externe (`expose_outer`). La bibliothèque refusait jusqu'ici la forme sans cap quel que soit le store. Sûr dans le bon sens, mais une application ne pouvait pas exprimer « fais circuler, la référence suffit » — le seul acte que le modèle rend gratuit — et son unique contournement était de distribuer la clé, ce qui détruit la confidentialité composable. `emulated-verifier/public-store.ts` émule le mécanisme SANS toucher à la garde. La possession reste l'unique critère : un document public est lisible non par exception mais parce que son cap est *obtenable*. Chaque porte de lecture demande d'abord (`readUnion`, `docs.sparqlQuery`, `ensureRepoOpen`, `documentInboxAddress`), puis le chemin ordinaire s'applique. Lire n'est pas écrire. Ce que le store sert est un droit de LECTURE : `learnFromPublicStore` le classe à part et `assertMayWrite` refuse l'écriture dessus. Sans cela une référence nue achetait une écriture, ce qu'aucun store amont n'accorde. Autres conséquences : - `recordInPublicStore` (marquer + frapper) devient `markInPublicStore` (marquer). Frapper un second cap à côté de celui qu'on vient de télécharger donnerait deux clés différentes le jour où la constante devient un secret. - `hasCap` quitte la porte polyfill : il se lisait « ai-je le droit de lire ceci ? » et un document public y répondait `false` jusqu'à ce qu'on demande son cap. Aucun appelant hors des tests. - Les tests cross-user ne font plus traverser de cap par une variable JS : Bob n'obtient que la référence nue, comme une vraie application. Écarts documentés plutôt que masqués : le pari sur un modèle DÉCLARÉ (`expose_outer` est câblé à `false` côté client et `ExtTopicSyncReq` est `unimplemented!()`), la découverte limitée à ce qu'on sait déjà nommer, `useShape` qui n'a pas d'await à dépenser, et l'absence de `locator`. 179 tests unitaires, e2e 42/42 contre le broker en ligne.
56 lines
3.2 KiB
TypeScript
56 lines
3.2 KiB
TypeScript
/**
|
|
* The app-facing slice of `store-registry` — and the reason it exists as a file.
|
|
*
|
|
* `store-registry.ts` holds two things that must not be exported together: the
|
|
* placement/addressing calls a consumer application legitimately makes, and the
|
|
* shim machinery that makes virtual users work at all (account resolution, the
|
|
* durable cap registers, the inbox-ownership predicate, cache resets). Until now
|
|
* `index.ts` did `export * as storeRegistry from "../shared-wallet/account-registry"` and shipped
|
|
* both, so an application could reach `ensureAccount`, `addLink` or
|
|
* `resetRegistryCache` from the SDK-identical entry — machinery it must never call,
|
|
* on the entry whose whole promise is "this survives migration unchanged".
|
|
*
|
|
* What is re-exported here is only what an application needs to do its own work,
|
|
* and each has a target-SDK counterpart (see `docs/api-contract.md`). Everything
|
|
* else stays reachable at `./store-registry` for the library's own modules, the
|
|
* unit tests and the e2e harness — an internal path, not a published one.
|
|
*
|
|
* At migration this file disappears: placement becomes the user's real per-scope
|
|
* stores and the calls below become native SDK ones.
|
|
*
|
|
* **No inbox ADDRESS is published here**, deliberately (`userInbox`,
|
|
* `documentInboxAddress`, removed 2026-08-05). An application deposits with
|
|
* `inbox.postToDocument(doc, …)`, shares with `inbox.share(doc, toUser)` and reads
|
|
* its own with `inbox.readForDocument(doc)` — always naming a document or a person,
|
|
* never an address. Upstream an address is resolved from a profile and never handled by
|
|
* a caller, so exposing one taught a step that has to be unlearned. The example
|
|
* application is the check: it must never name an inbox.
|
|
*/
|
|
|
|
export {
|
|
/** Create a document for ONE entity in `scope`, and record it in that scope's store. */
|
|
createEntityDoc,
|
|
/** The entity documents this user owns in `scope` — with their caps recovered. */
|
|
listMyEntityDocs,
|
|
/** The NURI to use as a READ scope for `scope` (what `useShape` is pointed at). */
|
|
resolveScopeGraph,
|
|
/** The NURI where GROUPED entities of `scope` are written (no per-entity document). */
|
|
resolveWriteGraph,
|
|
/** Open an inbox on a document you OWN, so others can deposit into it. */
|
|
/** WHERE to deposit for a document — readable by any holder of it. `undefined` if none. */
|
|
} from "../shared-wallet/account-registry";
|
|
|
|
export { openDocumentInbox } from "../emulated-verifier/branch-registers";
|
|
|
|
// No `linkTo` here, and its absence is deliberate (it existed 2026-08-06, one day).
|
|
//
|
|
// It returned a document's KEY where a caller would ask for its reference, which turns
|
|
// the access rule from "whoever has the reference AND the key reads" into "whoever has
|
|
// the reference reads" — see `docs/readcap-and-nuri-model.md` § 0. That is not a leak of
|
|
// hygiene, it is the rule changing: a document one circulates would grant everything it
|
|
// MENTIONS, and confidentiality could no longer be composed inside a shared document.
|
|
//
|
|
// An application names a document with the reference it already has (every call here
|
|
// returns bare ones), and grants access with `inbox.share(doc, toUser)`. What travels
|
|
// with a key in it is a deliberate act, not the result of asking for a link.
|