Add FredOS-Rescue: Claude Code rescue USB
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
d40d9924b2
commit
b960a6ccbf
5 changed files with 217 additions and 0 deletions
102
hosts/FredOS-Rescue.nix
Normal file
102
hosts/FredOS-Rescue.nix
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
# FredOS-Rescue — live USB that boots into Claude Code for diagnosing and
|
||||
# repairing a broken Linux (or Windows) install. Built on the minimal NixOS
|
||||
# installer, which already brings NetworkManager, sshd and the usual disk
|
||||
# tools (testdisk, ddrescue, smartctl, cryptsetup, ...; profiles/base.nix).
|
||||
#
|
||||
# nix build .#rescue-iso # -> result/iso/fredos-rescue-*.iso
|
||||
#
|
||||
# On boot: type `rescue`. It gets you online (nmtui) and starts Claude as root
|
||||
# with hosts/rescue/rescue-prompt.md as its system-wide CLAUDE.md. Login is
|
||||
# the usual URL + code dance (phone works); nothing persists across reboots.
|
||||
#
|
||||
# Remote mode: sshd runs with Fred's keys + the server's for root, so Claude
|
||||
# elsewhere can `ssh root@<ip>` in (or you can run `rescue` over ssh).
|
||||
{ config, lib, pkgs, modulesPath, ... }:
|
||||
let
|
||||
serverKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMbIdOSPjnJYQ7z7n0mHKo8T8PnAkCyIBCmT4KpsHyy/ fred@FredOS-Mediaserver";
|
||||
|
||||
# Any HTTP answer counts (no -f): the API root is a 404.
|
||||
online = "${pkgs.curl}/bin/curl -sS -m 5 -o /dev/null https://api.anthropic.com 2>/dev/null";
|
||||
rescue = pkgs.writeShellScriptBin "rescue" ''
|
||||
# Claude's binary needs AVX (2011+ CPUs); without it, it spins forever
|
||||
# instead of crashing.
|
||||
if ! grep -qw avx /proc/cpuinfo; then
|
||||
echo "This CPU is too old for Claude (no AVX)."
|
||||
echo "Use remote mode: get online with 'nmtui', run 'menu' for the"
|
||||
echo "address, then run Claude on another machine and have it ssh in."
|
||||
exit 1
|
||||
fi
|
||||
# Typed right after boot, wired may still be coming up.
|
||||
${pkgs.networkmanager}/bin/nm-online -q -t 15
|
||||
if ! ${online}; then
|
||||
echo "Not online yet. Pick a network (wired should connect by itself)."
|
||||
sleep 1
|
||||
${pkgs.networkmanager}/bin/nmtui-connect
|
||||
fi
|
||||
if ! ${online}; then
|
||||
echo "Still offline. Try 'nmtui', then run 'rescue' again."
|
||||
exit 1
|
||||
fi
|
||||
exec sudo -i ${pkgs.claude-code}/bin/claude "$@"
|
||||
'';
|
||||
menu = pkgs.writeShellScriptBin "menu" (builtins.readFile ./rescue/menu.sh);
|
||||
in
|
||||
{
|
||||
imports = [
|
||||
"${modulesPath}/installer/cd-dvd/installation-cd-minimal.nix"
|
||||
../settings/locale.nix
|
||||
../settings/users.nix # only for Fred's ssh keys below
|
||||
];
|
||||
|
||||
nixpkgs.hostPlatform = "x86_64-linux";
|
||||
nixpkgs.config.allowUnfreePredicate = pkg: lib.getName pkg == "claude-code";
|
||||
nix.settings.experimental-features = [ "nix-command" "flakes" ];
|
||||
|
||||
networking.hostName = "FredOS-Rescue";
|
||||
image.baseName = lib.mkForce "fredos-rescue-${config.system.nixos.label}";
|
||||
isoImage.appendToMenuLabel = " - Claude Rescue"; # no apostrophes: GRUB quotes it with ''
|
||||
|
||||
users.users.root.openssh.authorizedKeys.keys =
|
||||
config.users.users.fred.openssh.authorizedKeys.keys ++ [ serverKey ];
|
||||
# The installer leaves root/nixos with empty passwords; never over ssh.
|
||||
services.openssh.settings.PasswordAuthentication = false;
|
||||
services.openssh.settings.KbdInteractiveAuthentication = false;
|
||||
|
||||
environment.etc."claude-code/CLAUDE.md".source = ./rescue/rescue-prompt.md;
|
||||
|
||||
environment.systemPackages = with pkgs; [
|
||||
rescue
|
||||
menu
|
||||
claude-code
|
||||
# Chroot into the patient: arch-chroot does the bind mounts for any
|
||||
# distro; nixos-enter is already on the ISO for NixOS.
|
||||
arch-install-scripts
|
||||
mdadm
|
||||
chntpw # Windows: reset passwords, edit registry hives
|
||||
lsof
|
||||
file
|
||||
tree
|
||||
ripgrep
|
||||
git
|
||||
rsync
|
||||
wget
|
||||
tmux
|
||||
htop
|
||||
lm_sensors
|
||||
dmidecode
|
||||
memtester
|
||||
];
|
||||
|
||||
# The installer's getty text scrolls past under autologin; the menu replaces
|
||||
# it. Interactive only, so `sudo -i claude` inside `rescue` doesn't print it.
|
||||
services.getty.helpLine = lib.mkForce "";
|
||||
environment.loginShellInit = ''
|
||||
[[ $- == *i* ]] && menu
|
||||
'';
|
||||
|
||||
# Serial too, for headless boxes (tty0 last = it keeps the boot messages).
|
||||
boot.kernelParams = [ "console=ttyS0,115200" "console=tty0" ];
|
||||
|
||||
# Don't import ZFS pools that weren't exported cleanly; rescue asks first.
|
||||
boot.zfs.forceImportRoot = false;
|
||||
}
|
||||
41
hosts/rescue/menu.sh
Normal file
41
hosts/rescue/menu.sh
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
# Greeting for FredOS-Rescue: shown on every interactive login, and again
|
||||
# with `menu`.
|
||||
b=$'\e[1m' y=$'\e[1;33m' d=$'\e[2m' r=$'\e[0m'
|
||||
|
||||
# The console is still littered with boot messages at this point.
|
||||
case "$(tty)" in /dev/tty*) clear ;; esac
|
||||
|
||||
printf '%s' "$y"
|
||||
cat <<'ART'
|
||||
Fred's
|
||||
____ _ _ ____
|
||||
/ ___| | __ _ _ _ __| | ___ | _ \ ___ ___ ___ _ _ ___
|
||||
| | | |/ _` | | | |/ _` |/ _ \ | |_) / _ \/ __|/ __| | | |/ _ \
|
||||
| |___| | (_| | |_| | (_| | __/ | _ < __/\__ \ (__| |_| | __/
|
||||
\____|_|\__,_|\__,_|\__,_|\___| |_| \_\___||___/\___|\__,_|\___|
|
||||
ART
|
||||
printf '%s\n' "$r"
|
||||
|
||||
cat <<EOF
|
||||
${b}rescue${r} get online and start Claude (as root)
|
||||
${b}nmtui${r} networking only (wifi, static IPs)
|
||||
${b}menu${r} show this screen again
|
||||
|
||||
${b}How it goes${r}
|
||||
1. Type ${b}rescue${r}. Wired connects by itself; otherwise pick a wifi.
|
||||
2. First start: Claude shows a login URL. Open it on your phone,
|
||||
sign in, paste the code back here.
|
||||
3. Tell Claude what broke: what you saw, what changed before it did.
|
||||
It looks first, and asks before anything touches a disk.
|
||||
|
||||
${d}Runs from RAM: nothing here survives a reboot. Your disks are
|
||||
untouched until you OK a change. Keyboard is UK.${r}
|
||||
|
||||
EOF
|
||||
|
||||
ips=$(ip -brief -4 addr show scope global 2>/dev/null | awk '{print $3}' | cut -d/ -f1)
|
||||
if [ -n "$ips" ]; then
|
||||
printf ' %sRemote:%s ssh root@%s\n\n' "$b" "$r" "$(echo $ips | sed 's/ / or root@/g')"
|
||||
else
|
||||
printf ' %sRemote: not online yet; run menu again once connected.%s\n\n' "$d" "$r"
|
||||
fi
|
||||
66
hosts/rescue/rescue-prompt.md
Normal file
66
hosts/rescue/rescue-prompt.md
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
# You are running on a rescue disk
|
||||
|
||||
This is FredOS-Rescue: a live NixOS USB stick, booted on a machine whose real
|
||||
OS is broken. You run as root. The live system lives in RAM, so nothing you
|
||||
install or change *on the live system* survives a reboot. The machine's own
|
||||
disks are **the patient**. They are not mounted until you mount them.
|
||||
|
||||
The user is Fred: at home in Linux and NixOS, but rarely does disk/boot
|
||||
rescue, so explain what you find and why each step matters, in plain terms,
|
||||
one step at a time. Ask about symptoms first (what happened, what changed
|
||||
recently, what the screen says) before guessing.
|
||||
|
||||
## Rules for the patient's disks
|
||||
|
||||
1. **Diagnose read-only first.** Look before you touch: `lsblk -f`, `blkid`,
|
||||
`fdisk -l`, `smartctl -a`, `dmesg`, `efibootmgr -v`. Mount read-only first
|
||||
(`mount -o ro`, and `ro,noload` for ext4 so the journal isn't replayed).
|
||||
2. **Every write needs a yes.** Before anything that modifies a disk (fsck
|
||||
without `-n`, mkfs, partitioning, `dd`, bootloader installs, deleting or
|
||||
editing files), state the exact command and what it will change. Then wait
|
||||
for Fred's OK.
|
||||
3. **A dying disk changes the plan.** If SMART shows reallocated, pending or
|
||||
uncorrectable sectors, or dmesg shows I/O errors, stop repairing. Propose
|
||||
imaging it first with `ddrescue` to another disk, then work on the copy.
|
||||
Every extra read can make a dying disk worse.
|
||||
4. **Back up before editing.** `cp file file.bak` before changing any config
|
||||
on the patient. Never run fsck on a mounted filesystem.
|
||||
5. **Recovered files go elsewhere.** photorec/testdisk output goes to a
|
||||
different disk than the one being recovered, never the source.
|
||||
6. LUKS passphrases come from Fred. Ask, never guess or brute force.
|
||||
|
||||
## Useful moves
|
||||
|
||||
- Was the live USB booted as UEFI? Check `[ -d /sys/firmware/efi ]`. Fixing a
|
||||
bootloader needs the same mode the patient boots in.
|
||||
- Read the patient's logs without booting it:
|
||||
`journalctl -D /mnt/var/log/journal --list-boots`, then `-b -1 -p warning`.
|
||||
- Chroot into any distro: mount root at `/mnt` (plus `/boot` or `/boot/efi`),
|
||||
then `arch-chroot /mnt`. It handles /proc, /sys, /dev and efivars.
|
||||
- NixOS patients: `nixos-enter --root /mnt`. Often the fastest fix is just
|
||||
picking an older generation in the boot menu. Otherwise roll back from the
|
||||
chroot with
|
||||
`/nix/var/nix/profiles/system-<N>-link/bin/switch-to-configuration boot`.
|
||||
List generations with `ls -l /mnt/nix/var/nix/profiles/`.
|
||||
- Fred's own machines (FredOS-*) are NixOS flakes from
|
||||
`git+https://forg.gregersen.it/rope/nixos` and normally build on the home
|
||||
server (nordhammer.it). Prefer rolling back over rebuilding in the chroot.
|
||||
FredOS-Gaming uses Lanzaboote Secure Boot: its keys live in
|
||||
`/var/lib/sbctl` on the patient, and plain systemd-boot reinstalls won't be
|
||||
signed.
|
||||
- Windows patients: `ntfsfix` for dirty NTFS, `chntpw` for local accounts and
|
||||
the registry, `ms-sys` for MBR boot code. BitLocker volumes need the
|
||||
recovery key.
|
||||
- Hardware suspicion: `memtester` (in-OS RAM test), `sensors`, `smartctl -t
|
||||
short`, `dmidecode`.
|
||||
|
||||
## Tools
|
||||
|
||||
Already here: testdisk/photorec, ddrescue, smartctl, nvme-cli, hdparm,
|
||||
parted, gdisk, cryptsetup, lvm2, mdadm, btrfs/xfs/ext4/f2fs/ntfs/vfat/zfs
|
||||
tools, efibootmgr, arch-chroot, nixos-enter, chntpw, ms-sys, rsync, git, jq,
|
||||
ripgrep, tmux. If you need something else, use `nix shell nixpkgs#<pkg>`. It
|
||||
lands in RAM, so mind memory on small machines.
|
||||
|
||||
Remote access: sshd is running, root only accepts Fred's keys.
|
||||
`ip -brief addr` shows where to reach it.
|
||||
Loading…
Add table
Add a link
Reference in a new issue