4.5.0rc2 replaced the Agents panel with a compact one: a meter per limit, the percentage only on hover, no tokens by day or by model. noconnor.agents is a clone of omarchy.agents with Panel.qml, Main.qml and README.md from upstream v4.0.4, vendored as plain files in personal-desktop's config/ so the config step installs and captures it. capture.sh's plugin step skips plugins vendored that way instead of warning they aren't git checkouts. shell.json is synced from the live HP: the bar uses noconnor.agents, plus the 4.5 centerBegin/centerEnd layout, elsewhen and the antigravity plugin. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
19 KiB
omarchy-setup
Omarchy customizations on top of a stock install, as profiles: one per machine, each built from shared layers and generic roles.
| Profile | Machine |
|---|---|
hp-laptop |
The HP (omarchy-laptop): Intel, Anker dock, LG displays. Where this repo started. |
macbook-air-m2 |
The Air (omarchy-air): Asahi Linux on Arch Linux ARM, aarch64. |
coder-desktop |
The streamed Coder desktops, built by covert-cloud's image pipeline. |
| Role | Adds |
|---|---|
developer |
gh, delta, uv, just, direnv, shfmt/shellcheck, httpie, mise, VS Code |
devops |
kubectl, helm, k9s, kubectx, stern, kustomize, talosctl, cilium, tofu, terragrunt, packer, ansible, go-task, yq, sops/age, dig/mtr/nmap/tcpdump |
ai-agents |
Claude Code, OpenCode, Codex, Gemini CLI, Copilot CLI |
git clone https://git.nic-oconnor.com/public/omarchy-setup.git ~/omarchy-setup
cd ~/omarchy-setup
./install.sh --profiles # what exists
./install.sh --profile hp-laptop --show # its layers, and which layer owns each file
./install.sh --dry-run # see what it would change (profile picked by hostname)
./install.sh # do it
./install.sh --roles devops,ai-agents # add roles on top of the profile
install.sh is idempotent and backs up anything it overwrites as
<file>.bak.<epoch>. It records the profile and roles it applied in
~/.config/omarchy-setup/profile, so later runs (and capture.sh) need no
flags. capture.sh runs the other way, pulling live changes back into the
layer that owns each file, so the repo stays the source of truth.
How profiles are built
Everything lives under profiles/. A layer is a directory with a profile
file (bash: description, include=(...), and for machines hostnames,
arch, handoff_peer_ips) and any of the same pieces this repo always had:
| Path in a layer | Installed to |
|---|---|
config/ |
~/.config, mirrored |
themes/ |
~/.config/omarchy/themes |
plugins.txt, plugin-patches/ |
~/.config/omarchy/plugins, pinned; local work replayed |
bin/ |
~/.local/bin |
systemd/user/ |
user units, plus <unit>.service.d/*.conf drop-ins |
system/ |
/etc, mirrored (sudo) |
handoff/ |
the lan-mouse patch and example configs (install.sh handoff) |
packages.txt |
omarchy pkg add / omarchy pkg aur add |
setup.sh |
run last, for state outside ~/.config (markers, gsettings, a first-run theme) |
A profile resolves to an ordered list of layers: its includes first (depth
first, each once), then any --roles, then the profile itself. For a path
several layers provide, the last one wins, so a machine overrides the roles
and shared layers beneath it. ./install.sh --show prints the result.
| Layer | Used by | What |
|---|---|---|
base |
all | hyprland.lua with the hypr/layers/ loader; bindings.lua kept at stock |
personal |
both laptops | branding, default agent (claude), herdr, opencode, voxtype |
personal-desktop |
HP | the three hand-built themes, the shell plugins, bar layout, Claude/Herdr bindings |
features/handoff |
both laptops | omarchy-handoff + patched lan-mouse, its unit and binding |
roles/* |
per profile, or --roles |
packages, and any bindings the role needs |
machines/* |
— | displays, input, hardware packages, /etc drop-ins, per-machine overrides |
Hyprland settings never share a file. base's hyprland.lua loads every
~/.config/hypr/layers/*.lua in name order (Omarchy's own require_all, with
reload), after monitors/input/bindings/looknfeel/autostart. Each
layer ships its own NN-<layer>.lua there — roles 20s, personal-desktop 40,
features 50, machines 80 — and bindings.lua stays stock so nothing is bound
twice. install.sh warns about a file in hypr/layers/ that no layer of the
current profile provides (a role you dropped).
Packages lines are name [arch...] [cmd=a,b] # why, in [repo], [aur],
[optional] or [ignore] sections. An arch limits a line to x86_64 or
aarch64 (Arch Linux ARM lacks opencode, openai-codex, mise and
shellcheck). cmd= skips the package when one of those commands is already
on PATH — on the laptops, dotfiles' bin/ puts most devops and agent CLIs in
~/.local/bin, and the Arch package would only duplicate them; on a Coder
desktop, which has no dotfiles, the package is the tool. [ignore] is for
packages a machine has on purpose but this repo does not manage, so
capture.sh packages stays quiet about them.
Roles name the dotfiles modules they pair with (dotfiles=(...)), and
install.sh prints them when ~/.dotfiles is absent. It never runs
dotfiles itself: that needs a 1Password token, which a Coder desktop does
not have.
Coder desktops
The image is built by covert-cloud
(platform/workspaces/omarchy-desktop/image/). Its provision/user.sh clones
this repo at a pinned commit into ~/.local/share/omarchy-setup and runs
install.sh --profile coder-desktop, which bakes in base, developer and
the coder-desktop layer:
- one fixed
Virtual-1monitor at 1920x1080, GDK scale 1 — must matchselkies.service's--manual-width/--manual-height; the build fails if the line is missing; - no window animations, and GTK reduced motion: the VM has no GPU, so every frame is drawn on the CPU;
- Omarchy's stay-awake mode: the desktop user has no password, so a lock screen could never be unlocked; the build checks for the marker;
- Retro 82 as the default theme, set once only, so a theme the developer picks survives restarts. (The image itself installs the Covert Cloud themes.)
The plumbing stays in covert-cloud: Selkies, the Coder agent, the Super key
stand-in, the start page, autologin, and the lockdown (no SSH, no file
transfer, locked password). Nothing from personal* or handoff goes on a
desktop that other people use.
Roles per workspace. The Coder template has a Roles parameter
(multi-select: devops, ai-agents). At every workspace start it runs
install.sh --profile coder-desktop --roles <chosen> --no-clobber:
idempotent, so an unchanged choice costs a few seconds, and --no-clobber
keeps any file the developer has edited. Deselecting a role stops managing
it but does not uninstall its packages; its hypr/layers/ file is left in place
and reported.
Changing what the desktops get is a commit here plus a pin bump there:
OMARCHY_SETUP_REF in covert-cloud's provision/user.sh (rebuild the image)
and in the template's main.tf (template push; reaches running desktops at
their next start).
The MacBook Air M2
Captured as it was on 2026-10-16 (Omarchy 4.0.3rc4), read-only over SSH: no
hand-built themes, no shell plugins, stock bar and terminal fonts, so it
includes personal but not personal-desktop. Add that to its include= to
give it the HP's desktop. What is its own:
monitors.lua: monitor scale 1.33333, GDK scale 1, no named outputs.input.lua: stock gestures (no three-finger workspace swipe), and the measured note on whysensitivity/accel_profilerules are inert for the lan-mouse virtual pointer./etc/modprobe.d:hid_apple fnmode=1(the built-in keyboard; the HP uses 2 for an external Apple keyboard) andappledrm show_notch=1.lan-mouse.service.d/pointer-scale.conf:LAN_MOUSE_POINTER_SCALE=0.7, for the Air's 140 logical ppi against the LG's ~96.- Its own
herdr/config.toml(no agent-row customization yet),opencode.json("theme": "system") andvoxtype/config.toml. - The Asahi platform packages, listed so
capture.shnotices drift.
Left out: ~/.config/opencode/opencode.gateway.json (holds the LiteLLM key;
dotfiles' mcp-config writes it), ~/.config/lan-mouse/, and the
post-update hooks and omarchy-asahi-mic.service, which Omarchy installs
itself.
The HP laptop: its customizations, and why
Displays (profiles/machines/hp-laptop/config/hypr/monitors.lua) are the
fiddliest part. The file is the single source of truth for monitor rules on
purpose — overlapping rules spread across several files is what previously made
external displays unrecoverable. Three outputs are named by EDID description
rather than connector, so they match whichever port they land on: the built-in
Chimei panel at 1.25x, the LG IPS236 on HDMI at 1x, and the LG TV through the
Anker dock. The TV is deliberately left on preferred (3840x2160@30) — the dock
negotiates DP alt-mode with only two lanes, so forcing 4K60 produced a "monitor
has no mode" loop. GDK_SCALE=1 with a catch-all monitor scale of 1 (the
built-in panel and the TV set 1.25 in their own rules); the stock pairing is 2
and auto.
hyprland.lua sources hyprmoncfg-monitors.lua last so the hyprmoncfg
daemon's generated layout wins over anything above it.
profiles/machines/hp-laptop/bin/fix-stuck-displays recovers the related
kernel-side failure: after suspend/resume i915 can leave an unplugged connector
"disconnected" but still "enabled" on a pipe Hyprland has forgotten, which
blocks every other output from modesetting. It forces the connector on, disables
it through Hyprland to release the pipe, then returns it to auto-detect. It
needs passwordless sudo for tee /sys/class/drm/*/status.
Keyboard/mouse sharing (profiles/features/handoff/: bin/omarchy-handoff,
systemd/user/lan-mouse.service) drives lan-mouse to share input with the
other Omarchy laptop (the Air), bound to SUPER+SHIFT+K (SUPER+K is already
Keybindings). The script's header comment is the real documentation — it records
the non-obvious facts that cost hours: capture_backend must be layer-shell
(the portal backend is a silent no-op on Hyprland 0.56.2),
[authorized_fingerprints] is keyed by fingerprint and the reversed form fails
silently, and the log variable is LAN_MOUSE_LOG_LEVEL, not RUST_LOG. It runs
as a user unit rather than from autostart.lua because o.launch_on_start
sends both streams to /dev/null, hiding every pairing failure.
Three further facts were only established after the above was first written, and they are what actually made it work:
- Fingerprints are case-sensitive. lan-mouse generates them lowercase and
compares with a plain map lookup — no normalisation — while
opensslprints uppercase andcli authorize-keystores whatever it is handed. An uppercase digest is the correct digest, valid TOML, non-empty, passes every guard, and never matches: the DTLS handshake diesbad_certificate, indistinguishable from a wrong key or a dropped packet. This was the root cause of a full day of dead crossings.omarchy-handoff fixcaserepairs it;doctornow fails on it. - Authorization is receiver-side only, not mutual.
connect.rssetsinsecure_skip_verify, so the connecting side never checks the peer's certificate — only the listener consultsauthorized_fingerprints. Each direction therefore depends solely on the receiver's list, two-way control needs both lists, and a sender cannot tell whether it is talking to the real peer. Input goes to whatever answers on that IP:port. ufwis default-deny inbound, and a dropped inbound 4242 presents identically to a bad certificate.install.sh handoffopens it for the addresses in the profile'shandoff_peer_ips. Check it first:journalctl -k -g 'UFW BLOCK'.
profiles/features/handoff/handoff/patch/ carries a two-line source patch so
the daemon forwards the accelerated deltas, which is the only way to make the
sender's sensitivity and accel_profile affect remote pointer feel at all; it
also adds LAN_MOUSE_POINTER_SCALE for the residual difference in logical pixel
density between the two screens. install.sh builds it per-machine — the two
laptops are different architectures — and lan-mouse.service runs the result.
The Air's LAN_MOUSE_POINTER_SCALE=0.7 is a systemd drop-in in its own layer
(lan-mouse.service.d/pointer-scale.conf), so the shared unit stays at 1.0.
profiles/machines/hp-laptop/system/sysctl.d/99-omarchy-handoff-arp.conf goes
with it: Wi-Fi and the dock's ethernet both sit on 192.168.1.0/24, and with
the default ARP settings the subnet's mapping collapses onto whichever NIC
replied last.
Bar and shell (profiles/personal-desktop/config/omarchy/shell.json) adds
omarchy.active-window to the left, five plugin widgets, and indie.herdr-hud
as a non-bar plugin. Idle is stretched a long way past stock — screensaver at 20
minutes, lock at 30, against 2.5 and 5.
Keybindings live in two layer files.
profiles/personal-desktop/config/hypr/layers/40-personal-desktop.lua rebinds
SUPER+SHIFT+A from the stock ChatGPT webapp to Claude and adds four Herdr HUD
bindings; the handoff feature's 50-handoff.lua adds Handoff.
Input (profiles/machines/hp-laptop/config/hypr/input.lua) enables the
three-finger horizontal workspace gesture that ships commented out, and flattens
pointer acceleration for unknown-device*.
That second rule is a no-op, and the reasoning originally given for it was
wrong. It was added believing lan-mouse forwards deltas the sending compositor
had already accelerated, which the receiver then amplified a second time.
Upstream does the opposite: it forwards the unaccelerated deltas and discards
the accelerated pair in the same event
(profiles/features/handoff/handoff/patch/REBUILD.md). And the rule cannot act
either way — hl.device({name="unknown-device"}) does match the virtual pointer
(provable by toggling scroll_factor), but sensitivity and accel_profile
are libinput pointer-acceleration settings and a wlr-virtual-pointer is not a
libinput device, so they have nothing to act on. Measured 2026-10-10 on both
machines with lan-mouse test-emulation + hyprctl cursorpos: travel was
398x198px at sensitivity = 0 and identical at -0.9, exactly 1:1 with the raw
deltas. The lines are kept because they are harmless and the unknown-device
naming note is worth preserving; the real fix is handoff/patch/. Do not add
sensitivity or accel_profile rules here expecting them to change remote pointer
feel.
Terminals are all dropped to a smaller font (9 -> 8) except ghostty, which
went the other way to 12. shell.toml sets the bar font base size to 10.
Branding (profiles/personal/config/omarchy/branding/, shared with the Air)
replaces the about and screensaver ASCII art.
hid_apple fnmode=2 makes the F-keys act as F-keys on an Apple keyboard.
It only takes effect after sudo mkinitcpio -P or a module reload.
Local plugin work
indie.herdr-hud is the one piece that exists on no remote. Its local branch
carries commits — touchpad scroll feel, agent ordering, context-size display —
and they stay off the upstream PR deliberately.
profiles/personal-desktop/plugin-patches/ carries them as a git bundle plus a
patch for the uncommitted working tree, and install.sh replays both after
cloning. Two untracked scripts in the same plugin, bin/herdr-agents and
bin/herdr-web, ride along as indie.herdr-hud--bin--<name>.
capture.sh plugins re-bundles and re-diffs these, so local work keeps flowing
back into the repo.
noconnor.agents is the Agents bar widget held at the Omarchy 4.0.4 panel. 4.5
replaced it with a compact one (a meter per limit, the percentage only on
hover, no tokens by day or by model). It is a clone of omarchy.agents
(omarchy plugin clone) with Panel.qml, Main.qml and README.md taken from
upstream tag v4.0.4; Agent.qml and assets/ stay at 4.5, and the usage
data still comes from the system's omarchy-agent-usage-* collectors. It has
no git history of its own, so it is carried as plain files in
profiles/personal-desktop/config/omarchy/plugins/noconnor.agents/ and the
config step installs it and pulls it back. The 4.5 panel's account controls
(Use, Autoswitch, adding an account) are not in it. After installing it on a
running shell, omarchy restart shell: until then the built-in instance keeps
the omarchy.agents IPC target.
Not in here, on purpose
- Secrets.
~/.config/lan-mouse/holds a private key and per-machine fingerprints;~/.config/novnc/auth.env, SSH keys and the git credential helpers are likewise machine-local. Start fromprofiles/features/handoff/handoff/config/config.toml.example-{air,laptop}(which carry thecapture_backend, edge and lowercase-fingerprint rules), then pair withomarchy-handoff pair <peer> <lowercase-sha256>andomarchy-handoff doctor. A fresh install regenerates the certificate, so this machine's fingerprint changes and the peer has to re-pair. Back uplan-mouse.pembeforehand if you would rather keep it stable — somewhere private, not here. - Shell, git and dev tooling. That is already its own repo,
dotfiles(git.nic-oconnor.com/public/dotfiles), which handles zsh, mise, 1Password and the~/.local/bintool shims. The only overlap avoided here is~/.config/git/config, whose customizations are identity and credential helpers —dotfilesterritory. - The remote-access stack.
wayvnc,novnc,sunshine,freerdp,remminaandomarchy-remote-desktopare installed on this laptop but scoped out.kokd.remote-desktopis still inplugins.txtsince it is a bar plugin, but it is not placed inshell.json. - Two plugins with no remote yet.
niccoconnor.mantelandniccoconnor.keyhintsare installed on the HP and placed inshell.json, but neither has anorigin, soplugins.txtcannot pin them (capture.shsays so). Until they are pushed, another machine gets a bar with those two slots empty. - Work tooling. The keycloak helpers (
kcadm,kclogin,wren-keycloak) andpaperclipai.service. - aether-managed themes.
pink-gt3rs,porsche-caymanandporsche-raincarry a.aether-managedmarker and are reinstallable withaether, so their ~18MB of wallpapers and previews stay out. btop.conf. Its only diff against stock is v1.4.7 default drift, not a real customization.- Backup files. Fifteen
.bak.*and.prefix.*files are sitting in~/.config/hyprand~/.config/omarchyfrom earlier editing sessions. They are not tracked and are safe to delete.
Adding things
A file. capture.sh only refreshes what the repo already tracks, so start
it by hand, in the layer it belongs to:
cp ~/.config/foo/bar.conf profiles/machines/hp-laptop/config/foo/bar.conf
git add profiles/machines/hp-laptop/config/foo/bar.conf
Check first that it actually differs from stock —
diff /usr/share/omarchy/config/foo/bar.conf ~/.config/foo/bar.conf — or you
will be tracking a file Omarchy already ships. A Hyprland setting goes in that
layer's config/hypr/layers/NN-<layer>.lua, not in bindings.lua or
looknfeel.lua.
A role. mkdir profiles/roles/<name>, give it a profile (description=,
optionally dotfiles=(...)) and a packages.txt, plus
config/hypr/layers/2N-<name>.lua if it needs bindings. Use cmd= for any
package dotfiles might already provide, and an arch for anything Arch Linux
ARM lacks. To offer it on Coder desktops, add it to the template's Roles
parameter in covert-cloud.
A machine. mkdir profiles/machines/<name> with a profile naming
hostnames=, arch= and include=(base ...), then its monitors.lua,
input.lua, packages.txt and /etc drop-ins. Run ./install.sh --profile <name> --dry-run on it, then ./capture.sh after any hand-tuning.