Files
omarchy-setup/README.md
T
noconnorandClaude Opus 5.5 e1e5f05876 Capture omarchy-air's customizations as a per-host overlay
hosts/<hostname>/{config,system}/ now overlays the shared trees. install.sh
installs a host file in place of the shared copy at the same path, and
capture.sh pulls back into whichever copy the host installs from. That keeps
hardware-specific files off the wrong machine: the HP's EDID-named monitors.lua
and its fnmode=2 would otherwise land on the Air.

capture.sh refreshes the shared trees only on omarchy-laptop, where they were
captured, or when CAPTURE_SHARED=1 is set. On the Air, a plain run copied its
stock terminal, hypr and shell files over every one of the HP's customizations.

air's overlay holds monitors.lua (1.33333 scale and GDK_SCALE=1 for the
2560x1664 panel), input.lua (stock settings plus the inert-accel measurements),
herdr onboarding=false, git identity and credential helpers (no secrets),
mimeapps.list, and the Asahi installer's hid_apple fnmode=1 and show_notch=1.
bindings.lua is not overlaid because the shared one is a strict superset.
hosts/omarchy-air/README.md records what was left out on purpose and why, and
warns that the shared packages.txt is x86-specific.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KKPyqMc7MGKyBLKVdeDejp
2026-10-16 15:49:19 -04:00

200 lines
11 KiB
Markdown

# omarchy-setup
Every customization this laptop has on top of a stock Omarchy install, in one
place, so a second machine can be brought to the same state.
Captured from Omarchy **4.0.4-1** on **2026-10-10**.
```bash
git clone <this repo> ~/omarchy-setup
cd ~/omarchy-setup
./install.sh --dry-run # see what it would change
./install.sh # do it
```
`install.sh` is idempotent and backs up anything it overwrites as
`<file>.bak.<epoch>`. `capture.sh` runs the other way, pulling live changes back
into the repo so this stays the source of truth.
## What is in here
| Path | What |
|---|---|
| `config/` | Mirrors `~/.config`. Only files that differ from Omarchy's shipped defaults. |
| `themes/` | The three hand-built themes, wallpapers included. |
| `plugins.txt` | The 10 Omarchy shell plugins, each pinned to a commit. |
| `plugin-patches/` | Local plugin work that exists on no remote (see below). |
| `bin/` | `omarchy-handoff` and `fix-stuck-displays`. |
| `handoff/` | The lan-mouse source patch that fixes pointer feel, plus example `config.toml`s. Not a `~/.config` mirror. |
| `systemd/user/` | `lan-mouse.service`, `voxtype.service`. |
| `system/` | `/etc` drop-ins: the ARP sysctl and `hid_apple` fnmode. |
| `packages.txt` | Packages beyond the Omarchy baseline, each with a reason. |
| `hosts/<hostname>/` | Per-host overlay of `config/` and `system/`: a file there replaces the shared copy on that host only. See `hosts/omarchy-air/README.md`. |
## The customizations, and why
**Displays** (`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 1.25
monitor scale; 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.
`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** (`bin/omarchy-handoff`, `systemd/user/lan-mouse.service`)
drives `lan-mouse` to share input with the other Omarchy laptop, 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 `openssl` prints
uppercase and `cli authorize-key` stores whatever it is handed. An uppercase
digest is the *correct* digest, valid TOML, non-empty, passes every guard, and
never matches: the DTLS handshake dies `bad_certificate`, indistinguishable
from a wrong key or a dropped packet. This was the root cause of a full day of
dead crossings. `omarchy-handoff fixcase` repairs it; `doctor` now fails on it.
- **Authorization is receiver-side only, not mutual.** `connect.rs` sets
`insecure_skip_verify`, so the *connecting* side never checks the peer's
certificate — only the listener consults `authorized_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.
- **`ufw` is default-deny inbound**, and a dropped inbound 4242 presents
*identically* to a bad certificate. `install.sh --handoff` opens it for the
peer's addresses. Check it first: `journalctl -k -g 'UFW BLOCK'`.
`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 `systemd/user/lan-mouse.service` runs the result.
`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** (`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** (`config/hypr/bindings.lua`) rebinds `SUPER+SHIFT+A` from the
stock ChatGPT webapp to Claude, and adds four Herdr HUD bindings plus Handoff.
**Input** (`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 (`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** (`config/omarchy/branding/`) 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. Ten commits live on
its `local` branch — touchpad scroll feel, agent ordering, context-size display —
and they stay off the upstream PR deliberately. `plugin-patches/` carries them as
a git bundle plus a patch for the uncommitted working tree, and `install.sh`
replays both after cloning. `io.github.tallsam.navbar-cat` has a one-file working
tree change captured the same way.
`capture.sh plugins` re-bundles and re-diffs these, so local work keeps flowing
back into the repo.
## 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 from
`handoff/config/config.toml.example-{air,laptop}` (which carry the
`capture_backend`, edge and lowercase-fingerprint rules), then pair with
`omarchy-handoff pair <peer> <lowercase-sha256>` and `omarchy-handoff doctor`.
A fresh install regenerates the certificate, so this machine's fingerprint
changes and the *peer* has to re-pair. Back up `lan-mouse.pem` beforehand 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/bin` tool shims. The only overlap avoided here is
`~/.config/git/config`, whose customizations are identity and credential
helpers — `dotfiles` territory.
- **The remote-access stack.** `wayvnc`, `novnc`, `sunshine`, `freerdp`, `remmina`
and `omarchy-remote-desktop` are installed on this laptop but scoped out.
`kokd.remote-desktop` is still in `plugins.txt` since it is a bar plugin, but
it is not placed in `shell.json`.
- **Work tooling.** The keycloak helpers (`kcadm`, `kclogin`, `wren-keycloak`) and
`paperclipai.service`.
- **aether-managed themes.** `pink-gt3rs`, `porsche-cayman` and `porsche-rain`
carry a `.aether-managed` marker and are reinstallable with `aether`, 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/hypr` and `~/.config/omarchy` from earlier editing sessions. They are
not tracked and are safe to delete.
## Adding a file
`capture.sh` only refreshes what the repo already tracks, so start it by hand:
```bash
cp ~/.config/foo/bar.conf config/foo/bar.conf # path mirrors ~/.config
git add config/foo/bar.conf
```
`install.sh config` and `capture.sh config` both walk `config/` recursively, so
nothing else needs changing. Check first that the file 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.
### Per-host files
Hardware-specific files (monitor scale, fnmode) go under `hosts/<hostname>/`,
mirroring `config/` or `system/`, rather than into the shared tree. The shared
tree comes from `omarchy-laptop`, and `capture.sh` refreshes it only there (or
with `CAPTURE_SHARED=1`). On any other host it refreshes just that host's overlay,
so a machine running stock files cannot overwrite the shared customizations.
`OMARCHY_SETUP_HOST` overrides the detected hostname for both scripts.