1
0
Fork 0
Commit graph

68 commits

Author SHA1 Message Date
b78e23aa16 gentoo install scripts: guard with os_family like overlays
Both run_once_20-install-user-packages-gentoo.sh.tmpl and
run_once_40-install-sway-gentoo.sh.tmpl were missing the os_family
template guard (the overlays script got it in the previous fix but
these two slipped through). On non-gentoo boxes — arch, debian —
chezmoi apply would run them and they'd try to 'emerge', failing
with 'sudo: emerge: command not found' and exit status 1, which
breaks topgrade.

Wrap each body in '$0{ if eq .os_family "gentoo" }' / '$0{ else }',
mirroring the chaotic-aur script's pattern and the overlays fix from
the previous commit. On non-gentoo boxes the rendered script reduces
to a single 'skipping' log line and exits 0; on gentoo boxes the full
body (including the existing .sway_setup gate on the sway script) is
preserved.

Verified by rendering both scripts with a gentoo-config override:
  arch (live)  -> 'skipping gentoo user-packages' / 'skipping sway'
  gentoo        -> full body (293 / 87 lines)
Both pass bash -n and actually exit 0 when run on this arch box.
2026-06-25 18:58:27 -04:00
c54e770914 topgrade: drop ignore_failures so step failures halt the run
Previously 'node' failures were silently skipped. With this removed,
any non-zero step exit code aborts the run, matching the user
preference of 'I dont want it to skip anything'.
2026-06-25 18:41:21 -04:00
6b64fe0625 gentoo-overlays: guard with os_family so non-gentoo boxes skip it
The script's body was never wrapped in an os_family template conditional
despite the header claiming it was 'guarded by the chezmoi template
engine'. So topgrade/chezmoi apply would run it on every box and die
with '/var/db/repos/gentoo missing' on arch/debian.

Wrap the body in '{{ if eq .os_family "gentoo" }}' / '{{ else }}' (same
pattern the arch-only chaotic-aur script already uses). On non-gentoo
boxes the rendered script reduces to a single 'skipping' log line.
Also fix two stale comments in the header (filename was 05, not 10).

Verified by rendering with --config override: arch → no-op log line,
gentoo → full body intact, both pass bash -n.
2026-06-25 18:33:58 -04:00
d61ffacc22 omp secrets: use encrypted_private_ prefix (decrypts + 0600)
chezmoi parses attribute prefixes left-to-right and 'encrypted_' must
precede 'private_'. The 'private_encrypted_' order silently breaks
decryption: chezmoi consumes 'private_', then treats 'encrypted_foo.age'
as a literal filename and copies the ciphertext verbatim instead of
decrypting it.

models.yml was named 'private_encrypted_models.yml.age' since commit
3c3fab7 and was never decrypting — a stale 'encrypted_models.yml.age'
blob was sitting in ~/.omp/agent/ and the plaintext models.yml was an
unmanaged leftover. .env and zai.key used the plain 'encrypted_'
prefix and were decrypting at umask 0644 (world-readable in isolation;
the 700 ~/.omp/agent/ dir was the only thing shielding them).

This commit:

  * renames all three to encrypted_private_{zai.key,.env,models.yml}.age
    so chezmoi decrypts AND lands them at 0600 natively
  * rewrites run_onchange_35 as 'ensure-omp-secret-perms.sh' covering
    all three, as belt-and-suspenders for any box where a secret still
    sits at 0644 from a prior apply
  * removes the stale encrypted_models.yml.age verbatim blob and its
    orphan state entry
  * corrects the README perms section to document the prefix-order
    gotcha (was misleadingly claiming 'private_' alone gave 0600)

Verified end-to-end on this box: chezmoi managed lists all three as
decrypted targets, scoped apply writes them at 600, chmod script is
idempotent. Other boxes need a 'chezmoi apply' to pick up the rename
and the onchange chmod.
2026-06-25 18:33:58 -04:00
27ae3a3b18 sway: drop -t 2000/-t 3000 from notify-send, source restore-wayland-env in toggle-dropdown/wifi-menu
The actual cause of 'caffeine notification doesn't show' was the
-t 2000 timeout: the notification popped for 2 seconds and self-cleared
before the user could see it. Manual notify-send from a terminal (no
-t flag) used mako's default-timeout=5000 and was visible — so the user
saw my test notifications but not their own clicks.

Fix: drop the -t flag everywhere so mako config owns the duration.
Added: source restore-wayland-env.sh in toggle-dropdown.sh and
wifi-menu.sh — same env-loss pattern as caffeine.sh, would have
been next on the bug list.

Verified on tadbit: -t 2000 notification visible at +0.5s, gone at
+2.5s (user missed it). No -t flag: visible at +0.5s, gone at +5.5s
(visible long enough to read).
2026-06-23 22:24:16 -04:00
d78ba4152b sway: factor env restoration into restore-wayland-env.sh, fix caffeine notify
caffeine.sh click was working (icon toggled, swayidle restarted) but
the 'Caffeine ON/OFF' notification never appeared. Same root cause as
the prior swayidle bug: waybar's on-click context strips WAYLAND_DISPLAY
from the spawned script's env. notify-send writes to D-Bus (exit 0) but
mako can't display because it has no Wayland socket to render on. Click
appears to do nothing visually.

Fix: extract the /proc/*/environ probe into a shared helper
(restore-wayland-env.sh) and source it from both caffeine.sh AND
start-swayidle.sh. Now any future on-click script in this repo can
`. restore-wayland-env.sh` to inherit the right env.

Verified on tadbit:
- click 1 (caffeine ON): swayidle killed, flag set, 'Caffeine ON'
  notification appeared in mako
- click 2 (caffeine OFF): swayidle restarted, flag cleared, 'Caffeine
  OFF' notification appeared in mako

Both are visible in makoctl history now.
2026-06-23 22:18:01 -04:00
05e75bc41e sway: extract start-swayidle.sh as single source of truth (fix caffeine)
caffeine.sh click was silently breaking idle lock. Two bugs that
compounded:

1. caffeine.sh inlined its own swayidle command (with swaylock -f -i
   , not lock-fancy.sh), drifted out of sync with sway/config
   which used lock-fancy.sh. Toggle off would restart swayidle with a
   different lock command than the rest of the session.

2. WAYLAND_DISPLAY isn't set in waybar's on-click context. When
   caffeine.sh called 'swaymsg output * power off' (part of the
   restart command) and later called swaylock / grim (via lock-fancy),
   those failed silently. swayidle itself can run without
   WAYLAND_DISPLAY but it can't actually monitor input/output, so it
   exits immediately. Result: user clicks caffeine off, flag clears,
   icon goes back to 'inactive', but auto-lock is silently dead.
   The user thinks they turned caffeine off. They didn't. They're
   just unprotected.

Fix:
- new start-swayidle.sh holds the canonical swayidle command
- sway/config: exec $HOME/.config/sway/start-swayidle.sh
- caffeine.sh: killall swayidle; start-swayidle.sh &
- start-swayidle.sh probes /proc/*/environ for a Wayland client's
  env (mako always has it) and exports WAYLAND_DISPLAY /
  DBUS_SESSION_BUS_ADDRESS / XDG_RUNTIME_DIR / DISPLAY if missing
  before exec'ing swayidle

Single source of truth + env restoration = click works on every box
that has the same Wayland-capable process tree (mako, swaybar, etc.),
no matter who/what is calling the script.

Verified on tadbit: pre-fix toggle off killed swayidle permanently.
Post-fix: toggle off restarts swayidle successfully, env inherited
from mako's /proc/PID/environ.
2026-06-23 21:51:09 -04:00
6644666cb1 Font install check: anchor to exact family 'Maple Mono NF'
The previous substring check (*"Maple Mono NF"* glob / grep -q substring)
matched 'Maple Mono NF CN' too, which silently skipped the Latin-only NF
install on boxes that had only the CJK variant installed (byte, kaiser —
they shipped with maple-font system package that defaults to NF CN).

Result: foot (and any other config asking for 'Maple Mono NF') silently
fell back to system sans on byte and kaiser, while looking like a
working terminal. Compounded by foot.ini asking for 'Maple Mono CN'
(see prior commit c6779c5), which masked the missing NF install on
miche (which has both CN and NF).

Fix: use fc-list : family | grep -qxF 'Maple Mono NF' which:
- prints one family per line (no style suffixes to confuse grep)
- -x anchors whole line
- -F fixed string (no regex)
- matches exactly 'Maple Mono NF' and nothing else

Why not fc-match? fc-match reports a substitute when the family isn't
installed (Noto/Liberation/DejaVu depending on distro), so it can't
distinguish 'NF installed' from 'NF not installed, fell back'.

Verified on byte: fc-list : family | grep -qxF 'Maple Mono NF' returns
1 (false) because byte has only 'Maple Mono NF CN', so the install will
now run on next chezmoi apply.
2026-06-23 20:31:33 -04:00
c6779c57ce foot.ini: fix Maple Mono CN -> Maple Mono NF (was rendering Liberation Sans)
The font= line referenced 'Maple Mono CN', the CJK variant from a separate
release zip we don't install. The repo only installs Maple Mono NF (Latin
Nerd Font variant from subframe7536/Maple-font).

foot silently falls back to the system sans font when the requested family
is missing, so on every box except miche foot was rendering Liberation Sans
(noto, dejavu, etc.) while looking like a working terminal. miche has both
CN and NF installed from prior tinkering, which is why it was the only
box where the bug was invisible.

Every other config in this repo (nvim, wofi, waybar, mako) already uses
'Maple Mono NF' correctly. Only foot had the wrong suffix.

Removed the 'Terminus:size=14' fallback — Terminus isn't installed either,
so it was dead weight that also triggered foot to fall through to system
default if Maple Mono CN failed to match.

Verified: fc-match 'Maple Mono NF' now returns MapleMono-NF-Regular.ttf on
tadbit (was: Liberation Sans).
2026-06-23 20:29:03 -04:00
c51dc864d8 README: document omp zai-coding provider quirk + models.yml encrypted
Explains why models.yml is in chezmoi (encrypted) and why the literal
Z.ai API key is in apiKey: rather than in zai.key — short version:
omp v16.1.16's built-in zai provider routes to /api/anthropic which
rejects Bearer auth, so a custom zai-coding provider is needed at
/api/coding/paas/v4 which does accept Bearer. The custom provider's
apiKey field requires a literal value, so the key is embedded in the
encrypted file. See references/omp-provider-secrets.md in the chezmoi
skill for the long version + verification commands.
2026-06-23 19:47:19 -04:00
3927acd64f Add run_onchange to chmod 600 ~/.omp/agent/models.yml
The encrypted models.yml is named private_encrypted_models.yml.age so
chezmoi *should* set 600 on decrypt, but on boxes where the file
already existed from a prior apply (before the rename), the perm stays
at umask default (644). This run_onchange script normalizes to 600.

Runs only when the script body changes (chezmoi hashes the rendered
content). On a fresh apply, it brings the perm to 600 once, then stays
silent on subsequent applies until the body changes again.
2026-06-23 19:45:03 -04:00
3c3fab709b omp models.yml: rename to private_ prefix (chmod 600 on decrypt) 2026-06-23 19:44:09 -04:00
5b6ae8aa86 Add encrypted omp models.yml with literal zai key (fix 401)
omp v16.1.16 hardcodes the built-in zai provider to
https://api.z.ai/api/anthropic (Anthropic-compatible endpoint) which
requires 'x-api-key' header. omp only sends 'Authorization: Bearer', so
the built-in zai provider always returns 401 on real Z.ai API keys.

The OpenAI-compatible endpoint https://api.z.ai/api/coding/paas/v4
accepts Authorization: Bearer and works fine. The user's existing
~/.omp/agent/models.yml defines a custom 'zai-coding' provider pointed at
this endpoint, but the apiKey field was set to 'ZAI_CODING_API_KEY' which
omp treats as a literal string (not an env var reference), so the request
was 'Authorization: Bearer ZAI_CODING_API_KEY' which 401s.

Fix: include the literal zai API key in the apiKey field. This is
identical to the existing encrypted zai.key secret (same key, decrypted
on apply). The encrypted file is in chezmoi so it stays encrypted at rest
and chmod 644 on disk — same perms as the existing zai.key.

Verified manually on byte before commit:
  omp --print --model zai-coding/glm-5.2:xhigh 'hi'
  → 'Hi! What can I help you with today?'
  (was: '401 token expired or incorrect')

After applying this commit on all boxes:
  omp --print --model zai-coding/glm-5.2:xhigh 'hi'
should work everywhere.
2026-06-23 19:38:51 -04:00
252f166f15 Gentoo user-packages: add omp install via bun (was missing)
The universal arch/debian scripts had omp install via 'bun add -g
@oh-my-pi/pi-coding-agent' for both OSes. The gentoo script was
missing this block entirely — only the universal's os_family guard
handled arch/debian. On tadbit this meant omp never got installed.

Fix: added an omp install block to the gentoo script after the bun
verification step. Same wrapping in a subshell for undervoltage
safety. Now all three OSes install omp the same way (via bun global).
2026-06-23 17:58:25 -04:00
620eb0c633 Gentoo font unzip: add -o flag to overwrite without prompting
The universal arch/debian script extracts to $USER_HOME/.local/share/
fonts/maple-mono-nf/ (a subdir, no conflicts). The gentoo script
extracts directly to $USER_HOME/.local/share/fonts/ which already
contains existing fonts. 'unzip' prompts on overwrite in interactive
mode; in a non-interactive chezmoi apply this stalls forever with
EOF/timeout. Adding -o flag makes it overwrite without prompting.

Caught during tadbit onboarding: extracted font dir had
MapleMono-NF-ExtraBoldItalic.ttf already (from a previous run);
the unzip prompted, hit EOF, and the script exited 1.
2026-06-23 17:54:27 -04:00
a4434a1f01 Gentoo emerge: use --autounmask-continue (auto-apply + continue)
--autounmask-write=y writes the unmask file but emerge still
aborts because CONFIG_PROTECT marks the file as needing manual
review. --autounmask-continue=y is the right flag — it writes the
unmask AND continues the merge without aborting.

Discovered via the 'Autounmask changes successfully written.' message
followed by an immediate abort. The error message about
'autounmask-write' was actually about re-running with that flag,
not about what happened.

Applied to all three emerge invocations (main + GURU + sway).
2026-06-23 17:51:56 -04:00
0460a8c653 Gentoo emerge: add --autounmask-write (the missing piece for ~amd64)
Discovered via man: --autounmask=y alone only DISPLAYS the needed
keyword changes and aborts. The actual write to /etc/portage/...
files requires --autounmask-write=y (or --autounmask-continue=y
which also auto-continues after writing). Without --autounmask-write,
every lazygit (and any other ~amd64-only GURU package) install
fails with:

  The following keyword changes are necessary to proceed:
  =dev-vcs/lazygit-0.62.2 ~amd64
  Use --autounmask-write to write changes to config files

Added to all three emerge invocations:
- run_once_20-install-user-packages-gentoo.sh.tmpl: main loop
- run_once_20-install-user-packages-gentoo.sh.tmpl: GURU loop
- run_once_40-install-sway-gentoo.sh.tmpl
2026-06-23 17:50:51 -04:00
6ec6958a73 Gentoo GURU emerge: add --autounmask=y to handle ~amd64 keyword
The GURU emerge in run_once_20-install-user-packages-gentoo.sh.tmpl
was missing --autounmask=y (only had --autounmask-license=y). Same
fix as the main pkg loop: GURU packages like dev-vcs/lazygit are
~amd64-only and need the keyword unmask to be auto-written.

Symptom: 'The following keyword changes are necessary to proceed:
=dev-vcs/lazygit-0.62.2 ~amd64' even though --autounmask=y was
already on the main pkg loop. Fix: add the same flag to the GURU loop.
2026-06-23 17:49:37 -04:00
598ad9dfae Gentoo: add --autounmask=y to handle ~amd64 keyword unmask
lazygit and most GURU packages are ~amd64 only. Without --autounmask=y,
emerge fails with: =dev-vcs/lazygit-0.62.2 ~amd64 required.

Applied to run_once_20-install-user-packages-gentoo.sh.tmpl and
run_once_40-install-sway-gentoo.sh.tmpl.
2026-06-23 17:01:44 -04:00
03338d1f39 Gentoo user-packages: skip package if binary exists but not in portage
Some packages on tadbit have binaries installed OUTSIDE portage
(bat was installed by cargo earlier; eza/fd from a previous run
that the script didn't track). qlist -I correctly reports them as
not-in-portage, but command -v <bin> finds them on PATH.

Without this guard, every apply would re-emerge these packages,
causing spurious failures (e.g. 'there are no ebuilds to satisfy
app-text/bat' even though /usr/bin/bat works fine).

The detection now does:
  1. If qlist reports the package is in portage: skip
  2. If the binary is on PATH: log + skip
  3. Otherwise: add to MISSING_PKGS

Same fix applied to the GURU_MISSING detection.
2026-06-23 16:55:16 -04:00
94d3c0ebaf Gentoo user-packages: use qlist for missing-detect (handles non-binary pkgs)
The previous missing-detect used 'command -v <basename>' which
fails for packages that don't ship a binary of the same name
(zsh-completions installs /usr/share/zsh files, not
/usr/bin/zsh-completions). After the previous run installed these
packages, the next apply falsely reported them as missing and
tried to re-install them.

Switch to qlist -I which correctly reports whether a package is
in the installed-db. qlist is from app-portage/gentoolkit which
is already installed on gentoo. Fall back to equery, then to
the basename check, in case qlist is missing.
2026-06-23 16:47:11 -04:00
1d88429b14 Gentoo user-packages: lazygit is at dev-vcs/lazygit, topgrade via cargo
Two corrections to the GURU package list:

1. lazygit: was 'app-misc/lazygit' but GURU has it at 'dev-vcs/lazygit'.
   emerge failed with 'no ebuilds to satisfy' on app-misc/lazygit.

2. topgrade: not in any gentoo overlay. It's installed via cargo
   (handled by run_onchange_30-ensure-cargo.sh which has 'cargo
   install topgrade --locked' as the debian branch — same applies
   on gentoo). Removing it from the GURU emerge list avoids the
   same 'no ebuilds' error.
2026-06-23 16:45:06 -04:00
f17cafa245 Gentoo user-packages: fd is at sys-apps/fd, not app-misc/fd
On Gentoo, fd-find / fd is in sys-apps/fd (similar to eza being in
sys-apps/eza). Both are modern rust-based CLI tools in the sys-apps
category. The template used 'app-misc/fd' which doesn't exist on
gentoo's main tree; emerge failed with 'no ebuilds to satisfy'.
2026-06-23 16:29:56 -04:00
579d4ac952 Gentoo user-packages: fix eza category (sys-apps/eza, not app-misc/eza)
eza lives in sys-apps/ on Gentoo, not app-misc/. The previous
template used 'app-misc/eza' which made emerge fail with:
  !!! Error: 'app-misc/eza' not found
Also updated the USE flag block (sys-apps/eza git instead of
app-misc/eza git) so the file written to /etc/portage/package.use
is correct.
2026-06-23 16:27:11 -04:00
4556813e78 Gentoo bootstrap: fix ambiguous package names + early-return on os_family mismatch
Three issues caught during tadbit onboarding:

1. run_once_00-install-bootstrap-tools.sh.tmpl (gentoo branch):
   'gnupg' is ambiguous (app-crypt/gnupg vs app-vim/gnupg). Use full
   category/package names: app-crypt/gnupg, app-crypt/age, etc.
   The previous 'for p in age curl ...' loop fed short names to
   emerge which printed '!!! The short ebuild name gnupg is
   ambiguous' and exited 1.

2. run_once_20-install-user-packages-gentoo.sh.tmpl:
   sys-devel/base-devel doesn't exist on Gentoo (it's an Arch/Fedora
   concept; Gentoo's toolchain is the @system set which is always
   installed). Removed base-devel from the package list.

3. Universal scripts (run_once_20 + run_once_40) were running on
   gentoo and hitting either 'die unsupported os_family' (universal
   20) or 'WARNING sway packages not configured for gentoo' (universal
   40). Added early-return: 'if os_family == gentoo, exit 0' at the
   top of each universal script so the gentoo-specific scripts
   handle the box. (Same pattern the chaotic-aur script already had.)

4. run_once_10-add-gentoo-overlays.sh.tmpl:
   The cached ~/.cache/eselect-repo/repositories.xml was corrupt on
   tadbit (lxml.etree.XMLSyntaxError on every 'eselect repository
   list' call). Added a sanity check: parse the XML with
   xml.etree.ElementTree, delete if invalid, re-fetch.

After these fixes, the bootstrap on tadbit is expected to run cleanly
with --keep-going (the four failures above all become no-ops).
2026-06-23 16:18:54 -04:00
b5defc5a20 Re-encrypt secrets with all 8 recipients (incl. tadbit) 2026-06-23 15:59:39 -04:00
103153ae6c Add tadbit (Gentoo laptop) to age recipients + re-encrypt secrets
Tadbit is the 7th machine in the homelab. Generated a per-machine
age key on tadbit, added the pubkey as 8th recipient in
.chezmoi.yaml.tmpl, and re-encrypted the two .age secrets with
all 8 recipients (1 recovery + 7 machines).
2026-06-23 15:57:13 -04:00
36e9d3e0ce Add Gentoo support with GURU overlay requirement
Tadbit (tadbit-gentoo, x86_64, gcc 15.2.1, TKG kernel) joins the
homelab as the 7th machine — the first gentoo box. Adds Gentoo
support to the bootstrap alongside arch and debian.

New scripts:
- run_once_10-add-gentoo-overlays.sh.tmpl: enables GURU overlay
  via 'eselect repository enable guru' + 'emaint sync -r guru'.
  Idempotent — skips if GURU is already at /var/db/repos/guru.
  GURU is required because eza, lazygit, topgrade, and most modern
  CLI tools only live in GURU (not main).
- run_once_20-install-user-packages-gentoo.sh.tmpl: emerge-based
  user package set. Writes USE flags to package.use/ BEFORE
  emerging so foot/wofi/waybar get the right features. Detects
  missing packages via 'command -v <basename>'. Falls back to
  the official curl installer for bun (no gentoo package).
- run_once_40-install-sway-gentoo.sh.tmpl: sway stack via emerge
  with USE flags for X+wayland+tray+upower+wireplumber.

Updated scripts:
- .chezmoi.yaml.tmpl: os_family detection now also matches 'gentoo'.
  Critical fix: Gentoo's /etc/os-release uses single-quoted values
  ('gentoo' not 'gentoo' or "gentoo"), and chezmoi's parser doesn't
  strip them. Without trimAll "'", .chezmoi.osRelease.id returns
  the literal string 'gentoo' with quotes, and the eq test fails.
  Symptom: os_family silently becomes 'unknown'.
- run_once_00-install-bootstrap-tools.sh.tmpl: added gentoo branch
  that uses emerge --sync + emerge (skipping if tree is < 1 day old).
- run_onchange_30-ensure-cargo.sh.tmpl: added gentoo branch for
  bat (already installed by emerge, just verify), topgrade (GURU),
  cargo-update (dev-util/cargo-update in main).

README: documented Gentoo-specific quirks (USE flags, GURU,
single-quote parsing, no binary packages).
2026-06-23 15:55:28 -04:00
1b596bd894 Add /opt/rocm/bin to PATH in zshrc when it exists
ROCm installs to /opt/rocm (not /opt/rocm-X.Y versioning). The
upstream /etc/profile.d/rocm.sh tries to add /opt/rocm/bin to PATH
but uses 'append_path' which is a fish-shell function, not a bash/zsh
built-in — so on a zsh login the export is a no-op and the rocm
tools (amd-smi, rocm-smi, rocminfo, hipcc, etc.) are unreachable
without explicit PATH setup.

Add /opt/rocm/bin to the top of the path array in dot_zshrc.tmpl
when the directory exists on the host. Use stat() with the
not-(not-...) bool coercion (pitfall #35 in the chezmoi-bootstrap-
runbook skill) to avoid rendering the stat map's string repr.

Conditional on the directory existing so Pis (no ROCm) don't get a
dead PATH entry. Verified on miche:
  PATH=/usr/bin:/bin:/usr/local/sbin:/usr/local/bin:/usr/bin/site_perl:    /usr/bin/vendor_perl:/usr/bin/core_perl:/opt/rocm/bin
  /opt/rocm/bin/amd-smi
  /opt/rocm/bin/rocm-smi
  /opt/rocm/bin/rocminfo

amd-smi version: AMDSMI Tool: 26.4.0+3309c6114a | ROCm version: 7.13.0
2026-06-22 19:16:31 -04:00
3d329073fb Force bun as a system package on arch (no curl-install shadow)
Per user preference (memory 2026-06-22): PMs first (pacman via
chaotic-aur on arch, apt on debian), curl-install only as last-resort
fallback. bun is in [extra] on arch since 1.2.x, so the pacman
package is the right install path.

Three changes to run_once_20-install-user-packages.sh.tmpl:

1. Arch missing-detect: for the bun entry, check Installed From  : extra
Name            : bun
Version         : 1.3.14-1
Description     : Incredibly fast JavaScript runtime, bundler, test runner, and package manager – all in one
Architecture    : x86_64
URL             : https://github.com/oven-sh/bun
Licenses        : MIT
Groups          : None
Provides        : None
Depends On      : glibc  icu
Optional Deps   : None
Required By     : None
Optional For    : None
Conflicts With  : None
Replaces        : None
Installed Size  : 66.08 MiB
Packager        : Sven-Hendrik Haase <svenstaro@archlinux.org>
Build Date      : Sun 17 May 2026 09:29:39 AM EDT
Install Date    : Sun 14 Jun 2026 12:05:09 AM EDT
Install Reason  : Explicitly installed
Install Script  : No
Validated By    : Signature
   (system-package tracking) instead of /usr/bin/bun (binary on
   PATH). A curl-installed bun at ~/.bun/bin/bun or ~/.local/bin/bun
   would otherwise pass the command check, leaving us with a
   non-PM-managed install that topgrade wouldn't see.

2. Post-install verification on arch: assert that Installed From  : extra
Name            : bun
Version         : 1.3.14-1
Description     : Incredibly fast JavaScript runtime, bundler, test runner, and package manager – all in one
Architecture    : x86_64
URL             : https://github.com/oven-sh/bun
Licenses        : MIT
Groups          : None
Provides        : None
Depends On      : glibc  icu
Optional Deps   : None
Required By     : None
Optional For    : None
Conflicts With  : None
Replaces        : None
Installed Size  : 66.08 MiB
Packager        : Sven-Hendrik Haase <svenstaro@archlinux.org>
Build Date      : Sun 17 May 2026 09:29:39 AM EDT
Install Date    : Sun 14 Jun 2026 12:05:09 AM EDT
Install Reason  : Explicitly installed
Install Script  : No
Validated By    : Signature
   succeeds and report the installed version + repo. Fails loudly
   if bun is not tracked by pacman (curl shadow or failed install)
   so the operator can clean up and re-run.

3. Same post-install verification on debian: assert that
   /usr/bin/bun succeeds after the curl-install, and report
   the version. On debian bun is not in apt, so curl is the
   last-resort install path. Verify that the resulting binary is
   reachable on PATH.

PATH order: in run_once_20, /usr/bin and /bin are now first so any
pacman-managed bun wins over a curl-installed shadow at ~/.bun/bin
or ~/.local/bin. ~/.local/bin, ~/.bun/bin, ~/.cargo/bin still come
next so omp (installed via bun to ~/.bun/bin) is also reachable.

Triggered a re-run of the script on each box to confirm the
verification line shows up: 'bun: 1.3.14-1 (from extra) —
system-package install verified'.
2026-06-22 15:38:27 -04:00
36091d458b Add user-local bin dirs to PATH in run_once_20
The script runs from a non-interactive context (chezmoi apply over
SSH) where .zshrc isn't sourced. bun installs to ~/.bun/bin and
omp/cargo to ~/.local/bin / ~/.cargo/bin, none of which are on the
script's default PATH. Add them at the top so the post-install
'omp --version' and 'command -v omp' checks work.

Fixes the spurious 'omp: command not found' right after a successful
bun add -g on boxes where omp is freshly installed.
2026-06-22 15:19:11 -04:00
5e7fd61f02 Skip-sudo fixes for run_once_40 and run_once_20
Three real bugs caught during the bit-cachyos deploy:

1. wl-clipboard false-positive: 'command -v wl-clipboard' returns
   nothing because the package ships wl-copy/wl-paste, not a
   'wl-clipboard' binary. Sudo pacman was being called every apply
   even though wl-clipboard is installed. Fix: declare -A PKG_BIN
   map in the script.

2. omp segfault aborts the whole bootstrap: on rye, 'bun add -g
   @oh-my-pi/pi-coding-agent' segfaults (Pi undervoltage, see
   pitfall #30). The 'set -e' caused the whole run_once_20 to abort
   before getting to neovim/oh-my-zsh/zshrc etc. Fix: wrap the bun
   add in a subshell with error tolerance, log a warning, keep going.

3. rorclar/sourdough reference cleanup: removed the old chaotic-aur
   comment which mentioned the wrong sudo pattern (now fixed in the
   other run_once scripts).

Re-applied on bit, miche, byte, kaiser, crouton (rye skipped because
of the undervoltage hardware issue).
2026-06-22 15:17:58 -04:00
b40d724f6c Make run_once scripts sudo-prompt-free when packages already present
Several run_once scripts unconditionally called sudo pacman/apt to
install packages — even on boxes where every package was already
present. That triggered a sudo password prompt on every fresh
chezmoi apply for nothing.

Two changes:

1. .chezmoi.yaml.tmpl: fall back to ~/.local/bin/age if /usr/bin/age
   isn't installed (matters during initial bootstrap before age is
   installed system-wide).

2. run_once_*.sh.tmpl: detect missing packages first; only call sudo
   if there's actually something to install. For the LAN hosts script,
   detect the existing block and skip if it's already correct.

These changes are transparent on boxes that already had everything
installed (the existing 5): no behavior change. They reduce sudo
prompts on bit (the new box, where most packages are pre-installed)
from ~5 prompts to 1 (just for /etc/hosts).
2026-06-22 15:10:49 -04:00
a2cc669b22 Add bit (CachyOS laptop) to age recipients + re-encrypt secrets
bit-cachyos is the 6th machine in the hive. Generated a per-machine
age key on bit, added the pubkey to the recipients list in
.chezmoi.yaml.tmpl, and re-encrypted the two .age secrets
(dot_omp/agent/encrypted_.env.age and encrypted_zai.key.age) with all
7 recipients (1 recovery + 6 machines).

Bit's existing partial setup (pre-existing chezmoi source dir, omp
native binary at ~/.local/bin/omp) is backed up during the bootstrap
script to ~/.local/share/chezmoi.bak.<timestamp>.

See onboard-bit.sh on bit:/tmp/onboard-bit.sh for the no-sudo
bootstrap flow.
2026-06-22 15:03:54 -04:00
89881a740d README: document the new sway_setup prompt-based flow 2026-06-22 14:49:17 -04:00
a9ba9af8db Convert sway from hostname-allowlist to bootstrap prompt
Replaces the implicit hostname-based enable (miche/byte/kaiser got
sway automatically) with an explicit promptBool asked at first init.

New flow:
  1. `chezmoi init` asks: "sway_setup? [y/N]"
  2. User says y or N (default N)
  3. Answer is captured in data.sway_setup
  4. run_once_40-install-sway.sh.tmpl gates on .sway_setup
  5. Per-box override via marker files (preserved):
     - ~/.config/chezmoi/features/sway     → force ON
     - ~/.config/chezmoi/features/no-sway  → force OFF

Migration for existing boxes:
  - Miche/byte/kaiser already have ~/.config/chezmoi/features/sway
    marker (from previous hostname-allowlist install) → sway_setup=true
  - Rye/crouton have no marker → sway_setup=false (default)
  - Both groups pick up the new template on next chezmoi apply
2026-06-22 14:47:43 -04:00
5e2a3378cc Remove dead sway/config.d/ includes — main config is single-file
The main `~/.config/sway/config` is intentionally single-file (per
its own header comment: 'MINIMAL — single file, no includes. Tier-1+2+3
in one go.'). The `config.d/*.conf` files were leftovers from an
earlier split and are NOT included by the main config — so they were
dead weight in the repo.

Sway is NOT auto-launched. It's installed as a tool the user runs
manually from a TTY (`sway` from TTY1) for light desktops. The install
and configs sit ready; launching is the user's call.
2026-06-22 14:34:13 -04:00
5d21f700fc README: document sway/Wayland desktop stack and per-host toggle 2026-06-22 14:28:15 -04:00
bc2ceb5e52 Make sway hostname match support suffix forms (byte-arch etc.)
The previous `has .chezmoi.hostname ` only matched exact
strings. byte's hostname is `byte-arch` (set by the CachyOS installer),
so it didn't match the `byte` entry in the allowlist and got sway=false.

New logic: hostname matches if it equals any entry OR starts with
'<entry>-' or '<entry>.'. So 'byte', 'byte-arch', and 'byte.foo' all
match the 'byte' entry.
2026-06-22 14:26:01 -04:00
6bbaa8f2f5 Add sway/wofi/foot Wayland desktop stack with per-host toggle
New opt-in feature for x86_64 desktops: sway + wofi + foot + swaybg +
swaylock + swayidle + grim + slurp + waybar + wl-clipboard. mako on
arch, dunst on debian (mako isn't packaged for debian).

Files:
- .chezmoi.yaml.tmpl: added data.sway flag (true on miche/byte/kaiser,
  false on Pis). Override per host with ~/.config/chezmoi/features/sway
  or ~/.config/chezmoi/features/no-sway marker files.
- run_once_40-install-sway.sh.tmpl: installs packages if .sway=true,
  exits 0 otherwise. Sets up the marker file.
- dot_config/{sway,foot,wofi,waybar,mako}/: existing configs from miche.

Per-host toggle workflow:
  # On any box, enable sway:
  touch ~/.config/chezmoi/features/sway
  chezmoi apply

  # On a sway-enabled box, disable it:
  touch ~/.config/chezmoi/features/no-sway
  rm ~/.config/chezmoi/features/sway
  chezmoi apply

Currently sway packages are already installed on miche (existed before
this commit). Byte will get them via the new run_once_40 script.
Pis (rye, crouton) are unaffected — install script early-returns.
2026-06-22 14:23:56 -04:00
fe73bbecba Add libssl-dev pkg-config to debian bootstrap-tools
Many Rust crates (cargo-update, anything depending on openssl-sys)
need libssl-dev + pkg-config at build time. Without these,
`cargo install cargo-update` fails on debian with:

  Could not find openssl via pkg-config
  The system library `openssl` required by crate `openssl-sys`
  was not found.

Add them to APT_PKGS in run_once_00-install-bootstrap-tools.sh.tmpl
so new debian boxes have them from the start. Existing Pis
(rye, crouton) need a one-time `sudo apt-get install -y libssl-dev
pkg-config` after pulling this commit.
2026-06-22 12:11:56 -04:00
f4a0b59b7e Add LAN hosts script; clean up README onboarding 2026-06-22 01:50:30 -04:00
bd9b295b24 Add omp .env.age for provider API keys; install topgrade+cargo-update via PM
1. dot_omp/agent/encrypted_.env.age (NEW)
   Encrypted shell-sourceable file with all omp provider API keys.
   Decrypts to ~/.omp/agent/.env on apply. omp reads .env on startup
   per docs/environment-variables.md. All 6 recipients (recovery +
   5 boxes) can decrypt. Placeholder values for keys the user hasn't
   added yet — fill in real values per-provider.

2. run_onchange_30-ensure-cargo.sh.tmpl (UPDATED)
   - Add topgrade install: pacman on arch (via chaotic-aur), cargo on
     debian (not in apt)
   - Add cargo-update install: pacman on arch, cargo on debian
   - Prefer OS package managers over cargo install when both are
     available. cargo install only as fallback.

3. dot_omp/agent/config.yml (UNCHANGED)
   Per user request: keep .local host endpoints (llama-swap.miche,
   kaiser.local:8800). If a box can't reach them, it's not on the
   local network and omp will error gracefully at request time.
2026-06-22 01:34:02 -04:00
07dbe83f52 Fix omp zai.key encryption: use proper chezmoi 'encrypted_' attribute
The previous approach (private_dot_omp/agent/zai.key.age + manual
re-encryption) didn't work because:
1. The 'private_' prefix is for files NOT to push to remote, not for
   encrypted files. The 'encrypted_' prefix is what chezmoi recognizes
   as an encryption marker.
2. The encrypted file needs to be at dot_<path>/encrypted_<name>.age
   so chezmoi can both decrypt on apply AND strip the .age suffix
   to write the destination file as <name> (without .age).

Also fix chezmoi age config to actually decrypt non-interactively:
- Add useBuiltinAge: false to force external age binary
- Add age.command: /usr/bin/age (absolute path) so PATH issues
  don't matter in non-interactive SSH contexts

The encrypted file is at dot_omp/agent/encrypted_zai.key.age, decrypts
to ~/.omp/agent/zai.key on apply. Encrypted to all 6 recipients
(recovery + miche + byte + kaiser + rye + crouton).

Tested on miche:
  - chezmoi apply: rc=0
  - live zai.key: 50 bytes (correct content)
  - decrypts with miche per-machine key
  - would decrypt on other boxes with their respective keys
2026-06-22 00:44:51 -04:00
2b06a60d00 Re-encrypt zai.key.age with all 6 recipients (recovery + 5 boxes)
Each box now has its own per-machine age key at
~/.config/chezmoi/key.txt. The .age file is encrypted to all 6
recipients, so any of them can decrypt zai.key on next chezmoi apply.

Implementation note: chezmoi only honors the LAST --age-recipient
flag when given multiple. Use --age-recipient-file=path/to/file
(one pubkey per line) for multiple recipients in a single call.
2026-06-22 00:16:31 -04:00
dc72dc3a9a Add bun + pi-coding-agent + oh-my-pi to bootstrap; age encryption
Three parts:

1. .chezmoi.yaml.tmpl: reworked age config block
   - recipients moved under 'age:' key (correct structure per chezmoi docs)
   - identity: ~/.config/chezmoi/key.txt
   - recipients list with recovery key + miche per-machine key
   - recovery key pubkey: age1yyq42ctqwp5s5yd64week3aav9getk3p8aeyr5n5454d0v59a4dsjljsgs
   - miche pubkey: age1eja7trs8mmsgf0qga0h5fsdltaryxgk4ksumshar5xxtdx0exy3q0a5hc5
   - placeholders for byte/kaiser/rye/crouton (TODO: generate per-box keys
     and add when bootstrapping those boxes)

2. private_dot_omp/agent/: omp/oh-my-pi config from byte
   - config.yml (1.7KB) — model roles, fallback chains, theme, tools
   - mcp.json (351B) — firecrawl MCP server config
   - zai.key.age (540B) — zai-coding provider API key, age-encrypted to
     recovery + miche recipients. Decrypts to live ~/.omp/agent/zai.key
     on apply.

3. run_once_20: install bun + pi-coding-agent on both OSes
   - arch: bun from pacman (now in [extra])
   - debian: bun via curl-install to ~/.local (not in apt)
   - both: bun add -g @oh-my-pi/pi-coding-agent → omp binary in ~/.bun/bin
   - .zshrc.tmpl already adds ~/.bun/bin to PATH

To onboard a new box:
  1. ssh into the box
  2. age-keygen -o ~/.config/chezmoi/key.txt
  3. paste the public key into .chezmoi.yaml.tmpl recipients
  4. chezmoi age rekey   # rewrites *.age files to include new recipient
  5. commit + push
  6. chezmoi init --apply  # decrypts and writes zai.key live
2026-06-22 00:10:34 -04:00
6160efeb23 Drop paru for Maple font install on arch; use GitHub release zip directly
paru's post-build 'sudo pacman -U' step requires a TTY for sudo,
which fails in non-interactive chezmoi runs (miche doesn't have
NOPASSWD sudo for this case). The font package has no deps, so
the AUR build step adds complexity for no real benefit.

Download MappleMono-NF.zip from subframe7536/Maple-font v7.9 GitHub
release directly on both arch and debian. Same install path, no
paru dependency, no sudo, ~30 second install.

The maplemono-nf-cn AUR package is still available for users who
want it via system package management — this just makes the bootstrap
not depend on AUR helpers working non-interactively.
2026-06-21 23:42:44 -04:00
d537a5b577 Fix font check: pipefail + grep -q returns SIGPIPE 141
The bootstrap script has 'set -euo pipefail' at the top. The font
check was 'fc-list 2>/dev/null | grep -qi "Maple Mono NF"'. With
pipefail enabled, this fails:

1. grep -q exits 0 as soon as it finds the first match
2. grep closing its stdin early sends SIGPIPE to fc-list
3. fc-list exits with 141 (SIGPIPE)
4. With pipefail, the pipeline returns 141, not 0
5. The 'if' evaluates 141 as false → NO-MATCH
6. Bootstrap re-installs the font even though it's already there

Discovered on kaiser: bootstrap kept failing at 'paru -S maplemono-nf-cn'
because the font was already installed but the check said it wasn't.

Fix: use bash string matching instead of a pipeline.
  [[ "$(fc-list 2>/dev/null)" == *"Maple Mono NF"* ]]

This reads all output into a string (no pipe), then bash's built-in
glob match handles the test. No pipefail issue.
2026-06-21 23:20:40 -04:00
20639f31f9 Use --sudoloop for paru install in non-interactive bootstrap
Kaiser bootstrap failed at 'paru -S maplemono-nf-cn' with
'sudo: a terminal is required to read the password'. paru doesn't
need sudo for the build itself (it builds as the user), but it does
call sudo internally for some package operations — and on kaiser
the sudoers config requires a TTY password for those calls.

--sudoloop runs sudo in the background, which avoids the TTY-prompt
issue entirely. The package still builds as the user; only the
internal sudo calls go through the background loop.
2026-06-21 23:13:42 -04:00
678c61d4fb Add fzf-tab plugin (navigable Tab completion menu)
Kaiser's existing .zshrc had fzf-tab added to the plugins list —
it gives a navigable Tab completion menu with descriptions for each
match. Adding to the canonical plugin list so kaiser doesn't lose
functionality when the repo version overwrites the box-specific
one. Strictly additive: doesn't affect miche or other boxes.

Adds:
- fzf-tab to dot_zshrc.tmpl plugins array
- install_zsh_plugin Aloxaf/fzf-tab to run_once_20
2026-06-21 23:06:25 -04:00