107f9d1633
La correction de nomenclature du 2026-07-30 — en amont un *wallet* n'est qu'un trousseau, ce qui possède des stores est un **user** (un *site*) — s'était faite à la main. `walletInbox` y a échappé et a vécu des semaines, en faisant des dégâts : le nom rendait « une inbox par wallet » évident, masquant qu'un user en a **deux** en amont (repos de store public et protected, les deux seuls `AddInboxCap` du moteur). Une discipline appliquée à la main en oublie un ; un test non. D'où `test/vocabulary.test.ts` : tout nom publié est bâti sur des mots que la CIBLE emploie — vérifiés dans `nextgraph-rs` — ou porte un marqueur disant POURQUOI il n'existe qu'ici (`virtual`, `physical`, `shim`, `emulated`, `polyfill`), ce qui dit aussi quand il disparaît. Un échec n'est pas « renommer pour faire passer le test », c'est une question : la cible a-t-elle un mot pour ça ? la chose n'existe-t-elle qu'ici ? le mot est-il vraiment de la glue ? Ce que le test a trouvé, et les réponses : - `walletInbox` → `userInbox`, avec l'écart de cardinalité écrit noir sur blanc plutôt que caché par le nom. - `accounts` / `AccountRecord` / `AccountStorage` → `virtualUsers` / `VirtualUserRecord` / `VirtualUserStorage`, module `accounts.ts` → `virtual-users.ts`. « account » n'est pas de la cible : c'est notre mot pour l'utilisateur virtuel, et le marqueur le dit désormais. - `readModel` → la fonction `readUnion`, exposée directement. « model » n'était ni de la cible ni de la glue, et le namespace ne tenait qu'une fonction. - Le reste était du vocabulaire légitime à déclarer (`subject`, `base`, `schema`, `connected`, le modèle réactif de l'ORM). Corrigé au passage, sur signalement du contrat interne : l'en-tête d'`open-repo` justifiait son correctif par un mécanisme que le source contredit. Un repo absent de `self.repos` lève bien `RepoNotFound` (`engine/verifier/src/request_processor.rs:264,269`). Les 0 lignes observées viennent d'ailleurs — `Verifier::load` repeuple `self.repos` depuis le stockage sur un profil persistant (`verifier.rs:535-560`), et notre propre `readDoc` attrape toute erreur et rend `[]`. Le correctif est bon, le diagnostic écrit à côté ne l'était pas. 159 tests unitaires, typecheck src/test/e2e vert, e2e 40/40 contre le broker.
71 lines
4.0 KiB
Markdown
71 lines
4.0 KiB
Markdown
# ADR — Shared-wallet identity flow (perceived login)
|
|
|
|
**Date:** 2026-06-15 · **Status:** Accepted (frozen). The rationale behind how
|
|
the consumer application presents identity selection as a perceived login, and why
|
|
the lib's identity store (`shared-wallet/virtualUsers.ts`) must never touch NextGraph. The lib itself
|
|
no longer frames this as a login: it receives an identity id, set at wallet-import
|
|
time; the perceived-login UX lives entirely in the consumer application.
|
|
|
|
## Starting constraint
|
|
|
|
NextGraph login is not programmable: it is a web redirect to the broker
|
|
page (`nextgraph.net`). The shared wallet cannot be opened silently — at least one
|
|
broker-redirect pass is required per device. The question is therefore not "how to
|
|
avoid the redirect" but "how to order and present it" so the UX stays coherent.
|
|
|
|
## Decision — technical gate first, application "Connexion" second
|
|
|
|
Two distinct layers, presented in this order:
|
|
|
|
1. **Real layer (technical, not perceived as login).** The broker redirect appears
|
|
immediately, before any app render. Because it precedes the app, the user
|
|
reads it as a technical access barrier to the test environment (a beta
|
|
wall), not an application login. Same shared credentials for everyone
|
|
(given in the invitation, "access code" style). Once per device, then
|
|
persistent. It is not labelled "login."
|
|
2. **Application layer (perceived as the login).** A "Connexion" screen where the
|
|
user picks an identity id (relayed to the lib's identity store, persisted in
|
|
`localStorage`, the current principal). This is the login *in the user's
|
|
perception*, presented by the consumer application. No password — declarative
|
|
selection (anyone takes any id — coherent with zero-security / friends). In
|
|
practice the id is often a human-friendly handle. "Déconnexion" clears only
|
|
the stored id and returns to "Connexion"; it calls no NG function.
|
|
|
|
The real logout (`ng.session_stop` / `user_disconnect` / `wallet_close`) stays
|
|
hidden (settings/debug), because it forces a new redirect.
|
|
|
|
## Why (vs the rejected option)
|
|
|
|
**Rejected** — a perceived login first, then a warning page "enter this
|
|
id/password", then a *Continue* button triggering the redirect. Rejected: strange
|
|
workflow, dissonant double-login, a warning page that looks like a scam, and the
|
|
redirect resurfacing mid-use on every session expiry.
|
|
|
|
**Chosen** because: the mental model stays coherent (the technical barrier not
|
|
being perceived as login, the app-level Connexion/Déconnexion pair is complete and
|
|
self-consistent); graceful degradation (a re-gate after a browser restart reads as
|
|
"reconnecting to the environment", not a bug); and similarity to the target
|
|
infra — the "broker redirect → app" shape is exactly the real multi-wallet flow.
|
|
At migration you remove the id "Connexion" screen and the technical
|
|
barrier becomes the real per-user login — the flow shape does not change.
|
|
|
|
## Verified technical facts (`nextgraph-rs`, 2026-06-15)
|
|
|
|
- **Session persistence: yes.** Wallet remembered iframe-side (`localStorage`
|
|
long-term + `sessionStorage` for the active session); on reload `init()`
|
|
recovers the session without re-triggering the redirect while the broker
|
|
session exists (`sdk/js/web/src/index.ts`, `sdk/js/api-web/main.ts`). A full
|
|
browser restart (losing `sessionStorage`) can re-trigger the gate.
|
|
- **Real logout exposed: yes.** `ng.session_stop()`, `ng.user_disconnect()`,
|
|
`ng.wallet_close()` (`sdk/js/lib-wasm/src/lib.rs`); they stop the session /
|
|
clear the wallet and force a new redirect afterwards — hence the app-level
|
|
"Déconnexion" does not call them, and the real logout stays hidden.
|
|
|
|
## How this lib realizes it
|
|
|
|
`shared-wallet/virtualUsers.ts` is an `IdentityStore`: `set(id)` / `clear()` / `get()` only read/write
|
|
the identity id in an injected `VirtualUserStorage`; they never call NG. The id is set at
|
|
wallet-import time and relayed via the lib's current-identity call; the perceived
|
|
login is the consumer application's. See the identity store in
|
|
[`../simulation.md`](../simulation.md).
|