Web dashboard for a Termux server: CPU / load / memory / battery / storage / network / runit service status, served as a small dark page that refreshes every 2s. Runs on Python's standard library.
tdash runs on a Termux instance acting as a server and monitors that machine; its services are runit-managed, and the dashboard shows their status with restart/down controls.
Grab the single-file build from the latest release
(tdash.pyz), then:
python3 tdash.pyz # or: python3 tdash.py (source checkout)
python3 tdash.pyz -p mypasswordOpen http://<device-ip>:8080/, log in as admin. Flags: --host 127.0.0.1
(localhost only), --no-stop sshd,crond (never-stoppable services),
--no-batt-api (never call termux-battery-status), --no-auth (only behind a
real auth layer AND localhost — see its warning). python3 tdash.py works
from a source checkout instead.
Configure Shizuku so tdash can read CPU/network/battery without root:
- Install and start Shizuku (wireless-ADB or root).
- Put
rish+rish_shizuku.dexon the Termux$PATH. - Keep Shizuku alive: disable its battery optimization, and set the device's default USB configuration to Charge only (it runs over a wireless-ADB connection that must not be dropped for a cable).
Rooted devices skip all of this and read /proc//sys directly.
How it works
Termux (the untrusted_app domain) can't read /proc/stat, /proc/net/dev,
or often /sys/class/power_supply/* without root. On a PermissionError,
tdash starts a resident Shizuku rish loop (ADB shell domain) that mirrors
those files plus dumpsys battery into /data/local/tmp, and reads the
copies. Battery source order: sysfs → sysfs mirror → dumpsys mirror →
termux-api. The loop writes atomically (temp-file + mv), skips unchanged
writes, exits after 5 min without viewers (heartbeat), and is restarted on
demand.
The Termux instance is the server being monitored; keep it alive with
termux-wake-lock (prevents Doze freezing it), and its services supervised
by runit. runsvdir can occasionally die; a standalone watchdog that restarts
it — independent of runit, so it survives a crash — pairs well with this:
runsvdir watchdog.
- Basic Auth — fine on a trusted LAN; for public exposure put TLS + real auth in front (e.g. Cloudflare Tunnel + Access).
- Rate-limited logins (5 fails → 5 min lockout).
X-Forwarded-Foris trusted only from the local tunnel (127.0.0.1), so LAN clients can't spoof it. - Service actions are whitelisted to restart/down;
--no-stopservices can't be stopped remotely.
Source layout: tdash.py + __main__.py (server), static/ (frontend,
loaded at startup — restart after edits), scripts/mirror.sh (rish loop).
Pre-commit lint runs py_compile, node --check, bash -n:
cp hooks/pre-commit .git/hooks/pre-commit && chmod +x .git/hooks/pre-commitPushing to main runs that lint, then rebuilds and replaces the single
latest GitHub Release asset (tdash.pyz, the runnable single file): the
old release is deleted, no version tags:
https://github.com/inchei/tdash/releases/tag/latest.
To build tdash.pyz locally:
tmp=$(mktemp -d)
cp tdash.py __main__.py "$tmp/"; cp -r static scripts "$tmp/"
python3 -m zipapp "$tmp" -o tdash.pyz -p "/usr/bin/env python3"GPL-3.0-or-later. See LICENSE.
