Repository navigation
feat(z890): NATS leaf node for multi-node Docker networking #1034
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| 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" | ||
| }] | ||
| } |
| 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
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
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 "" | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This health check only probes the local admin endpoint, so
nats-leafbecomes healthy even when the upstream leafnode connection is still down (for example, the hostportproxywas not created or 5090 is not listening on:7422). Becausemesh-agentnow depends on this health status,make up-z890can start a fully green-looking stack that publishesmesh.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 👍 / 👎.