mirror of
https://github.com/allaunthefox/Research-Stack.git
synced 2026-07-31 03:05:21 +00:00
feat: Tailscale graceful degradation — chain never fails
RouteCost.lean: - latencyClass 4 = 'offline' (Tailscale down/unreachable) - networkLatencyCost returns qOne for offline (maximum cost) - Computation continues with local-only fallback scale_space_solver.py: - detect_tailscale(): returns available=False if not installed/running - get_latency_class(): returns 4 (offline) when Tailscale unavailable - latency_to_voltage/sigma(): map any class to FPGA parameters - Chain never raises — offline is just another latency class Verified: - Tailscale up: 4 peers detected, latency classes assigned - Tailscale down: returns class 4 (offline), computation continues - Unknown IP: returns class 4 (offline), no crash
This commit is contained in:
parent
eeec672dc7
commit
5698d6e54b
2 changed files with 103 additions and 0 deletions
|
|
@ -210,12 +210,18 @@ def fammMemoryBonus (a b : RouteNode) : Nat :=
|
|||
- near (1): same cluster, 1-10ms → qEighth
|
||||
- far (2): cross-network, 10-100ms → qQuarter
|
||||
- derp (3): DERP relay, >100ms → qHalf
|
||||
- offline (4): Tailscale down/unreachable → qOne (maximum cost)
|
||||
|
||||
When Tailscale doesn't exist or is down, the chain must not fail.
|
||||
Offline nodes get latencyClass=4, which routes computation to local-only.
|
||||
The cost is still computable — offline is just another latency class.
|
||||
|
||||
The latency class determines the FPGA voltage mode:
|
||||
- local → σ₀ (1.2V, exact)
|
||||
- near → σ₁ (1.0V, normal)
|
||||
- far → σ₂ (0.8V, approximate)
|
||||
- derp → σ₃ (0.6V, coarse)
|
||||
- offline → σ₃ (0.6V, coarse, local-only fallback)
|
||||
|
||||
This is the "coursing agent" — latency shapes the computation. -/
|
||||
def networkLatencyCost (a b : RouteNode) : Nat :=
|
||||
|
|
@ -227,6 +233,7 @@ def networkLatencyCost (a b : RouteNode) : Nat :=
|
|||
| 1 => qEighth -- near: 1-10ms
|
||||
| 2 => qQuarter -- far: 10-100ms
|
||||
| 3 => qHalf -- derp: >100ms (DERP relay)
|
||||
| 4 => qOne -- offline: Tailscale down, maximum cost
|
||||
| _ => qOne -- unknown: maximum cost
|
||||
|
||||
def weighted (weight component : Nat) : Nat :=
|
||||
|
|
|
|||
|
|
@ -15,9 +15,105 @@ smoothing. At each scale σ, nodes whose pairwise cost is below σ·max_cost
|
|||
are clustered together. The reduced problem is solved, then expanded back.
|
||||
"""
|
||||
|
||||
import json
|
||||
import math
|
||||
import subprocess
|
||||
from typing import Optional
|
||||
|
||||
# ── Tailscale Detection (graceful degradation) ──────────────────────────
|
||||
|
||||
_LATENCY_CLASSES = {
|
||||
0: {'name': 'local', 'ms_max': 1, 'voltage': 1200, 'sigma': 0.0},
|
||||
1: {'name': 'near', 'ms_max': 10, 'voltage': 1000, 'sigma': 0.25},
|
||||
2: {'name': 'far', 'ms_max': 100, 'voltage': 800, 'sigma': 0.50},
|
||||
3: {'name': 'derp', 'ms_max': 1000, 'voltage': 600, 'sigma': 1.0},
|
||||
4: {'name': 'offline', 'ms_max': None, 'voltage': 600, 'sigma': 1.0},
|
||||
}
|
||||
|
||||
|
||||
def detect_tailscale() -> dict:
|
||||
"""Detect Tailscale status. Returns dict with 'available', 'peers', 'latency_map'.
|
||||
|
||||
If Tailscale is not installed or not running, returns available=False
|
||||
with empty peers and latency_map. The chain never fails.
|
||||
"""
|
||||
result = {
|
||||
'available': False,
|
||||
'peers': {},
|
||||
'latency_map': {},
|
||||
'derp_region': None,
|
||||
}
|
||||
|
||||
# Check if tailscale binary exists
|
||||
try:
|
||||
proc = subprocess.run(
|
||||
['tailscale', 'status', '--json'],
|
||||
capture_output=True, text=True, timeout=5
|
||||
)
|
||||
if proc.returncode != 0:
|
||||
return result # tailscale not running
|
||||
except (FileNotFoundError, subprocess.TimeoutExpired):
|
||||
return result # tailscale not installed
|
||||
|
||||
try:
|
||||
status = json.loads(proc.stdout)
|
||||
result['available'] = True
|
||||
result['derp_region'] = status.get('CurrentTailnet', {}).get('Name')
|
||||
|
||||
for peer_id, peer in status.get('Peer', {}).items():
|
||||
hostname = peer.get('HostName', peer_id)
|
||||
tailscale_ip = peer.get('TailscaleIPs', [None])[0]
|
||||
relay = peer.get('Relay', '')
|
||||
latency = peer.get('CurAddr', '')
|
||||
|
||||
# Classify latency
|
||||
if not peer.get('Online', False):
|
||||
latency_class = 4 # offline
|
||||
elif relay: # DERP relay
|
||||
latency_class = 3 # derp
|
||||
elif tailscale_ip:
|
||||
latency_class = 1 # near (same tailnet)
|
||||
else:
|
||||
latency_class = 2 # far
|
||||
|
||||
result['peers'][hostname] = {
|
||||
'ip': tailscale_ip,
|
||||
'latency_class': latency_class,
|
||||
'relay': relay,
|
||||
'online': peer.get('Online', False),
|
||||
}
|
||||
if tailscale_ip:
|
||||
result['latency_map'][tailscale_ip] = latency_class
|
||||
|
||||
except (json.JSONDecodeError, KeyError, TypeError):
|
||||
pass # malformed status, return what we have
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def get_latency_class(node_ip: str, ts_status: Optional[dict] = None) -> int:
|
||||
"""Get latency class for a node. Returns 4 (offline) if Tailscale unavailable.
|
||||
|
||||
The chain never fails — offline is just another latency class.
|
||||
"""
|
||||
if ts_status is None:
|
||||
ts_status = detect_tailscale()
|
||||
|
||||
if not ts_status['available']:
|
||||
return 4 # offline — Tailscale not running
|
||||
|
||||
return ts_status['latency_map'].get(node_ip, 4) # default to offline
|
||||
|
||||
|
||||
def latency_to_voltage(latency_class: int) -> int:
|
||||
"""Map latency class to FPGA voltage in millivolts."""
|
||||
return _LATENCY_CLASSES.get(latency_class, _LATENCY_CLASSES[4])['voltage']
|
||||
|
||||
|
||||
def latency_to_sigma(latency_class: int) -> float:
|
||||
"""Map latency class to scale space sigma."""
|
||||
return _LATENCY_CLASSES.get(latency_class, _LATENCY_CLASSES[4])['sigma']
|
||||
|
||||
try:
|
||||
import numpy as np
|
||||
HAS_NUMPY = True
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue