My personal dotfiles, for now.
dotfiles BOOTSTRAP.md
10 kB · 133 lines
Markdown

Bootstrap Procedure #

Lessons from the last install #

  • Do not use Flatpak 1Password, it's built into the actual bootc image now, as a first class citizen.
  • No need to worry about browsers, Helium is baked into the image too, because you do not want to use a Chromium browser in a Flatpak, and absolutely no Firefox.
  • SSH/YubiKeys are now handled by SOPS secrets, and nushell helper scripts.
  • Run dfe for dotfile edits so changes land in a fresh jj change, and aren't just added to the working copy.

Basic assumptions #

This checklist is assuming an install onto zircadia, my custom bootc image. This image comes with chezmoi, homebrew, Helium Browser, Steam, 1Password, ykman, age, sops, age-plugin-yubikey, and jj preinstalled. They will absolutely be present. If they are not, this list does not apply.

Stage 0: Pre-clone requirements to get to access the rest of the tools #

[ ] brew install helix This gives me my preferred editor. [ ] curl -L https://sw.kovidgoyal.net/kitty/installer.sh | sh /dev/stdin My preferred terminal. [ ] Launch kitty [ ] nu We have to switch to nu, in order to use nushell commands consistently. [ ] mkdir ~/.ssh [ ] chmod 700 ~/.ssh [ ] cd ~/.ssh [ ] ssh-keygen -K Repeat with exactly one YubiKey inserted at a time, and preserve the identity-handle filenames expected by the encrypted SSH inventory. [ ] Write a temporary minimal ~/.ssh/config entry for Host tangled.org; all you need is:

  • IdentityFile <path to the Tangled key downloaded by ssh-keygen -K>
  • IdentitiesOnly true
  • IdentityAgent none [ ] chmod 600 ~/.ssh/config

Stage 1: Fetching dotfiles from the remote #

[ ] ls ~/.local/share/chezmoi to see if the directory already exists. [ ] mkdir ~/.local/share/chezmoi if it doesn't. [ ] cd ~/.local/share/chezmoi [ ] jj git clone --no-colocate git@tangled.org:threadweaver.dev/dotfiles . This will copy down the files, and the bookmarks, setting the working copy one commit ahead of the trunk. [ ] mkdir .git This needs to exist as an empty directory so that chezmoi isn't confused. A test clone showed that no .git directory is created as part of the cloning process, and therefore it has to get made manually, so chezmoi init doesn't create a real git repo. [ ] jj st To check the repo status.

Stage 2: Run the post-clone bootstrap #

The remaining stages are implemented by the standalone repository script bootstrap.nu. It intentionally runs outside chezmoi application and starts Nushell with --no-config-file, so it does not depend on the configuration or packages it is about to install. [ ] cd ~/.local/share/chezmoi [ ] nu --no-config-file ./bootstrap.nu --plan Review the ordered operations. [ ] nu --no-config-file ./bootstrap.nu

The script stops on command errors, displays the chezmoi diff and dry-run before asking permission to apply, pauses for the licensed-font drive, and pauses for exactly one labeled YubiKey at a time. It never commits, moves jj bookmarks, pushes, or reboots. The detailed stages below document what it performs and remain the manual recovery path.

Recovery/diagnostic options:

  • --font-source <path>: supply the mounted licensed-font directory instead of entering it at the prompt.
  • --skip-fonts: skip the licensed-font drive ceremony.
  • --skip-secrets: skip YubiKey/SOPS/SSH setup.
  • --skip-themes: skip network downloads and public theme installation.
  • --skip-auth-tests: install SSH outputs without the per-YubiKey remote-login confirmations (the automated ssh -G identity checks still run).
  • --test-stop-after <stage>: stop after a named stage (chezmoi-review, fonts, packages, apply, secrets, themes). Test-harness checkpoint; not part of the normal bootstrap workflow.
  • --yes: accept confirmation prompts; physical-action instructions still print. Use only for controlled testing or reruns.

Stage 2.5: Restoring licensed fonts #

Proprietary fonts (PragmataPro Mono, Berkeley Mono) cannot live in this public repo, so they are restored from a flash drive before chezmoi apply installs the configs that reference them. Doing this before apply means the terminals and DankMaterialShell resolve the real families on first launch instead of caching a fallback. [ ] Insert and mount the flash drive holding the fonts. [ ] let font_source = "<path to the mounted fonts directory>" [ ] mkdir ~/.local/share/fonts [ ] let font_files = (glob $"($font_source)/**/*.{otf,ttf}") [ ] if ($font_files | is-empty) { error make {msg: "No .otf or .ttf files found on the font drive"} } [ ] $font_files | each {|font| cp $font ~/.local/share/fonts/ } [ ] glob ~/.local/share/fonts/*.{otf,ttf} | each {|font| chmod 644 $font } [ ] fc-cache --force ~/.local/share/fonts [ ] fc-match "PragmataPro Mono" Confirm it names PragmataPro Mono, not a fallback. [ ] fc-match "Berkeley Mono" Confirm it names Berkeley Mono, not a fallback. [ ] Unmount the flash drive.

Stage 3: Installing packages and applying the dotfiles #

The tracked Nushell configuration invokes carapace and starship during startup, so bootstrap.nu restores the Brewfile before launching Nushell normally. [ ] On the first run, chezmoi init prompts for an email address and GitHub username (from home/.chezmoi.toml.tmpl); the answers are stored in ~/.config/chezmoi/chezmoi.toml. The script runs this step in the foreground so the prompts are visible. [ ] bootstrap.nu trusts the third-party entries declared in home/dot_Brewfile and restores Flatpaks, formulae/casks, Cargo packages, and VS Code extensions in dependency-safe stages. [ ] bootstrap.nu preserves the temporary Tangled SSH config while packages, fonts, SOPS identities, generated SSH outputs, and themes are prepared. [ ] bootstrap.nu performs the single real chezmoi apply near the end, only after those prerequisites and generated assets are ready. [ ] After the script finishes, nu loads the applied configuration and helper scripts.

Stage 4: Applying secrets #

bootstrap.nu performs this interactively, requiring exactly one labeled YubiKey at a time. Because it runs with --no-config-file, it explicitly sets PCSCLITE_CSOCK_NAME=/run/pcscd/pcscd.comm and validates that socket before using age-plugin-yubikey; it does not wait for chezmoi to apply environment.d/pcsclite.conf. These commands remain the manual recovery path.

4.1 - Create the baseline directories #

[ ] mkdir ~/.config/sops/age [ ] chmod 700 ~/.config/sops [ ] chmod 700 ~/.config/sops/age

4.2 - YubiKey age identity loop, repeat for each YubiKey #

[ ] age-plugin-yubikey --identity | save -f ~/.config/sops/age/yubikey-<LABEL>.identity.txt [ ] chmod 600 ~/.config/sops/age/yubikey-<LABEL>.identity.txt

4.3 - Create the keys.txt #

[ ] glob ~/.config/sops/age/yubikey-*.identity.txt | sort | each {|path| open $path } | str join "\n" | save -f ~/.config/sops/age/keys.txt [ ] chmod 600 ~/.config/sops/age/keys.txt [ ] Confirm $env.SOPS_AGE_KEY_FILE resolves to ~/.config/sops/age/keys.txt; the tracked Nushell configuration sets it persistently.

4.4 - Testing and generating the results of the secrets #

[ ] dotfiles secrets ssh --check to test that the nushell helper scripts are installed, and that the secrets helpers are working. [ ] Assuming that the previous step comes back clean dotfiles secrets ssh --apply [ ] ssh-keygen -lf ~/.ssh/authorized_keys Confirm the expected fleet-key fingerprints are installed without printing the key blobs. [ ] For each YubiKey, insert only that key and run ssh -G github.com | lines | where {|line| $line | str starts-with "identityfile " }; repeat for tangled.org and yggdrasil and confirm exactly one real identity follows identityfile none. [ ] Test real authentication with each YubiKey: ssh -T github.com, ssh -T tangled.org, and ssh yggdrasil.

Stage 5: Checking package and dotfile parity #

[ ] dotfiles packages check [ ] dfs [ ] dotfiles audit [ ] chezmoi status

Stage 6: Manual theming assets #

[ ] cd ~/Pictures [ ] git clone https://github.com/p4rfait/rose-pine-wallpapers.git [ ] Download the Rose Pine Cursors [ ] mkdir ~/.local/share/icons [ ] tar -xvf ~/Downloads/BreezeX-RosePine-Linux.tar.xz -C ~/.local/share/icons The archive contains the required BreezeX-RosePine-Linux/ top-level directory. [ ] Download Rose Pine gtk themes and icons [ ] mkdir ~/.themes [ ] let gtk_staging = (mktemp --directory --tmpdir "rose-pine-gtk.XXXXXX") [ ] tar -xvf ~/Downloads/gtk3.tar.gz -C $gtk_staging [ ] cp --recursive ($gtk_staging | path join "gtk3" "rose-pine-gtk") ~/.themes/rose-pine-gtk [ ] rm --recursive --force $gtk_staging [ ] mkdir ~/.icons [ ] let icon_staging = (mktemp --directory --tmpdir "rose-pine-icons.XXXXXX") [ ] tar -xvf ~/Downloads/rose-pine-icons.tar.gz -C $icon_staging [ ] cp --recursive ($icon_staging | path join "icons" "rose-pine-icons") ~/.icons/rose-pine-icons [ ] rm --recursive --force $icon_staging [ ] Confirm ~/.themes/rose-pine-gtk/index.theme and ~/.icons/rose-pine-icons/index.theme exist. [ ] After logging out and back in, confirm QT_QPA_PLATFORM=wayland and ELECTRON_OZONE_PLATFORM_HINT=auto from the tracked ~/.config/environment.d/wayland.conf are present in the graphical session. [ ] Build a distrobox for MEGAsync. [ ] MAYBE: Set up launch arguments for VSCode.

Stage 7: Final validation #

[ ] Log out and back in so environment.d, fonts, cursor settings, and session applications start from the applied state. [ ] fc-match "PragmataPro Mono" and fc-match "Berkeley Mono" [ ] dotfiles packages check [ ] dfs [ ] dotfiles audit [ ] chezmoi status [ ] Confirm Kitty, Foot, DankMaterialShell, GTK, Qt, cursor, wallpaper, and default application behavior. [ ] Confirm the required changes are reachable from the intended jj bookmark and pushed before relying on this procedure for another machine. [ ] Perform a disposable fresh clone and dry-run from the pushed bookmark before declaring bootstrap-ready.