Skip to content

Releases

v0.4.3 — a pinned location warns when it looks stale

Choose a tag to compare

@mrCode mrCode released this 26 Sep 16:47

A pinned location no longer goes quiet when you travel.

Pinning (auto_update = false, set for you by a manual relocate --lat/--lon/...) stops the app following your IP. That is the point of it — IP geolocation can be hundreds of kilometres wrong without you going anywhere.

But it also meant a pin could sit on the wrong city indefinitely after you actually moved, calculating every prayer time for somewhere you left, and saying nothing about it.

What changed

While pinned, if your system timezone is in a different country than the one you pinned, you now get one notification a day:

Prayer times: pinned location may be stale
Pinned to Palo Alto, US, but this system's timezone is Asia/Riyadh. Prayer times are being calculated for Palo Alto. Run omarchy-prayer relocate --auto to follow your location again, or re-pin with relocate --lat … --lon ….

The same line goes to the journal, so journalctl --user -u omarchy-prayer-schedule shows it too.

It never unpins by itself. You chose the pin; silently overriding that would be the previous release's bug in the other direction. It tells you and leaves the decision with you.

Why the timezone and not the IP

Your timezone is set by you or your OS. Your IP is guessed by a third party — and on the connection that prompted all of this, two providers disagreed by hundreds of kilometres while the user had not moved at all. A country-level disagreement between /etc/localtime and a pinned country is real evidence of travel; an IP disagreement is not.

The check is country-granular on purpose, and understands zones shared by several countries (Asia/Riyadh covers SA, KW, YE and AQ — pinning to any of them stays silent). A false alarm on a pin you set deliberately would just train you to ignore the warning.

Not noisy

The scheduler runs at 00:01, on resume, and on every network-up. The warning is rate-limited to one notification per day per mismatch, and re-notifies at once if the mismatch changes — moving again is new information. If it cannot write its marker it errs toward warning rather than staying silent.


340 tests, green under both invocations. Every guard is mutation-tested. The widget is unchanged; the plugin stays at 0.4.0.

Also in this release

--country is validated. relocate --country "Saudi Arabia" used to be written verbatim, which silently fell back to the wrong calculation method. It now requires a two-letter ISO code.

CI is green for the first time since April. Four tests had been failing on every push, unnoticed. The shims used #!/usr/bin/env bash while the tests that simulate a missing binary strip PATH to the shim directory alone — so env could not find bash and the shim never ran. They passed on a developer machine only because the code falls back to checking for /usr/share/omarchy/shell, which exists there and not on a runner: green locally for the wrong reason. The shims now use an absolute interpreter, and the fallback is injectable so the tests prove the path they claim to.

v0.4.2 — manual relocate pins the location instead of being reverted

Choose a tag to compare

@mrCode mrCode released this 07 Sep 01:48

omarchy-prayer relocate with manual coordinates now works. Until this release it did not: the command wrote your coordinates and then immediately re-detected by IP and overwrote them, inside the same invocation, exiting 0.

The bug

omarchy-prayer relocate --lat 37.4419 --lon -122.1430 --city "Palo Alto" --country US
grep city ~/.config/omarchy-prayer/config.toml
#   city = "San Diego"      ← not what you asked for

relocate writes the config, then hands off to the scheduler. With the default auto_update = true, the scheduler re-detects location by IP, sees the new coordinates are more than 50 km from what the IP says, and "corrects" them. Nothing distinguished a location you had just set on purpose from stale auto-detected data. The only visible clue read like normal operation:

omarchy-prayer: auto-relocated Palo Alto, US → San Diego, US (Δ 694 km)

The timezone cross-check did not help. It compares countries, so an IP result several hundred kilometres wrong inside one country passes straight through — and IP geolocation genuinely is that wrong sometimes. On the reporter's connection two providers disagreed by hundreds of kilometres.

The fix

A manual override now pins your location: it also sets auto_update = false, and tells you so.

omarchy-prayer: location set to Palo Alto, US (37.4419, -122.1430)
  pinned: auto-update disabled so this location sticks (re-enable with `omarchy-prayer relocate --auto`)
  • omarchy-prayer relocate — one-shot IP re-detect, leaves the setting alone
  • omarchy-prayer relocate --lat … --lon … --city … --country … — pins
  • omarchy-prayer relocate --auto — resumes automatic tracking

Passing --auto together with manual coordinates is now refused rather than doing both.

Also fixed

The confirmation line was lost through a pipe. exec replaces the process without flushing stdout, so omarchy-prayer relocate … | cat printed nothing while the contradictory stderr line still showed — which made the first bug harder to diagnose.

A commented section header could break your config. [location] # my home is valid TOML but the config writer did not recognise it, so writing a key appended a second [location] table and the file stopped parsing — taking every command down until repaired by hand. Both the reader and the writer accept it now.


Thanks to whoever filed the report — the reproduction, the root-cause trace, and the option analysis were all accurate, including the detail that suppressing auto-relocate for one run would not have been enough on its own.

329 tests, green under both invocations. The widget is unchanged; the plugin stays at 0.4.0.

v0.4.1 — sanitise Aladhan timings, escape Pango, harden the schedule run

Choose a tag to compare

@mrCode mrCode released this 03 Sep 22:16

Security and robustness fixes following a review of v0.4.0. No user-facing behaviour changes if your city has no & in it.

Sanitised: prayer times from the Aladhan API

hijri from that response was already treated as untrusted; the timings from the same response were not. Extraction kept anything without whitespace — which an OSC 52 terminal escape satisfies — and it reached the TUI, omarchy-prayer today, and the waybar tooltip unfiltered.

Validated at ingress now, and cleaned again in Today — ingress alone was not enough, because the month cache and today.json are re-read verbatim and the cache is preferred over the API, so a payload written by an older version would have kept being served all month.

Fixed: an & in your city name blanked the whole bar

waybar renders these labels through Pango markup unconditionally, and an unescaped & fails parsing outright, leaving the label unset. Any legitimate place name with an ampersand has quietly broken the module all along. Now escaped.

Corrected: markup: true never did anything

v0.4.0's notes said this key gated Pango parsing. It does not exist in waybar's custom-module contract (text, tooltip, class, percentage) and markup was always parsed. The key is removed and the comment in the source corrected, so nobody relaxes the sanitiser believing markup is opt-in.

Hardened: a failed schedule no longer takes the CLI down

omarchy-prayer-schedule runs as a systemd unit and every command shells out to it. Above roughly 60° latitude the offline calculator cannot compute a sunrise and raised, producing a backtrace and no prayer times at all. It now reports the failure and exits cleanly, leaving the previous schedule in place.

Two matching guards on the same theme: only the five displayed prayers can reject a month of API times — an unexpected field in Aladhan's response no longer costs every user their times — and a missing prayer time stays missing instead of becoming a 00:00 adhan.


316 tests, green under both the bundled and bundler-less invocations. Every guard here is mutation-tested: reverting any one of them fails the suite.

The widget is unchanged in this release; the plugin stays at 0.4.0.

v0.4.0 — prayer icons, 12-hour time, coloured names

Choose a tag to compare

@mrCode mrCode released this 03 Sep 21:15

Bar display options, contributed by @ch-arslanahmad in #4.

Three optional ways to change how the bar reads. All are off by default — nothing about your bar changes on upgrade.

{icon} — a per-prayer emoji

A fifth format placeholder, alongside {city}, {prayer}, {time} and {countdown}:

[bar]
format = "{icon} {prayer} {countdown}"
⛅ Asr 2h 14m

Glyphs are keyed to time of day: 🌛 Fajr, ☀️ Dhuhr, ⛅ Asr, 🌇 Maghrib, 🌙 Isha.

time_12h — 12-hour clock

omarchy-prayer bar time12 on

Applies to the pill, the tooltip, and the Quickshell panel — everywhere the bar shows a time.

colored — prayer name coloured by time of day

omarchy-prayer bar colored on

Dawn purple through night blue. waybar only — the Quickshell widget renders plain text by design, so markup would appear there as literal tags rather than colour. Enabling it also switches waybar's module into Pango markup mode, which is why it is opt-in rather than automatic.


The glyphs, the time-of-day palette, and the 12-hour formatting are @ch-arslanahmad's. Their PR was written against pre-0.2.0 code and could not be rebased — [waybar] had become [bar], the renderer had moved onto the shared Status structure, and 0.3.0 had added presets on the same path — so the plumbing was rewritten while keeping their design.

Three deliberate differences from that PR:

  • Icons are a placeholder, not an on-by-default switch. The original would have added emoji to every existing waybar user's bar on upgrade.
  • 12-hour time is applied in Status, so the Quickshell panel honours it too, not only waybar.
  • markup: true is emitted only when colours are on. CORRECTED — see v0.4.1. This bullet was wrong. A security review after release established that waybar has no markup key at all — its custom-module contract is {text, tooltip, class, percentage} — and that it renders these labels through Pango markup unconditionally. The key was inert, and {city} has been reaching a markup-parsing label since the waybar module first shipped, so the sanitiser was already load-bearing for every user regardless of this setting. @ch-arslanahmad's unconditional version was not the riskier choice it is described as here. v0.4.1 removes the key and corrects the comment in the source.

v0.3.4 — notifier test no longer flakes under load

Choose a tag to compare

@mrCode mrCode released this 31 Aug 15:56

Test-only release, shipped so the fix reaches the AUR package.

test_audio_spawned_before_blocking_notify_send failed roughly 1 run in 45 on a loaded machine. The product code was never wrong — audio has always been spawned before the blocking notify-send. The test was: it compared the order two shims appended to a log, but mpv is spawned detached, so under CPU load the kernel can schedule it after the foreground notify-send has already written. The log inverts while the spawn order stays correct.

This is worth a release because the suite runs in the AUR PKGBUILD's check(), on the machine of everyone who builds the package — and a build machine is loaded by definition, so the flake surfaced as occasional install failures rather than as a red CI run someone would notice.

The test now asserts the property directly: with notify-send blocked for 3s, the adhan must be audible within 2s, while it is still blocking. Verified by mutation — moving the audio call after notify-send makes it fail — and by 60 runs under 4-way CPU load with zero failures.

No behaviour change for users.

v0.3.3 — security: geolocation trust boundary

Choose a tag to compare

@mrCode mrCode released this 31 Aug 15:56

Security release — a full-codebase scan of the geolocation trust boundary.

The geolocation HTTP response is a trust root: it is written into config.toml, replayed on every run, printed to terminals, and painted on the bar. It was being handled as if it were our own data. Three findings:

1. Cleartext transport (high). The endpoint was http://ip-api.com/json/, and auto-relocate fires on every NetworkManager connection-up — so anyone on-path on a hostile network could permanently rewrite your coordinates, and with them your prayer times. The existing timezone cross-check did not close this: it only catches cross-country spoofing and is skipped entirely when timezone detection returns nil.

The provider is now https://ipwho.is/ (ip-api's free tier is HTTP-only), and a guard refuses any non-HTTPS endpoint except loopback, so a pinned URL cannot silently downgrade. Coordinates must be numeric and in range; the country code must be two letters.

2. Terminal control-character injection (high). An attacker never sends a raw ESC byte — that would break TOML parsing. They send the six-character escape sequence that a TOML parser decodes into one (backslash, then u001b). It carries no markup, survives naive filtering, and forms a valid TOML basic string — which the parser then decodes back into a real ESC on load. The payload is OSC 52, which writes your clipboard; Omarchy's shipped Alacritty config sets osc52 = "CopyPaste", so the next paste into a shell would run attacker-chosen text.

Sanitisation now strips C0/C1 controls and Unicode format characters (covering bidi overrides), applied at every display sink — the status JSON, the TUI, the plain status line, the notification body, and the auto-relocate log line.

3. TOML injection (low/medium). city/country were spliced into config.toml unescaped, so a quote closed the literal early and the rest parsed as TOML — enough to flip auto_update back on for someone who had pinned their location. Escalation to code execution via [audio].player was not reachable, since the parser rejects duplicate table headers.

Sanitisation is applied at every sink rather than at ingress alone, because poisoned values may already be on disk from before this release.

v0.3.2 — security: enforce plain text at the render site

Choose a tag to compare

@mrCode mrCode released this 31 Aug 15:56

Security fix — completes the incomplete fix in v0.3.1. Reported again by @ryanrhughes.

v0.3.1 sanitised the data reaching the bar pill but left the render site on Qt's Text.AutoText default, because WidgetButton's label is a private Text whose textFormat cannot be set from outside the component. That made the guarantee depend on every upstream producer sanitising correctly, forever.

The pill now suppresses WidgetButton's own label and paints its own Text with textFormat: Text.PlainText, so enforcement lives in this snapshot rather than upstream. Model.plain() strips markup at assembly as a second layer, and the Ruby-side sanitisation is the third. Every Text in the panel sets PlainText explicitly.

v0.3.1 — security: sanitise geolocation display data

Choose a tag to compare

@mrCode mrCode released this 31 Aug 15:56

Security fix. Reported by @ryanrhughes during the plugin marketplace review.

city and country come from a third-party IP geolocation response, and QML's Text defaults to Text.AutoText, which renders markup-shaped input as rich text. Qt rich text honours <img src="..."> and will load local and remote resources from inside the shell process — so a hostile or intercepted response could have put a remote image reference on the bar and had it fetched unattended on the next auto-relocate.

This release sanitises those fields at the source and strips markup-significant characters before they reach a display surface.

This fix was incomplete — see v0.3.2, which fixes the render site itself. Upgrade past this version.

v0.3.0 — bar pill design presets

Choose a tag to compare

@mrCode mrCode released this 31 Aug 15:56
42ee3aa

Choose how the bar pill looks.

Three presets, switchable from the panel's design chips or the CLI:

Preset Shows
full Riyadh · Isha 1h 26m
minimal Isha 1h 26m
icon the mosque glyph alone
omarchy-prayer bar preset minimal

The preset name is derived from your format string rather than stored, so hand-editing [bar].format to something custom is still first-class — bar preset list marks it as custom.

Also in this release:

  • Arabic prayer names — omarchy-prayer bar names arabic, with U+200E anchoring so an RTL name does not visually reorder the LTR countdown beside it
  • Compact countdown — 1:57 instead of 1h 57m
  • Quiet until near — collapse the pill to the glyph until a prayer is within N minutes

The icon preset renders empty on waybar (its module has no glyph of its own), so the CLI warns when you select it there.

v0.2.2 — namespaced plugin id

Choose a tag to compare

@mrCode mrCode released this 31 Aug 15:56

Breaking (plugin id): the Quickshell plugin id is now io.github.mrcode.prayer-times, namespaced ahead of the marketplace listing that would have made the old bare prayer.times permanent.

Existing installs migrate automatically — omarchy-prayer setup moves the plugin directory and rewrites the shell's plugin list. The shell keys plugins on the manifest id rather than the directory name, so a stale manifest in a renamed directory would silently break the widget; setup now warns if it detects that mismatch.

Also fixed: a missing CLI is now detected by a start watchdog. Quickshell does not emit onExited when a binary is absent — it only logs "Process failed to start" — so without this the widget sat on an empty pill forever. It now says omarchy-prayer is not installed — run: yay -S omarchy-prayer.