# Omaprox — development notes
## Next release backlog
From the full security re-scan of 2026-08-17 (nothing here is exploitable
as-is; optional hardening):
- **`Text` elements default to `AutoText`.** API-derived strings that look
like HTML (a guest named `pwned`, an error message) render styled
in the panel. Cosmetic spoofing only — no code execution. Set
`textFormat: Text.PlainText` on the row/error/name texts.
- **Warn on world-readable token file.** The README says `chmod 600` but
the plugin never checks. Stat `credentialsPath` and surface a warning in
the setup/error area when the file is group/other-readable.
- **`ssh --` separator in `bin/omaprox-ssh`.** Belt-and-suspenders only —
OpenSSH 10.5 rejects option-parsing in the `user@host` form (tested).
Add `--` before `$TARGET` in `connect()` and `ssh-copy-id` anyway.
- **`curlConfig` url quoting.** The host is interpolated into a quoted
config string without escaping. Only reachable through the user's own
config today; escape `"`/`\` if that surface ever widens.
Things that cost time here, recorded so they do not cost it twice:
- **Editing a `.js` file does not hot-reload.** Saving under
`~/.config/omarchy/plugins/` logs `Local plugin changed, reloading` and
reloads `.qml`, but an imported `.js` module keeps its old contents. Run
`omarchy restart shell` after touching `Api.js` or `Model.js`. A changed QML
*binding* can also need a restart before it re-evaluates.
- **QML load failures are silent on screen.** The widget just does not appear.
`quickshell list --all`, then `quickshell log -i -t 100`.
- **The debug hatch is `omarchy-shell io.github.andressm415.omaprox diagnose`**,
which dumps credential state, host resolution, per-type counts, the cursor
position and the last error as JSON. It is how most of the bugs below were
found.
- **curl config booleans are bare words.** `insecure = true` is rejected as
"unsupported trailing garbage" and kills the whole request.
- **Proxmox answers 401 with a body that looks like a successful empty one**, so
the HTTP status has to be captured separately — `write-out = "\n%{http_code}"`
— or a rejected token is indistinguishable from an empty cluster.
- **`hostLabel` must not shorten an address.** Trimming at the first dot turns a
perfectly good title into `10`, and the panel spent a while calling itself
`127`.
- **A `TextField` holds its own text independently of the property bound to it.**
Clearing `filter` on reopen without clearing `filterField.text` left the last
search visible in the box, and the next keystroke appended to it.
- **Check Nerd Font codepoints before using them.** Read the installed font's
charset rather than trusting a chart; a missing glyph renders as tofu or, worse,
as an unrelated character that looks deliberate.
- **`secret-tool search` splits its output across both streams.** The label and
the secret go to stdout; the *attributes* go to stderr. `2>/dev/null` throws
away the very fields you were searching for, silently. Read attributes with
`2>&1 >/dev/null`, which also keeps the secret out of the pipe.
- **A detached GUI client cannot ask for a password.** FreeRDP prompts on its
controlling terminal, so `execDetached` turned every Windows console into a
button that did nothing at all, with no error anywhere. The fix is not "always
wrap it in a terminal" but "give it a terminal only when it has something to
ask" — the helper decides by testing whether stdin is a tty.
- **Store a credential when it is verified, not when the session ends.** The
first version saved only after `xfreerdp` exited 0, which never happens when
you close the window normally, so nothing was ever remembered. `+auth-only`
checks a password in about a second without opening anything.
- **FreeRDP's exit status does not tell you why it failed.** Wrong password
gives 134 and an unreachable host gives 141 — both signal codes from an
abort, not answers. The `ERRCONNECT_*` token in the output is the real signal.
- **Do not infer success from FreeRDP's log.** `+auth-only` reports a *cancelled
connection* when it tears the link down after a login that worked, which
pattern-matches as a failure and made correct passwords look like an
unreachable host. Check the port yourself with a TCP connect, then treat only
an explicit `LOGON_FAILURE`/`ACCESS_DENIED`-class token as bad credentials and
everything else as fine.
- **Do not verify with `+auth-only` and then connect for real.** Authenticating,
dropping the connection and immediately reconnecting makes Windows take a
minute or more to admit the second one — the desktop opens as a black window
and hangs there, where a single connection is instant. Connect once and read
the outcome from that connection as it happens: a rejection lands in about
half a second, so anything still healthy after a few seconds authenticated.
- **`FileView` cannot watch a file in a directory that does not exist.**
`QFileSystemWatcher::addPath` fails silently on a missing directory and never
retries, so a plugin enabled before its config directory existed watches
nothing forever. Create the directory first.
## Marketplace
Listed via [omarchyplugins.com](https://omarchyplugins.com). Conformance:
- public repo with `manifest.json` at the root
- all eight required manifest fields: `schemaVersion`, `id`, `name`, `version`,
`author`, `description`, `kinds`, `entryPoints`
- `README.md` and `LICENSE` present
- safe install and removal — a git checkout with no install hooks
- `preview.png` for the listing card
- passes `omarchy plugin validate`
The preview image is a real screenshot of the plugin driven against a stand-in
Proxmox API serving fabricated hosts and figures. No real cluster is shown.
Category: **System**. Tags: **Quickshell**, **Bar**, **System**.
## Layout
```
manifest.json id, kind, entry point, settings schema
Panel.qml bar icon, panel, cursor, row components
Service.qml credentials, polling, per-guest fetches, console launcher
Api.js URLs, curl config, response parsing, command templates
Model.js row shaping, formatting, health rules
ProxmoxIcon.qml the mark, drawn from primitives
bin/omaprox-rdp keyring-backed credentials for Windows consoles
bin/omaprox-ssh one-time SSH key setup so consoles stop asking
```
`qs.Ui` and `qs.Commons` are imported directly — `qs` is an engine-global import
path, so a third-party plugin resolves them exactly as a first-party one does.
Nothing is vendored.