docs+refactor(client): fidelity pass — id identity, drop connections, no faux-login, accurate NextGraph framing
Align the polyfill's surface and docs with the verified NextGraph reality and
remove application-level concepts:
- Identity is an ID, not a username: AccountRecord.id, shim predicate shim:id,
normalizeId; accounts core becomes IdentityStore (set/clear/get) — the faux
login/logout framing is gone (identity is set at wallet-import time).
- Relationship/connection is an application concept, not a platform primitive
(NextGraph has no bilateral-connection primitive: grantee is unpersisted
scaffolding, cap-send is unimplemented). Remove connections.ts; caps exposes
only a directed grantRead(doc, granteeId) + a read-only protectedDocsOf(owner).
Delete the now-dead isolation.ts social-visibility axis.
- Inbox docs: NextGraph has no separate curator — the recipient's own verifier
unseals and applies each queued sealed message inline (process_inbox);
inbox_post_link is a proposed/future API. Stop attributing the emulated
curator to the platform.
- Read isolation reframed around the outcome: no cap -> empty union read;
targeted read of an unheld repo -> RepoNotFound; cap introspection
(canRead/governsRead) is emulation-only with no NextGraph API behind it.
- read-model.md corrected: the listing path is per-doc ANCHORED default-graph
queries, never the anchorless GRAPH ?g union (that is O(wallet)); the probe
section no longer claims the opposite.
- README recap table restructured (target | current NextGraph status | current
emulation); INDEX_ACCOUNT documented as reservedAccount("index") in the
sentinel namespace; de-domained generic-layer comments; softened tone.
Consumer application (Festipod) rewired separately to own the relationship
concept and feed the lib an id. Lib gates: bun test 83 pass / 0 fail, tsc clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,64 +1,70 @@
|
||||
# ADR — Shared-wallet login/logout flow
|
||||
# ADR — Shared-wallet identity flow (perceived login)
|
||||
|
||||
**Date:** 2026-06-15 · **Status:** Accepted (frozen). The rationale behind this
|
||||
lib's **faux login** (`accounts.ts`) and why it must never touch NextGraph.
|
||||
**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 (`accounts.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
|
||||
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 auth layers, presented in this order:
|
||||
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
|
||||
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. **Never labelled "login."**
|
||||
2. **Application layer (perceived as THE login).** A **"Connexion"** screen =
|
||||
**username only** (→ `localStorage`, the current principal). This is the login
|
||||
*in the user's perception*. **No password** → declarative connection (anyone
|
||||
takes any username — coherent with zero-security / friends). **"Déconnexion"**
|
||||
clears **only** the username and returns to "Connexion"; it **calls no NG
|
||||
function**.
|
||||
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.
|
||||
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** — faux login first, then a warning page "enter this username/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.
|
||||
**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 username "Connexion" screen** and the **technical
|
||||
barrier becomes the real per-user login** — the flow shape does not change.
|
||||
"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`
|
||||
- **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
|
||||
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()`,
|
||||
- **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: do NOT call
|
||||
them in the app-level "Déconnexion," and hide the real logout.
|
||||
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
|
||||
|
||||
`accounts.ts` `AccountStore.login()`/`logout()` only read/write the username in an
|
||||
injected `AccountStorage`; they **never** call NG. See the faux login in
|
||||
`accounts.ts` is an `IdentityStore`: `set(id)` / `clear()` / `get()` only read/write
|
||||
the identity id in an injected `AccountStorage`; 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).
|
||||
|
||||
Reference in New Issue
Block a user