Ship ShadowBroker v0.9.83 with live Infonet gate messaging and DM protocols.

Gate hashchain replication, Tor/SOCKS transport hardening, terminal session teardown, v0.9.83 UI/changelog, and release digest pins for seamless updater verification.
This commit is contained in:
BigBodyCobain
2026-06-15 15:37:29 -06:00
parent 8fcb01276c
commit 5ede669a12
35 changed files with 589 additions and 263 deletions
@@ -14,13 +14,26 @@ interface Stats {
nodeEnabled: boolean;
syncOutcome: string;
syncError: string;
artiReady: boolean | null;
}
const EMPTY: Stats = {
meshtastic: 0, aprs: 0, ledgerNodes: 0, infonetEvents: 0,
syncPeers: 0, seedPeers: 0, nodeEnabled: false, syncOutcome: 'offline', syncError: '',
artiReady: null,
};
function isArtiTransportBlocked(syncError: string, artiReady: boolean | null): boolean {
if (artiReady === true) return false;
if (artiReady === false) return true;
const lower = syncError.toLowerCase();
return (
lower.includes('ready arti transport')
|| lower.includes('require arti to be enabled')
|| lower.includes('onion peer requests require a ready arti')
);
}
export default function NetworkStats() {
const [stats, setStats] = useState<Stats>(EMPTY);
@@ -28,10 +41,11 @@ export default function NetworkStats() {
let alive = true;
const poll = async () => {
try {
const [meshRes, channelsRes, infonet] = await Promise.all([
const [meshRes, channelsRes, infonet, wormholeRes] = await Promise.all([
fetch(`${API_BASE}/api/mesh/status`).then(r => r.ok ? r.json() : null).catch(() => null),
fetch(`${API_BASE}/api/mesh/channels`).then(r => r.ok ? r.json() : null).catch(() => null),
fetchInfonetNodeStatusSnapshot(true).catch(() => null),
fetch(`${API_BASE}/api/wormhole/status`).then(r => r.ok ? r.json() : null).catch(() => null),
]);
if (!alive) return;
const authorNodes = Number(infonet?.author_nodes ?? infonet?.known_nodes ?? 0);
@@ -43,6 +57,7 @@ export default function NetworkStats() {
?? 0,
);
const syncOutcome = String(infonet?.sync_runtime?.last_outcome || 'offline').toLowerCase();
const artiReady = typeof wormholeRes?.arti_ready === 'boolean' ? wormholeRes.arti_ready : null;
setStats({
meshtastic: Number(channelsRes?.total_live || channelsRes?.total_nodes || meshRes?.signal_counts?.meshtastic || 0),
aprs: Number(meshRes?.signal_counts?.aprs || 0),
@@ -53,6 +68,7 @@ export default function NetworkStats() {
nodeEnabled: Boolean(infonet?.node_enabled),
syncOutcome,
syncError: String(infonet?.sync_runtime?.last_error || '').trim(),
artiReady,
});
} catch { /* ignore */ }
};
@@ -61,8 +77,7 @@ export default function NetworkStats() {
return () => { alive = false; clearInterval(interval); };
}, []);
const syncErrorLower = stats.syncError.toLowerCase();
const artiBlocked = syncErrorLower.includes('arti') || syncErrorLower.includes('onion');
const artiBlocked = isArtiTransportBlocked(stats.syncError, stats.artiReady);
const nodeColor = stats.syncOutcome === 'ok' || stats.syncOutcome === 'solo' ? 'text-green-400'
: stats.syncOutcome === 'running' ? 'text-amber-400'
: stats.nodeEnabled ? 'text-amber-400' : 'text-gray-600';
@@ -3,9 +3,7 @@
import React, { useEffect } from 'react';
import { AnimatePresence, motion } from 'framer-motion';
import { X } from 'lucide-react';
import {
ensureInfonetParticipantNodeReady,
} from '@/mesh/controlPlaneStatusClient';
import { beginInfonetTerminalSession } from '@/lib/infonetTerminalSession';
import InfonetShell from './InfonetShell';
interface InfonetTerminalProps {
@@ -38,7 +36,7 @@ export default function InfonetTerminal({
const connectParticipantNode = async () => {
try {
if (cancelled) return;
await ensureInfonetParticipantNodeReady();
await beginInfonetTerminalSession();
} catch {
// Remote/shared viewers may not have local-operator rights. Leave manual controls intact.
}