103 lines
3.9 KiB
Nix
103 lines
3.9 KiB
Nix
|
|
# 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;
|
||
|
|
}
|