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).
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).
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).
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.
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
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.
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.
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
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.
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
debian-stable's /etc/os-release has no ID_LIKE field. Template crashed
with 'map has no entry for key idLike' when chezmoi init ran on rye.
Two fixes:
1. hasKey() guard around .chezmoi.osRelease.idLike so missing key
doesn't error out
2. Flip contains() arg order: sprig's signature is contains(substr, str),
not contains(str, substr). Was checking backwards.
Tested against:
- miche (ID=debian-derivative with ID_LIKE=arch) -> os_family=arch
- empty ID_LIKE fallback (debian-stable) -> falls through to .id=debian
-> os_family=debian