Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 22 additions & 6 deletions pmoves/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
54 changes: 54 additions & 0 deletions pmoves/configs/nats-leaf-z890.conf
Original file line number Diff line number Diff line change
@@ -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"
}]
}
35 changes: 32 additions & 3 deletions pmoves/docker-compose.z890.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
Comment on lines +35 to +36

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Fail fast when the leaf never reaches the 5090 hub

This health check only probes the local admin endpoint, so nats-leaf becomes healthy even when the upstream leafnode connection is still down (for example, the host portproxy was not created or 5090 is not listening on :7422). Because mesh-agent now depends on this health status, make up-z890 can start a fully green-looking stack that publishes mesh.node.announce.* into an isolated local broker and never forwards those announcements to 5090—the exact failure mode this change is meant to eliminate.

Useful? React with 👍 / 👎.

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]
Expand Down Expand Up @@ -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
Expand All @@ -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:
Expand All @@ -86,6 +114,7 @@ services:

volumes:
pmoves-ollama-models: {}
nats-leaf-data: {}

networks:
z890_net:
Expand Down
168 changes: 168 additions & 0 deletions pmoves/scripts/z890_host_setup.ps1
Original file line number Diff line number Diff line change
@@ -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"
Comment on lines +21 to +24

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Remove the LAN-specific default from the host proxy helper

make z890-host-setup invokes this script with its defaults, which means the generated netsh interface portproxy always targets 192.168.1.65. On any installation where the 5090 hub uses a different LAN address or must be reached over Tailscale, the advertised setup path writes a proxy to the wrong host and the leaf can never connect upstream. Since the Make target does not pass an override, this breaks the out-of-the-box flow for every non-matching network.

Useful? React with 👍 / 👎.

)

$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 ""
Loading