Harden v0.9.75 wormhole node sync and telemetry panels

Add Tor/onion runtime wiring and faster Infonet node status refresh.

Keep node bootstrap state clearer across Docker and local runtimes.

Use selected aircraft trail history for cumulative tracked-aircraft emissions.
This commit is contained in:
BigBodyCobain
2026-05-06 14:04:16 -06:00
parent 8926e08009
commit b8ac0fb9e7
15 changed files with 698 additions and 201 deletions
+6 -1
View File
@@ -128,7 +128,12 @@ AIS_API_KEY= # https://aisstream.io/ — free tier WebSocket key
# MESH_BOOTSTRAP_DISABLED=false
# MESH_BOOTSTRAP_MANIFEST_PATH=data/bootstrap_peers.json
# MESH_BOOTSTRAP_SIGNER_PUBLIC_KEY=
# MESH_DEFAULT_SYNC_PEERS=https://node.shadowbroker.info # bundled pull-only public seed for fresh installs
# Infonet/Wormhole fails closed to onion/RNS by default. Only enable clearnet
# sync for local relay development or an explicitly public testnet.
# MESH_INFONET_ALLOW_CLEARNET_SYNC=false
# MESH_BOOTSTRAP_SEED_PEERS=http://gqpbunqbgtkcqilvclm3xrkt3zowjyl3s62kkktvojgvxzizamvbrqid.onion:8000
# Add comma-separated http://*.onion peers as more private seed/relay nodes come online.
# MESH_DEFAULT_SYNC_PEERS= # legacy alias; prefer MESH_BOOTSTRAP_SEED_PEERS
# MESH_RELAY_PEERS= # comma-separated operator-trusted sync/push peers (empty by default)
# MESH_PEER_PUSH_SECRET= # REQUIRED when relay/RNS peers are configured (min 16 chars, generate with: python -c "import secrets; print(secrets.token_urlsafe(32))")
# MESH_SYNC_INTERVAL_S=300
+106 -28
View File
@@ -1114,9 +1114,10 @@ def _participant_node_enabled() -> bool:
def _node_runtime_snapshot() -> dict[str, Any]:
with _NODE_RUNTIME_LOCK:
return {
"node_mode": _NODE_BOOTSTRAP_STATE.get("node_mode", "participant"),
"node_mode": _current_node_mode(),
"node_enabled": _participant_node_enabled(),
"bootstrap": dict(_NODE_BOOTSTRAP_STATE),
"private_transport_required": _infonet_private_transport_required(),
"bootstrap": {**dict(_NODE_BOOTSTRAP_STATE), "node_mode": _current_node_mode()},
"sync_runtime": get_sync_state().to_dict(),
"push_runtime": dict(_NODE_PUSH_STATE),
}
@@ -1149,6 +1150,35 @@ def _set_participant_node_enabled(enabled: bool) -> dict[str, Any]:
}
def _infonet_private_transport_required() -> bool:
return not bool(getattr(get_settings(), "MESH_INFONET_ALLOW_CLEARNET_SYNC", False))
def _infonet_private_transport_error() -> str:
return "private Infonet requires onion/RNS transport; no clearnet sync fallback"
def _is_private_infonet_transport(transport: str) -> bool:
return str(transport or "").strip().lower() in {"onion", "rns"}
def _filter_infonet_sync_records(records: list[Any]) -> list[Any]:
if not _infonet_private_transport_required():
return records
return [
record
for record in records
if _is_private_infonet_transport(str(getattr(record, "transport", "") or ""))
]
def _configured_bootstrap_seed_peer_urls() -> list[str]:
settings = get_settings()
primary = str(getattr(settings, "MESH_BOOTSTRAP_SEED_PEERS", "") or "").strip()
legacy = str(getattr(settings, "MESH_DEFAULT_SYNC_PEERS", "") or "").strip()
return parse_configured_relay_peers(primary or legacy)
def _refresh_node_peer_store(*, now: float | None = None) -> dict[str, Any]:
from services.mesh.mesh_bootstrap_manifest import load_bootstrap_manifest_from_settings
from services.mesh.mesh_peer_store import (
@@ -1167,14 +1197,17 @@ def _refresh_node_peer_store(*, now: float | None = None) -> dict[str, Any]:
except Exception:
store = PeerStore(DEFAULT_PEER_STORE_PATH)
private_transport_required = _infonet_private_transport_required()
operator_peers = configured_relay_peer_urls()
default_sync_peers = parse_configured_relay_peers(
str(getattr(get_settings(), "MESH_DEFAULT_SYNC_PEERS", "") or "")
)
bootstrap_seed_peers = _configured_bootstrap_seed_peer_urls()
skipped_clearnet_peers = 0
for peer_url in operator_peers:
transport = peer_transport_kind(peer_url)
if not transport:
continue
if private_transport_required and not _is_private_infonet_transport(transport):
skipped_clearnet_peers += 1
continue
store.upsert(
make_sync_peer_record(
peer_url=peer_url,
@@ -1195,19 +1228,22 @@ def _refresh_node_peer_store(*, now: float | None = None) -> dict[str, Any]:
)
operator_peer_set = set(operator_peers)
for peer_url in default_sync_peers:
for peer_url in bootstrap_seed_peers:
if peer_url in operator_peer_set:
continue
transport = peer_transport_kind(peer_url)
if not transport:
continue
if private_transport_required and not _is_private_infonet_transport(transport):
skipped_clearnet_peers += 1
continue
store.upsert(
make_bootstrap_peer_record(
peer_url=peer_url,
transport=transport,
role="seed",
label="ShadowBroker default seed",
signer_id="shadowbroker-default",
label="ShadowBroker bootstrap seed",
signer_id="shadowbroker-bootstrap",
now=timestamp,
)
)
@@ -1217,8 +1253,8 @@ def _refresh_node_peer_store(*, now: float | None = None) -> dict[str, Any]:
transport=transport,
role="seed",
source="bundle",
label="ShadowBroker default seed",
signer_id="shadowbroker-default",
label="ShadowBroker bootstrap seed",
signer_id="shadowbroker-bootstrap",
now=timestamp,
)
)
@@ -1232,6 +1268,9 @@ def _refresh_node_peer_store(*, now: float | None = None) -> dict[str, Any]:
if manifest is not None:
for peer in manifest.peers:
if private_transport_required and not _is_private_infonet_transport(peer.transport):
skipped_clearnet_peers += 1
continue
store.upsert(
make_bootstrap_peer_record(
peer_url=peer.peer_url,
@@ -1254,17 +1293,30 @@ def _refresh_node_peer_store(*, now: float | None = None) -> dict[str, Any]:
)
)
if private_transport_required and skipped_clearnet_peers and not bootstrap_error:
bootstrap_error = _infonet_private_transport_error()
store.save()
bootstrap_records = store.records_for_bucket("bootstrap")
sync_records = store.records_for_bucket("sync")
push_records = store.records_for_bucket("push")
if private_transport_required:
bootstrap_records = [record for record in bootstrap_records if _is_private_infonet_transport(record.transport)]
sync_records = [record for record in sync_records if _is_private_infonet_transport(record.transport)]
push_records = [record for record in push_records if _is_private_infonet_transport(record.transport)]
snapshot = {
"node_mode": mode,
"private_transport_required": private_transport_required,
"skipped_clearnet_peer_count": skipped_clearnet_peers,
"manifest_loaded": manifest is not None,
"manifest_signer_id": manifest.signer_id if manifest is not None else "",
"manifest_valid_until": int(manifest.valid_until or 0) if manifest is not None else 0,
"bootstrap_peer_count": len(store.records_for_bucket("bootstrap")),
"sync_peer_count": len(store.records_for_bucket("sync")),
"push_peer_count": len(store.records_for_bucket("push")),
"bootstrap_peer_count": len(bootstrap_records),
"sync_peer_count": len(sync_records),
"push_peer_count": len(push_records),
"operator_peer_count": len(operator_peers),
"default_sync_peer_count": len(default_sync_peers),
"bootstrap_seed_peer_count": len(bootstrap_seed_peers),
"default_sync_peer_count": len(bootstrap_seed_peers),
"last_bootstrap_error": bootstrap_error,
}
with _NODE_RUNTIME_LOCK:
@@ -1285,6 +1337,9 @@ def _peer_sync_response(peer_url: str, body: dict[str, Any]) -> dict[str, Any]:
normalized = normalize_peer_url(peer_url)
if not normalized:
raise ValueError("invalid peer URL")
transport = peer_transport_kind(normalized)
if _infonet_private_transport_required() and not _is_private_infonet_transport(transport):
raise RuntimeError(_infonet_private_transport_error())
timeout = int(get_settings().MESH_RELAY_PUSH_TIMEOUT_S or 10)
kwargs: dict[str, Any] = {
@@ -1292,7 +1347,7 @@ def _peer_sync_response(peer_url: str, body: dict[str, Any]) -> dict[str, Any]:
"timeout": timeout,
"headers": {"Content-Type": "application/json"},
}
if peer_transport_kind(normalized) == "onion":
if transport == "onion":
if not bool(get_settings().MESH_ARTI_ENABLED):
raise RuntimeError("onion sync requires Arti to be enabled")
if not _check_arti_ready():
@@ -1407,16 +1462,27 @@ def _run_public_sync_cycle() -> SyncWorkerState:
except Exception:
store = PeerStore(DEFAULT_PEER_STORE_PATH)
peers = eligible_sync_peers(store.records(), now=time.time())
records = _filter_infonet_sync_records(store.records())
peers = eligible_sync_peers(records, now=time.time())
with _NODE_RUNTIME_LOCK:
current_state = get_sync_state()
if not peers:
updated = finish_solo_sync(
current_state,
now=time.time(),
current_head=infonet.head_hash,
interval_s=int(get_settings().MESH_SYNC_INTERVAL_S or 300),
)
if _infonet_private_transport_required():
updated = finish_sync(
current_state,
ok=False,
error=_infonet_private_transport_error(),
now=time.time(),
current_head=infonet.head_hash,
failure_backoff_s=int(get_settings().MESH_SYNC_FAILURE_BACKOFF_S or 60),
)
else:
updated = finish_solo_sync(
current_state,
now=time.time(),
current_head=infonet.head_hash,
interval_s=int(get_settings().MESH_SYNC_INTERVAL_S or 300),
)
with _NODE_RUNTIME_LOCK:
set_sync_state(updated)
return updated
@@ -9542,18 +9608,29 @@ async def api_wormhole_join(request: Request):
existing = read_wormhole_settings()
updated = write_wormhole_settings(
enabled=True,
transport="direct",
socks_proxy="",
transport="tor_arti",
socks_proxy=f"socks5h://127.0.0.1:{int(get_settings().MESH_ARTI_SOCKS_PORT or 9050)}",
socks_dns=True,
anonymous_mode=False,
anonymous_mode=True,
)
transport_changed = (
str(existing.get("transport", "direct")) != "direct"
or str(existing.get("socks_proxy", "")) != ""
str(existing.get("transport", "direct")) != "tor_arti"
or str(existing.get("socks_proxy", "")) != str(updated.get("socks_proxy", ""))
or bool(existing.get("socks_dns", True)) is not True
or bool(existing.get("anonymous_mode", False)) is not False
or bool(existing.get("anonymous_mode", False)) is not True
or bool(existing.get("enabled", False)) is not True
)
tor_result: dict[str, Any] = {"ok": False, "detail": "not started"}
try:
from services.tor_hidden_service import tor_service
from routers.ai_intel import _write_env_value
tor_result = await asyncio.to_thread(tor_service.start)
if tor_result.get("ok"):
_write_env_value("MESH_ARTI_ENABLED", "true")
get_settings.cache_clear()
except Exception as exc:
tor_result = {"ok": False, "detail": str(exc or type(exc).__name__)}
bootstrap_wormhole_identity()
bootstrap_wormhole_persona_state()
state = (
@@ -9575,6 +9652,7 @@ async def api_wormhole_join(request: Request):
"identity": get_transport_identity(),
"runtime": state,
"settings": updated,
"tor": tor_result,
}
+61 -14
View File
@@ -96,9 +96,10 @@ def _participant_node_enabled() -> bool:
def _node_runtime_snapshot() -> dict[str, Any]:
with _NODE_RUNTIME_LOCK:
return {
"node_mode": _NODE_BOOTSTRAP_STATE.get("node_mode", "participant"),
"node_mode": _current_node_mode(),
"node_enabled": _participant_node_enabled(),
"bootstrap": dict(_NODE_BOOTSTRAP_STATE),
"private_transport_required": _infonet_private_transport_required(),
"bootstrap": {**dict(_NODE_BOOTSTRAP_STATE), "node_mode": _current_node_mode()},
"sync_runtime": get_sync_state().to_dict(),
"push_runtime": dict(_NODE_PUSH_STATE),
}
@@ -131,6 +132,30 @@ def _set_participant_node_enabled(enabled: bool) -> dict[str, Any]:
}
def _infonet_private_transport_required() -> bool:
from services.config import get_settings
return not bool(getattr(get_settings(), "MESH_INFONET_ALLOW_CLEARNET_SYNC", False))
def _infonet_private_transport_error() -> str:
return "private Infonet requires onion/RNS transport; no clearnet sync fallback"
def _is_private_infonet_transport(transport: str) -> bool:
return str(transport or "").strip().lower() in {"onion", "rns"}
def _configured_bootstrap_seed_peer_urls() -> list[str]:
from services.config import get_settings
from services.mesh.mesh_router import parse_configured_relay_peers
settings = get_settings()
primary = str(getattr(settings, "MESH_BOOTSTRAP_SEED_PEERS", "") or "").strip()
legacy = str(getattr(settings, "MESH_DEFAULT_SYNC_PEERS", "") or "").strip()
return parse_configured_relay_peers(primary or legacy)
def _refresh_node_peer_store(*, now: float | None = None) -> dict[str, Any]:
from services.config import get_settings
from services.mesh.mesh_bootstrap_manifest import load_bootstrap_manifest_from_settings
@@ -155,14 +180,17 @@ def _refresh_node_peer_store(*, now: float | None = None) -> dict[str, Any]:
except Exception:
store = PeerStore(DEFAULT_PEER_STORE_PATH)
private_transport_required = _infonet_private_transport_required()
operator_peers = configured_relay_peer_urls()
default_sync_peers = parse_configured_relay_peers(
str(getattr(get_settings(), "MESH_DEFAULT_SYNC_PEERS", "") or "")
)
bootstrap_seed_peers = _configured_bootstrap_seed_peer_urls()
skipped_clearnet_peers = 0
for peer_url in operator_peers:
transport = peer_transport_kind(peer_url)
if not transport:
continue
if private_transport_required and not _is_private_infonet_transport(transport):
skipped_clearnet_peers += 1
continue
store.upsert(
make_sync_peer_record(
peer_url=peer_url,
@@ -183,19 +211,22 @@ def _refresh_node_peer_store(*, now: float | None = None) -> dict[str, Any]:
)
operator_peer_set = set(operator_peers)
for peer_url in default_sync_peers:
for peer_url in bootstrap_seed_peers:
if peer_url in operator_peer_set:
continue
transport = peer_transport_kind(peer_url)
if not transport:
continue
if private_transport_required and not _is_private_infonet_transport(transport):
skipped_clearnet_peers += 1
continue
store.upsert(
make_bootstrap_peer_record(
peer_url=peer_url,
transport=transport,
role="seed",
label="ShadowBroker default seed",
signer_id="shadowbroker-default",
label="ShadowBroker bootstrap seed",
signer_id="shadowbroker-bootstrap",
now=timestamp,
)
)
@@ -205,8 +236,8 @@ def _refresh_node_peer_store(*, now: float | None = None) -> dict[str, Any]:
transport=transport,
role="seed",
source="bundle",
label="ShadowBroker default seed",
signer_id="shadowbroker-default",
label="ShadowBroker bootstrap seed",
signer_id="shadowbroker-bootstrap",
now=timestamp,
)
)
@@ -220,6 +251,9 @@ def _refresh_node_peer_store(*, now: float | None = None) -> dict[str, Any]:
if manifest is not None:
for peer in manifest.peers:
if private_transport_required and not _is_private_infonet_transport(peer.transport):
skipped_clearnet_peers += 1
continue
store.upsert(
make_bootstrap_peer_record(
peer_url=peer.peer_url,
@@ -242,17 +276,30 @@ def _refresh_node_peer_store(*, now: float | None = None) -> dict[str, Any]:
)
)
if private_transport_required and skipped_clearnet_peers and not bootstrap_error:
bootstrap_error = _infonet_private_transport_error()
store.save()
bootstrap_records = store.records_for_bucket("bootstrap")
sync_records = store.records_for_bucket("sync")
push_records = store.records_for_bucket("push")
if private_transport_required:
bootstrap_records = [record for record in bootstrap_records if _is_private_infonet_transport(record.transport)]
sync_records = [record for record in sync_records if _is_private_infonet_transport(record.transport)]
push_records = [record for record in push_records if _is_private_infonet_transport(record.transport)]
snapshot = {
"node_mode": mode,
"private_transport_required": private_transport_required,
"skipped_clearnet_peer_count": skipped_clearnet_peers,
"manifest_loaded": manifest is not None,
"manifest_signer_id": manifest.signer_id if manifest is not None else "",
"manifest_valid_until": int(manifest.valid_until or 0) if manifest is not None else 0,
"bootstrap_peer_count": len(store.records_for_bucket("bootstrap")),
"sync_peer_count": len(store.records_for_bucket("sync")),
"push_peer_count": len(store.records_for_bucket("push")),
"bootstrap_peer_count": len(bootstrap_records),
"sync_peer_count": len(sync_records),
"push_peer_count": len(push_records),
"operator_peer_count": len(operator_peers),
"default_sync_peer_count": len(default_sync_peers),
"bootstrap_seed_peer_count": len(bootstrap_seed_peers),
"default_sync_peer_count": len(bootstrap_seed_peers),
"last_bootstrap_error": bootstrap_error,
}
with _NODE_RUNTIME_LOCK:
+1
View File
@@ -25,6 +25,7 @@ dependencies = [
"pystac-client==0.8.6",
"python-dotenv==1.2.2",
"requests==2.31.0",
"PySocks==1.7.1",
"reverse-geocoder==1.5.1",
"sgp4==2.25",
"meshtastic>=2.5.0",
+21 -6
View File
@@ -869,21 +869,35 @@ async def api_get_wormhole_status(request: Request):
@router.post("/api/wormhole/join", dependencies=[Depends(require_local_operator)])
@limiter.limit("10/minute")
async def api_wormhole_join(request: Request):
from services.config import get_settings
existing = read_wormhole_settings()
updated = write_wormhole_settings(
enabled=True,
transport="direct",
socks_proxy="",
transport="tor_arti",
socks_proxy=f"socks5h://127.0.0.1:{int(get_settings().MESH_ARTI_SOCKS_PORT or 9050)}",
socks_dns=True,
anonymous_mode=False,
anonymous_mode=True,
)
transport_changed = (
str(existing.get("transport", "direct")) != "direct"
or str(existing.get("socks_proxy", "")) != ""
str(existing.get("transport", "direct")) != "tor_arti"
or str(existing.get("socks_proxy", "")) != str(updated.get("socks_proxy", ""))
or bool(existing.get("socks_dns", True)) is not True
or bool(existing.get("anonymous_mode", False)) is not False
or bool(existing.get("anonymous_mode", False)) is not True
or bool(existing.get("enabled", False)) is not True
)
tor_result: dict[str, Any] = {"ok": False, "detail": "not started"}
try:
import asyncio
from routers.ai_intel import _write_env_value
from services.tor_hidden_service import tor_service
tor_result = await asyncio.to_thread(tor_service.start)
if tor_result.get("ok"):
_write_env_value("MESH_ARTI_ENABLED", "true")
get_settings.cache_clear()
except Exception as exc:
tor_result = {"ok": False, "detail": str(exc or type(exc).__name__)}
bootstrap_wormhole_identity()
bootstrap_wormhole_persona_state()
state = (
@@ -905,6 +919,7 @@ async def api_wormhole_join(request: Request):
"identity": get_transport_identity(),
"runtime": state,
"settings": updated,
"tor": tor_result,
}
+8 -1
View File
@@ -32,7 +32,14 @@ class Settings(BaseSettings):
MESH_ARTI_ENABLED: bool = False
MESH_ARTI_SOCKS_PORT: int = 9050
MESH_RELAY_PEERS: str = ""
MESH_DEFAULT_SYNC_PEERS: str = "https://node.shadowbroker.info"
# Bootstrap seeds are discovery hints, not authoritative network roots.
# Nodes promote healthy discovered peers from the store/manifest over time.
MESH_BOOTSTRAP_SEED_PEERS: str = "http://gqpbunqbgtkcqilvclm3xrkt3zowjyl3s62kkktvojgvxzizamvbrqid.onion:8000"
# Legacy name kept for older compose/.env files.
MESH_DEFAULT_SYNC_PEERS: str = ""
# Infonet/Wormhole must fail closed to private transports by default.
# Set true only for local relay development or explicitly public testnets.
MESH_INFONET_ALLOW_CLEARNET_SYNC: bool = False
MESH_BOOTSTRAP_DISABLED: bool = False
MESH_BOOTSTRAP_MANIFEST_PATH: str = "data/bootstrap_peers.json"
MESH_BOOTSTRAP_SIGNER_PUBLIC_KEY: str = ""
@@ -52,7 +52,9 @@ def test_refresh_node_peer_store_promotes_manifest_peers_to_sync_only(tmp_path,
monkeypatch.setenv("MESH_BOOTSTRAP_SIGNER_PUBLIC_KEY", manifest_pub)
monkeypatch.setenv("MESH_BOOTSTRAP_MANIFEST_PATH", str(manifest_path))
monkeypatch.setenv("MESH_RELAY_PEERS", "https://operator.example")
monkeypatch.setenv("MESH_BOOTSTRAP_SEED_PEERS", "")
monkeypatch.setenv("MESH_DEFAULT_SYNC_PEERS", "")
monkeypatch.setenv("MESH_INFONET_ALLOW_CLEARNET_SYNC", "true")
get_settings.cache_clear()
try:
@@ -74,7 +76,7 @@ def test_refresh_node_peer_store_promotes_manifest_peers_to_sync_only(tmp_path,
assert [record.peer_url for record in store.records_for_bucket("push")] == ["https://operator.example"]
def test_refresh_node_peer_store_adds_default_seed_as_pull_only_peer(tmp_path, monkeypatch):
def test_refresh_node_peer_store_adds_bootstrap_seed_as_pull_only_peer(tmp_path, monkeypatch):
import main
from services.config import get_settings
from services.mesh import mesh_peer_store as peer_store_mod
@@ -82,7 +84,9 @@ def test_refresh_node_peer_store_adds_default_seed_as_pull_only_peer(tmp_path, m
peer_store_path = tmp_path / "peer_store.json"
monkeypatch.setattr(peer_store_mod, "DEFAULT_PEER_STORE_PATH", peer_store_path)
monkeypatch.setenv("MESH_RELAY_PEERS", "")
monkeypatch.setenv("MESH_DEFAULT_SYNC_PEERS", "https://node.shadowbroker.info")
monkeypatch.setenv("MESH_BOOTSTRAP_SEED_PEERS", "https://node.shadowbroker.info")
monkeypatch.setenv("MESH_DEFAULT_SYNC_PEERS", "")
monkeypatch.setenv("MESH_INFONET_ALLOW_CLEARNET_SYNC", "true")
monkeypatch.setenv("MESH_BOOTSTRAP_SIGNER_PUBLIC_KEY", "")
get_settings.cache_clear()
@@ -94,6 +98,7 @@ def test_refresh_node_peer_store_adds_default_seed_as_pull_only_peer(tmp_path, m
get_settings.cache_clear()
assert snapshot["manifest_loaded"] is False
assert snapshot["bootstrap_seed_peer_count"] == 1
assert snapshot["default_sync_peer_count"] == 1
assert snapshot["bootstrap_peer_count"] == 1
assert snapshot["sync_peer_count"] == 1
@@ -107,6 +112,36 @@ def test_refresh_node_peer_store_adds_default_seed_as_pull_only_peer(tmp_path, m
assert store.records_for_bucket("sync")[0].source == "bundle"
def test_refresh_node_peer_store_suppresses_clearnet_seed_by_default(tmp_path, monkeypatch):
import main
from services.config import get_settings
from services.mesh import mesh_peer_store as peer_store_mod
peer_store_path = tmp_path / "peer_store.json"
monkeypatch.setattr(peer_store_mod, "DEFAULT_PEER_STORE_PATH", peer_store_path)
monkeypatch.setenv("MESH_RELAY_PEERS", "")
monkeypatch.setenv("MESH_BOOTSTRAP_SEED_PEERS", "https://node.shadowbroker.info")
monkeypatch.setenv("MESH_DEFAULT_SYNC_PEERS", "")
monkeypatch.delenv("MESH_INFONET_ALLOW_CLEARNET_SYNC", raising=False)
monkeypatch.setenv("MESH_BOOTSTRAP_SIGNER_PUBLIC_KEY", "")
get_settings.cache_clear()
try:
snapshot = main._refresh_node_peer_store(now=1_750_000_000)
store = peer_store_mod.PeerStore(peer_store_path)
store.load()
finally:
get_settings.cache_clear()
assert snapshot["private_transport_required"] is True
assert snapshot["skipped_clearnet_peer_count"] == 1
assert snapshot["bootstrap_peer_count"] == 0
assert snapshot["sync_peer_count"] == 0
assert "no clearnet sync fallback" in snapshot["last_bootstrap_error"]
assert store.records_for_bucket("bootstrap") == []
assert store.records_for_bucket("sync") == []
def test_verify_peer_push_hmac_requires_allowlisted_peer(monkeypatch):
import hashlib
import hmac
@@ -172,13 +207,19 @@ def test_infonet_status_includes_node_runtime_snapshot(monkeypatch):
def test_public_sync_cycle_allows_first_node_without_peers(tmp_path, monkeypatch):
import main
from services.config import get_settings
from services.mesh import mesh_peer_store as peer_store_mod
peer_store_path = tmp_path / "peer_store.json"
monkeypatch.setattr(peer_store_mod, "DEFAULT_PEER_STORE_PATH", peer_store_path)
monkeypatch.setattr(main, "_participant_node_enabled", lambda: True)
monkeypatch.setenv("MESH_INFONET_ALLOW_CLEARNET_SYNC", "true")
get_settings.cache_clear()
result = main._run_public_sync_cycle()
try:
result = main._run_public_sync_cycle()
finally:
get_settings.cache_clear()
assert result.last_outcome == "solo"
assert result.last_error == ""