mirror of
https://github.com/BigBodyCobain/Shadowbroker.git
synced 2026-06-21 05:20:11 +02:00
Enable zero-config Infonet fleet join for all participant nodes.
Ship sb-testnet fleet defaults, swarm/join API, NODE launcher registration step, and meshnode script defaults so users discover peers via the signed seed manifest without manual peer lists. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -230,7 +230,10 @@ AIS_API_KEY= # https://aisstream.io/ — free tier WebSocket key
|
||||
# Swarm discovery (signed peer manifest). Participants need only the public key;
|
||||
# the seed operator sets MESH_BOOTSTRAP_SIGNER_PRIVATE_KEY (never commit it).
|
||||
# Generate a fleet keypair: uv run python backend/scripts/bootstrap_manifest_helper.py generate-keypair
|
||||
# MESH_BOOTSTRAP_SIGNER_PUBLIC_KEY=
|
||||
# Public sb-testnet fleet defaults (auto-used when MESH_INFONET_FLEET_JOIN=true).
|
||||
# MESH_BOOTSTRAP_SIGNER_PUBLIC_KEY=ul1d0kj/ODPIp0OhHzX8eLAVXzJ3CVvzW1vn2IC6q3I=
|
||||
# MESH_INFONET_FLEET_JOIN=true
|
||||
# MESH_INFONET_FLEET_JOIN_DISABLED=false
|
||||
# MESH_BOOTSTRAP_SIGNER_PRIVATE_KEY= # seed only
|
||||
# MESH_BOOTSTRAP_SIGNER_ID=shadowbroker-seed
|
||||
# MESH_PEER_REGISTRY_ENABLED=true # seed only (auto-enabled when private key is set)
|
||||
|
||||
+31
-1
@@ -1289,10 +1289,14 @@ def _ensure_infonet_private_transport_ready(reason: str = "") -> bool:
|
||||
|
||||
|
||||
def _configured_bootstrap_seed_peer_urls() -> list[str]:
|
||||
from services.mesh.mesh_fleet_defaults import configured_bootstrap_seed_peers_with_fleet_default
|
||||
|
||||
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)
|
||||
return configured_bootstrap_seed_peers_with_fleet_default(
|
||||
parse_configured_relay_peers(primary or legacy)
|
||||
)
|
||||
|
||||
|
||||
def _refresh_node_peer_store(*, now: float | None = None) -> dict[str, Any]:
|
||||
@@ -9238,9 +9242,35 @@ async def api_set_node_settings(request: Request, body: NodeSettingsUpdate):
|
||||
if bool(body.enabled):
|
||||
_start_infonet_node_runtime("operator_enable")
|
||||
_kick_public_sync_background("operator_enable")
|
||||
threading.Thread(target=_swarm_bootstrap_after_transport_ready, daemon=True).start()
|
||||
return result
|
||||
|
||||
|
||||
@app.post("/api/mesh/infonet/swarm/join")
|
||||
@limiter.limit("10/minute")
|
||||
async def infonet_swarm_join(request: Request):
|
||||
"""Announce this node to the fleet seed and pull the signed peer manifest."""
|
||||
if not _participant_node_enabled():
|
||||
return {"ok": False, "detail": "participant node is disabled"}
|
||||
if _infonet_private_transport_required() and not _ensure_infonet_private_transport_ready("swarm_join"):
|
||||
return JSONResponse(
|
||||
{"ok": False, "detail": _infonet_private_transport_error()},
|
||||
status_code=503,
|
||||
)
|
||||
|
||||
from services.mesh.mesh_swarm_runtime import announce_local_peer_to_seeds, refresh_swarm_manifest_from_seeds
|
||||
|
||||
announce = await asyncio.to_thread(announce_local_peer_to_seeds, force=True)
|
||||
manifest = await asyncio.to_thread(refresh_swarm_manifest_from_seeds, force=True)
|
||||
if manifest.get("ok"):
|
||||
await asyncio.to_thread(_refresh_node_peer_store)
|
||||
return {
|
||||
"ok": bool(announce.get("ok")) or bool(manifest.get("ok")),
|
||||
"announce": announce,
|
||||
"manifest_pull": manifest,
|
||||
}
|
||||
|
||||
|
||||
@app.get("/api/settings/wormhole")
|
||||
@limiter.limit("30/minute")
|
||||
async def api_get_wormhole_settings(request: Request):
|
||||
|
||||
@@ -43,8 +43,14 @@ class Settings(BaseSettings):
|
||||
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 = ""
|
||||
# Public sb-testnet-0 fleet signer (participants). Seed operator holds the private key.
|
||||
MESH_BOOTSTRAP_SIGNER_PUBLIC_KEY: str = (
|
||||
"ul1d0kj/ODPIp0OhHzX8eLAVXzJ3CVvzW1vn2IC6q3I="
|
||||
)
|
||||
MESH_BOOTSTRAP_SIGNER_PRIVATE_KEY: str = ""
|
||||
# When true, empty MESH_PEER_PUSH_SECRET uses the public fleet HMAC for seed join/announce.
|
||||
MESH_INFONET_FLEET_JOIN: bool = True
|
||||
MESH_INFONET_FLEET_JOIN_DISABLED: bool = False
|
||||
MESH_BOOTSTRAP_SIGNER_ID: str = ""
|
||||
MESH_PEER_REGISTRY_ENABLED: bool = False
|
||||
MESH_PEER_REGISTRY_DISABLED: bool = False
|
||||
|
||||
@@ -342,7 +342,9 @@ def load_bootstrap_manifest_from_settings(*, now: float | None = None) -> Bootst
|
||||
settings = get_settings()
|
||||
if bool(getattr(settings, "MESH_BOOTSTRAP_DISABLED", False)):
|
||||
return None
|
||||
signer_public_key_b64 = str(getattr(settings, "MESH_BOOTSTRAP_SIGNER_PUBLIC_KEY", "") or "").strip()
|
||||
from services.mesh.mesh_fleet_defaults import effective_bootstrap_signer_public_key_b64
|
||||
|
||||
signer_public_key_b64 = effective_bootstrap_signer_public_key_b64()
|
||||
if not signer_public_key_b64:
|
||||
return None
|
||||
manifest_path = _resolve_manifest_path(str(getattr(settings, "MESH_BOOTSTRAP_MANIFEST_PATH", "") or ""))
|
||||
|
||||
@@ -168,9 +168,9 @@ def resolve_peer_key_for_url(peer_url: str) -> bytes:
|
||||
try:
|
||||
from services.config import get_settings
|
||||
|
||||
global_secret = str(
|
||||
getattr(get_settings(), "MESH_PEER_PUSH_SECRET", "") or ""
|
||||
).strip()
|
||||
from services.mesh.mesh_fleet_defaults import effective_peer_push_secret
|
||||
|
||||
global_secret = effective_peer_push_secret()
|
||||
except Exception:
|
||||
return b""
|
||||
if not global_secret:
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
"""Public Infonet fleet defaults for sb-testnet-0 participants.
|
||||
|
||||
Operators who run private single-node installs can set ``MESH_INFONET_FLEET_JOIN=false``
|
||||
and provide their own signer keys / peer secrets.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
FLEET_NETWORK_ID = "sb-testnet-0"
|
||||
FLEET_SEED_ONION_URL = (
|
||||
"http://gqpbunqbgtkcqilvclm3xrkt3zowjyl3s62kkktvojgvxzizamvbrqid.onion:8000"
|
||||
)
|
||||
FLEET_BOOTSTRAP_SIGNER_PUBLIC_KEY_B64 = (
|
||||
"ul1d0kj/ODPIp0OhHzX8eLAVXzJ3CVvzW1vn2IC6q3I="
|
||||
)
|
||||
# Shared fleet HMAC for sb-testnet peer announce/push/sync. Public testnet join model.
|
||||
FLEET_PEER_PUSH_SECRET = "b7GoqsvoUD9MV7tyt0ZOzMptLA84QG6KCfaV9nDqz5Y"
|
||||
|
||||
|
||||
def infonet_fleet_join_enabled() -> bool:
|
||||
try:
|
||||
from services.config import get_settings
|
||||
|
||||
if bool(getattr(get_settings(), "MESH_INFONET_FLEET_JOIN_DISABLED", False)):
|
||||
return False
|
||||
return bool(getattr(get_settings(), "MESH_INFONET_FLEET_JOIN", True))
|
||||
except Exception:
|
||||
return True
|
||||
|
||||
|
||||
def effective_bootstrap_signer_public_key_b64() -> str:
|
||||
try:
|
||||
from services.config import get_settings
|
||||
|
||||
configured = str(getattr(get_settings(), "MESH_BOOTSTRAP_SIGNER_PUBLIC_KEY", "") or "").strip()
|
||||
if configured:
|
||||
return configured
|
||||
except Exception:
|
||||
pass
|
||||
if infonet_fleet_join_enabled():
|
||||
return FLEET_BOOTSTRAP_SIGNER_PUBLIC_KEY_B64
|
||||
return ""
|
||||
|
||||
|
||||
def effective_peer_push_secret() -> str:
|
||||
try:
|
||||
from services.config import get_settings
|
||||
|
||||
configured = str(getattr(get_settings(), "MESH_PEER_PUSH_SECRET", "") or "").strip()
|
||||
if configured:
|
||||
return configured
|
||||
except Exception:
|
||||
pass
|
||||
if infonet_fleet_join_enabled():
|
||||
return FLEET_PEER_PUSH_SECRET
|
||||
return ""
|
||||
|
||||
|
||||
def configured_bootstrap_seed_peers_with_fleet_default(peers: list[str]) -> list[str]:
|
||||
if peers:
|
||||
return peers
|
||||
if infonet_fleet_join_enabled():
|
||||
return [FLEET_SEED_ONION_URL]
|
||||
return []
|
||||
@@ -50,7 +50,9 @@ def _manifest_path() -> str:
|
||||
|
||||
|
||||
def _signer_public_key_b64() -> str:
|
||||
return str(getattr(get_settings(), "MESH_BOOTSTRAP_SIGNER_PUBLIC_KEY", "") or "").strip()
|
||||
from services.mesh.mesh_fleet_defaults import effective_bootstrap_signer_public_key_b64
|
||||
|
||||
return effective_bootstrap_signer_public_key_b64()
|
||||
|
||||
|
||||
def _signer_private_key_b64() -> str:
|
||||
@@ -67,10 +69,14 @@ def _private_transport_required() -> bool:
|
||||
|
||||
|
||||
def _configured_seed_peer_urls() -> list[str]:
|
||||
from services.mesh.mesh_fleet_defaults import configured_bootstrap_seed_peers_with_fleet_default
|
||||
|
||||
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)
|
||||
return configured_bootstrap_seed_peers_with_fleet_default(
|
||||
parse_configured_relay_peers(primary or legacy)
|
||||
)
|
||||
|
||||
|
||||
def _seed_manifest_peers() -> list[dict[str, str]]:
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
from services.mesh.mesh_fleet_defaults import (
|
||||
FLEET_PEER_PUSH_SECRET,
|
||||
effective_bootstrap_signer_public_key_b64,
|
||||
effective_peer_push_secret,
|
||||
infonet_fleet_join_enabled,
|
||||
)
|
||||
|
||||
|
||||
def test_fleet_defaults_apply_when_join_enabled(monkeypatch):
|
||||
from services.config import get_settings
|
||||
|
||||
monkeypatch.delenv("MESH_BOOTSTRAP_SIGNER_PUBLIC_KEY", raising=False)
|
||||
monkeypatch.delenv("MESH_PEER_PUSH_SECRET", raising=False)
|
||||
monkeypatch.setenv("MESH_INFONET_FLEET_JOIN", "true")
|
||||
get_settings.cache_clear()
|
||||
try:
|
||||
assert infonet_fleet_join_enabled() is True
|
||||
assert effective_bootstrap_signer_public_key_b64()
|
||||
assert effective_peer_push_secret() == FLEET_PEER_PUSH_SECRET
|
||||
finally:
|
||||
get_settings.cache_clear()
|
||||
|
||||
|
||||
def test_fleet_defaults_disabled(monkeypatch):
|
||||
from services.config import get_settings
|
||||
|
||||
monkeypatch.setenv("MESH_BOOTSTRAP_SIGNER_PUBLIC_KEY", "")
|
||||
monkeypatch.setenv("MESH_PEER_PUSH_SECRET", "")
|
||||
monkeypatch.setenv("MESH_INFONET_FLEET_JOIN_DISABLED", "true")
|
||||
get_settings.cache_clear()
|
||||
try:
|
||||
assert infonet_fleet_join_enabled() is False
|
||||
assert effective_peer_push_secret() == ""
|
||||
finally:
|
||||
get_settings.cache_clear()
|
||||
@@ -102,6 +102,7 @@ def test_refresh_node_peer_store_promotes_manifest_peers_to_sync_only(tmp_path,
|
||||
monkeypatch.setenv("MESH_BOOTSTRAP_SEED_PEERS", "")
|
||||
monkeypatch.setenv("MESH_DEFAULT_SYNC_PEERS", "")
|
||||
monkeypatch.setenv("MESH_INFONET_ALLOW_CLEARNET_SYNC", "true")
|
||||
monkeypatch.setenv("MESH_INFONET_FLEET_JOIN_DISABLED", "true")
|
||||
get_settings.cache_clear()
|
||||
|
||||
try:
|
||||
@@ -135,6 +136,7 @@ def test_refresh_node_peer_store_adds_bootstrap_seed_as_pull_only_peer(tmp_path,
|
||||
monkeypatch.setenv("MESH_DEFAULT_SYNC_PEERS", "")
|
||||
monkeypatch.setenv("MESH_INFONET_ALLOW_CLEARNET_SYNC", "true")
|
||||
monkeypatch.setenv("MESH_BOOTSTRAP_SIGNER_PUBLIC_KEY", "")
|
||||
monkeypatch.setenv("MESH_INFONET_FLEET_JOIN_DISABLED", "true")
|
||||
get_settings.cache_clear()
|
||||
|
||||
try:
|
||||
@@ -171,6 +173,7 @@ def test_refresh_node_peer_store_suppresses_clearnet_seed_by_default(tmp_path, m
|
||||
monkeypatch.setenv("MESH_DEFAULT_SYNC_PEERS", "")
|
||||
monkeypatch.delenv("MESH_INFONET_ALLOW_CLEARNET_SYNC", raising=False)
|
||||
monkeypatch.setenv("MESH_BOOTSTRAP_SIGNER_PUBLIC_KEY", "")
|
||||
monkeypatch.setenv("MESH_INFONET_FLEET_JOIN_DISABLED", "true")
|
||||
get_settings.cache_clear()
|
||||
|
||||
try:
|
||||
@@ -184,7 +187,7 @@ def test_refresh_node_peer_store_suppresses_clearnet_seed_by_default(tmp_path, m
|
||||
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 snapshot["last_bootstrap_error"]
|
||||
assert store.records_for_bucket("bootstrap") == []
|
||||
assert store.records_for_bucket("sync") == []
|
||||
|
||||
|
||||
+5
-1
@@ -29,9 +29,13 @@ services:
|
||||
- CORS_ORIGINS=${CORS_ORIGINS:-}
|
||||
# Private Infonet bootstrap seeds. Seeds are discovery hints, not fixed roots.
|
||||
- MESH_BOOTSTRAP_SEED_PEERS=${MESH_BOOTSTRAP_SEED_PEERS:-http://gqpbunqbgtkcqilvclm3xrkt3zowjyl3s62kkktvojgvxzizamvbrqid.onion:8000}
|
||||
- MESH_BOOTSTRAP_SIGNER_PUBLIC_KEY=${MESH_BOOTSTRAP_SIGNER_PUBLIC_KEY:-ul1d0kj/ODPIp0OhHzX8eLAVXzJ3CVvzW1vn2IC6q3I=}
|
||||
- MESH_INFONET_FLEET_JOIN=${MESH_INFONET_FLEET_JOIN:-true}
|
||||
- MESH_DEFAULT_SYNC_PEERS=${MESH_DEFAULT_SYNC_PEERS:-}
|
||||
- MESH_SYNC_TIMEOUT_S=${MESH_SYNC_TIMEOUT_S:-5}
|
||||
- MESH_SYNC_TIMEOUT_S=${MESH_SYNC_TIMEOUT_S:-45}
|
||||
- MESH_RELAY_PUSH_TIMEOUT_S=${MESH_RELAY_PUSH_TIMEOUT_S:-45}
|
||||
- MESH_SYNC_MAX_PEERS_PER_CYCLE=${MESH_SYNC_MAX_PEERS_PER_CYCLE:-5}
|
||||
- MESH_SWARM_MANIFEST_PULL_INTERVAL_S=${MESH_SWARM_MANIFEST_PULL_INTERVAL_S:-300}
|
||||
# Explicitly opt into HTTPS/IP-based peer sync. Default remains private transports only.
|
||||
- MESH_INFONET_ALLOW_CLEARNET_SYNC=${MESH_INFONET_ALLOW_CLEARNET_SYNC:-false}
|
||||
# Tor/Arti SOCKS transport for private .onion Infonet sync.
|
||||
|
||||
@@ -35,6 +35,7 @@ import { purgeBrowserContactGraph, purgeBrowserSigningMaterial, setSecureModeCac
|
||||
import { purgeBrowserDmState } from '@/mesh/meshDmWorkerClient';
|
||||
import {
|
||||
fetchInfonetNodeStatusSnapshot,
|
||||
joinInfonetSwarm,
|
||||
startTorHiddenService,
|
||||
type InfonetNodeStatusSnapshot,
|
||||
} from '@/mesh/controlPlaneStatusClient';
|
||||
@@ -109,7 +110,7 @@ export default function TopRightControls({
|
||||
const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||
const [launcherOpen, setLauncherOpen] = useState(false);
|
||||
const [nodeStep, setNodeStep] = useState<'prompt' | 'terms' | 'activating' | 'disable'>('prompt');
|
||||
const [activatingPhase, setActivatingPhase] = useState<'keys' | 'peers' | 'sync' | 'done'>('keys');
|
||||
const [activatingPhase, setActivatingPhase] = useState<'keys' | 'peers' | 'swarm' | 'sync' | 'done'>('keys');
|
||||
const activatingPollRef = useRef<ReturnType<typeof setInterval> | null>(null);
|
||||
const activatingTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||
const [activatingTimedOut, setActivatingTimedOut] = useState(false);
|
||||
@@ -299,8 +300,11 @@ export default function TopRightControls({
|
||||
await refreshNodeStatus();
|
||||
|
||||
if (enabled) {
|
||||
// Start fast-polling for sync progress
|
||||
setActivatingPhase('swarm');
|
||||
await joinInfonetSwarm().catch(() => null);
|
||||
await refreshNodeStatus();
|
||||
setActivatingPhase('sync');
|
||||
// Start fast-polling for sync progress
|
||||
stopActivatingPolls();
|
||||
activatingPollRef.current = setInterval(async () => {
|
||||
try {
|
||||
@@ -789,7 +793,14 @@ export default function TopRightControls({
|
||||
<div className="mt-2 text-[9px] text-cyan-200/70 normal-case tracking-normal flex flex-wrap gap-x-3">
|
||||
<span>{syncOutcome.toLowerCase()}</span>
|
||||
{(nodeStatus?.total_events ?? 0) > 0 && <span>{nodeStatus?.total_events} {t('node.events')}</span>}
|
||||
{(nodeStatus?.bootstrap?.sync_peer_count ?? 0) > 0 && <span>{nodeStatus?.bootstrap?.sync_peer_count} {t('node.peers')}</span>}
|
||||
{(nodeStatus?.bootstrap?.sync_peer_count ?? 0) > 0 && (
|
||||
<span>
|
||||
{nodeStatus?.bootstrap?.sync_peer_count} {t('node.peers')}
|
||||
{(nodeStatus?.bootstrap?.swarm_sync_peer_count ?? 0) > 0
|
||||
? ` (${nodeStatus?.bootstrap?.swarm_sync_peer_count} swarm)`
|
||||
: ''}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<div className="mt-3 text-[11px] text-[var(--text-muted)] normal-case tracking-normal leading-[1.8]">
|
||||
{t('node.keepSyncing')}
|
||||
@@ -855,9 +866,32 @@ export default function TopRightControls({
|
||||
: t('node.peersReady')}
|
||||
</span>
|
||||
</div>
|
||||
{/* Step: Register with swarm */}
|
||||
<div className="flex items-center gap-3 text-[10px] font-mono">
|
||||
{activatingPhase === 'keys' || activatingPhase === 'peers' ? (
|
||||
<span className="w-[11px] h-[11px] shrink-0" />
|
||||
) : activatingPhase === 'swarm' ? (
|
||||
<RefreshCw size={11} className="text-cyan-400 animate-spin shrink-0" />
|
||||
) : (
|
||||
<CheckCircle2 size={11} className="text-green-400 shrink-0" />
|
||||
)}
|
||||
<span className={
|
||||
activatingPhase === 'keys' || activatingPhase === 'peers' ? 'text-[var(--text-muted)]'
|
||||
: activatingPhase === 'swarm' ? 'text-cyan-300'
|
||||
: 'text-green-300'
|
||||
}>
|
||||
{activatingPhase === 'swarm'
|
||||
? 'Registering with swarm...'
|
||||
: activatingPhase === 'keys' || activatingPhase === 'peers'
|
||||
? 'Join private Infonet swarm'
|
||||
: `Swarm ready${(nodeStatus?.bootstrap?.swarm_sync_peer_count ?? 0) > 0
|
||||
? ` (${nodeStatus?.bootstrap?.swarm_sync_peer_count} discovered)`
|
||||
: ''}`}
|
||||
</span>
|
||||
</div>
|
||||
{/* Step: Sync chain */}
|
||||
<div className="flex items-center gap-3 text-[10px] font-mono">
|
||||
{(activatingPhase === 'keys' || activatingPhase === 'peers') ? (
|
||||
{(activatingPhase === 'keys' || activatingPhase === 'peers' || activatingPhase === 'swarm') ? (
|
||||
<span className="w-[11px] h-[11px] shrink-0" />
|
||||
) : activatingPhase === 'sync' ? (
|
||||
<RefreshCw size={11} className="text-cyan-400 animate-spin shrink-0" />
|
||||
@@ -865,7 +899,7 @@ export default function TopRightControls({
|
||||
<CheckCircle2 size={11} className="text-green-400 shrink-0" />
|
||||
)}
|
||||
<span className={
|
||||
(activatingPhase === 'keys' || activatingPhase === 'peers') ? 'text-[var(--text-muted)]'
|
||||
(activatingPhase === 'keys' || activatingPhase === 'peers' || activatingPhase === 'swarm') ? 'text-[var(--text-muted)]'
|
||||
: activatingPhase === 'sync' ? 'text-cyan-300'
|
||||
: 'text-green-300'
|
||||
}>
|
||||
|
||||
@@ -249,12 +249,41 @@ export async function startTorHiddenService(): Promise<TorHiddenServiceSnapshot>
|
||||
});
|
||||
}
|
||||
|
||||
/** Warm Tor/Arti and (re)enable the participant node so Infonet seed sync can run. */
|
||||
export interface InfonetSwarmJoinSnapshot {
|
||||
ok?: boolean;
|
||||
detail?: string;
|
||||
announce?: {
|
||||
ok?: boolean;
|
||||
peer_url?: string;
|
||||
skipped?: boolean;
|
||||
results?: Array<{ seed_peer_url?: string; ok?: boolean; status_code?: number }>;
|
||||
};
|
||||
manifest_pull?: {
|
||||
ok?: boolean;
|
||||
peer_count?: number;
|
||||
merged_peer_count?: number;
|
||||
seed_peer_url?: string;
|
||||
detail?: string;
|
||||
};
|
||||
}
|
||||
|
||||
/** Register with the fleet seed and pull the signed peer manifest. */
|
||||
export async function joinInfonetSwarm(): Promise<InfonetSwarmJoinSnapshot> {
|
||||
const result = await controlPlaneJson<InfonetSwarmJoinSnapshot>('/api/mesh/infonet/swarm/join', {
|
||||
method: 'POST',
|
||||
requireAdminSession: false,
|
||||
});
|
||||
invalidateInfonetNodeStatusCache();
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Warm Tor/Arti, enable the node, and join the private Infonet swarm. */
|
||||
export async function ensureInfonetParticipantNodeReady(): Promise<void> {
|
||||
if (!getNodeIdentity()) {
|
||||
await generateNodeKeys().catch(() => null);
|
||||
}
|
||||
await startTorHiddenService().catch(() => null);
|
||||
await setInfonetNodeEnabled(true);
|
||||
await joinInfonetSwarm().catch(() => null);
|
||||
await fetchInfonetNodeStatusSnapshot(true).catch(() => null);
|
||||
}
|
||||
|
||||
@@ -105,12 +105,19 @@ set MESH_ARTI_ENABLED=true
|
||||
set MESH_DM_HASHCHAIN_SPOOL_LIMIT=2
|
||||
set MESH_DM_HASHCHAIN_SPOOL_TTL_S=3600
|
||||
if "%MESH_BOOTSTRAP_SEED_PEERS%"=="" set MESH_BOOTSTRAP_SEED_PEERS=http://gqpbunqbgtkcqilvclm3xrkt3zowjyl3s62kkktvojgvxzizamvbrqid.onion:8000
|
||||
if "%MESH_INFONET_FLEET_JOIN%"=="" set MESH_INFONET_FLEET_JOIN=true
|
||||
if "%MESH_BOOTSTRAP_SIGNER_PUBLIC_KEY%"=="" set MESH_BOOTSTRAP_SIGNER_PUBLIC_KEY=ul1d0kj/ODPIp0OhHzX8eLAVXzJ3CVvzW1vn2IC6q3I=
|
||||
if "%MESH_SYNC_TIMEOUT_S%"=="" set MESH_SYNC_TIMEOUT_S=45
|
||||
if "%MESH_RELAY_PUSH_TIMEOUT_S%"=="" set MESH_RELAY_PUSH_TIMEOUT_S=45
|
||||
if "%MESH_SYNC_MAX_PEERS_PER_CYCLE%"=="" set MESH_SYNC_MAX_PEERS_PER_CYCLE=5
|
||||
if "%MESH_SWARM_MANIFEST_PULL_INTERVAL_S%"=="" set MESH_SWARM_MANIFEST_PULL_INTERVAL_S=300
|
||||
|
||||
echo.
|
||||
echo ===================================================
|
||||
echo Mesh node starting on port 8000
|
||||
echo Mode: MESH_ONLY (no data feeds)
|
||||
echo Bootstrap: %MESH_BOOTSTRAP_SEED_PEERS%
|
||||
echo Swarm: announce + signed manifest pull (fleet join)
|
||||
echo Press Ctrl+C to stop
|
||||
echo ===================================================
|
||||
echo.
|
||||
|
||||
@@ -60,12 +60,19 @@ export MESH_ARTI_ENABLED=true
|
||||
export MESH_DM_HASHCHAIN_SPOOL_LIMIT=2
|
||||
export MESH_DM_HASHCHAIN_SPOOL_TTL_S=3600
|
||||
export MESH_BOOTSTRAP_SEED_PEERS="${MESH_BOOTSTRAP_SEED_PEERS:-http://gqpbunqbgtkcqilvclm3xrkt3zowjyl3s62kkktvojgvxzizamvbrqid.onion:8000}"
|
||||
export MESH_INFONET_FLEET_JOIN="${MESH_INFONET_FLEET_JOIN:-true}"
|
||||
export MESH_BOOTSTRAP_SIGNER_PUBLIC_KEY="${MESH_BOOTSTRAP_SIGNER_PUBLIC_KEY:-ul1d0kj/ODPIp0OhHzX8eLAVXzJ3CVvzW1vn2IC6q3I=}"
|
||||
export MESH_SYNC_TIMEOUT_S="${MESH_SYNC_TIMEOUT_S:-45}"
|
||||
export MESH_RELAY_PUSH_TIMEOUT_S="${MESH_RELAY_PUSH_TIMEOUT_S:-45}"
|
||||
export MESH_SYNC_MAX_PEERS_PER_CYCLE="${MESH_SYNC_MAX_PEERS_PER_CYCLE:-5}"
|
||||
export MESH_SWARM_MANIFEST_PULL_INTERVAL_S="${MESH_SWARM_MANIFEST_PULL_INTERVAL_S:-300}"
|
||||
|
||||
echo ""
|
||||
echo "==================================================="
|
||||
echo " Mesh node starting on port 8000"
|
||||
echo " Mode: MESH_ONLY (no data feeds)"
|
||||
echo " Bootstrap: ${MESH_BOOTSTRAP_SEED_PEERS}"
|
||||
echo " Swarm: announce + signed manifest pull (fleet join)"
|
||||
echo " Press Ctrl+C to stop"
|
||||
echo "==================================================="
|
||||
echo ""
|
||||
|
||||
Reference in New Issue
Block a user