--- type: knowledge summary: The sign-in flow end to end, and which layer owns each step — in particular that the broker redirect belongs to @ng-org/web, not to this package --- # How a user gets in ## The steps, and who owns them A newcomer holds nothing. To act they need a wallet imported once into the wallet application, and an identifier naming their virtual space. The `barrier` hands out the first three — the wallet file, its password, a link to the wallet application — and takes the fourth. Then the page is handed to the broker, which opens the wallet and reloads the application **inside an iframe it hosts**. Outside that iframe there is no session. **The redirect is not ours.** `@ng-org/web`'s `init()` performs it as its first statement — same host, same URL shape, same frame check. Verified in its shipped bundle: when `window.self === window.top` it assigns `window.location.href` from a hard-coded `redirect` constant. This package must not reimplement it; doing so would double a function of the layer below and diverge from it in the meantime (see `app-contract/rule_no-divergence-from-nextgraph`). What is genuinely ours is the one thing `init()` cannot do: **put the identifier into the URL before `init()` reads it**. `init()` hands over `window.location.href`; it does not know the parameter exists. ## Why the identifier travels by URL The flow runs in **two contexts with separate storage partitions** — the top-level page and the broker iframe — because browsers partition storage by top-level site. A value written top-level is not the value the iframe reads. What crosses is the URL: the redirect embeds the full application URL, query included, and reloads it in the iframe. Hence the resolution order, which must not be "simplified": 1. the identifier in the URL — it wins whenever present, being the only thing that crosses; 2. otherwise storage — same-partition convenience, and prefill on reload. The parameter must therefore be written on **every** path that settles an identity, not only when the `barrier` asked. An identity coming from storage without the parameter being written is the silent failure: the iframe reads an empty identity, provisions a second virtual space, and the returning user lands somewhere empty with no error. Writing to storage and writing to the address bar are two independent attempts, deliberately: a storage that refuses writes must not cost the round-trip its parameter. ## What survives migration The `barrier` evaporates whole — handing out a shared wallet and naming a space exist only because the wallet is shared. The redirect does **not**: upstream's real multi-wallet flow has the same shape, and the barrier becomes the real per-user sign-in. The call site survives too, which is why the published call takes no identifier.