vssh 3.6.1__tar.gz → 3.6.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: vssh
3
- Version: 3.6.1
3
+ Version: 3.6.2
4
4
  Summary: Secure SSH/SCP tool with Tailscale failover, P2P transport, and MCP server
5
5
  Author-email: MeshPOP <mpop@mpop.dev>
6
6
  License: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "vssh"
7
- version = "3.6.1"
7
+ version = "3.6.2"
8
8
  description = "Secure SSH/SCP tool with Tailscale failover, P2P transport, and MCP server"
9
9
  readme = "README.md"
10
10
  license = {text = "MIT"}
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: vssh
3
- Version: 3.6.1
3
+ Version: 3.6.2
4
4
  Summary: Secure SSH/SCP tool with Tailscale failover, P2P transport, and MCP server
5
5
  Author-email: MeshPOP <mpop@mpop.dev>
6
6
  License: MIT
@@ -515,23 +515,26 @@ _name_cache = {} # name -> vpn_ip
515
515
  _name_cache_time = 0
516
516
 
517
517
  def resolve_name(host: str) -> str:
518
- """Resolve server name to VPN IP via Wire VPN /peers API (standalone)"""
518
+ """Resolve server name to VPN IP via Wire VPN /peers API (coordinator-first)
519
+
520
+ Priority:
521
+ 1. Fresh in-memory cache (< 60s) — avoids repeated network calls
522
+ 2. Wire coordinator /peers — source of truth (IPs change when nodes move)
523
+ 3. ~/.vssh/config static mapping — fallback when coordinator unreachable
524
+ """
519
525
  global _name_cache, _name_cache_time
520
526
  # Strip user@ prefix
521
527
  if '@' in host:
522
528
  host = host.split('@', 1)[1]
523
529
  if not host or host[0].isdigit():
524
530
  return host
525
- # 1. Check ~/.vssh/config local mapping (instant, no network)
526
- if host in VSSH_CONFIG:
527
- return VSSH_CONFIG[host]
528
- # Use cache if fresh (< 60s)
531
+ # 1. Use fresh cache coordinator already queried recently
529
532
  if host in _name_cache and time.time() - _name_cache_time < 60:
530
533
  return _name_cache[host]
531
- # Query Wire VPN server
534
+ # 2. Query Wire coordinator (source of truth — IPs change when nodes move)
535
+ import urllib.request
532
536
  for srv_ip in WIRE_SERVERS:
533
537
  try:
534
- import urllib.request
535
538
  req = urllib.request.Request(f"http://{srv_ip}:8790/peers")
536
539
  with urllib.request.urlopen(req, timeout=3) as resp:
537
540
  data = json.loads(resp.read())
@@ -544,9 +547,12 @@ def resolve_name(host: str) -> str:
544
547
  _name_cache_time = time.time()
545
548
  if host in _name_cache:
546
549
  return _name_cache[host]
547
- break # got peers, just no match
550
+ break # got peers from coordinator, just no match for this host
548
551
  except (OSError, ValueError, TimeoutError):
549
552
  continue
553
+ # 3. Fall back to static config (useful when all coordinators unreachable)
554
+ if host in VSSH_CONFIG:
555
+ return VSSH_CONFIG[host]
550
556
  return host
551
557
 
552
558
 
@@ -3873,11 +3879,25 @@ Env: VSSH_SECRET
3873
3879
  import socket as _sock
3874
3880
  import concurrent.futures
3875
3881
  full_mode = '--full' in args or '-f' in args
3876
- # Use vssh standalone config (no mpop dependency)
3882
+ # Build server list: coordinator first (source of truth), config as supplement
3877
3883
  _servers = {}
3884
+ import urllib.request as _ureq
3885
+ for _srv_ip in WIRE_SERVERS:
3886
+ try:
3887
+ with _ureq.urlopen(f"http://{_srv_ip}:8790/peers", timeout=3) as _r:
3888
+ _peers = json.loads(_r.read()).get("peers", [])
3889
+ for _p in _peers:
3890
+ _nn = _p.get("node_name", "")
3891
+ _vip = _p.get("vpn_ip", "")
3892
+ if _nn and _vip:
3893
+ _servers[_nn] = _vip
3894
+ break
3895
+ except (OSError, ValueError, TimeoutError):
3896
+ continue
3897
+ # Also include any config entries not in coordinator (offline/static nodes)
3878
3898
  for k, v in VSSH_CONFIG.items():
3879
- if k in ('SECRET','SSH_PORT','SCP_PORT'): continue
3880
- if v and v[0].isdigit():
3899
+ if k in ('SECRET', 'SSH_PORT', 'SCP_PORT'): continue
3900
+ if v and v[0].isdigit() and k not in _servers:
3881
3901
  _servers[k] = v
3882
3902
 
3883
3903
  def _check_node(name, ip):
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes