laptop-config/MIGRATION.md
Hermes 921fc1402a T490 + Windows 11 dual-boot support
- modules/laptop.nix: add systemd-boot extraEntries for Windows Boot Manager
  Chainloads $ESP/EFI/Microsoft/Boot/bootmgfw.efi. sort-key 'z_windows' puts
  it after NixOS generations in the menu. Explicit entry works on all
  systemd-boot versions; NixOS 26.05 'Yarara' may also autodetect on
  shared ESP, in which case the duplicate is harmless.
- install.sh: guided dual-boot installer. Shrinks Win11 NTFS with ntfsresize
  (no data loss), creates NixOS ext4 root, mounts shared ESP, runs
  nixos-install with this flake. Idempotent except for the partition shrink.
- MIGRATION.md: new T490 + Windows 11 dual-boot section, with manual
  fallback and NixOS 26.05 compatibility notes.
2026-06-05 19:53:56 -07:00

7 KiB

Laptop Migration: MacBook → x86_64 NixOS

The T490 already has Windows 11 installed. To add NixOS alongside it without losing Windows:

1. Boot the NixOS installer from a Ventoy stick

From the live installer environment:

# Network access is required; either wifi-menu or plug in ethernet
# Then run the guided installer — it handles partitioning:
curl -L https://os.scoble.me/forgejo/sscoble/laptop-config/raw/branch/main/install.sh | sudo bash

The script:

  • Detects /dev/nvme0n1 and the existing ESP
  • Shrinks the Windows NTFS partition with ntfsresize (no data loss)
  • Creates a NixOS ext4 root partition in the freed space
  • Mounts the existing Windows ESP at /mnt/boot (shared, not reformatted)
  • Runs nixos-install with this flake
  • Reboots into a systemd-boot menu with both NixOS and Windows entries

2. After install

First boot → systemd-boot menu shows:

  • NixOS (default) — most recent generation
  • Older NixOS generations (up to 50, configurable in modules/laptop.nix)
  • Windows Boot Manager — chainloads the standard Microsoft EFI binary at $ESP/EFI/Microsoft/Boot/bootmgfw.efi

If you ever want to skip the menu and boot straight to Windows, hold F12 at the Lenovo splash and pick "Windows Boot Manager" from the firmware menu.

3. Fall back to manual partition if the script fails

The script handles the common case. For unusual layouts (multiple disks, LVM, existing NixOS partitions, etc.), do it by hand:

# Identify partitions
lsblk -f
# Target the existing Windows ESP for /boot
mount /dev/nvme0n1p1 /mnt/boot   # adjust to your partition
# Create NixOS root
mkfs.ext4 -L nixos /dev/nvme0n1pX
mount /dev/nvme0n1pX /mnt
nixos-generate-config --root /mnt
nixos-install --flake git+https://os.scoble.me/forgejo/sscoble/laptop-config.git#laptop --root /mnt

4. NixOS 26.05 ("Yarara") compatibility

The flake pins nixos-unstable, which as of June 2026 tracks NixOS 26.05+. Key 26.05 changes that affect this config:

  • boot.initrd.systemd.enable is now default true. Most things Just Work.
  • boot.supportedFilesystems is now an attrset (not a list) — nix warns but the existing config still parses.
  • systemd.extraConfig is removed; use systemd.settings.Manager instead. We don't use systemd.extraConfig anywhere, so this is a no-op for us.
  • The new boot.loader.systemd-boot.windows first-class option exists for multi-drive dual-boot, but we don't need it — sharing a single ESP means our explicit extraEntries works on all systemd-boot versions.

Order of operations

1. Install NixOS

Boot the NixOS ISO on the new laptop. Partition, format, mount to /mnt.

# Generate base config
nixos-generate-config --root /mnt

# Then rebuild from the flake (replaces generated config):
sudo nixos-rebuild switch --flake git+https://os.scoble.me/forgejo/sscoble/laptop-config.git#laptop --root /mnt

2. First build: npmDepsHash

The first nixos-rebuild will fail with a hash mismatch error for pi-coding-agent. The error output contains the correct hash:

error: hash mismatch in fixed-output derivation ...
  specified: sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
  got:       sha256-1q2w3e4r5t6y7u8i9o0p1q2w3e4r5t6y7u8i9o0p1q=

Copy the got: hash, paste into flake.nix → npmDepsHash = "sha256-...". Commit, push, rebuild.

3. Transfer secrets (before reboot!)

From the MacBook, while both laptops are on the same LAN:

# SSH keys
scp ~/.ssh/id_ed25519 ~/.ssh/id_ed25519.pub new-laptop:~/.ssh/

# Pi agent config (has API keys — do NOT commit to git)
scp ~/.pi/agent/SYSTEM.md new-laptop:~/.pi/agent/
scp ~/.pi/agent/models.json new-laptop:~/.pi/agent/
scp ~/.pi/agent/mcp.json new-laptop:~/.pi/agent/
scp ~/.pi/agent/settings.json new-laptop:~/.pi/agent/
scp ~/.pi/agent/auth.json new-laptop:~/.pi/agent/

# Wallpapers
scp ~/path/to/wallpaper.jpg new-laptop:~/wallpapers/default.jpg

4. Transfer projects

Push any repos not yet in Forgejo, then re-clone:

# From MacBook: push scobleclaw to Forgejo
cd ~/Projects/scobleclaw
git remote add forgejo https://os.scoble.me/forgejo/sscoble/scobleclaw.git
git push forgejo main

# From new laptop: re-clone
cd ~/Projects
git clone https://os.scoble.me/forgejo/sscoble/scobleclaw.git
git clone https://os.scoble.me/forgejo/sscoble/wgu.git

# Other projects: rsync or push individually
rsync -avz --progress ~/Projects/other-project/ new-laptop:~/Projects/other-project/

5. Transfer documents and media

# Documents (small, ~30MB)
rsync -avz --progress ~/Documents/ new-laptop:~/Documents/

# Personal media (larger — be selective)
rsync -avz --progress ~/Pictures/ new-laptop:~/Pictures/
rsync -avz --progress ~/Music/ new-laptop:~/Music/

6. Reboot and login

sudo reboot
# Login via greetd → sscoble password → Hyprland starts
# SUPER+Return → ghostty with zellij

7. Verify everything works

pi                              # pi agent should start
qmd status                      # vault search
zotero                          # reference manager
obsidian ~/vault                # vault notes
code ~/Projects/scobleclaw      # VS Code

# OB1 tunnel
ssh -fNL 3987:localhost:3987 n97-mini
curl -s http://localhost:3987/health  # should return ok

# Discord: @mention Marcus in #speaker-site

8. T490 hardware setup

# Firmware updates (BIOS, EC, Thunderbolt, fingerprint reader)
fwupdmgr refresh && fwupdmgr update
# May need to disable Boot Order Lock in BIOS first

# Enroll fingerprint
fprintd-enroll

# Check sleep mode
cat /sys/power/mem_sleep
# Should show [deep] if S3 is available and configured

# Set up remote build SSH key
sudo ssh-keygen -t ed25519 -f /root/.ssh/remote-build -N ""
# Copy /root/.ssh/remote-build.pub to each cluster node's /root/.ssh/authorized_keys

# Set up Syncthing (pair with cluster nodes for vault sync)
systemctl --user enable --now syncthing

Files to NOT transfer

These contain caches, build artifacts, or macOS-specific junk:

  • ~/Library/ — macOS only
  • ~/Applications/ — macOS apps
  • ~/.ollama/ — model cache (too large, re-download)
  • ~/.cargo/ — rust cache
  • ~/.bun/ — bun cache
  • ~/.npm/ — npm cache
  • ~/go/ — go module cache
  • ~/tmp/ — temp files
  • ~/Downloads/ — temp downloads (clean first, then selectively transfer)
  • ~/.Trash/ — obviously

Post-migration checklist

  • pi works
  • qmd status works
  • vscode opens with typst LSP
  • zotero launches
  • obsidian ~/vault opens vault
  • brave launches and syncs
  • SSH keys work (ssh n97-mini)
  • OB1 tunnel works
  • Hyprland: SUPER+D opens rofi, SUPER+Return opens ghostty+zellij
  • Discord: @mention Marcus → gets a reply
  • Wallpaper appears after login
  • Firmware updated (fwupdmgr update)
  • Fingerprint enrolled (fprintd-enroll)
  • S3 sleep working (cat /sys/power/mem_sleep shows [deep])
  • Remote Nix builders working (nix build --rebuild nixpkgs#hello)
  • Syncthing running for vault sync