This is an Omarchy plugin that puts your ploi.io servers in the status bar. You get server health at a glance, a dashboard with monitoring sparklines, sites and certificates, and desktop notifications when something breaks.
- The bar pill shows the healthy-server count and turns urgent when a server is unreachable, a certificate is expired or a poll fails.
- The dashboard lists all servers with a text filter and drill-down views per server and per site.
- Monitoring shows CPU, RAM, disk and load sparklines for every server that has it enabled.
- Certificates are shown per site with color-coded expiry.
- SSH opens a session to a server or to a single site right from the panel.
- Restart reboots a server behind an explicit confirmation.
- Notifications report server problems, expiring certificates and failed deploys.
- The settings dialog covers the API key, the pill mode, the poll interval and the notification toggles.
You need Omarchy with a current
omarchy-shell. Everything else the plugin runs (curl, python3
and ssh) ships with the system.
omarchy plugin add https://github.com/pdaether/omarchy-ploi --enableAfter the install, left-click the pill and paste your ploi.io API key (ploi.io → Profile → API Keys).
The pill mode and the poll interval can also be set via
omarchy bar set:
| Setting | Values | Default |
|---|---|---|
pillMode |
servers (healthy count) · attention (problem count, hidden when zero) |
servers |
pollMinutes |
1 to 120 |
5 |
All traffic goes to https://ploi.io only. The API key is stored in
~/.config/omarchy/ploi/config.json with owner-only permissions.
omarchy plugin remove pdaether.ploiYour API key and a small cache survive removal, so a reinstall picks
up where you left off. Delete ~/.config/omarchy/ploi/ and
~/.cache/omarchy/ploi/ if you want no trace.
The repository root is the plugin directory, so manifest.json sits at
the root, as omarchy plugin add and the marketplace require. The
plugin is loaded through a symlink:
ln -sfn "$PWD" ~/.config/omarchy/plugins/pdaether.ploiReloading after edits:
./dev-reload # full restart with a cold QML cache (QML edits)
./dev-reload --rescan # IPC rescan only, enough for JS-only changesThe pure layers (status tones, formatting, certificate expiry math, sparkline geometry, notification diffing and sanitizer, normalizers, pagination, item caps, ssh and url grammars, request shape) run in Node without Qt:
node tests/smoke-js.js # ~349 checks, also exercises tests/fixtures/
node tests/check-textformat.mjs # every QML Text sink pins Text.PlainText
node tests/check-tree.mjs # published-tree hygiene (scanner limits)The shell registers its qs.Commons / qs.Ui modules only at
runtime, so a bare qmllint cannot resolve the kit types. With
qt6-tools 6.11.2, a Panel-rooted file can even make that
unresolved-type resolution spin forever. dev-lint works around both
problems. It builds a throwaway import-path shim (qs → /usr/share/omarchy/shell, which ships real qmldir files) so the kit
resolves statically, and it then lints in about 1 s with real signal:
./dev-lintThe remaining warnings are missing-property hits on the var-typed
theme singletons such as Color.popups.text and Style.font.family.
That is the shared environmental baseline that first-party plugins
show under the same lint.
tests/mock-server.mjs serves the sanitized fixtures over the real
route shapes (Bearer auth and ?simulate=429 / ?simulate=500 fault
injection) so the panel can be exercised offline. POST /api/servers/{id}/restart reports the server as rebooting for about
25 s before returning it to its fixture status, so the restart
follow-up flow can be watched end to end.
The easy way is a single command. It starts the server, swaps the config to the mock key and restores your real config on exit (Ctrl+C):
./dev-mock # or: ./dev-mock --port 9000Run the pieces by hand if you want them separately:
node tests/mock-server.mjs --port 8787
# then point the plugin at it via ~/.config/omarchy/ploi/config.json:
# { "apiKey": "mocktoken1234567890", "devBaseUrl": "http://127.0.0.1:8787" }devBaseUrl is dev-only and loopback-gated. The service and the fetch
helper each reject any non-loopback URL, so the API key can never be
redirected at a remote host.
MIT. See LICENSE.
