Harden infonet control surfaces

This commit is contained in:
BigBodyCobain
2026-05-18 11:22:38 -06:00
parent 25a98a9869
commit 11ea345518
30 changed files with 1810 additions and 276 deletions
@@ -305,6 +305,42 @@ function createPublicMeshAddress(): string {
return `!${fallback.toString(16).padStart(8, '0')}`;
}
function errorMessage(err: unknown, fallback: string = 'unknown error'): string {
if (err instanceof Error && err.message) return err.message;
if (typeof err === 'string' && err.trim()) return err.trim();
if (typeof err === 'object' && err !== null && 'message' in err) {
const message = String((err as { message?: unknown }).message || '').trim();
if (message) return message;
}
return fallback;
}
function describeMeshChatControlError(raw: string): string {
const message = String(raw || '').trim();
if (!message) return 'MeshChat could not update the local control plane.';
if (
message === 'control_plane_request_failed:530' ||
message === 'HTTP 530' ||
message.includes('control_plane_request_failed:530')
) {
return 'The local control plane did not complete the lane switch. Check that the backend is running and reachable, then try Mesh again.';
}
if (
message === 'control_plane_request_failed:502' ||
message === 'HTTP 502' ||
/Backend unavailable/i.test(message)
) {
return 'The frontend cannot reach the backend right now. Start or restart the backend, then try Mesh again.';
}
if (message === 'admin_session_required' || /local operator access only/i.test(message)) {
return 'This control action needs a local operator session. Open Settings or Node controls once so the app can authorize local changes, then try Mesh again.';
}
if (message.startsWith('{') || message.startsWith('<')) {
return 'MeshChat could not update the local control plane. Check the backend log for the upstream error.';
}
return message;
}
function describeGateCompatConsentRequired(): string {
return 'Local gate runtime is unavailable for this room.';
}
@@ -507,8 +543,13 @@ export function useMeshChatController({
body: JSON.stringify(body),
});
if (!res.ok) {
const detail = await res.text().catch(() => '');
throw new Error(detail || `HTTP ${res.status}`);
const data = await res.clone().json().catch(() => null) as
| { detail?: unknown; message?: unknown; error?: unknown }
| null;
const detail =
String(data?.detail || data?.message || data?.error || '').trim() ||
(await res.text().catch(() => '')).trim();
throw new Error(describeMeshChatControlError(detail || `HTTP ${res.status}`));
}
const data = (await res.json()) as MeshMqttSettings;
applyMeshMqttSettings(data);
@@ -528,7 +569,7 @@ export function useMeshChatController({
setMeshMqttStatusText(status);
return { ok: true as const, text: status, data };
} catch (err) {
const text = err instanceof Error ? err.message : 'MQTT settings update failed';
const text = describeMeshChatControlError(errorMessage(err, 'MQTT settings update failed'));
setMeshMqttStatusText(text);
return { ok: false as const, text };
} finally {
@@ -4222,7 +4263,14 @@ export function useMeshChatController({
);
const disablePrivateNodeForPublicMesh = useCallback(async () => {
await setInfonetNodeEnabled(false);
try {
await setInfonetNodeEnabled(false);
} catch (err) {
console.warn(
'[mesh] private node pre-disable failed before public Mesh activation; MQTT enable will retry lane isolation',
err,
);
}
}, []);
const disableWormholeForPublicMesh = useCallback(async () => {
@@ -4287,10 +4335,7 @@ export function useMeshChatController({
}
return { ok: true as const, text: successText };
} catch (err) {
const message =
typeof err === 'object' && err !== null && 'message' in err
? String((err as { message?: string }).message)
: 'unknown error';
const message = describeMeshChatControlError(errorMessage(err));
const errorText =
message === 'browser_identity_blocked_secure_mode'
? 'Mesh key creation is blocked while Wormhole secure mode is active. Turn Wormhole off first if you want a separate public mesh key.'
@@ -4345,10 +4390,7 @@ export function useMeshChatController({
setMeshQuickStatus(null);
return { ok: true as const, text };
} catch (err) {
const message =
typeof err === 'object' && err !== null && 'message' in err
? String((err as { message?: string }).message)
: 'unknown error';
const message = describeMeshChatControlError(errorMessage(err));
const text = `Could not turn MeshChat on: ${message}`;
setIdentityWizardStatus({ type: 'err', text });
setMeshQuickStatus({ type: 'err', text });