---
name: eqs-portable-lan-deploy
description: Deploy, update, recover, and verify the standalone EQS Windows FastAPI bundle over SSH, including system-visible PM2 commands, persistent Windows startup, narrowly scoped LAN access, and local plus LAN HTTP validation. Use when deploying or updating EQS to a Windows factory host such as 10.x.x.x, making it reachable from the LAN, or diagnosing its portable service.
---

# EQS Portable LAN Deploy

Deploy only the requested standalone bundle. Do not alter PLC data, application records, or database contents unless explicitly asked.

## Required inputs and safety boundaries

- Obtain the remote host, Windows account, authentication method, bundle root, and permitted LAN subnet from the task context.
- Do not place passwords, private keys, DSNs, or `EQS_SECRET_KEY` in commands, logs, skill files, or reports.
- Verify the host key out of band before accepting it. Do not routinely disable host-key checking.
- Treat an occupied port as user-owned until its process command line is confirmed to point to the requested bundle.
- Permit only the requested inbound subnet, such as `10.1.0.0/24`; do not open port 8000 to `Any` without explicit approval.

## SSH and PowerShell

For an interactive operator session, use OpenSSH and enter the password only at its prompt:

```powershell
ssh Administrator@10.1.0.250
```

Run remote PowerShell without profiles. For a short query:

```powershell
ssh -T Administrator@10.1.0.250 "powershell -NoProfile -Command \"Get-NetTCPConnection -LocalPort 8000 -State Listen\""
```

For a multi-line command or paths with fragile quoting, send an encoded, reviewed, non-secret PowerShell command. Never persist supplied credentials in scripts.

## First deployment: prerequisites and PM2

1. Confirm the bundle has `python\python.exe`, `scripts\portable\bootstrap.py`, `data\instance.env`, `data\app.db`, and `app\frontend-dist\index.html`.
2. Install PowerShell 7 when absent, then use its full path for deployment scripts. Keep the Windows PowerShell 5 encoded bootstrap only for SSH compatibility.
3. Make PM2 commands system-visible without changing the existing Administrator PM2 daemon ownership:

```powershell
$prefix = 'C:\ProgramData\npm'
New-Item -ItemType Directory -Force -Path $prefix
[Environment]::SetEnvironmentVariable('NPM_CONFIG_PREFIX', $prefix, 'Machine')
# Append C:\ProgramData\npm to the Machine Path once, then install:
& 'C:\Program Files\nodejs\npm.cmd' install --global --prefix $prefix pm2@7.0.3 pm2-windows-startup
& "$prefix\pm2-startup.cmd" install
& "$prefix\pm2.cmd" save
```

4. Keep `PM2_HOME` and the startup registration in the deployment account's profile. "System global" here means all local accounts can resolve `pm2`; it does not make a daemon or its process list shared across accounts.
5. Start or reload the named app with `C:\ProgramData\npm\pm2.cmd`, then run `pm2 save`. Do not rely on a process directly launched by SSH, because OpenSSH may reap it after disconnect.

## Deploy or update

1. Read only `EQS_HOST` and `EQS_PORT` from `instance.env`; require `EQS_HOST=0.0.0.0`. The standard port is `8000`.
2. Use the repo's portable update entrypoint. It must copy only changed files, preserve the instance database/configuration, make one rollback copy before applying, and reload the named PM2 process.
3. Before replacing a process, confirm its executable and command line reference the exact bundle `bootstrap.py`. Do not stop an unrelated owner of port 8000.
4. Add or retain an idempotent firewall rule for TCP on the configured port, limited to the approved subnet and Domain/Private profiles.

## Validate before declaring success

Require all checks below and report each result separately:

1. `pm2 pid <app-name>` returns a PID and `pm2 status` shows it online.
2. The host listens on `0.0.0.0:<port>`.
3. `http://127.0.0.1:<port>/api/health` returns HTTP 200 and `{"status":"ok"}`.
4. A distinct allowed LAN client receives HTTP 200 from `http://<host>:<port>/api/health`.
5. That LAN client receives HTTP 200 for `/` and the expected frontend HTML.
6. The system `NPM_CONFIG_PREFIX` is `C:\ProgramData\npm`, its Machine Path contains that directory, and `C:\ProgramData\npm\pm2.cmd --version` succeeds.

## Failure handling

- If loopback health fails, diagnose PM2 logs, the exact bundle command line, and bootstrap preflight before changing the firewall.
- If loopback works but LAN fails, verify listener address, firewall profile/scope, routing, and client IP before widening access.
- If PM2 is system-visible but cannot see the app, check which Windows account owns `PM2_HOME`; do not overwrite another account's PM2 state.
- Keep the previous bundle rollback until the update and all validations succeed.
