Skip to content

Commit 702ae5b

Browse files
committed
debug(mobile): temporary iOS discovery diagnostics
TEMP: log each discovery request's network outcome ([CVPN-DISCOVERY]) plus a standalone arrayBuffer-vs-text self-test against one live gateway, to pin down why iOS shows "no gateways reachable" even with the ATS fix — distinguishing a failed fetch (network/ATS) from a failed signed-response body read (res.arrayBuffer(), fragile on RN iOS). Remove once iOS discovery is confirmed.
1 parent aa30fa8 commit 702ae5b

1 file changed

Lines changed: 66 additions & 1 deletion

File tree

clients/mobile/src/lib/gateways.ts

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -270,11 +270,76 @@ export async function measureLatency(gw: GatewayInfo): Promise<number | null> {
270270
return rttMs;
271271
}
272272

273+
/**
274+
* TEMP iOS discovery diagnostic. Logs every discovery request's network outcome
275+
* (without reading the body, so it can't disturb the real read) so we can tell,
276+
* from the device console, whether the failure is the fetch itself (ATS /
277+
* reachability) or something after it. Tagged [CVPN-DISCOVERY]. Remove once iOS
278+
* discovery is confirmed working.
279+
*/
280+
const diagFetch: typeof fetch = async (input, init) => {
281+
const url =
282+
typeof input === 'string' ? input : input instanceof URL ? input.href : (input as Request).url;
283+
try {
284+
const res = await fetch(input as Parameters<typeof fetch>[0], init);
285+
// eslint-disable-next-line no-console
286+
console.warn(`[CVPN-DISCOVERY] ${res.status} ${url}`);
287+
return res;
288+
} catch (e) {
289+
// eslint-disable-next-line no-console
290+
console.warn(`[CVPN-DISCOVERY] FETCH_ERR ${url} :: ${String((e as Error)?.message ?? e)}`);
291+
throw e;
292+
}
293+
};
294+
295+
/**
296+
* TEMP: independently fetch one gateway's /v1/info and log status + how the body
297+
* reads (arrayBuffer vs text) — `fetchSigned` uses res.arrayBuffer(), which is a
298+
* known-fragile path on RN iOS. Runs off the first Flux-API candidate so it never
299+
* touches the real discovery reads. Non-blocking; failures only log.
300+
*/
301+
async function diagArrayBufferSelfTest(): Promise<void> {
302+
try {
303+
const specs = bundledSpecs();
304+
if (specs.length === 0) return;
305+
const locRes = await fetch(`https://api.runonflux.io/apps/location/${specs[0]}`);
306+
const loc = (await locRes.json()) as { data?: { ip?: string }[] };
307+
const ip = loc.data?.[0]?.ip?.split(':')[0];
308+
if (!ip) {
309+
// eslint-disable-next-line no-console
310+
console.warn('[CVPN-DISCOVERY] selftest: no candidate IP from Flux API');
311+
return;
312+
}
313+
const url = `http://${ip}:51821/v1/info`;
314+
const res = await fetch(url);
315+
let ab = 'n/a';
316+
let txt = 'n/a';
317+
try {
318+
ab = String((await res.clone().arrayBuffer()).byteLength);
319+
} catch (e) {
320+
ab = `ERR ${String((e as Error)?.message ?? e)}`;
321+
}
322+
try {
323+
txt = String((await res.text()).length);
324+
} catch (e) {
325+
txt = `ERR ${String((e as Error)?.message ?? e)}`;
326+
}
327+
// eslint-disable-next-line no-console
328+
console.warn(
329+
`[CVPN-DISCOVERY] selftest ${url} status=${res.status} arrayBuffer=${ab} text=${txt}`,
330+
);
331+
} catch (e) {
332+
// eslint-disable-next-line no-console
333+
console.warn(`[CVPN-DISCOVERY] selftest FAILED :: ${String((e as Error)?.message ?? e)}`);
334+
}
335+
}
336+
273337
/**
274338
* Resolve the live gateway fleet from the Flux network, using the bundled
275339
* signed snapshot for spec names + seed nodes. Networking is delegated to core.
276340
* POC: disk-cache tier of the discovery order is not implemented here.
277341
*/
278342
export async function discoverFleet(): Promise<GatewayInfo[]> {
279-
return discoverGateways(bundledSpecs(), { nodes: [...seedNodeIps()] });
343+
void diagArrayBufferSelfTest(); // TEMP: fire-and-forget iOS diagnostic
344+
return discoverGateways(bundledSpecs(), { nodes: [...seedNodeIps()], fetchImpl: diagFetch });
280345
}

0 commit comments

Comments
 (0)