Skip to content

Latest commit

 

History

8 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Proxmarchy

A bar widget for Omarchy Quattro showing Proxmox VE guest status, CPU/memory/disk usage, power actions (start/stop/shutdown/ reboot), and console access (LXC shell, VM SPICE/noVNC) — at a glance.

Forked from g-desoutter/omarchy-plugin-proxmox (the read-only status widget) and extended with actions, console, host stats, and multi-cluster support.

The bar shows running / total. Click it for a panel listing every guest with live metrics, action buttons, and console access. The widget turns to the bar's alert colour when the API is unreachable, or when a guest you explicitly watch is not running.

Bar widget: guests running / total, per cluster

Panel: host stats, guest list (LXC+QEMU), and action buttons A second cluster, same widget, one instance per host

Features

  • Status: guest list (LXC + QEMU) with CPU/RAM, sorted running-first, plus a host-level CPU/RAM/disk summary for the node itself
  • Actions: start / graceful shutdown / forced stop / reboot, with a two-click confirmation on anything destructive — via a Proxmox API token scoped to VM.PowerMgmt only, no SSH involved
  • Console:
    • LXC → opens a terminal with pct enter over SSH
    • VM with SPICE configured → generates a ticket via the API and opens it with remote-viewer (from virt-viewer)
    • VM without SPICE → falls back to noVNC in a browser app window, no guest-side configuration needed
    • "Terminal on host" button for a direct SSH shell on the node
  • Multi-cluster: allowMultiple is enabled — add one widget instance per Proxmox cluster/host

Two separate credentials, two separate blast radii

Status polling and actions use different API tokens with different roles, on purpose:

  • Polling (continuous, every interval seconds) uses a token scoped to PVEAuditor — read-only. A leaked polling token exposes visibility only.
  • Actions (only invoked on an explicit click) use a separate token scoped to a custom role containing only VM.PowerMgmt — it can start/stop/reboot guests and nothing else: no config read/write, no console, no datastore access, no user/ACL management.

Console access is the one feature that genuinely needs SSH (there is no API equivalent for an interactive LXC shell or generating a local terminal session), scoped to whatever user you configure in sshUser — separate from both API tokens.

Requirements

  • curl, jq, ssh on the machine running Omarchy
  • virt-viewer (for remote-viewer, SPICE consoles) — optional, only needed for VMs listed in spiceVms
  • A Proxmox VE cluster reachable from that machine

Setup

1. Create the read-only status token

On your Proxmox node:

pveum user add omarchy@pve --comment "Omarchy bar widget"
pveum user token add omarchy@pve bar --privsep 1
pveum acl modify / --users  'omarchy@pve'     --roles PVEAuditor
pveum acl modify / --tokens 'omarchy@pve!bar' --roles PVEAuditor

The token secret is displayed once, at creation. Copy it now.

Both ACL lines are required. With privilege separation enabled, a token's effective permissions are the intersection of the user's and the token's — granting the role to only one of them yields no access at all, and /cluster/resources returns an empty list rather than a 403.

2. Create the scoped power-actions token

Same user, a second token, with a custom role containing only VM.PowerMgmt — not one of the built-in roles, which all bundle other privileges (config access, snapshots, migration, etc.) you don't want to hand to a bar widget:

pveum role add OmarchyPowerMgmt --privs VM.PowerMgmt
pveum user token add omarchy@pve actions --privsep 1
pveum acl modify / --users  'omarchy@pve'           --roles OmarchyPowerMgmt
pveum acl modify / --tokens 'omarchy@pve!actions'   --roles OmarchyPowerMgmt

Same intersection rule as above applies — both ACL lines are required.

If you don't want power actions at all, skip this step and leave actionCredentialsFile unset: the buttons will just fail cleanly on click.

3. Store both credentials outside shell.json

install -d -m 700 ~/.config/omarchy/proxmox

cat > ~/.config/omarchy/proxmox/credentials <<'EOF'
PVE_TOKEN_ID='omarchy@pve!bar'
PVE_TOKEN_SECRET='<secret from step 1>'
EOF

cat > ~/.config/omarchy/proxmox/actions-credentials <<'EOF'
PVE_TOKEN_ID='omarchy@pve!actions'
PVE_TOKEN_SECRET='<secret from step 2>'
EOF

chmod 600 ~/.config/omarchy/proxmox/credentials ~/.config/omarchy/proxmox/actions-credentials

Single quotes matter: the token id contains !, which bash would otherwise treat as history expansion.

Never put a token in shell.json. That file is plugin configuration, not a secret store, and it's the kind of file people keep in a dotfiles repo. The widget only ever learns the path to each credentials file.

4. Trust the cluster CA

Proxmox generates its own PKI at install time. Copy the cluster CA to the system trust store so TLS verification succeeds without special-casing:

# Print it on the node, paste it locally
cat /etc/pve/pve-root-ca.pem

sudo cp pve-root-ca.pem /etc/ca-certificates/trust-source/anchors/pve.crt
sudo trust extract-compat

Verify — a 401 means the TLS handshake succeeded and only authentication is missing, which is what you want at this point:

curl -s -o /dev/null -w '%{http_code}\n' https://your-node.example.com:8006/api2/json/version

The endpoint host must match the certificate's CN or a SAN entry. Check what the node presents with:

openssl s_client -connect <node>:8006 </dev/null 2>/dev/null \
  | openssl x509 -noout -subject -ext subjectAltName

If it doesn't (self-signed certs on some setups only cover the default hostname, e.g. pve/pve.local), use the node's IP as the endpoint instead of a custom hostname, or point caBundle at a local copy of the CA rather than touching the system trust store.

5. SSH access for console (optional)

Only needed if you want the console/terminal features. Passwordless SSH as sshUser (root by default) to each Proxmox node:

ssh-copy-id root@your-node.example.com

This grants the widget a full root shell on the host when you click console/terminal buttons — that's inherent to what an interactive LXC shell requires, not something a scoped API token can replace. If you're not comfortable with that, skip this step: status and actions work independently of console access.

Install

omarchy plugin add https://github.com/boyoyooo/proxmarchy.git --enable

Settings

Settings are inline on the widget's entry in ~/.config/omarchy/shell.json:

{
  "id": "io.github.boyoyooo.proxmarchy",
  "endpoint": "https://your-node.example.com:8006",
  "credentialsFile": "/home/you/.config/omarchy/proxmox/credentials",
  "actionCredentialsFile": "/home/you/.config/omarchy/proxmox/actions-credentials",
  "label": "homelab",
  "watchedGuests": [500],
  "sshUser": "root",
  "spiceVms": [110],
  "terminal": "xdg-terminal-exec",
  "browser": "chromium"
}
Key Default Description
endpoint https://proxmox.example.com:8006 PVE API base URL. Must match the certificate CN or a SAN.
credentialsFile ~/.config/omarchy/proxmox/credentials Read-only PVEAuditor token, for polling.
actionCredentialsFile (unset) Scoped VM.PowerMgmt token, for start/stop/shutdown/reboot. Leave unset to disable actions.
caBundle (system trust store) Absolute path to a CA bundle, if you did not install the CA system-wide.
interval 15 Refresh interval in seconds. Values below 10 gain nothing — see below.
label (the endpoint) Name shown in the panel header.
watchedGuests [] VMIDs whose shutdown is abnormal. Empty means never alert on a stopped guest.
sshUser root SSH user for console/terminal access only — never used for actions.
spiceVms [] VMIDs to open with SPICE (remote-viewer) instead of the noVNC fallback.
terminal xdg-terminal-exec Terminal launcher command, for LXC console and host terminal.
browser chromium Browser command, for the noVNC fallback.

allowMultiple is enabled, so you can add one widget per cluster/host.

On watchedGuests

A stopped guest is not inherently a problem — lab VMs and templates are meant to be off. The widget cannot know your intent, so you declare it: list only the VMIDs whose absence is genuinely abnormal. Everything else is ignored, and the widget stays quiet.

Notes

Refresh interval. pvestatd refreshes /cluster/resources roughly every 10 seconds, so polling faster than that re-reads the same sample. The widget floors the interval at 10s for this reason. Guest status changes, unlike metrics, are immediate — stopping a VM is reflected on the next poll, or instantly via the panel's refresh button.

Memory figures. mem is memory as seen by the hypervisor, not pressure inside the guest. A Linux guest filling its page cache, or a Windows guest caching aggressively, will read high without being short of RAM. Don't size guests from this number; check Available Memory inside the guest instead.

CPU figures. cpu is already normalised across all vCPUs assigned to the guest, so 100% means the guest is saturating its allocation — not one core.

Guest OS icons. The OS type comes from each VM's ostype config field, which is declarative rather than detected — a guest created with the wrong value shows the wrong icon. It is cached for 24 hours under $XDG_CACHE_HOME/omarchy-proxmox, so the extra request happens once per guest per day. Containers always show the container icon.

SPICE requires VirtIO GPU display. A VM set to "Standard Display" will not offer a SPICE console — switch it to "VirtIO GPU" in the VM's Display settings, or leave it out of spiceVms to use the noVNC fallback instead.

Privileges

  • The poller issues read-only GET requests through curl, using a token scoped to PVEAuditor. It never writes to the cluster.
  • The actions script issues POST .../status/{start,stop,shutdown,reboot} requests, using a separate token scoped to a custom role containing only VM.PowerMgmt. It cannot read VM config, manage snapshots, touch storage, or do anything outside power state changes — even if there were a bug in this script's own code, that's the ceiling on what it could do.
  • The console/terminal features use SSH as sshUser (root by default) — this is the one place the plugin has full host access, because an interactive LXC shell has no API equivalent. It's opt-in: skip step 5 in Setup and console buttons simply won't work, everything else still does.

Like every Omarchy plugin, this runs unsandboxed inside the long-lived shell process, with your user's permissions. Read the scripts in bin/ before enabling the plugin.

Remove

omarchy plugin remove io.github.boyoyooo.proxmarchy

This only removes the plugin files — it does not revoke the credentials or trust it created during Setup. To fully clean up, also do the following.

Proxmox side (on each host you configured):

pveum user delete omarchy@pve          # removes both API tokens (bar, actions) and their ACLs
pveum role delete OmarchyPowerMgmt     # only if you set up the actions/power-management token

If you enabled console access (SSH), Setup step 5 (ssh-copy-id) installed your local public key in that node's /root/.ssh/authorized_keys — remove it there too (passwordless root access otherwise survives the plugin):

ssh root@your-node.example.com "sed -i '/<a distinctive fragment of your pubkey>/d' ~/.ssh/authorized_keys"

Local side (on the machine that ran Omarchy):

rm -rf ~/.config/omarchy/proxmox       # the two plaintext token files from Setup step 3
rm -rf ~/.cache/omarchy-proxmox        # cached guest OS-type lookups
sudo rm -f /etc/ca-certificates/trust-source/anchors/pve.crt   # the cluster CA trusted in Setup step 4
sudo trust extract-compat
ssh-keygen -R <your-proxmox-host>      # accepted host key, if you used console access

License

MIT — see LICENSE. Includes code from g-desoutter/omarchy-plugin-proxmox (MIT).

About

Proxmox VE status, actions (scoped API token, no SSH) and console for the Omarchy bar — fork of g-desoutter/omarchy-plugin-proxmox

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages