Omahub
← All plugins
A

Temporal

by Anil Celik

Workflow execution status across every Temporal server and namespace you can reach, in the Omarchy bar.

Security review

Review recommended · 2 findings

Deterministic scan — not a security guarantee

Medium
Risk level
Medium
Analyzed commit
02686c0
Scanned
1 month ago
  • medium external_hosts bin/omtemporal:150

    Downloads or connects to an external HTTP(S) host.

    curl -sf -m 2 "http://127.0.0.1:$port/api/v1/cluster-info" >/dev/null 2>&1; then
  • medium package_manager …/worker/Dockerfile:8

    System-wide Python package installation (not --user).

    pip install --no-cache-dir "temporalio==1.9.0"

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
02686c0
Reviewed
1 month ago

This is a read-only Temporal monitoring widget; the sampled code is clear, well-structured, and contains no obfuscation, hidden persistence, credential theft, or destructive install-time behavior. The deterministic findings are over-stated: the flagged curl targets 127.0.0.1 during local server discovery, and the pip install is confined to the developer testbed Docker image. The only residual risk is the documented, user-configured apiKeyCommand feature, which intentionally runs a shell command to fetch credentials.

  • The apiKeyCommand setting executes an arbitrary user-supplied shell command under bash -lc; a malicious or compromised server config could run unwanted commands, but this requires explicit user configuration and is clearly documented.
  • Inline apiKey values are stored in shell.json and are only as protected as that file; the plugin warns about this rather than treating it as a secret store.
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/ancelik/omarchy-temporal --enable
Developer Tools #bar #quickshell

Temporal for Omarchy

Your Temporal fleet in the Omarchy bar — servers, namespaces, workflows, activities, task queues and schedules, each shown as its own kind of thing, arranged the way they actually nest.

The bar shows how many Workflow Executions are running and how many have failed recently. Clicking opens a panel you drill down through:

Servers  ›  local  ›  orders  ›  retry-orders
                                 ⚡ keeps_failing
                                   cannot reach billing-orders (attempt 7)
                                   Scheduled · retry in 7s

It is read-only. Nothing here can cancel, terminate, signal or reset a Workflow — the only outbound action is opening a page in your browser.

Install

omarchy plugin add https://github.com/ancelik/omarchy-temporal.git --enable

Then open the widget. With nothing configured it opens on a setup screen that looks for servers on this machine and reads any profiles in your temporal.toml, and adds one on a keystroke. Or from a terminal:

omtemporal setup

Remove

omarchy plugin remove io.github.ancelik.omarchy-temporal

That deletes ~/.config/omarchy/plugins/io.github.ancelik.omarchy-temporal/ and drops the widget from the bar. Your servers are stored on the widget's entry in ~/.config/omarchy/shell.json, which omarchy plugin remove takes with it — if you want to keep them, copy that entry first.

Nothing else is installed: no system packages, no services, no files outside Omarchy's own plugin and config directories.

The primitives

Each level of the panel shows exactly one kind of thing, with its own glyph and a one-line explanation of what it is. That is the point: a namespace and a task queue are not the same sort of object, and a list that mixes them teaches you nothing.

Level Shows Answers
Servers every configured server, version, reachability what am I connected to
Server its cluster (version, persistence and visibility stores) and its namespaces what lives on this one
Namespace retention, task queues, schedules, batch operations, executions what is going on in here
Workflow the execution, plus its pending activities — attempt count, last failure, next retry why is this one stuck
Task queue the workers polling each side of it, and the backlog is anything actually listening

The task-queue level is the one to reach for when work stops moving. Temporal will happily accept executions onto a queue nobody is polling, and this is the only view that says so out loud.

Transports

Each server is reached one of two ways, chosen explicitly per server.

http cli
How Temporal's HTTP API, straight from the shell shells out to temporal … -o json
Needs httpPort enabled server-side the temporal CLI on PATH
Speed fast; polls as often as you like a process start per poll, so no faster than 15s
Auth API keys, bearer tokens, custom headers all of that, plus mTLS, temporal.toml profiles and Temporal Cloud

Prefer http where it is available. Reach for cli when the HTTP listener is off — which is the default for most real deployments — when the connection needs client certificates, which the shell's HTTP client cannot present, or for Temporal Cloud, which publishes no HTTP API. A server configured with a client certificate is moved onto cli automatically and told you so.

See AUTH.md for every mechanism, which transport each needs, and worked examples including Temporal Cloud and mTLS.

Both go through the same parsers, so the two render identically. testbed/parity-test.mjs exists to keep that true.

Configuring

The setup screen and omtemporal both write for you. To edit by hand, the widget's entry lives in ~/.config/omarchy/shell.json:

{
  "id": "io.github.ancelik.omarchy-temporal",
  "refreshIntervalSec": 30,
  "servers": [
    { "label": "local", "url": "http://localhost:7243", "uiUrl": "http://localhost:8233" },
    { "label": "prod",  "transport": "cli", "profile": "prod", "namespaces": ["payments"] }
  ]
}
Key Meaning
label Name shown in the panel. Defaults to the host.
transport http or cli. Inferred from which address you give.
url HTTP API base, for http. /api/v1 and http:// are filled in if missing.
address gRPC host:port, for cli.
profile A profile name from temporal.toml, for cli. Brings its own address, credentials and TLS.
uiUrl Web UI base, used to build links. Falls back to url.
namespaces Pin the namespaces to poll. Omit to discover them. Also the way to work with a credential that cannot call ListNamespaces.
apiKey Bearer token, inline. Works, warns — shell.json is not a secret store.
apiKeyCommand Shell command whose stdout is the token. The preferred way: pass, gopass, secret-tool, op.
apiKeyTtlSec How long a resolved token is reused before the command runs again. Default 900. 0 resolves once and keeps it.
headers Extra headers on every request, as a map or as "Name: value" lines. For Cloudflare Access and friends.
tls Force base TLS on or off (cli). Omit to let the CLI decide — it turns TLS on by itself when an API key is present.
tlsCertPath Client certificate (cli). Its presence moves the server onto the cli transport.
tlsKeyPath The certificate's key (cli). Required alongside tlsCertPath.
tlsCaPath CA to verify the server with (cli).
tlsServerName Override the expected TLS server name (cli).
tlsDisableHostVerification Skip checking the server's identity (cli). Warned about.

Credentials never reach a command line: the collector spec goes in on stdin and temporal gets its key from the environment. See Secrets and argv.

You may see "servers": {"list": [...]}. That is what the setup screen writes. The shell's setBarWidget IPC silently drops a setting whose value is a bare JSON array, but preserves the identical array nested in an object. Both forms are read the same way, so a hand-written plain array keeps working.

Widget settings

Setting Default Meaning
refreshIntervalSec 30 Poll interval while the panel is closed
openRefreshIntervalSec 5 Poll interval while it is open
recentLimit 25 Executions fetched per namespace
requestTimeoutSec 8 When a server is declared unreachable
hideWhenIdle false Hide the widget when nothing is running
cliPath temporal Path to the CLI, if it is not on PATH

Deep data — activities, pollers, schedules, batch operations — is fetched when you drill into it, not polled for every namespace. A six-namespace fleet costs two requests per namespace per tick, not twenty.

Using it

Bar
Left click Open the panel
Right click Refresh now
Middle click Open the Web UI
Key Action
j / k, arrows Move the cursor
enter, l Go in
esc, h Go back — closes the panel at the top
f Cycle the filter: all → running → needs attention
r Refresh this level
o Open what you are looking at in the Web UI
s Servers / setup

Breadcrumbs are clickable, so you can jump back several levels at once.

Command line

omtemporal setup                              # interactive picker
omtemporal list                               # configured servers
omtemporal add http://localhost:7243 local    # HTTP
omtemporal add localhost:7233                 # gRPC, via the CLI
omtemporal add profile:prod                   # a temporal.toml profile
omtemporal add https://temporal.corp:7243 prod apiKeyCommand='pass temporal/prod'
omtemporal remove local
omtemporal status
omtemporal doctor                             # why is the widget empty
omtemporal open namespace 0 orders            # jump straight to a level

add and remove call into the running widget, so the terminal and the panel share one implementation and cannot drift apart.

Credentials are editable from the panel too — press s for the server list, Enter on a server, and every field is there: API key, key command, headers, pinned namespaces, TLS paths and the transport. Keys are entered masked and shown only as their length afterwards, and an empty submission leaves a stored key untouched rather than wiping it.

omtemporal doctor is the first thing to run when something looks wrong: it checks the shell, the widget, the CLI, every server's reachability, whether each HTTP server's API is actually enabled — and, since that is now the leading cause of an empty panel, its credentials. What each server carries, anything already known to be wrong with the way it is configured, whether the certificates it names exist and are readable, and whether the key command runs. It reports how many characters the command produced, never the token.

omtemporal open is useful as a keybind — point it at the namespace you care about and skip the drilling.

Two different failure counts, on purpose

The panel shows each namespace's lifetime totals from CountWorkflowExecutions — the true number of failed executions it has ever accumulated.

The bar counts only failures inside the recent window. A namespace that failed something last Tuesday would otherwise pin the widget to its urgent colour forever, which trains you to ignore it. The bar answers "is something going wrong now?"; the panel answers "how much has gone wrong here?".

IPC

omarchy-shell temporal toggle
omarchy-shell temporal refresh
omarchy-shell temporal status
omarchy-shell temporal servers      # per-server reachability
omarchy-shell temporal auth         # per-server credentials, for doctor
omarchy-shell temporal openAt '{"level":"namespace","serverIndex":0,"namespace":"orders"}'

Development

testbed/ brings up two Temporal servers with six namespaces between them and workers that keep every primitive populated — including a Workflow whose Activity retries forever, so the Activity view always has something to show. See testbed/README.md.

bin/dev-install               # validate, copy into ~/.config/omarchy/plugins/, rescan
node testbed/parity-test.mjs  # assert HTTP and CLI parse identically

# An authenticating front door, for the 401/403 and namespace-fallback paths.
# `temporal server start-dev` cannot enforce anything, so a proxy stands in.
docker compose -f testbed/compose.yaml --profile auth up -d authproxy

Saving a file under ~/.config/omarchy/plugins/ hot-reloads plugin QML, but the QML engine caches imported .js files and does not always pick up changes to IPC handlers — after editing Model.js or adding an IPC method, run omarchy-restart-shell. Errors go to the shell's journal:

journalctl --user -f | grep io.github.ancelik.omarchy-temporal

Layout

File Role
Panel.qml Bar button, router, breadcrumb, keys
Service.qml Both transports, poll lifecycle, on-demand detail fetches
Model.js Parsing, rollups, formatting, and the entry builders that decide what each level contains
EntryList.qml / PrimitiveRow.qml The one renderer every level uses
SetupView.qml Discovery, onboarding, persistence
AUTH.md Every credential the plugin can present, and where to keep it
collect.py CLI transport — runs temporal, returns raw payloads for Model.js to parse
TemporalIcon.qml The mark, drawn on a Canvas so it stays sharp at bar sizes

Levels are not separate files on purpose. Each one is a list of entries built by a function in Model.js and drawn by EntryList, which is what keeps a namespace looking like a namespace everywhere it appears — and makes the entry builders testable without a running shell.

Limits

  • mTLS needs the cli transport. QML's XMLHttpRequest cannot present a client certificate, so a server configured with one is moved onto cli.
  • Temporal Cloud needs the cli transport. Cloud publishes no documented HTTP API; see AUTH.md.
  • Standalone Activities are not shown. The API exposes them, but the Activity view is built on pendingActivities from a Workflow description, which is the one that answers why something is stuck.
  • Batch operations are listed, not started. Neither the Python SDK nor temporal batch can start one.

Branding

The mark is Temporal's own symbol — the path from their published Temporal_Symbol SVG, converted to beziers and drawn on a Canvas so it stays sharp at bar sizes and can take the theme's colour. It is reproduced faithfully, including the merge at the lower-right crossing that makes the mark subtly asymmetric (that is in Temporal's own PNG export too, so it is the mark rather than an export artefact).

By default the mark is drawn in your Omarchy theme's foreground colour, and in the theme's urgent colour when something needs attention. That is a deliberate departure from painting it Temporal's brand indigo: an Omarchy bar is themed end to end, a widget that ignores the active theme looks broken next to every other icon, and a mark that stays branded while the fleet is on fire is worse than one that turns red. Set brandColor: true to use Temporal's primary brand colour, UV #444CE7, when nothing is wrong.

Temporal is a trademark of Temporal Technologies, Inc. This plugin is unofficial, is not affiliated with or endorsed by Temporal, and uses the mark only to identify the product it monitors. Brand assets and guidelines: https://temporal.io/brand.

License and dependencies

MIT — see LICENSE, which also lists third-party dependencies.

The plugin itself is self-contained QML plus one stdlib-only Python script. It requires nothing beyond Omarchy unless you use the cli transport, which needs the temporal CLI (MIT) on your PATH and python3 to run collect.py. The http transport needs neither.

Everything under testbed/ is for development only. It is not loaded by the plugin and is not needed to run it.