From 884a913a254098671542c813596155c2bf5688f8 Mon Sep 17 00:00:00 2001 From: POWERFULMOVES Date: Thu, 19 Mar 2026 17:14:45 -0400 Subject: [PATCH 1/2] feat(z890): add NATS leaf node config and compose service NATS leaf node bridges Docker bridge network to the 5090 hub via the leafnodes protocol. Solves Docker Desktop WSL2 limitation where containers cannot route to LAN/Tailscale IPs. - Add nats-leaf-z890.conf with JetStream, auth, upstream remote - Add nats-leaf service to docker-compose.z890.yml (nats:2.11.8-alpine) - Update mesh-agent with depends_on nats-leaf - Add nats-leaf-data volume for JetStream persistence Co-Authored-By: Claude Opus 4.6 (1M context) --- pmoves/configs/nats-leaf-z890.conf | 54 ++++++++++++++++++++++++++++++ pmoves/docker-compose.z890.yml | 35 +++++++++++++++++-- 2 files changed, 86 insertions(+), 3 deletions(-) create mode 100644 pmoves/configs/nats-leaf-z890.conf diff --git a/pmoves/configs/nats-leaf-z890.conf b/pmoves/configs/nats-leaf-z890.conf new file mode 100644 index 000000000..e50feed34 --- /dev/null +++ b/pmoves/configs/nats-leaf-z890.conf @@ -0,0 +1,54 @@ +# ═══════════════════════════════════════════════════════════════════════════════ +# PMOVES.AI — Z890 NATS Leaf Node Configuration +# ═══════════════════════════════════════════════════════════════════════════════ +# +# Local NATS instance for z890 Docker containers. Bridges the Docker bridge +# network to the upstream NATS hub on 5090 via the leafnodes protocol. +# +# Architecture: +# 5090 (hub) <──leafnodes──> z890 (leaf) <──client──> containers +# :7422 :4222 nats-leaf:4222 +# +# Prerequisites: +# 1. 5090 NATS hub — add to config: leafnodes { port: 7422 } +# 2. z890 host — port proxy (Admin PowerShell): +# netsh interface portproxy add v4tov4 ^ +# listenport=7422 listenaddress=0.0.0.0 ^ +# connectport=7422 connectaddress=192.168.1.65 +# OR via Tailscale: +# netsh interface portproxy add v4tov4 ^ +# listenport=7422 listenaddress=0.0.0.0 ^ +# connectport=7422 connectaddress=100.73.74.3 +# +# The leaf retries upstream indefinitely — works standalone until hub is ready. +# ═══════════════════════════════════════════════════════════════════════════════ + +server_name: z890-leaf +port: 4222 +http_port: 8222 + +# ── JetStream (local cache — survives upstream disconnects) ───────────────── +jetstream { + store_dir: /data/jetstream + max_mem: 256MB + max_file: 1GB +} + +# ── Authorization (matches upstream credentials) ──────────────────────────── +authorization { + user: nats + password: pmoves +} + +# ── Upstream Leaf Connection ──────────────────────────────────────────────── +# Connects to 5090 hub via host.docker.internal (Docker → Windows host) +# then via netsh portproxy (Windows host → 5090 LAN IP). +# NATS reconnects automatically on failure — no manual intervention needed. +# +# NOTE: Leafnode auth is separate from client auth. The hub's leafnodes +# block has authorization configured, so credentials are required here. +leafnodes { + remotes [{ + url: "nats://nats:pmoves@host.docker.internal:7422" + }] +} diff --git a/pmoves/docker-compose.z890.yml b/pmoves/docker-compose.z890.yml index 74ddaa048..63ca02842 100644 --- a/pmoves/docker-compose.z890.yml +++ b/pmoves/docker-compose.z890.yml @@ -14,12 +14,38 @@ # docker compose -f docker-compose.z890.yml --env-file env.z890 up -d services: + # ── NATS Leaf Node ────────────────────────────────────────────────────────── + # Local NATS instance bridging Docker containers to 5090 hub via leafnodes. + # Containers connect here (nats-leaf:4222) instead of crossing the Docker + # bridge → LAN boundary which Docker Desktop WSL2 can't route. + nats-leaf: + image: nats:2.11.8-alpine + restart: unless-stopped + command: ["-c", "/etc/nats/leaf.conf"] + extra_hosts: + - "host.docker.internal:host-gateway" + volumes: + - ./configs/nats-leaf-z890.conf:/etc/nats/leaf.conf:ro + - nats-leaf-data:/data + ports: + - "127.0.0.1:4222:4222" + - "127.0.0.1:8222:8222" + networks: + - z890_net + healthcheck: + test: ["CMD-SHELL", "wget --no-verbose --tries=1 --spider http://localhost:8222/varz || exit 1"] + interval: 20s + timeout: 5s + retries: 5 + start_period: 10s + pmoves-ollama: image: ${PMOVES_OLLAMA_IMAGE:-ollama/ollama:0.18.0} restart: unless-stopped env_file: - env.shared - env.tier-llm + - env.z890 cap_drop: [ALL] cap_add: [NET_BIND_SERVICE, CHOWN, SETGID, SETUID] security_opt: [no-new-privileges:true] @@ -60,6 +86,7 @@ services: env_file: - env.shared - env.tier-agent + - env.z890 cap_drop: [ALL] cap_add: [NET_BIND_SERVICE] read_only: true @@ -71,10 +98,11 @@ services: - NODE_NAME=${NODE_NAME:-pmoves-3090ti} - NODE_TYPE=${NODE_TYPE:-gpu-desktop} - NODE_CAPABILITIES=${NODE_CAPABILITIES:-cuda,rtx3090ti,24gb-vram} - - NATS_URL=${NATS_URL} - - HIRAG_URL=${HIRAG_URL:-http://hi-rag-gateway-v2-gpu:8086} + # NATS_URL comes from env.z890 → nats://nats:pmoves@nats-leaf:4222 - ANNOUNCE_SEC=${ANNOUNCE_SEC:-15} - # No depends_on — NATS runs on main PC, not this node + depends_on: + nats-leaf: + condition: service_healthy networks: - z890_net healthcheck: @@ -86,6 +114,7 @@ services: volumes: pmoves-ollama-models: {} + nats-leaf-data: {} networks: z890_net: From 24fed2ca2a0734fe31ca17d658cf61bfc7f2eb48 Mon Sep 17 00:00:00 2001 From: POWERFULMOVES Date: Thu, 19 Mar 2026 17:15:25 -0400 Subject: [PATCH 2/2] feat(z890): add host network setup script with Make targets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Idempotent PowerShell script configures netsh port proxies for NATS leafnode upstream connection (Docker → host → 5090 LAN). - Add z890_host_setup.ps1 with admin check, -Verify, -Remove modes - Add Make targets: z890-host-setup, z890-host-verify, z890-host-remove - Update LOAD_ENV_Z890 to source env.z890 for correct variable layering - Update up-z890/down-z890/ps-z890/logs-z890 to use LOAD_ENV_Z890 Co-Authored-By: Claude Opus 4.6 (1M context) --- pmoves/Makefile | 28 +++-- pmoves/scripts/z890_host_setup.ps1 | 168 +++++++++++++++++++++++++++++ 2 files changed, 190 insertions(+), 6 deletions(-) create mode 100644 pmoves/scripts/z890_host_setup.ps1 diff --git a/pmoves/Makefile b/pmoves/Makefile index 9863c970c..3bbcb6d55 100644 --- a/pmoves/Makefile +++ b/pmoves/Makefile @@ -2591,25 +2591,41 @@ vibevoice-smoke: ## Check VibeVoice realtime /config (requires VibeVoice running # Z890 / RTX 3090 Ti GPU NODE # ============================================================================ +# Z890: env.z890 is sourced AFTER with-env.sh so node-specific values +# (NATS_URL, QDRANT_URL, etc.) override the defaults from env.shared. +# Shell env beats docker compose env_file, so both must agree. Z890_COMPOSE := -f docker-compose.z890.yml --env-file env.z890 +LOAD_ENV_Z890 = $(LOAD_ENV_SHARED) set -a && . ./env.z890 && set +a && + +.PHONY: z890-host-setup +z890-host-setup: ## [Admin] Configure z890 host netsh port proxies for NATS leafnode + @powershell -ExecutionPolicy Bypass -File scripts/z890_host_setup.ps1 + +.PHONY: z890-host-verify +z890-host-verify: ## Verify z890 host port proxies are configured + @powershell -ExecutionPolicy Bypass -File scripts/z890_host_setup.ps1 -Verify + +.PHONY: z890-host-remove +z890-host-remove: ## [Admin] Remove z890 host port proxies + @powershell -ExecutionPolicy Bypass -File scripts/z890_host_setup.ps1 -Remove .PHONY: up-z890 -up-z890: ## Start Z890 GPU node (Ollama + Mesh Agent, data/agents from main PC) - @$(LOAD_ENV_SHARED) docker compose -p $(PROJECT)-z890 $(Z890_COMPOSE) up -d - @echo "✔ Z890 node started (Ollama + Mesh Agent). Data services from main PC." +up-z890: ## Start Z890 GPU node (Ollama + NATS Leaf + Mesh Agent + Container Agent) + @$(LOAD_ENV_Z890) docker compose -p $(PROJECT)-z890 $(Z890_COMPOSE) up -d --force-recreate + @echo "✔ Z890 node started (NATS Leaf + Ollama + Mesh Agent + Container Agent)." .PHONY: down-z890 down-z890: ## Stop Z890 GPU node services - @$(LOAD_ENV_SHARED) docker compose -p $(PROJECT)-z890 $(Z890_COMPOSE) down + @$(LOAD_ENV_Z890) docker compose -p $(PROJECT)-z890 $(Z890_COMPOSE) down @echo "✔ Z890 node stopped." .PHONY: ps-z890 ps-z890: ## Show Z890 node service status - @$(LOAD_ENV_SHARED) docker compose -p $(PROJECT)-z890 $(Z890_COMPOSE) ps + @$(LOAD_ENV_Z890) docker compose -p $(PROJECT)-z890 $(Z890_COMPOSE) ps .PHONY: logs-z890 logs-z890: ## Tail Z890 GPU node logs - @$(LOAD_ENV_SHARED) docker compose -p $(PROJECT)-z890 $(Z890_COMPOSE) logs -f --tail=50 + @$(LOAD_ENV_Z890) docker compose -p $(PROJECT)-z890 $(Z890_COMPOSE) logs -f --tail=50 # Status helper ps: diff --git a/pmoves/scripts/z890_host_setup.ps1 b/pmoves/scripts/z890_host_setup.ps1 new file mode 100644 index 000000000..55c6dbaa4 --- /dev/null +++ b/pmoves/scripts/z890_host_setup.ps1 @@ -0,0 +1,168 @@ +# ═══════════════════════════════════════════════════════════════════════════════ +# PMOVES.AI -- Z890 Host Network Setup +# ═══════════════════════════════════════════════════════════════════════════════ +# +# One-time host-side setup for Z890 node. Configures netsh port proxies so +# Docker containers (running behind WSL2's virtual network) can reach the +# 5090 NATS hub via host.docker.internal. +# +# Requires: Admin PowerShell +# Idempotent: safe to re-run -- skips rules that already exist. +# +# Usage: +# make -C pmoves z890-host-setup +# # or directly: +# powershell -ExecutionPolicy Bypass -File pmoves/scripts/z890_host_setup.ps1 +# +# To remove all rules: +# powershell -ExecutionPolicy Bypass -File pmoves/scripts/z890_host_setup.ps1 -Remove +# ═══════════════════════════════════════════════════════════════════════════════ + +Param( + [switch]$Remove, + [switch]$Verify, + [string]$UpstreamIP = "192.168.1.65" +) + +$ErrorActionPreference = 'Stop' + +# ── Admin check ───────────────────────────────────────────────────────────── + +function Is-Admin { + $id = [Security.Principal.WindowsIdentity]::GetCurrent() + $p = New-Object Security.Principal.WindowsPrincipal($id) + return $p.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) +} + +if (-not $Verify -and -not (Is-Admin)) { + Write-Host "[ERROR] This script requires Administrator privileges." -ForegroundColor Red + Write-Host " Right-click PowerShell -> 'Run as Administrator', then re-run." -ForegroundColor Yellow + exit 1 +} + +# ── Port proxy rules ──────────────────────────────────────────────────────── +# Each entry: [listenport, connectport, connectaddress, description] + +$rules = @( + ,@(7422, 7422, $UpstreamIP, "NATS leafnode (z890 leaf -> 5090 hub)") +) + +# ── Helpers ────────────────────────────────────────────────────────────────── + +function Get-ExistingProxies { + $raw = netsh interface portproxy show v4tov4 2>&1 + return $raw +} + +function Test-RuleExists { + Param([int]$Port) + $proxies = Get-ExistingProxies + foreach ($line in $proxies) { + if ($line -match "0\.0\.0\.0\s+$Port\s+") { + return $true + } + } + return $false +} + +function Add-ProxyRule { + Param([int]$ListenPort, [int]$ConnectPort, [string]$ConnectAddr, [string]$Desc) + + if (Test-RuleExists -Port $ListenPort) { + Write-Host ("[SKIP] Port $ListenPort already proxied -- $Desc") -ForegroundColor Green + return + } + + Write-Host ("[ADD] 0.0.0.0:$ListenPort -> ${ConnectAddr}:$ConnectPort -- $Desc") -ForegroundColor Cyan + netsh interface portproxy add v4tov4 ` + listenport=$ListenPort listenaddress=0.0.0.0 ` + connectport=$ConnectPort connectaddress=$ConnectAddr | Out-Null + + if (Test-RuleExists -Port $ListenPort) { + Write-Host ("[OK] Rule added successfully.") -ForegroundColor Green + } else { + Write-Host ("[FAIL] Rule not created -- check netsh output.") -ForegroundColor Red + } +} + +function Remove-ProxyRule { + Param([int]$ListenPort, [string]$Desc) + + if (-not (Test-RuleExists -Port $ListenPort)) { + Write-Host ("[SKIP] Port $ListenPort not proxied -- nothing to remove.") -ForegroundColor Yellow + return + } + + Write-Host ("[DEL] Removing proxy on port $ListenPort -- $Desc") -ForegroundColor Yellow + netsh interface portproxy delete v4tov4 ` + listenport=$ListenPort listenaddress=0.0.0.0 | Out-Null + + if (-not (Test-RuleExists -Port $ListenPort)) { + Write-Host ("[OK] Rule removed.") -ForegroundColor Green + } else { + Write-Host ("[FAIL] Rule still exists.") -ForegroundColor Red + } +} + +function Test-Upstream { + Param([string]$TargetHost, [int]$Port, [string]$Desc) + + Write-Host ("[TEST] ${TargetHost}:$Port -- $Desc") -ForegroundColor Cyan -NoNewline + try { + $tcp = New-Object System.Net.Sockets.TcpClient + $result = $tcp.BeginConnect($TargetHost, $Port, $null, $null) + $ok = $result.AsyncWaitHandle.WaitOne(2000) + if ($ok -and $tcp.Connected) { + $tcp.Close() + Write-Host " OPEN" -ForegroundColor Green + return $true + } + $tcp.Close() + } catch {} + Write-Host " CLOSED" -ForegroundColor Red + return $false +} + +# ── Main ───────────────────────────────────────────────────────────────────── + +Write-Host "" +Write-Host "== PMOVES Z890 Host Network Setup ==" -ForegroundColor Cyan +Write-Host (" Upstream IP: {0}" -f $UpstreamIP) +Write-Host "" + +if ($Remove) { + Write-Host "--- Removing port proxy rules ---" -ForegroundColor Yellow + foreach ($r in $rules) { + Remove-ProxyRule -ListenPort $r[0] -Desc $r[3] + } +} elseif ($Verify) { + Write-Host "--- Verifying port proxy rules ---" -ForegroundColor Cyan + foreach ($r in $rules) { + $exists = Test-RuleExists -Port $r[0] + if ($exists) { + Write-Host ("[OK] Port $($r[0]) proxied -- $($r[3])") -ForegroundColor Green + } else { + Write-Host ("[MISS] Port $($r[0]) NOT proxied -- $($r[3])") -ForegroundColor Red + } + } +} else { + Write-Host "--- Configuring port proxy rules ---" -ForegroundColor Cyan + foreach ($r in $rules) { + Add-ProxyRule -ListenPort $r[0] -ConnectPort $r[1] -ConnectAddr $r[2] -Desc $r[3] + } +} + +# Always show current state and test connectivity +Write-Host "" +Write-Host "--- Current port proxies ---" -ForegroundColor Cyan +netsh interface portproxy show v4tov4 + +Write-Host "" +Write-Host "--- Upstream connectivity ---" -ForegroundColor Cyan +Test-Upstream -TargetHost $UpstreamIP -Port 7422 -Desc "5090 NATS leafnode" +Test-Upstream -TargetHost $UpstreamIP -Port 4222 -Desc "5090 NATS client" + +Write-Host "" +Write-Host "Done. Next: docker compose -f docker-compose.z890.yml --env-file env.z890 up -d" -ForegroundColor Green +Write-Host "Or: make -C pmoves up-z890" -ForegroundColor Green +Write-Host ""