hardware/windows/pi-web-autostart.md
Table of Contents
pi-web autostart (Windows, logon task)
Pi Web is a local browser UI for the pi coding agent. This document describes the
autostart setup on this machine (Nuk, user domin), which starts the server
in the background at every logon, with no console window.
Set up: 2026-09-17 ยท Host: Nuk ยท User: NUK\domin ยท Windows 11 Pro (build 26200)
A second pi-web runs inside WSL on port 30142 (
pi-wsl.dph.am), using the WSL Node/pi install rather than this Windows one. See../ubuntu-wsl/services/pi-web.md.
1. What happens at logon
- Windows Task Scheduler fires the
pi-webtask 30 seconds afterNUK\dominlogs on. - The task runs a hidden Windows PowerShell process (no window, no taskbar entry).
- That process (
autostart.ps1) sets the pi-web environment variables and launches the globalpi-webshim bound to0.0.0.0:30141. - All stdout/stderr goes to a log file; the browser is not opened automatically.
Result: Pi Web is reachable at http://192.168.1.153:30141/ or http://Nuk:30141/ shortly after logging in.
2. Files
All files live in %LOCALAPPDATA%\pi-web, i.e. C:\Users\domin\AppData\Local\pi-web.
| File | Purpose |
|---|---|
autostart.ps1 |
The launcher. Environment variables live here. Starts pi-web, writes the log, propagates the exit code. Accepts optional -Port, -Hostname, -MaxLogBytes for testing. |
install-autostart.ps1 |
Registers/refreshes the pi-web scheduled task. Idempotent โ safe to re-run any time. Does not require admin rights. |
logs\pi-web.log |
Runtime output (appended, UTF-8). |
logs\pi-web.log.1 |
Previous log, kept after rotation. |
Related but separate:
| File | Purpose |
|---|---|
C:\Users\domin\Desktop\Start pi-web.cmd |
Original manual launcher (visible window, pauses on error). Has its own copy of the environment variables. |
C:\Program Files\nodejs\pi-web.cmd |
Global npm shim for @agegr/pi-web. Resolved at each start, so upgrading pi-web does not break autostart. |
3. Environment variables
Set inside autostart.ps1. Defaults in use:
| Variable | Value | Meaning |
|---|---|---|
PI_WEB_ALLOWED_HOSTS |
192.168.1.153,Nuk |
Extra hostnames accepted by the server (reverse proxies / direct LAN access). |
PI_WEB_NO_OPEN |
1 |
Never open a browser automatically. |
Commented-out options available in the file:
| Variable | Effect |
|---|---|
PI_WEB_PASSWORD |
Require a password for browser logins (HTTP Basic Auth, user pi). Recommended while bound to 0.0.0.0. |
PI_WEB_IDLE_TIMEOUT_MS |
0 disables the 10-minute idle shutdown. |
PI_WEB_SKIP_VERSION_CHECK |
1 skips update checks on every boot. |
HTTP_PROXY / HTTPS_PROXY / NO_PROXY |
Honored for server-side model and API requests if set; not configured here. |
Windows Firewall
An inbound rule named pi-web (TCP 30141) allows LAN access: Allow, TCP 30141,
profile Private, no Program restriction (see Troubleshooting below for why a
Program-scoped rule doesn't work here). Recreate it if missing:
New-NetFirewallRule -DisplayName "pi-web (TCP 30141)" `
-Direction Inbound -Action Allow -Protocol TCP -LocalPort 30141 -Profile Private
Bind address and port are not environment variables here โ the wrapper passes them as
CLI flags (-H 0.0.0.0 -p 30141). Pi Web recognizes PI_WEB_HOSTNAME/PORT as the
equivalent environment form, and the CLI sets PI_WEB_HOSTNAME for its child process.
โ ๏ธ Duplication:
PI_WEB_ALLOWED_HOSTSand the bind address also exist inDesktop\Start pi-web.cmd. If the machine's DHCP address changes, update both files (or make the desktop script delegate toautostart.ps1).
4. Scheduled task definition
Task name: pi-web (root folder \, visible in Task Scheduler).
| Field | Value |
|---|---|
| Trigger | At logon of NUK\domin, delay PT30S |
| Action | C:\WINDOWS\System32\WindowsPowerShell\v1.0\powershell.exe |
| Arguments | -NoProfile -NonInteractive -ExecutionPolicy Bypass -WindowStyle Hidden -File "C:\Users\domin\AppData\Local\pi-web\autostart.ps1" |
| Principal | NUK\domin, logon type Interactive, run level Limited (no elevation) |
| Execution time limit | PT0S (unlimited โ the server runs until logoff) |
| Multiple instances | IgnoreNew (a second run cannot double-start the server) |
| Start when available | Enabled (catches up if the logon trigger was missed) |
| Power | Starts on battery; does not stop when switching to battery |
| Restart on failure | 3 attempts, 1 minute apart |
install-autostart.ps1 is the source of truth for these settings โ edit it and re-run
rather than tweaking the task by hand.
5. Common operations
Run these in a normal (non-elevated) PowerShell window.
# Status of the task
Get-ScheduledTask -TaskName pi-web | Select-Object TaskName, State
Get-ScheduledTaskInfo -TaskName pi-web | Select-Object LastRunTime, LastTaskResult
# Start it now (stop any running pi-web first - see Troubleshooting)
Start-ScheduledTask -TaskName pi-web
# Watch the log
Get-Content "$env:LOCALAPPDATA\pi-web\logs\pi-web.log" -Encoding UTF8 -Wait
# Stop it
Stop-ScheduledTask -TaskName pi-web
# Temporarily disable / re-enable autostart
Disable-ScheduledTask -TaskName pi-web
Enable-ScheduledTask -TaskName pi-web
# Remove autostart completely
Unregister-ScheduledTask -TaskName pi-web -Confirm:$false
# Re-apply settings (e.g. after changing the delay)
& "$env:SystemRoot\System32\WindowsPowerShell\v1.0\powershell.exe" `
-NoProfile -ExecutionPolicy Bypass `
-File "$env:LOCALAPPDATA\pi-web\install-autostart.ps1"
Stopping the task stops the wrapper; the node child processes normally exit with it.
If they linger, see below.
6. Verification performed at setup
The wrapper and the real task definition were both tested on a throwaway port (30142) so the live instance on 30141 was never disturbed:
| Check | Result |
|---|---|
| Env vars present in the child process | PI_WEB_ALLOWED_HOSTS=192.168.1.153,Nuk logged |
| Server responds | HTTP 200 from http://127.0.0.1:30142/ |
| Window hidden | MainWindowHandle=0 on the wrapper process |
| Task plumbing | Duplicate task ran the wrapper via Task Scheduler, result 0x41301 (running) |
| Log encoding | Clean UTF-8, no PowerShell error-record noise |
The temporary task, its processes, and the test log were all removed afterwards.
Test the real thing at the next logon, or manually with Start-ScheduledTask.
7. Troubleshooting
| Symptom | Cause / fix |
|---|---|
| Nothing in the log after logon | Task disabled or not triggered. Check Get-ScheduledTask -TaskName pi-web. Also LastTaskResult โ 0x41301 means still running. |
EADDRINUSE / port 30141 already used |
Another pi-web is already running (e.g. the Desktop .cmd). Stop it, or Get-NetTCPConnection -LocalPort 30141 -State Listen to find the owner. |
| Browser shows "Invalid host header" | The address you used is not in PI_WEB_ALLOWED_HOSTS. Add it to autostart.ps1 and restart the task. |
| Unreachable from another device | Confirm you bound 0.0.0.0 (yes, by default) and that Windows Firewall allows port 30141 inbound. Gotcha (found 2026-09-18): C:\Program Files\nodejs is an nvm-windows symlink (currently โ C:\Users\domin\AppData\Roaming\nvm\v26.9.0). A Windows Firewall rule scoped to -Program "C:\Program Files\nodejs\node.exe" silently never matched real traffic, even though Get-Process reported that exact path. Fix: use a port-only inbound Allow rule with no -Program filter (see rule pi-web (TCP 30141), Private profile, TCP 30141, Program=Any). Symlink path matching for firewall rules is unreliable with nvm-windows โ don't scope future rules to a Program path here. |
Works via curl/on the Windows machine itself, not from another device's browser |
Testing http://<LAN-IP>:30141 from the same Windows PC isn't a real cross-device test โ Windows shortcuts self-addressed traffic internally, bypassing the normal per-NIC firewall path. Use curl -v http://192.168.1.153:30141 from the other device's terminal for a real test (bypasses browser weirdness โ shows a clean connect/HTTP status instead of a vague "site can't be reached"). |
| Works in Safari but not Chrome (Incognito) on a Mac client | Seen 2026-09-18: confirmed via curl that networking + Windows Firewall were fine; Safari loaded the page fine, Chrome Incognito still failed. Resolved by fully quitting and reopening Chrome โ stale cached state (likely HSTS/connection cache) for that host:port, not a server/firewall issue. If it recurs, try a full Chrome restart first, then chrome://net-internals/#hsts. |
Wrong Node version / pi-web not found |
The wrapper falls back to pi-web on PATH. nvm-windows repoints C:\Program Files\nodejs, so activate the intended version with nvm use <ver> and re-run the task. |
Stale node processes after stopping |
Get-Process node then Stop-Process -Id <pid>. Killing the parent does not always kill children on Windows. |
| Log grows too large | Rotation is automatic at 5 MB (pi-web.log โ pi-web.log.1). Change -MaxLogBytes in autostart.ps1. |
Useful one-liner to see what is actually listening:
Get-NetTCPConnection -LocalPort 30141 -State Listen |
ForEach-Object { Get-CimInstance Win32_Process -Filter "ProcessId=$($_.OwningProcess)" } |
Select-Object ProcessId, CommandLine
8. Security notes
- The server binds
0.0.0.0, so any device on the LAN can reach it. - No password is configured. Pi Web drives an agent with filesystem access, so
anyone on the network can execute commands through it. Set
PI_WEB_PASSWORDinautostart.ps1(a long random value) and restart the task. - Password auth does not encrypt traffic โ HTTP only. For access beyond a trusted LAN, use HTTPS via a reverse proxy or a VPN.
- The task runs unelevated (
RunLevel: Limited), so pi-web gets normal user rights only.
9. Limitations and design notes
- Logon trigger, not boot trigger. Pi Web needs the user profile and
~/.pi/agentdata, so it starts whendominlogs in. Running it with no one logged in would require a "run whether user is logged on or not" task with stored credentials. - 30-second delay exists so
PATH, Node, and the network are ready. Change withinstall-autostart.ps1 -DelaySeconds <n>. - No auto-open โ the browser is deliberately not launched on boot.
- Single instance โ
IgnoreNewplus the fixed port prevent duplicates. - Updating the package (
npm install -g @agegr/pi-web@latest) needs no changes here.