Every command PiHelm runs on your Pi

PiHelm is closed source and it asks for SSH credentials. That's a reasonable thing to be suspicious about, so here is the complete list of what it executes, generated from the source.

How it connects. Plain SSH, with a password or an RSA key in OpenSSH format. Ed25519 and ECDSA keys are not supported yet — the SSH library underneath only parses RSA. Nothing is installed on the Pi, no agent, no daemon, no cron job. There is no PiHelm server: your phone talks to your Pi and to nobody else. Credentials live in the iOS Keychain, and the host key is verified before any credential is sent.

Commands marked SUDO only run when you tap the action that needs them. The two marked SUDO -n are Health Score checks that try passwordless sudo and accept failure without prompting you. Everything else — every metric, every service check — is read-only and unelevated.

Identifying the machineOnce, when you connect.

hostnameName shown in the header
uname -rKernel version
cat /proc/device-tree/modelWhich Raspberry Pi model, so thresholds match the hardware
. /etc/os-release && echo "$PRETTY_NAME"Distribution
hostname -ILocal addresses
whoami · nprocUser and core count
uptime -p · cat /proc/uptimeUptime, and the reboot guard for alerts
tailscale ip -4Only to offer you the address for remote access. Fails silently if Tailscale isn't installed

MetricsOn the refresh interval you choose, from 3 to 30 seconds.

grep '^cpu' /proc/statCPU, total and per core
top -bn1 | grep 'Cpu(s)'CPU for the quick background sample
free -mMemory and swap
df -BM / · df -P /Disk usage
cat /proc/loadavgLoad average
cat /sys/class/thermal/thermal_zone0/tempTemperature
vcgencmd measure_temp · vcgencmd get_throttledGPU temperature and throttling / undervoltage flags
cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freqClock speed
cat /proc/net/devNetwork throughput
grep -E ' (mmcblk0|sda|nvme0n1|vda) ' /proc/diskstatsDisk I/O
findmnt -no SOURCE /Whether root is on SD, USB or NVMe

Finding your servicesOne batched command on connect, and a lighter state check every 20 seconds.

systemctl list-unit-files --type=serviceWhat's installed, and whether it's enabled at boot
systemctl list-units --all --type=serviceWhat's loaded and what state it's in
docker ps -a --format '{{.Names}}|{{.Image}}|{{.Status}}|{{.Ports}}'Containers, images, status and published ports
docker inspect --format '{{.Name}}|{{.HostConfig.RestartPolicy.Name}}|{{.State.OOMKilled}}|{{.State.ExitCode}}|{{.State.FinishedAt}}' $idsRestart policy and exit code, to tell a deliberate stop from a crash
ps -eo comm= · ss -ltn · ss -lun · cat /proc/uptimeCorroborating signals. Never used alone to identify a service

The liveness probescurl, from the Pi to its own loopback, with a 2-second cap. Only endpoints that need no credentials.

Every probe is curl -s -o /dev/null -w '%{http_code}' --connect-timeout 1 --max-time 2 against the address the container actually publishes, chosen from its port mapping rather than assumed:

Home Assistant → :8123/HTTP status only
Plex → :32400/identityUnauthenticated by design
Jellyfin → :8096/System/Info/PublicPublic endpoint
Node-RED → :1880/settingsAnswers 200 or 401; either proves it's alive
OctoPrint → :5000/api/versionNo API key needed for version
Frigate → :5000/api/versionInternal port, unauthenticated
Syncthing → :8384/rest/noauth/healthExplicitly the no-auth endpoint
Grafana → :3000/api/healthUnauthenticated by design
Transmission → :9091/transmission/rpcThe 409 handshake is proof of life

Pi-hole, AdGuard Home, Mosquitto, Zigbee2MQTT, Portainer and Uptime Kuma have no endpoint that answers without credentials, so PiHelm does not probe them and says running, unverified instead of pretending to know.

Only when you tap something

docker start|stop|restart NAMEContainer buttons
docker logs --tail 50 NAMEThe Logs button
docker pull IMAGEPulls a newer image. It does not recreate the running container — that one keeps its old image until you recreate it yourself
docker rm -f NAMERemove, behind a confirmation
systemctl start|stop|restart UNITSUDOsystemd controls
journalctl -u UNIT --no-pager -n NUnit logs
ps -eo pid,user,%cpu,%mem,comm --sort=-%cpu|-%mem --no-headers | head -25Process list, sorted by whichever column you picked
kill -SIGNAL PIDSUDOKilling a process, behind a confirmation. Elevated because the list includes processes you don't own
ip -o addr · ip -o link · ip route get 8.8.8.8 · ss -tunlHThe network screen
ping -c 4 -W 2 HOSTNetwork tools · ping
traceroute -n -w 2 -m 15 HOSTNetwork tools · traceroute
dig +short A|AAAA|MX HOST, falling back to host HOST then getent hosts HOSTNetwork tools · DNS, whichever of the three exists
arp-scan --localnetSUDO, falling back to ip neigh show then arp -aNetwork tools · neighbours. The sudo attempt is sudo -n and failure is expected
for p in 22 80 443 53 25 110 143 3306 5432 6379 8080 8443 9000 27017; do timeout 1 bash -c "echo > /dev/tcp/HOST/$p"; doneNetwork tools · common-port check against a host you type
Whatever you typedThe terminal and your own saved commands run exactly what you wrote, nothing added

Health Score checksRead-only, and only when you open the Health Score. Two try sudo -n and accept failure rather than asking you for anything.

getent passwd piWhether the default account still exists
sudo -n grep PasswordAuthentication /etc/ssh/sshd_configSUDO -nWhether SSH still accepts passwords. On stock Raspberry Pi OS that file is world-readable and sudo is unnecessary; the elevation is there for systems that lock it down, and it gives up quietly
sudo -n trueSUDO -nTests whether sudo needs a password at all
stat -c %Y /var/cache/apt/pkgcache.binHow long since the last apt update

How sudo is handled

If you saved a sudo password, it is piped to sudo -S for that one command and never written anywhere on the Pi. If you didn't, PiHelm tries sudo -n, which only works when your user already has passwordless sudo, and gives up cleanly when it doesn't. It never edits sudoers, never installs a helper, and never keeps a session open.