Players online, tick health and world state in the Omarchy 4 (Quattro) bar, with broadcast messages and a restart countdown.
Any Minecraft server that speaks RCON. Everything the widget reads comes from ordinary console commands, so vanilla, Spigot, Paper, Purpur, Folia, Fabric and Forge servers are all supported — nothing has to be installed on the server beyond enabling RCON.
Some rows depend on commands only certain server software provides. Rows the server does not answer are left out rather than rendered empty, so a vanilla server simply shows a shorter panel than a Paper one:
| Row | Command | Available on |
|---|---|---|
| Online / offline, MOTD, version, latency | Server List Ping | every server, no RCON needed |
| Players online + names | list |
every server |
| World day and clock | time query |
every server |
| TPS | tps |
Paper, Spigot, Purpur and forks |
| MSPT (tick times) | mspt |
Paper and forks |
The Server List Ping row is the reason an unreachable server is reported in well under a second: it runs first, on the game port, and RCON is skipped entirely when it fails.
- Broadcast any message to every player, from a text field or one of the presets.
- Restart countdown — broadcasts a warning at each configured mark (15, 10, 5, 3, 1 minutes by default) and can end by stopping the server.
- Save world — announces, then runs
save-all. - Kick a player from the player list, behind a confirmation.
omarchy plugin add https://github.com/webmatze/omarchy-minecraft-server-status.git --enableThe command clones the repo into ~/.config/omarchy/plugins/, validates the
manifest and asks where in the bar you want it. Plugins land disabled by
default so you can read the code first; --enable turns it on in the same
step, or do it separately:
omarchy plugin enable io.github.webmatze.omarchy-minecraft-server-statusThen configure the server connection — see Setup. Until you do, the widget shows a crossed-out block and the panel says why.
omarchy plugin update io.github.webmatze.omarchy-minecraft-server-status
omarchy plugin remove io.github.webmatze.omarchy-minecraft-server-statusRemoval takes the plugin out of shell.json and deletes its folder. Your
credentials live outside the plugin at
~/.config/omarchy/omarchy-minecraft-server-status/config.json and are
deliberately left alone — delete that file yourself if you want it gone.
In server.properties:
enable-rcon=true
rcon.port=25575
rcon.password=<a long random password>
broadcast-rcon-to-ops=falseRestart the server — RCON is only initialised at startup.
RCON is unencrypted and sends the password in clear text. Only expose the port on a network you trust. Never forward it to the internet; use a VPN or an SSH tunnel if you need it from outside.
mkdir -p ~/.config/omarchy/omarchy-minecraft-server-status
cp ~/.config/omarchy/plugins/io.github.webmatze.omarchy-minecraft-server-status/config.example.json \
~/.config/omarchy/omarchy-minecraft-server-status/config.json
chmod 600 ~/.config/omarchy/omarchy-minecraft-server-status/config.json
$EDITOR ~/.config/omarchy/omarchy-minecraft-server-status/config.json{
"host": "192.168.1.50",
"rconPort": 25575,
"gamePort": 25565,
"password": "your-rcon-password"
}The password lives here rather than in shell.json because shell.json is a
file people paste into issues and copy between machines. The helper reads the
file itself, so the password is never passed as a command-line argument and
never appears in the process list. The panel warns if the file is readable by
anyone but you.
cd ~/.config/omarchy/plugins/io.github.webmatze.omarchy-minecraft-server-status
bin/mc-status status | jq
bin/mc-status status --raw | jq .raw # every raw RCON reply, verbatim
bin/mc-status say "hello from the bar"--raw shows exactly what your server answered to each command, which is the
quickest way to see why a row is missing on your server software.
Set inline on the shell.json bar entry, or with omarchy bar set:
omarchy bar set io.github.webmatze.omarchy-minecraft-server-status refreshIntervalSec 30
omarchy bar set io.github.webmatze.omarchy-minecraft-server-status stopAfterCountdown On| Key | Default | Meaning |
|---|---|---|
credentialsPath |
"" |
Override the credentials file. Empty means ~/.config/omarchy/omarchy-minecraft-server-status/config.json. |
refreshIntervalSec |
15 |
Poll interval, clamped to at least 5. |
restartWarnings |
"15,10,5,3,1" |
Minutes before the restart at which a warning is broadcast. |
stopAfterCountdown |
"Off" |
When On, the countdown may end with /stop — always behind a confirmation. |
showPlayerCount |
true |
Show the player count next to the bar icon. |
lowTpsThreshold |
18 |
Bar icon turns urgent below this TPS. Ignored on servers with no TPS command. |
- Left click opens the panel, middle click refreshes immediately.
- In the panel:
rrefreshes,bjumps to the broadcast field,Escapecloses (once to leave the text field, again to close the panel),Tabswitches to the neighbouring bar panel. - The bar icon turns urgent when the server is down, RCON is refusing, or TPS
has fallen below
lowTpsThreshold. A crossed-out block means no credentials. - Restart countdown: with
stopAfterCountdownset toOnthe countdown ends by running/stop, which is why it asks for confirmation before starting rather than at the end — by then nobody may be at the keyboard. - The countdown lives in the shell process.
omarchy restart shell, a logout or a shell crash ends it, and the panel says so while one is running.
These are deliberately absent. The obvious source would be the
spark profiler, but spark renders /spark health
and /spark tps asynchronously: over RCON the reply is empty except for the
occasional lucky call, which would make those rows flicker in and out between
polls. A row that is sometimes there is worse than no row.
Host metrics need a host-level source — SSH into the machine, or a hypervisor API — which is outside what this plugin does today. Issues and pull requests welcome.
omarchy-shell io.github.webmatze.omarchy-minecraft-server-status diagnose | jq
omarchy-shell io.github.webmatze.omarchy-minecraft-server-status refresh
omarchy-shell io.github.webmatze.omarchy-minecraft-server-status say "test"
quickshell list --all && quickshell log -i <instance> -t 100diagnose returns the full parsed server state plus the resolved helper and
credentials paths — start there when a row is missing or the widget looks
stuck.
A QML load failure is silent on screen: the widget simply does not appear. The
quickshell log is the place to look.
For contributors: editing a .qml file reloads it automatically, but .js
files do not hot-reload — run omarchy restart shell after changing
Model.js. The Canvas in MinecraftIcon.qml also keeps its last painted
frame across a hot reload, so a shell restart is the only way to see an edited
icon.
Runs entirely as your user and never asks for elevation.
- On this machine: Python 3 standard library only. No AUR package, no pip
install, no
mcrcon. The RCON and Server List Ping clients are implemented inbin/mc-status. - On the server: RCON enabled. Nothing else — no plugin, no mod.
- Network: outbound TCP to the configured host on the RCON and game ports. Nothing else is contacted; there is no telemetry and no third-party API.
- Files: reads
~/.config/omarchy/omarchy-minecraft-server-status/config.json(orcredentialsPath). Writes nothing outside its own plugin folder.
Anything the plugin can do to your server, it does through RCON commands you
could type into the console yourself: list, tps, mspt, time query,
say, save-all, kick, and — only when you switch it on and confirm —
stop.
The server on the other end declares its own frame sizes and decides how many packets to send, so the helper bounds all of it rather than believing any of it. Every limit is far above what a healthy server produces:
| Bound | Limit |
|---|---|
| Server List Ping payload | 256 KB, rejected before anything is allocated |
| RCON packet | 8 KB (the protocol caps the payload at 4096) |
| RCON fragments per command | 256 |
| RCON reply, accumulated | 256 KB |
| Whole handshake / command / ping | 12 s monotonic deadline, re-checked per read |
A whole status poll |
20 s, one budget shared by the connect and every command |
| Text each parser scans | 256 B for counts, 1 KB for tps/mspt, 32 KB for names |
| Numbers derived from server text | must be finite, or the row is left out |
Player names kept from list |
500, each capped at 32 characters |
| MOTD tree | depth 16, 512 characters |
| JSON printed to the shell | 512 KB, re-checked by the QML side |
Server-authored strings — player names, MOTD, version — never reach a Text
that could interpret them. Our own Text elements set textFormat: Text.PlainText,
but tooltips and confirmation dialogs are kit components whose Text we do not
own, and Qt's default AutoText promotes anything markup-shaped to rich text —
enough for a player named <img src=…> to make the shell fetch a remote URL.
Angle brackets and control characters are therefore stripped before a string
leaves the helper, and again in Model.plainText() before it enters any
component the plugin does not configure. Player names additionally go through
an allowlist of the characters a Minecraft name can actually contain.
The parsers only ever scan a window large enough for a real reply, because a
reply of pure digits makes the count and triple regexes backtrack
quadratically — and that work happens after the socket read, where no deadline
covers it. Numbers are rejected unless math.isfinite, so a server cannot turn
a row into Infinity and make the printed document stop being JSON. Whatever
still goes wrong is caught and reported as {"error": ...}: the helper's
contract with the panel is always JSON on stdout and always exit 0, so a
traceback would leave the widget with nothing to parse and no reason why.
The restart countdown counts down tick by tick rather than measuring against a
wall-clock end time, and clamps any single tick to five seconds. Suspending the
laptop or an NTP step would otherwise fire every warning at once and run /stop
minutes before it was announced.
The credentials file is opened once with O_NOFOLLOW and O_NONBLOCK and
validated on that same descriptor with fstat — regular file, owned by you,
not group- or world-writable, within 64 KB — before a byte is read. A symlink,
a FIFO or a file owned by someone else is refused with an explanatory message;
merely being group-readable is a warning shown in the panel.
MIT — see LICENSE.
