"Checking what this server uses…" was also the failure state
🔴 **The relay probe had no deadline and no way to say it failed.** `discovered` is null both while the probe is in flight and after it has come back with nothing, so `identityDescription` shows "Checking what this server uses…" in both cases. A relay that never answers leaves the sign-up form on that sentence permanently — no error, no retry, nothing to act on. Found by the instrumented suite on a clean emulator: nine of ten tests failed with `nav-contacts did not appear within 120000ms`, and the screen dump showed the form still checking. ⚠️ **The reason it never answered is worth writing down.** The app was pointed at `http://10.0.2.2:18810`, and Android has blocked cleartext HTTP by default since API 28 — there is no `usesCleartextTraffic` and no network security config here. The request never left the device. 🔴 That refusal is CORRECT and is not what changed. `SiteConfig` already says why: a bare hostname becoming `http://` would hand key material to anyone on the path "and it would work, which is the worst outcome." What was wrong is that it failed silently, so somebody self-hosting who omits the scheme gets a dead screen and no idea why. Now: bounded at fifteen seconds — far longer than a healthy relay needs — and a failed probe is a distinct state that says "Couldn't reach that server. Check the address — and note it has to be https, not http." Same defect as android#19 one screen earlier, which I fixed this morning without noticing the probe had it too. Also adds `scripts/instrumented.sh`, which closes two ways an on-device run silently points at production: the suites DEFAULT to live hosts when no relay is named, and a saved emulator snapshot restores whatever the AVD was last doing — observed today as a snapshot signed in as `fnreviewbeta` against relay.flamenet.io, under a run told to use a local relay. The script has no default, refuses both live hosts by name, cold boots with `-no-snapshot-load`, and exits 3 on a skip because a skip is not a pass.
This commit is contained in:
@@ -339,10 +339,23 @@ class MessengerViewModel(app: Application) : AndroidViewModel(app) {
|
||||
describeJob = viewModelScope.launch {
|
||||
kotlinx.coroutines.delay(400)
|
||||
val config = SiteConfig.from(value) ?: return@launch
|
||||
val found = withContext(Dispatchers.IO) {
|
||||
io.flamenet.client.RelayDiscovery.describe(config.baseUrl)
|
||||
// ⚠️ **Bounded.** `describe` has no deadline of its own, and a
|
||||
// host that accepts a connection and never answers would leave the
|
||||
// sign-up form on "Checking what this server uses…" indefinitely.
|
||||
// Fifteen seconds is far longer than any healthy relay needs.
|
||||
val found = withTimeoutOrNull(RELAY_PROBE_TIMEOUT_MS) {
|
||||
withContext(Dispatchers.IO) {
|
||||
io.flamenet.client.RelayDiscovery.describe(config.baseUrl)
|
||||
}
|
||||
}
|
||||
if (_relayInput.value != value) return@launch
|
||||
if (found == null) {
|
||||
// Say so rather than leaving the spinner-in-words up. The
|
||||
// commonest cause is an http:// address, which Android refuses
|
||||
// outright, so the message names it.
|
||||
_site.value = config.copy(probeFailed = true)
|
||||
return@launch
|
||||
}
|
||||
// copy(), not a mutation: SiteConfig is a data class and the flow
|
||||
// conflates on equality, so an in-place write is indistinguishable
|
||||
// from the value already there and never reaches the UI.
|
||||
@@ -3570,6 +3583,9 @@ class MessengerViewModel(app: Application) : AndroidViewModel(app) {
|
||||
*/
|
||||
private const val SIGN_IN_TIMEOUT_MS = 60_000L
|
||||
|
||||
/** How long to wait for a relay to describe itself before saying it did not. */
|
||||
private const val RELAY_PROBE_TIMEOUT_MS = 15_000L
|
||||
|
||||
/**
|
||||
* The presence chips, in order.
|
||||
*
|
||||
|
||||
@@ -29,6 +29,18 @@ data class SiteConfig(
|
||||
* it with `copy(...)`.
|
||||
*/
|
||||
val discovered: io.flamenet.client.RelayDiscovery.Descriptor? = null,
|
||||
/**
|
||||
* The probe was made and came back with nothing.
|
||||
*
|
||||
* 🔴 **Without this, "still asking" and "asked and could not reach it" are
|
||||
* the same state.** `discovered` is null in both, so a relay that never
|
||||
* answers leaves "Checking what this server uses…" on screen forever and
|
||||
* the form permanently unusable — no error, no retry, nothing to act on.
|
||||
* Seen 2026-09-05 pointing the app at a plain-HTTP relay, which Android
|
||||
* blocks outright from API 28: the request never left, and the screen said
|
||||
* it was still thinking about it.
|
||||
*/
|
||||
val probeFailed: Boolean = false,
|
||||
) {
|
||||
|
||||
enum class AccountProvider {
|
||||
@@ -95,6 +107,9 @@ data class SiteConfig(
|
||||
/** What to tell the user this relay does about identity, in their words. */
|
||||
val identityDescription: String
|
||||
get() = when {
|
||||
probeFailed ->
|
||||
"Couldn't reach that server. Check the address — and note it has " +
|
||||
"to be https, not http."
|
||||
discovered == null -> "Checking what this server uses…"
|
||||
accountProvider == AccountProvider.SHARED ->
|
||||
"One Flamenet account, shared with Flamenet Files."
|
||||
|
||||
Reference in New Issue
Block a user