Skip to content

Mobile first run: serve the dashboard without the chain, show real progress - #469

Merged
MudDev merged 3 commits into
mainfrom
feat/firstrun-flow
Sep 4, 2026
Merged

Mobile first run: serve the dashboard without the chain, show real progress#469
MudDev merged 3 commits into
mainfrom
feat/firstrun-flow

Conversation

@MudDev

@MudDev MudDev commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Problem

On a phone the first run was bad, and an existing install could stop loading at all:

  • Fresh install: the node deferred dashboard.epix until the xID light client had a trust pin, and the UI showed a 404 "Looking up this name" page (older builds: the raw text "could not resolve dashboard.epix"). The bootstrap probed 4 RPCs one after another with 20 s timeouts, so this was minutes over Tor and never ended with fewer than 2 RPCs reachable.
  • Existing install with the chain unreachable: every light-client advance invalidated the cached name binding, so the next boot had to ask the chain, and with a pin installed a failed lookup failed boot ("EpixNet could not start").
  • The dashboard was never pre-granted ADMIN, so a fresh install got the "This xite requests permission" prompt over an empty list and the refused feedQuery crashed the feed.
  • Loading-screen dead ends: every failure said "No peers found", the Tor wait before a clone was invisible, no retry, jargon on WebSocket loss.

Changes

Node (epix-node, epix-ui, epix-chain)

  • Boot never touches the chain. A cached name serves at once; a name whose binding was superseded serves the saved copy as verified:false and is re-verified in the background; dashboard.epix with nothing on disk resolves to the compiled-in dashboard address (what epix-server already launches by) and downloads from peers with no RPC at all. Legacy/unbound cache entries stay misses in finality mode.
  • On-demand resolution waits for trust and the network instead of failing, publishes a per-host resolveStatus (state + reason), retries with backoff, and is woken by the new networkRetry command and the shells' network_changed().
  • A "not found" from one RPC is never a verdict (no proof behind it): retried slowly, never persisted, never switches a name. Only a proven answer moves the homepage name, and the old xite loses the alias.
  • ADMIN is granted automatically only to the compiled-in dashboard address, never to whatever a name resolves to.
  • Light client: parallel bootstrap probes with 10 s timeouts, a status cell exposed in serverInfo.xid_trust (phase, sources reachable, last error), TrustNotEstablished before any HTTP, and the RPC list is re-read every cycle so a Config-page change takes effect without a restart (phones cannot restart).
  • Clone failures carry a reason (no_peers, tor_unavailable, offline, files_unavailable, content_unverified); the Tor wait before a clone pushes waiting_tor / tor_skipped events.
  • serverInfo.launch { display, address, verified, deferred }.
  • certXid's on-chain identity discovery is bounded to 8 s, so a xite's account picker never hangs on "Checking..." while the registry is unreachable.
  • The waiting_tor event is re-pushed every few seconds during the Tor wait before a clone (the single push went out before the page's socket was open).

Wrapper (ui/media/all.js, all.css, wrapper.html)

  • Resolving mode replaces the 404 page: the same loading screen shows "Connecting to the network / Checking the Epix name registry / Looking up X" with plain-language reasons, a Retry button and a Connection settings button, and reloads itself when the name lands.
  • Honest failure texts by reason, a "Connecting to Tor" stage, a 30 s no-peers watchdog, "Reconnecting to EpixNet..." instead of the UiServer wording, the home button hidden on the dashboard's own loading screen, "Connection settings" corner link.
  • A "Downloading content... N files from K peers" pill for any xite whose posts live in per-user files and stream in after the page opened (forums, blogs, mail): shows from the first user-content file event, hides 12 s after the last one. Generic, so third-party xites get it without changes.
  • The resolving screen offers a Connection settings button (to the Config page) whenever changing a setting could be the way out: no name servers reachable, repeated name-server failures, offline mode, Always-Tor without Tor.

Shells

  • Android: error page shows the node's own error with "Try again" (boots again in place) and "Reset connection settings and try again" (sets private/config.json aside, kept as config.json.broken-<time>); no dead 15 s port wait after a failed boot; a "No internet connection" line; the splash covers the page area in the page's colour so the hand-off does not flash; a bare word in the address bar opens <word>.epix; Tor "failed" has its own badge colour; the node is told when the network comes back.
  • iOS: the same error/retry/reset contract, a splash guard, a page for provisional load failures, an NWPathMonitor hook. Not built here.

Verified on the Small_Phone emulator (debug APK)

  • Fresh install: launch to dashboard in about 5 s, no prompt, no feed error (was about 28 s with the 404 page and the prompt).
  • Fresh install with every chain RPC dead (dead endpoints in config): dashboard in about 2 s by address while xid_trust.state stays establishing.
  • Airplane mode with the resolve cache deleted: the dashboard opens from the saved copy at once; 5 s after the network returns the name is re-verified and the cache rebound.
  • Fresh install offline: the resolving screen reports 0/4 name servers reachable; 1.5 s after the network returns it resolves and loads by itself.
  • talk.epix from the address bar: resolved in about 1 s, Epix Talk up in about 4 s. With dead RPCs it shows "Can't reach the Epix name servers ... change the servers in Connection settings" with Retry and Connection settings; changing the servers on the Config page and tapping Retry resolves it within seconds.
  • Corrupt config: error page with the cause; "Try again" recovers once the file is fixed; "Reset connection settings" sets it aside and boots with defaults.
  • Opening the published Epix Talk on a fresh install: the loading screen, then the page with the wrapper's "Downloading content... N files" pill counting up while the topics fill in.
  • Tests: cargo test -p epix-chain 86/86, -p epix-node 38/38, -p epix-ui 359/360 (the failure is the timing-flaky EDX commit test, passes alone).

Scope note

Everything is in the node, the wrapper and the shells; no xite needs a change. Earlier companion PRs against EpixDash-Xite and EpixTalk-Xite were closed for that reason.

Follow-ups

  • The iOS shell changes need an Xcode build.
  • The pre-existing same-origin WebSocket binding (any xite's script can open a socket bound to the dashboard address) was flagged in review; it is unchanged by this PR since the dashboard was already granted ADMIN by every user, but worth a per-render token later.

…ogress

Fresh installs waited on the xID name registry before the dashboard could
download, and showed a bare "could not resolve dashboard.epix" page until
trust bootstrapped (minutes over Tor, forever with fewer than two RPCs
reachable). A returning phone whose cached name binding had been superseded
by a light-client advance had to ask the chain at boot, and with a pin
installed a failed lookup failed boot outright ("EpixNet could not start").

Node
- Boot never touches the chain. A cached name serves at once; a superseded
  binding serves the saved copy as unverified and is re-verified in the
  background; dashboard.epix with nothing on disk resolves to the compiled-in
  dashboard address (what epix-server already launches by) and downloads from
  peers with no RPC at all. Legacy/unbound cache entries stay misses in
  finality mode.
- On-demand resolution waits for trust and the network instead of failing,
  publishes a per-host resolveStatus (state and reason), retries with backoff
  and is woken by networkRetry / the shells' network_changed().
- A "not found" from one RPC is never a verdict: retried slowly, never
  persisted, never switches a name. Only a proven answer moves the homepage
  name; the old xite loses the alias.
- ADMIN is granted automatically only to the compiled-in dashboard address.
- Light client: parallel bootstrap probes with 10s timeouts, a status cell
  (phase, sources reachable, last error), TrustNotEstablished before any HTTP,
  RPC list re-read every cycle so a Config change works without a restart.
- Clone failures carry a reason (no_peers, tor_unavailable, offline, ...)
  and the Tor wait before a clone pushes waiting_tor events.

Wrapper
- A resolving mode replaces the 404 page: the same loading screen shows
  "Connecting to the network / Checking the Epix name registry / Looking up
  X" with plain-language reasons, Retry and Connection settings buttons, and
  reloads itself when the name lands. Honest failure texts by reason, a Tor
  stage, a 30s watchdog, clearer WebSocket-loss wording.
- The dashboard's home button is hidden on its own loading screen; the
  dashboard's ADMIN prompt reads "The dashboard needs permission to manage
  this EpixNet node" (not reached on a fresh install any more).

Shells
- Android: the error page shows the node's own error with "Try again" and
  "Reset connection settings and try again" (sets private/config.json aside),
  no dead 15s port wait after a failed boot, a "No internet connection" line,
  the splash covers the page area in the page's colour, a bare word in the
  address bar opens <word>.epix, Tor "failed" gets its own badge colour, the
  node is told when the network comes back.
- iOS: same error/retry/reset contract, splash guard, provisional-failure
  page, NWPathMonitor hook (not built here).
The first-run fixes must work for every xite, including ones we do not
maintain, so the pieces that had been prototyped inside EpixDash and
EpixTalk are replaced by generic ones here:

- Wrapper: a "Downloading content... N files from K peers" pill for any xite
  whose posts live in per-user files and stream in after the page opened
  (forums, blogs, mail). Shows from the first user-content file event, hides
  12s after the last one. An empty first screen no longer reads as broken.
- Node: certXid's on-chain identity discovery is bounded to 8s, so a xite's
  account picker never hangs on "Checking..." while the name registry is
  unreachable; it opens with the local choices instead.
- Node: the waiting_tor event is re-pushed every few seconds during the Tor
  wait before a clone. The single push went out before the page's socket
  had opened, so the loading screen sat on "Searching for peers" through a
  50s Tor bootstrap without saying so.
…r steps

SonarCloud flagged await_tor_ready, resolve_with_status and the
spawn_xid_lightclient loop for cognitive complexity. Each is now a short
driver over named helpers:

- await_tor_ready: poll_tor_bootstrap does the polling and returns
  Up / Failed / TimedOut; tell_tor pushes the waiting_tor and tor_skipped
  events.
- resolve_with_status: saved_mapping_for_registered (serve a registered
  xite from its saved mapping), await_tor_for_resolve (the Tor-Always
  gate) and resolve_until_budget (the retry loop).
- spawn_xid_lightclient: rpc_candidates_for, record_light_client_outcome,
  on_trust_anchored and light_client_delay.

No behaviour change. cargo test -p epix-node passes (38 tests).
@sonarqubecloud

sonarqubecloud Bot commented Sep 4, 2026

Copy link
Copy Markdown

@MudDev
MudDev merged commit f6bc73d into main Sep 4, 2026
12 checks passed
@MudDev
MudDev deleted the feat/firstrun-flow branch September 4, 2026 22:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant