fix(caps): créer un document en donne le cap, + corriger 9 faits NextGraph
Le trou trouvé par l'e2e contre le broker en ligne : `docs.docCreate` ne
déposait aucun cap pour le créateur, donc un consommateur pouvait créer un
document par la primitive publique puis se voir refuser sa lecture et son
écriture. En amont c'est impossible — `doc_create` commite
`AddRepo { read_cap }` sur la branche Store du store, et le créateur le détient
dès le premier instant. Délibérément non répliqué dans `physical.ts` : les
documents du shim n'appartiennent à aucun utilisateur virtuel, et
`store-registry` classe leurs caps là où il sait à qui ils sont.
e2e : 22 passés / 8 échoués → 39 / 0. Les autres échecs venaient du harnais,
qui agissait comme une seconde identité sans l'établir, ou lisait un document
quelconque comme une inbox. Un run e2e contre un wallet persistant exige une
identité FRAÎCHE par run : `walletInbox(id)` rend l'inbox stable pour son
propriétaire — c'est son intérêt — donc un id fixe accumule les dépôts des runs
précédents (vert au 2e run, rouge au 3e, à code inchangé).
Revue adverse de la documentation, 9 défauts, tous vérifiés à la source avant
correction :
- « chaque document a une inbox native » est FAUX. Seuls les repos de store
public et protected en ont une (`site.rs:128,149`) ; `new_store_default` n'en
pose que `if !private` et `doc_create` laisse `inbox: None`. Le store privé
n'en a pas non plus. Ce que le code fait est donc une ANTICIPATION — assumée
et notée comme telle dans `documentInbox`, le brief et l'ADR discovery. Ce qui
est vérifié, c'est la FORME : `AddInboxCapV0` est clé par `repo_id`.
- `InboxMsgContent::Link` est une variante unit sans charge utile : l'inbox ne
transporte aucun ReadCap. `shareCap` était juste et le reste ; ses citations
sont complétées aux deux bouts (émetteur `unimplemented!()`, récepteur qui
ignore `details.read_cap`).
- les 3 stores appartiennent au user (`SiteV0`), pas au wallet ;
- le TODO `OpenRepo` ne concerne pas la lecture cross-wallet — il est dans
`open_branch_`, après `RepoNotFound` ; charger par cap, c'est
`load_repo_from_read_cap` ;
- la liste des méthodes JS était un sous-ensemble présenté comme la surface
(77 exportées) ;
- `outbox-log.ts` n'enregistre rien : il inspecte l'outbox du SDK ;
- l'ADR private-store-nuri-scope citait `orm_start_graph` au présent, remplacé
par `ensureRepoOpen` ;
- l'incident write-loss plaçait `disconnections_sender.send` dans `broker.rs` ;
- la section « Apps & services » n'a aucune citation et rien ne lui correspond
dans le moteur : marquée à re-confirmer, pas à citer comme vérifiée.
Aussi : `fileOwnCaps` n'existe plus (`holdOwnCap` / `readStoreCaps` /
`fileOwnStructure`) — pointeur mort corrigé dans `caps.ts`.
This commit is contained in:
@@ -4,7 +4,9 @@
|
||||
* Stopgap / polyfill-era. Emulates the target infrastructure — where each
|
||||
* user owns their own public/protected/private stores — on top of one shared
|
||||
* wallet. It creates one document per (account × scope) inside that shared
|
||||
* wallet (via the `docs.docCreate` primitive), so the `scope`
|
||||
* wallet (via `physical.physicalCreate` — the UNGUARDED primitive, since a store is
|
||||
* machinery and its cap is filed here, where it is known whose it is; the public
|
||||
* `docs.docCreate` files the creator's cap itself), so the `scope`
|
||||
* (`public|protected|private`) is a logical attribute tracked here, not a
|
||||
* physical NextGraph store. Isolation is enforced by the app layer + the
|
||||
* emulated cap registry, not by crypto.
|
||||
@@ -1015,12 +1017,21 @@ export async function userStoreDoc(id: string, scope: Scope): Promise<Nuri> {
|
||||
* The inbox of a document this user owns — resolved, and created on first ask.
|
||||
*
|
||||
* Upstream a repo carries `inbox: Option<PrivKey>` (`engine/repo/src/repo.rs:126`):
|
||||
* an inbox is a keypair on the document, whose PRIVATE half its owner holds. That
|
||||
* half is recorded with `AddInboxCap { repo_id, overlay, priv_key }` — *"into the
|
||||
* user branch, so that a user can share with all its device"*
|
||||
* (`engine/repo/src/types.rs:1969-1981`), the same branch that carries `AddLink`.
|
||||
* So "which inboxes may I read" is answered by the User branch, and that is what
|
||||
* this emulates.
|
||||
* an inbox is a keypair on the repo, whose PRIVATE half its owner holds. That half is
|
||||
* recorded with `AddInboxCap { repo_id, overlay, priv_key }` — *"into the user branch,
|
||||
* so that a user can share with all its device"* (`engine/repo/src/types.rs:1973`), the
|
||||
* same branch that carries `AddLink`. So "which inboxes may I read" is answered by the
|
||||
* User branch, and that is what this emulates.
|
||||
*
|
||||
* **This ANTICIPATES: no document has an inbox upstream today.** `new_store_default`
|
||||
* attaches one only `if !private` (`engine/verifier/src/verifier.rs:2994`), and
|
||||
* `doc_create` goes through `new_repo_default`, which leaves `inbox: None`
|
||||
* (`engine/repo/src/repo.rs:574`) — the only two `AddInboxCap` commits in the engine
|
||||
* are for the public and protected STORE repos (`engine/verifier/src/site.rs:128,149`).
|
||||
* What is verified is the SHAPE: the record is keyed by `repo_id`, so it accommodates
|
||||
* an inbox on any repo. What is not verified is that anything upstream will create one
|
||||
* per document. At migration this either becomes native or stays emulated here; either
|
||||
* way the consumer-facing act is unchanged.
|
||||
*
|
||||
* Lazy on purpose: creating an inbox document for every entity up front would
|
||||
* double every `createEntityDoc` for inboxes most documents never receive anything
|
||||
|
||||
Reference in New Issue
Block a user