Stabilize Infonet private sync and selected telemetry

This commit is contained in:
BigBodyCobain
2026-05-06 22:10:04 -06:00
parent b8ac0fb9e7
commit 5ee4f8ecd7
8 changed files with 119 additions and 44 deletions
+61 -5
View File
@@ -1084,6 +1084,7 @@ _WORMHOLE_PUBLIC_PROFILE_FIELDS = {"profile", "wormhole_enabled"}
_PRIVATE_LANE_CONTROL_FIELDS = {"private_lane_tier", "private_lane_policy"}
_PUBLIC_RNS_STATUS_FIELDS = {"enabled", "ready", "configured_peers", "active_peers"}
_NODE_PUBLIC_EVENT_HOOK_REGISTERED = False
_INFONET_PRIVATE_TRANSPORT_LOCK = threading.Lock()
def _current_node_mode() -> str:
@@ -1172,6 +1173,50 @@ def _filter_infonet_sync_records(records: list[Any]) -> list[Any]:
]
def _ensure_infonet_private_transport_ready(reason: str = "") -> bool:
"""Warm the local onion transport before private Infonet sync.
Infonet may know about an onion seed before the Wormhole UI is opened. The
sync worker still needs Arti marked enabled and a ready SOCKS listener, so
do that lazily in the worker instead of making users manually open another
panel just to participate in the Infonet.
"""
if not _infonet_private_transport_required():
return True
try:
from services.wormhole_supervisor import _check_arti_ready
if bool(get_settings().MESH_ARTI_ENABLED) and _check_arti_ready():
return True
except Exception:
pass
if not _INFONET_PRIVATE_TRANSPORT_LOCK.acquire(blocking=False):
return False
try:
from routers.ai_intel import _write_env_value
from services.tor_hidden_service import tor_service
from services.wormhole_supervisor import _check_arti_ready
label = f" ({reason})" if reason else ""
logger.info("Infonet private transport warmup starting%s", label)
tor_result = tor_service.start(target_port=8000)
if tor_result.get("ok"):
_write_env_value("MESH_ARTI_ENABLED", "true")
get_settings.cache_clear()
if _check_arti_ready():
logger.info("Infonet private transport ready%s", label)
return True
logger.warning("Infonet private transport warmup incomplete%s: %s", label, tor_result)
return False
except Exception as exc:
logger.warning("Infonet private transport warmup failed: %s", exc)
return False
finally:
_INFONET_PRIVATE_TRANSPORT_LOCK.release()
def _configured_bootstrap_seed_peer_urls() -> list[str]:
settings = get_settings()
primary = str(getattr(settings, "MESH_BOOTSTRAP_SEED_PEERS", "") or "").strip()
@@ -1487,6 +1532,12 @@ def _run_public_sync_cycle() -> SyncWorkerState:
set_sync_state(updated)
return updated
if _infonet_private_transport_required() and any(
str(getattr(record, "transport", "") or "").strip().lower() == "onion"
for record in peers
):
_ensure_infonet_private_transport_ready("sync")
last_error = "sync failed"
for record in peers:
started = begin_sync(
@@ -2295,7 +2346,13 @@ async def lifespan(app: FastAPI):
_refresh_node_peer_store()
if _node_runtime_supported():
if not _participant_node_enabled():
set_sync_state(_set_node_sync_disabled_state())
logger.info("Infonet participant auto-enabled for private seed sync")
_set_participant_node_enabled(True)
threading.Thread(
target=lambda: _ensure_infonet_private_transport_ready("startup"),
daemon=True,
name="infonet-private-transport-warmup",
).start()
_NODE_SYNC_STOP.clear()
threading.Thread(target=_public_infonet_sync_loop, daemon=True).start()
_kick_public_sync_background("startup")
@@ -9662,10 +9719,9 @@ async def api_wormhole_leave(request: Request):
updated = write_wormhole_settings(enabled=False)
state = disconnect_wormhole(reason="leave_wormhole")
# Disable node participation when the user leaves the Wormhole.
from services.node_settings import write_node_settings
write_node_settings(enabled=False)
# Leaving private DM mode must not disable Infonet participation. Infonet
# sync has its own private transport warmup and can remain connected to
# seed/peer nodes while MeshChat stays separately opt-in.
return {
"ok": True,
+3 -4
View File
@@ -929,10 +929,9 @@ async def api_wormhole_leave(request: Request):
updated = write_wormhole_settings(enabled=False)
state = disconnect_wormhole(reason="leave_wormhole")
# Disable node participation when the user leaves the Wormhole.
from services.node_settings import write_node_settings
write_node_settings(enabled=False)
# Leaving private DM mode must not disable Infonet participation. Infonet
# sync has its own private transport warmup and can remain connected to
# seed/peer nodes while MeshChat stays separately opt-in.
return {
"ok": True,
+12 -10
View File
@@ -256,7 +256,7 @@ PRIVATE_JET_TYPES = {
# Flight trails state
flight_trails = {} # {icao_hex: {points: [[lat, lng, alt, ts], ...], last_seen: ts}}
_trails_lock = threading.Lock()
_MAX_TRACKED_TRAILS = 2000
_MAX_TRACKED_TRAILS = 20000
def get_flight_trail(icao24: str) -> list:
@@ -624,7 +624,7 @@ def _classify_and_publish(all_adsb_flights):
# --- Trail Accumulation ---
_TRAIL_INTERVAL_S = 60 # selected trails need enough resolution to show where unknown-route traffic came from
def _accumulate_trail(f, now_ts, check_route=True):
def _accumulate_trail(f, now_ts, attach_known_route_trail=False):
hex_id = f.get("icao24", "").lower()
if not hex_id:
return 0, None
@@ -637,12 +637,9 @@ def _classify_and_publish(all_adsb_flights):
(f.get("origin_loc") and f.get("dest_loc"))
or (_known_route_name(f.get("origin_name")) and _known_route_name(f.get("dest_name")))
)
if check_route and has_known_route:
f["trail"] = []
return 0, hex_id
lat, lng, alt = f.get("lat"), f.get("lng"), f.get("alt", 0)
if lat is None or lng is None:
f["trail"] = flight_trails.get(hex_id, {}).get("points", [])
f["trail"] = [] if has_known_route and not attach_known_route_trail else flight_trails.get(hex_id, {}).get("points", [])
return 0, hex_id
point = [round(lat, 5), round(lng, 5), round(alt, 1), round(now_ts)]
if hex_id not in flight_trails:
@@ -663,7 +660,10 @@ def _classify_and_publish(all_adsb_flights):
trail_data["last_seen"] = now_ts
if len(trail_data["points"]) > 200:
trail_data["points"] = trail_data["points"][-200:]
f["trail"] = trail_data["points"]
# Keep known-route flights visually clean in the main payload; selected
# detail panels can still fetch this server-side trail to compute
# observed fuel/CO2 burn.
f["trail"] = [] if has_known_route and not attach_known_route_trail else trail_data["points"]
return 1, hex_id
now_ts = datetime.utcnow().timestamp()
@@ -675,7 +675,9 @@ def _classify_and_publish(all_adsb_flights):
tracked_snapshot = copy.deepcopy(latest_data.get("tracked_flights", []))
raw_flights_snapshot = list(latest_data.get("flights", []))
# Skip trails for any flight with a known route; the route line replaces historical trail.
# Accumulate trails for every aircraft so selected details can estimate
# observed fuel/CO2 burn. Known-route flights keep an empty payload trail so
# the route line, not historical breadcrumbs, remains the visible map path.
route_check_lists = [commercial_snapshot, private_jets_snapshot, private_ga_snapshot]
always_trail_lists = [tracked_snapshot, military_snapshot]
seen_hexes = set()
@@ -683,14 +685,14 @@ def _classify_and_publish(all_adsb_flights):
with _trails_lock:
for flist in route_check_lists:
for f in flist:
count, hex_id = _accumulate_trail(f, now_ts, check_route=True)
count, hex_id = _accumulate_trail(f, now_ts, attach_known_route_trail=False)
trail_count += count
if hex_id:
seen_hexes.add(hex_id)
for flist in always_trail_lists:
for f in flist:
count, hex_id = _accumulate_trail(f, now_ts, check_route=True)
count, hex_id = _accumulate_trail(f, now_ts, attach_known_route_trail=False)
trail_count += count
if hex_id:
seen_hexes.add(hex_id)
+8
View File
@@ -6,6 +6,7 @@ import time
import requests
from services.network_utils import fetch_with_curl
from services.fetchers._store import latest_data, _data_lock, _mark_fresh
from services.fetchers.emissions import get_emissions_info
from services.fetchers.plane_alert import enrich_with_plane_alert
logger = logging.getLogger("services.data_fetcher")
@@ -289,6 +290,13 @@ def fetch_military_flights():
remaining_mil = []
for mf in military_flights:
enrich_with_plane_alert(mf)
model = mf.get("model")
if not model or str(model).strip().lower() in {"", "unknown"}:
model = mf.get("alert_type") or ""
if model:
emissions = get_emissions_info(model)
if emissions:
mf["emissions"] = emissions
if mf.get("alert_category"):
mf["type"] = "tracked_flight"
tracked_mil.append(mf)
@@ -61,7 +61,7 @@ export default function NetworkStats() {
const nodeColor = stats.syncOutcome === 'ok' ? 'text-green-400'
: stats.syncOutcome === 'running' ? 'text-amber-400'
: stats.nodeEnabled ? 'text-amber-400' : 'text-gray-600';
const nodeLabel = stats.syncOutcome === 'ok' ? 'CONNECTED'
const nodeLabel = stats.syncOutcome === 'ok' ? 'SEED SYNCED'
: stats.syncOutcome === 'running' ? 'SYNCING'
: stats.syncOutcome === 'error' || stats.syncOutcome === 'fork' ? 'RETRYING'
: stats.nodeEnabled ? 'WAITING' : 'OFFLINE';
+16 -11
View File
@@ -274,7 +274,7 @@ function hasKnownRouteName(value?: string | null): boolean {
function flightHasKnownRoute(entity: ReturnType<typeof findSelectedEntity>, dynamicRoute: DynamicRoute | null): boolean {
if (!entity) return false;
if (dynamicRoute?.orig_loc || dynamicRoute?.dest_loc) return true;
if (dynamicRoute?.orig_loc && dynamicRoute?.dest_loc) return true;
if (!('origin_loc' in entity) && !('origin_name' in entity)) return false;
const flight = entity as Flight;
return Boolean(
@@ -683,19 +683,24 @@ const MaplibreViewer = ({
const endpoint = isShip
? `${API_BASE}/api/trail/ship/${encodeURIComponent(trailId)}`
: `${API_BASE}/api/trail/flight/${encodeURIComponent(trailId)}`;
fetch(endpoint)
.then((res) => (res.ok ? res.json() : null))
.then((payload) => {
if (cancelled || !payload) return;
const points = parseTrailPoints(payload.trail, kind);
setSelectedTrailPoints(points.length >= 2 ? points : fallback);
})
.catch(() => {
if (!cancelled) setSelectedTrailPoints(fallback);
});
const refreshSelectedTrail = () => {
fetch(endpoint, { cache: 'no-store' })
.then((res) => (res.ok ? res.json() : null))
.then((payload) => {
if (cancelled || !payload) return;
const points = parseTrailPoints(payload.trail, kind);
setSelectedTrailPoints(points.length >= 2 ? points : fallback);
})
.catch(() => {
if (!cancelled) setSelectedTrailPoints(fallback);
});
};
refreshSelectedTrail();
const trailRefreshTimer = window.setInterval(refreshSelectedTrail, 30000);
return () => {
cancelled = true;
window.clearInterval(trailRefreshTimer);
};
}, [selectedEntity, data, dynamicRoute]);
+3 -3
View File
@@ -5953,7 +5953,7 @@ export default function MeshTerminal({ isOpen, launchToken = 0, onClose, onDmCou
PARTICIPANT NODE
</div>
<div className="mt-1 text-sm leading-5 text-slate-400">
Backend bootstrap is configured; activate the participant node to sync the public testnet seed without Wormhole.
Backend bootstrap is configured; the participant node syncs the testnet seed over the private seed lane.
</div>
</div>
<div className="border border-cyan-500/20 bg-cyan-500/8 px-3 py-1.5 text-[13px] tracking-[0.22em] text-cyan-200">
@@ -6008,10 +6008,10 @@ export default function MeshTerminal({ isOpen, launchToken = 0, onClose, onDmCou
<div className="border border-amber-400/16 bg-amber-400/6 px-4 py-3 text-sm leading-6 text-amber-100/85">
<div className="text-[13px] font-mono tracking-[0.24em] text-amber-300">
WORMHOLE OPTIONAL FOR NODE SYNC
PRIVATE SEED LANE
</div>
<div className="mt-2">
Participant-node bootstrap, sync, and public chain hosting run on the backend lane without Wormhole.
Participant-node bootstrap, sync, and public chain hosting use the backend private seed lane.
</div>
<div className="mt-2 text-amber-200/75">
Turn Wormhole on for gates, obfuscated inbox, and the stronger obfuscated lane only.
+15 -10
View File
@@ -480,19 +480,24 @@ function NewsFeedInner({ selectedEntity, regionDossier, regionDossierLoading, on
}
let cancelled = false;
fetch(`${API_BASE}/api/trail/flight/${encodeURIComponent(trailId)}`, { cache: 'no-store' })
.then((res) => (res.ok ? res.json() : null))
.then((payload) => {
if (cancelled) return;
const trail = Array.isArray(payload?.trail) ? payload.trail as FlightTrailPoint[] : [];
setSelectedFlightTrail(trail);
})
.catch(() => {
if (!cancelled) setSelectedFlightTrail([]);
});
const refreshSelectedFlightTrail = () => {
fetch(`${API_BASE}/api/trail/flight/${encodeURIComponent(trailId)}`, { cache: 'no-store' })
.then((res) => (res.ok ? res.json() : null))
.then((payload) => {
if (cancelled) return;
const trail = Array.isArray(payload?.trail) ? payload.trail as FlightTrailPoint[] : [];
setSelectedFlightTrail(trail);
})
.catch(() => {
if (!cancelled) setSelectedFlightTrail([]);
});
};
refreshSelectedFlightTrail();
const trailRefreshTimer = window.setInterval(refreshSelectedFlightTrail, 30000);
return () => {
cancelled = true;
window.clearInterval(trailRefreshTimer);
};
}, [selectedEntity?.id, selectedEntity?.type]);