Omahub
← All plugins
O

SoliDB

by Olivier Bonnaure

Live health of every SoliDB server: databases, collections, storage stats, and an SDBQL console.

Security review

No obvious issues detected

Deterministic scan — not a security guarantee

None
Risk level
None
Analyzed commit
80002b2
Scanned
1 month ago

No potentially dangerous behavior detected in the analyzed commit.

Automated analysis only — not a security guarantee.

AI advisory review

No obvious issues detected

Language-model assessment · ~deepseek/deepseek-v4-flash-latest — advisory only

Low
AI risk level
Low
Recommendation
install
Model
~deepseek/deepseek-v4-flash-latest
Analyzed commit
80002b2
Reviewed
1 month ago

The plugin is a well-engineered SoliDB client that communicates over HTTP with strong security practices: no shell injection, credentials passed via file or environment rather than argv, redirects refused to prevent header leakage, URL scheme restricted to http/https, and mutating queries gated behind confirmation or a read-only flag. No obfuscation, persistence, or destructive install-time behavior was found.

  • Credentials may be stored in plaintext in ~/.config/omarchy/soli-db/servers.json, though the file is created 0600 and the README recommends environment variables for sensitive values.
  • The console allows arbitrary SDBQL queries, including mutating ones, but these are user-initiated and protected by a confirmation dialog and per-server readOnly flag.
How this check works

This review combines the deterministic scan (the rule-based results above) with an independent look at the plugin's code by a language model. The model reads a trimmed sample of the repository's files, the manifest, and the README, then gives a plain-language risk level and a recommendation: install (no notable danger), review (look closer first), or avoid (clearly dangerous).

It runs on the same analyzed commit as the deterministic scan and is strictly advisory — it is not a security guarantee and never blocks a plugin by itself. A human moderator still reviews plugins before they are listed.

AI advisory only — automated analysis, not a security guarantee.

Install
$ omarchy plugin add https://github.com/solisoft/omarchy-soli-db --enable
Developer Tools #bar #quickshell #system

SoliDB bar widget

Reads the SoliDB HTTP API and surfaces every configured server in the Omarchy bar — databases, collections, storage and traffic — with a full-screen SDBQL console one keystroke away.

SoliDB: a bar widget listing databases and collections, and the SDBQL console showing a query and its results as a table

The preview is drawn, not screenshotted: tools/make-preview.py rebuilds it from the geometry in Panel.qml and Console.qml, so no real instance's database names, counts, or sizes appear in it. Pass a theme to render it in another palette (tools/make-preview.py matte-black); it writes preview.svg and preview.png and needs rsvg-convert plus a Nerd Font.

  • Service.qml polls /_api/health, /_api/cluster/status, and /_api/databases in one helper round-trip so liveness, statistics, and the database list can never disagree with each other. Collections are fetched on demand when a row is expanded.
  • solidb-fetch.py is the only thing that speaks HTTP. It takes its whole job from the environment and uses urllib, so no credential and no character of user-typed SDBQL ever becomes a shell word.
  • Model.js decodes, parses, and formats. Pure functions, no QML imports.
  • Panel.qml is the bar button plus the popup; Console.qml is the overlay; QueryEditor.qml is the multi-line editor the shared kit does not provide.

Nerd Font glyphs are written as \uXXXX escapes rather than literal characters. Basic-plane private-use characters do not survive every tool that edits these files, and a stripped glyph leaves a silently blank icon.

Requirements

  • Omarchy Quattro with omarchy-shell
  • A reachable SoliDB server
  • bash, python3, and wl-clipboard (for wl-copy) — all present on a stock Omarchy install

No curl: the helper uses Python's own HTTP client.

Installation

omarchy plugin add https://github.com/solisoft/omarchy-soli-db.git

The plugin lands disabled so you can read the code before it runs. Enable it once you have:

omarchy plugin enable io.github.solisoft.soli-db --section right

Then tell it about your servers — see Servers. Later updates are a fast-forward pull:

omarchy plugin update io.github.solisoft.soli-db

Removal

omarchy plugin remove io.github.solisoft.soli-db

That disables both halves, drops the entry from ~/.config/omarchy/shell.json, and deletes ~/.config/omarchy/plugins/io.github.solisoft.soli-db/. It does not remove ~/.config/omarchy/soli-db/, which holds your server list and query history; delete that yourself if you want it gone. Nothing is installed outside those two directories: no system packages, no services, no files under /etc, /usr, or ~/.local. Removing it leaves SoliDB itself untouched.

Servers

Servers live in ~/.config/omarchy/soli-db/servers.json, not in shell.json, so credentials are not sitting in a world-readable config. The file is created 0600 on first run and must stay that way.

{
  "servers": [
    {
      "name": "local",
      "url": "http://127.0.0.1:6745",
      "username": "admin",
      "password": "admin"
    },
    {
      "name": "staging",
      "url": "https://db.staging.internal:6745",
      "usernameEnv": "SOLIDB_STAGING_USER",
      "passwordEnv": "SOLIDB_STAGING_PASSWORD"
    },
    {
      "name": "prod",
      "url": "https://db.example.com",
      "apiKey": "sk_...",
      "readOnly": true
    }
  ]
}
Key Meaning
name shown in the bar and the console's server picker
url base URL, trailing slashes ignored
apiKey sent as X-API-Key; takes precedence over a username
username / password the plugin calls POST /auth/login and caches the JWT
usernameEnv / passwordEnv names of environment variables holding those, read from the shell's own environment
readOnly refuses a mutating query outright, with no dialog

The JWT is held in memory only, keyed by server name, and re-minted on the first 401. It is never written to disk. Credentials reach the helper through the process environment rather than argv, so they do not show up in ps.

usernameEnv/passwordEnv read the environment of omarchy-shell itself, which is the Hyprland session environment — not your interactive shell's. If a variable is not exported at session start the plugin will report Not authorized; use literal username/password or an apiKey in that case.

Bar icon

󰆼 on its own when a server is unreachable or refusing, 󰆼 N with the document count when it is healthy, and dimmed when nothing answers.

Interaction Effect
left click open/close the popup
right click refresh now
middle click open the SDBQL console

Popup

The hero carries the active server, its uptime, and a one-line summary. Below it are the live figures from /_api/cluster/status, then the server list (when more than one is configured), then every database. Expanding a database lists its collections with document counts and on-disk size.

Key Effect
↑ / ↓ move between rows
enter expand a database, or switch to a server
q open the console on the selected database
s cycle to the next server
y copy the row's name
r refresh
esc collapse an open database, then close

Left-clicking a row does what enter does; right-clicking copies its name.

Console

A full-screen overlay: server and database pickers, a multi-line SDBQL editor with line numbers and syntax highlighting, and the results as a table or as JSON.

Keywords come from the server's own lexer, so the editor agrees with the parser rather than with a guess, and the ones that write — INSERT, UPDATE, UPSERT, REMOVE, REPLACE, CREATE, REFRESH — are coloured apart from the rest, so a mutation is visible while you are typing it rather than only in the confirmation dialog. Token colours are rotated off the theme's accent, so they follow omarchy theme set instead of being pinned to one palette.

Key Effect
ctrl+enter / ctrl+r run
enter open the focused row in full (results focused)
enter newline — SDBQL is a multi-line language
ctrl+e clear the editor
ctrl+c cancel a running query
ctrl+↑ / ctrl+↓ walk the query history
t toggle table ⇄ JSON (results focused)
m load the next batch
y copy the focused row as JSON
double-click open a row in full
/ jump back to the editor
esc back out one layer, then close

Results are virtualized: the list model is a row count and delegates index into a plain array, so a 1000-row batch instantiates only what is on screen. Table mode uses a constant row height — a height that depends on wrapped text means the scrollbar cannot know where it is.

Reading one document

A table row is one line and a JSON row is capped at twelve, which is right for scanning and no use for reading. enter on the focused row — or a double-click — opens the whole document over the console: pretty-printed, syntax-coloured, selectable, and scrollable in both directions.

Columns in the table are ordered so a document's own fields come first and SoliDB's bookkeeping (_id, _rev, the timestamps) sits after them, with _key kept at the front. Widths are measured once per batch from the rows themselves, so a timestamp column is wide and a chunk count is not.

Attachments

A document from a blob collection is a file, so the sheet shows it as one: name, MIME type, size and chunk count, with an inline preview when the bytes are an image. The rest get a description and a button that hands the file to whatever application your desktop uses for that type.

The document carries its own address — SoliDB stamps _id as database:collection/key — so this works from any query without the plugin having to parse one. Bytes are fetched from /_api/blob/{db}/{collection}/{key} into $XDG_RUNTIME_DIR/omarchy-soli-db/ at 0600, under the original filename so the desktop can pick an application by suffix. That directory is a tmpfs, and stale files from a previous session are cleared at startup rather than on close, since an externally-opened file should not be deleted out from under the application showing it. Anything over 16 MiB is described rather than fetched.

Mutating queries

A query containing INSERT, UPDATE, UPSERT, REMOVE, REPLACE, DELETE, TRUNCATE, CREATE, or REFRESH as a word — anywhere, not just as the leading keyword, because FOR u IN users FILTER u.stale REMOVE u IN users is a mutation that does not start with one — runs behind a confirmation prompt. String literals and comments are stripped before the test, so RETURN "delete me" does not trip it.

The list is deliberately wider than the language: a false positive costs one keystroke, a false negative writes to your database. The server enforces the real permission regardless, so this is a speed bump for the operator, not the security boundary. For a server you never want to write to, set "readOnly": true and the query is refused outright.

If a query turns out to have modified data without going through the prompt, the status line says so in the urgent colour. There is no undo; the honest response is to be loud about it.

Cursors

A result set larger than one batch leaves a cursor open on the server. Every path that abandons one — a new query, switching database or server, closing the overlay, the plugin reloading — funnels through a single releaseCursor(), which issues DELETE /_api/cursor/{id} detached, so it still runs when the QML engine is going away.

What it executes

Omarchy shell plugins run as unsandboxed code inside the long-lived omarchy-shell process, so it is worth being explicit about what this one spawns. Nothing here runs with elevated privileges and nothing is downloaded at runtime.

What Where Why
python3 solidb-fetch.py Service.qml every HTTP call; credentials go through the environment, not argv, and the query body over stdin
python3 solidb-fetch.py --server-file … Service.qml detached cursor release; reads the credential from the 0600 config, so argv holds only a cursor id
bash -c writing servers.json / history.json Service.qml creates them under umask 077; content arrives through the environment
omarchy-shell shell summon … Panel.qml opens the console overlay
wl-copy Panel.qml, Console.qml copy a name or a result row

There are no user-supplied shell commands anywhere in this plugin.

Security

This plugin runs unsandboxed inside omarchy-shell, holds credentials, and renders data from a network service, so the trust boundaries are worth stating.

Redirects are refused. urllib copies request headers onto a redirect target, across hosts, so following a 302 would hand X-API-Key or the bearer token to wherever the server pointed. A database API has no reason to redirect; a 3xx is surfaced as an error instead. This matters most for a server reached over plain http://, where anyone on the path can inject one.

Only http and https URLs are accepted. urllib will open file:// and ftp:// just as happily, which would turn a URL in servers.json into a local file read.

Credentials never reach argv. They go through the process environment, and the detached cursor release — which cannot take an environment — re-reads them from the 0600 config, so argv carries only a cursor id. A minted JWT is reported on stderr, held in memory, and never written to disk.

Server-supplied text is rendered literally. Every Text in this plugin pins textFormat: Text.PlainText. Qt's default is AutoText, which renders anything HTML-shaped as rich text — wrong for a database browser regardless of intent, since a document containing <b> should display as <b>. Short strings that reach shared-kit components this plugin cannot pin — a status label, an error message in the bar tooltip — are additionally stripped of angle brackets and bounded in Model.plain().

Nothing user-supplied reaches a shell. Every spawn is an argv array, never a shell string; the two bash -c scripts reference only quoted environment variables and never interpolate a value into the script text. wl-copy is called with -- before the payload.

history.json holds your query text at 0600, which is worth knowing if you paste credentials or personal data into a literal. Delete the file to clear it, or set historyLimit low.

Known and accepted: servers.json is read by whatever can read your home directory, so it is only as private as the account; and TLS uses urllib's default verified context, with no option to disable verification.

Limits

Responses are bounded at 4 MiB per invocation — a budget across every endpoint a single call requests, not an allowance each, so a three-endpoint poll cannot total three times the figure. The bound is on what is read off the socket rather than on a Content-Length the peer chose to send, so a chunked or lying endpoint cannot grow the shell's memory. Exceeding it reports as oversize, distinctly from an outage. A result set that trips it is asking for a LIMIT or a smaller batchSize; for reference, ten thousand rows of ordinary documents weigh about 1.2 MB.

The query body travels over stdin, framed as a byte count, a newline, then that many bytes. execve caps a single environment variable at ~128 KiB (MAX_ARG_STRLEN, 32 pages, not raisable by any ulimit), and a generated bulk INSERT passes that easily — sending the body through the environment would fail to launch the process at all, with E2BIG rather than anything legible. Length framing rather than read-to-EOF means neither side depends on the pipe being closed. Queries are refused above 8 MiB with a sentence saying so.

Settings

Display preferences are set inline on the widget's entry in ~/.config/omarchy/shell.json. Anything to do with servers or credentials belongs in servers.json instead.

Key Default Meaning
refreshIntervalSec 15 poll interval
defaultServer "" server selected at startup; blank means the first
showDocCount true show the document count next to the bar icon
resultView "table" console result view: table or json
historyLimit 100 queries kept in the console history
batchSize 1000 rows fetched per query batch

IPC

omarchy-shell io.github.solisoft.soli-db status     # one-line health summary
omarchy-shell io.github.solisoft.soli-db refresh
omarchy-shell io.github.solisoft.soli-db toggle
omarchy-shell io.github.solisoft.soli-db query shop # open the console on a database
omarchy-shell io.github.solisoft.soli-db server prod

omarchy-shell shell summon io.github.solisoft.soli-db '{"database":"shop"}'
omarchy-shell soli-db-console open '{"database":"shop","query":"RETURN 1+1","run":true}'

The summon form is what to bind in Hyprland: because this plugin declares an overlay kind, shell summon routes to the console rather than the bar popup.

Known SoliDB bug

GET /_api/database/{db}/collection returns 403 for any database holding an _env collection, so those databases cannot list collections at all. Two is_protected_collection functions in the server disagree: src/server/handlers/system.rs (used to skip collections) only guards _system, while src/storage/protected.rs (enforced inside Database::get_collection) guards _env in every database — so the listing walks into _env, is refused, and the error aborts the whole response.

The widget renders that as listing blocked (403 — _env) on the row rather than an empty expansion. Fixing it upstream is a one-line change in the server.

Notes for editing this plugin

Saving a file hot-reloads the plugin, but three things need a full omarchy restart shell:

  • new IPC methods — a hot reload keeps the handler already registered for the target, so the new function is reported as "Function not found";
  • Component {} blocks assigned to a property (such as the hero's trailingControl) — a hot reload keeps the previously instantiated item, so edits inside them appear to do nothing;
  • a plugin directory that is a symlink — the watcher does not follow it, so edits to the real path are invisible until a rescan or restart.

A method named console is rejected by QML with Illegal method name; the IPC verb is query for that reason.

License

MIT — see LICENSE.