Omahub
← All plugins
S

Omarchy Spaces

by Samy Lemcelli

Context spaces for Omarchy. Each space owns its browser profile, email, assistant accounts, workspaces, appearance, and notification policy.

Security review

Review recommended · 2 findings

Deterministic scan — not a security guarantee

Low
Risk level
Low
Analyzed commit
d329b31
Scanned
1 month ago

Flagged patterns appear only in documentation files (README / docs) — descriptive examples, not executable code.

  • Docs external_hosts docs/Installation.md:6

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

    git clone https://github.com/Samy104/omarchy-spaces.git
  • Docs external_hosts README.md:107

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

    git clone https://github.com/Samy104/omarchy-spaces.git

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

No malicious or obfuscated code found in the sampled executable files. The deterministic scan's medium findings are documentation-only `git clone` snippets, not part of the executable plugin. The plugin is transparent about its opt-in system changes (default browser handler, optional workspace widget replacement) and includes careful file-reading practices such as symlink/FIFO rejection and bounded reads.

  • Optional install flags (`--with-url-handler`, `--replace-workspaces`) intentionally change the default browser or disables Omarchy's workspace widget; they are clearly documented and only apply when explicitly requested.
  • The plugin runs executable hooks from `~/.config/omarchy-spaces/hooks/` on every space switch, which is user-level code execution by design but should be understood as a trust boundary.
  • The full `bin/omarchy-spaces` source was only partially sampled, though the visible portions and accompanying tests show security-conscious patterns.
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/Samy104/omarchy-spaces --enable
Productivity #Hyprland #bar #workspaces

Omarchy Spaces

<img src="assets/icon.png" alt="Omarchy Spaces" width="128">

Context spaces for Omarchy. A space is a named context like Personal or HiScale. Each one owns a browser profile, an email address, assistant accounts, and a notification policy. Switching spaces re-points all of them at once.

Omarchy already has workspaces, which are about window layout. Spaces are about identity. You can be on workspace 3 in either context, and the difference that matters is whether a Slack message should interrupt you.

Screenshots

<img src="docs/screenshots/bar-indicator.png" alt="The bar indicator" width="620">

The bar shows the active space. Click it for the picker, which switches spaces and opens the settings app.

<img src="docs/screenshots/picker.png" alt="The Spaces picker" width="440"> <img src="docs/screenshots/general.png" alt="General settings" width="720">

More in docs/screenshots, and every page is walked through in the documentation.

What it does

Switch with a hotkey, the bar widget, or the CLI. The active space decides:

Which notifications reach you. In the work space you do not see personal notifications. In the personal space you can choose to see both. Rules are per space and can vary by time of day.

Where links open. Click a link in any app and it opens in that space's browser profile. Personal links land in your personal Brave profile, work links in the work one, with no profile picker in between.

Which accounts apply. Each space carries an email address and a set of assistant accounts, readable by scripts through omarchy-spaces get.

Which windows you see. Each space owns a range of real Hyprland workspaces, so SUPER+1 reaches different windows depending on the space. Windows never move on a switch, which keeps it instant.

What the desktop looks like. A space can pin a theme, font, and background, applied on switch.

What opens where. A space can record what each of its workspaces contains and replay it on demand or at login, tiled or stacked. See Startup layouts.

Notification policy

Each space declares which spaces' notifications it will show, and a schedule that overrides that baseline by time of day.

{
  "id": "personal",
  "notifications": {
    "allowFrom": ["personal", "work"],
    "schedule": [
      { "from": "08:00", "to": "17:00", "allowFrom": ["personal", "work"] },
      { "from": "17:00", "to": "22:00", "allowFrom": ["personal"] },
      { "from": "22:00", "to": "08:00", "allowFrom": [], "allowUnassigned": false }
    ]
  }
}

That reads as: during working hours show everything, in the evening show personal only, and go silent overnight. Windows may wrap past midnight. The end bound is exclusive, so 08:00 to 17:00 and 17:00 to 22:00 never both match.

A notification belongs to a space when that space lists its app in apps. Anything unmatched is unassigned, and allowUnassigned decides whether those still get through. Critical notifications bypass filtering unless you set criticalBypass to false, because losing a low battery warning to a context filter is a bad trade.

Run omarchy-spaces validate to find gaps, overlaps, and references to spaces that do not exist.

How filtering works

The plugin does not replace Omarchy's notification daemon. It lets omarchy.notifications accept everything, then removes the popups the current policy blocks. Three things follow from that choice.

History stays complete. A notification blocked in the work space is still in history when you switch back to personal, so nothing is lost, only deferred.

The stock DND toggle keeps working. When a policy blocks everything, the service mirrors that into the built-in doNotDisturb flag, so the normal DND icon tells the truth. It only clears the flag again if it was the one that set it, so a manual DND is never undone by a schedule boundary.

Omarchy owns the D-Bus name. Replacing the daemon would mean reimplementing history, images, actions, and the popup lifecycle, and it would break whenever Omarchy changed any of them.

Install

git clone https://github.com/Samy104/omarchy-spaces.git
cd omarchy-spaces
./install.sh --with-url-handler
omarchy plugin enable io.github.samy104.omarchy-spaces
omarchy restart shell

install.sh copies the plugin into ~/.config/omarchy/plugins/, puts the CLI on your PATH, writes a starter config, and enables the plugin. Everything lives under $HOME and needs no sudo.

Nothing outside this plugin changes unless you ask. --with-url-handler reassigns your default browser so links follow the active space. --replace-workspaces swaps in the slot-numbered workspace widget and disables omarchy.workspaces, which is documented in full under Installation since it is the one step that turns off something of Omarchy's own. --all does both. ./install.sh --help lists them.

Add the space rows to the Omarchy menu:

omarchy-spaces install-menu

That splices a delimited block into ~/.config/omarchy/extensions/omarchy-menu.jsonc, leaving your own entries and comments untouched. Re-run it after adding a space. omarchy-spaces remove-menu takes it back out.

Add keybindings from hypr/spaces.lua to ~/.config/hypr/bindings.lua. Check for conflicts first with omarchy menu keybindings --print.

Keys Action
SUPER + ALT + O Open the picker
SUPER + ALT + N Next space
SUPER + ALT + I Show the current policy
SUPER + ALT + P Switch to personal
SUPER + ALT + W Switch to work

Command line

omarchy-spaces list                 every space, active one marked
omarchy-spaces current [--json]     the active space id
omarchy-spaces switch <id>          make a space active
omarchy-spaces next | prev          cycle
omarchy-spaces status [--json]      the policy in effect right now
omarchy-spaces open <url>           open a url in the active space's browser
omarchy-spaces which [url]          print the argv `open` would run
omarchy-spaces get <field>          read a field off the active space
omarchy-spaces init [--force]       write a starter config
omarchy-spaces validate             check for gaps, overlaps, bad references
omarchy-spaces install-menu         add the space rows to the Omarchy menu
omarchy-spaces remove-menu          take them back out
omarchy-spaces menu                 print the rows without writing anything

get reads any top level field, plus assistant.<tool>:

omarchy-spaces get email            # samy@hiscalesolutions.com
omarchy-spaces get assistant.codex  # work

Hooks

Any executable in ~/.config/omarchy-spaces/hooks/ runs on every switch, in filename order, with OMARCHY_SPACE and OMARCHY_SPACE_PREVIOUS in the environment. Use them to swap SSH configs, git identities, or VPN profiles alongside the space.

Configuration

Config lives at ~/.config/omarchy-spaces/spaces.json. The active space is stored separately in ~/.local/state/omarchy-spaces/active, so config is something you edit and state is something the system writes.

Field Meaning
id Stable identifier used by the CLI and by allowFrom
name Display name in the bar and panel
icon Glyph for the bar, a Nerd Font codepoint works
color Accent color for the icon
browser.command Executable, for example brave
browser.profile Passed as --profile-directory
browser.args Extra flags before the profile flag
email Address associated with the space
assistants Map of tool name to account name
apps App names owned by this space, matched case insensitively
notifications.allowFrom Baseline list of spaces to show
notifications.allowUnassigned Whether unmatched apps get through
notifications.schedule Time windows that override the baseline

Testing

./test/run.sh             # everything

Four suites. 25 unit tests over the policy rules, 2880 parity comparisons, a check that the embedded starter config matches the repo copy, and a CLI smoke test that runs the binary from a directory with no repo above it.

The policy rules exist twice, once in SpacesLogic.js for the shell plugin and once in bin/omarchy-spaces for the CLI. The CLI is python because Hyprland keybindings and desktop handlers run with a minimal PATH where a mise managed node may not resolve, while python3 is always present.

Two implementations can drift, so test/parity.sh runs both across every minute of the day, for every space and a set of sample apps, and fails if a single decision differs. That is 2880 comparisons per run.

Status

Version 1.2.1, running on Omarchy 4.0.0 with no QML warnings.

Verified on a live shell: the bar widget renders and follows a switch made from anywhere, a personal-app notification is suppressed while the work space is active, a work-app notification still gets through, and both land in history either way. The policy engine, CLI, browser routing, hooks, menu picker, and config validation are covered by the test suite.

Also verified live: switching jumps to the space's workspace range, a window moved with move-to-space lands in the target range while the rest stay put, and a pinned theme is applied on switch.

omarchy-spaces-config is a GTK4 and libadwaita editor for all of it. Click the Spaces indicator in the bar and pick "Space settings" to open it. It has General, Defaults, and Keybinds pages beside the per-space ones. Keybinds are recorded by pressing the combination, checked for conflicts, and written to a delimited block in bindings.lua.

A defaults block supplies values every space inherits, so a shared browser, schedule, or workspace count is written once. See Defaults and inheritance.

The bar widget has two faces, chosen per placement by a mode setting. Place it twice, once in workspaces mode and once in indicator mode, and the numbers on the bar match the numbers you press. Omarchy's stock widget prints real ids, which read 11 to 20 in the work space.

Audited against the Omarchy plugin develop and publish guidelines. See Publishing.

Requirements

Omarchy 4.0.0 or newer, and python3, which Omarchy already ships.

The configuration app additionally needs python-gobject, gtk4, and libadwaita. install.sh skips it when they are absent, and everything else keeps working.

Nothing here needs root. There is no sudo, no system directory is touched, and every file is written under $HOME. The plugin runs inside the Omarchy shell process with your user's permissions, unsandboxed, like every Omarchy plugin.

The CLI shells out to hyprctl for workspaces, and to omarchy theme, omarchy font, and omarchy theme bg for appearance.

Removal

omarchy plugin disable io.github.samy104.omarchy-spaces
omarchy plugin enable omarchy.workspaces
omarchy-spaces remove-menu
rm -rf ~/.config/omarchy/plugins/io.github.samy104.omarchy-spaces
rm -f ~/.local/bin/omarchy-spaces ~/.local/bin/omarchy-spaces-config
rm -f ~/.local/share/applications/omarchy-spaces-open.desktop
rm -f ~/.local/share/applications/omarchy-spaces-config.desktop
xdg-settings set default-web-browser brave-browser.desktop

Remove the Omarchy Spaces block from ~/.config/hypr/bindings.lua, then hyprctl reload.

Your config at ~/.config/omarchy-spaces/ is left in place. Delete it for a clean slate. Export it first with omarchy-spaces export ~/spaces-backup.json if you might come back.

Documentation

Full docs live in docs/, and are mirrored to the wiki.

License

MIT