diff --git a/flake-modules/distant-shore.nix b/flake-modules/distant-shore.nix deleted file mode 100644 index 6c5a023..0000000 --- a/flake-modules/distant-shore.nix +++ /dev/null @@ -1,14 +0,0 @@ -# Standalone nixosSystem registration for distant-shore. -# Temporary: clan integration (zerotier/data-mesher/dm-pull-deploy) needs -# vars generated via sops on the admin machine. Until that runs, this -# keeps the box buildable without clan deps. Delete this file when -# distant-shore moves into flake-modules/clan.nix. -{ inputs, ... }: { - flake.nixosConfigurations.distant-shore = inputs.nixpkgs.lib.nixosSystem { - system = "x86_64-linux"; - modules = [ - inputs.disko.nixosModules.disko - ../nixos/hosts/distant-shore.nix - ]; - }; -} diff --git a/nixos/disko-distant-shore.nix b/nixos/disko-distant-shore.nix deleted file mode 100644 index ab35aac..0000000 --- a/nixos/disko-distant-shore.nix +++ /dev/null @@ -1,37 +0,0 @@ -# Declarative disk layout for distant-shore (ThinkPad X13 Gen 2 — 256 GB -# SK Hynix NVMe). UEFI/systemd-boot, no encryption: it's a headless, -# WiFi-only server that must reboot unattended (clan dm-pull-deploy), so -# a LUKS passphrase prompt at boot would hang it. Mirrors sunken-ship's -# plain-ext4 choice. Device is wiped + repartitioned at install time by -# clan/nixos-anywhere. -{ - disko.devices = { - disk.main = { - type = "disk"; - device = "/dev/nvme0n1"; - content = { - type = "gpt"; - partitions = { - ESP = { - size = "512M"; - type = "EF00"; - content = { - type = "filesystem"; - format = "vfat"; - mountpoint = "/boot"; - mountOptions = [ "fmask=0022" "dmask=0022" ]; - }; - }; - root = { - size = "100%"; - content = { - type = "filesystem"; - format = "ext4"; - mountpoint = "/"; - }; - }; - }; - }; - }; - }; -} diff --git a/nixos/hosts/distant-shore-hardware.nix b/nixos/hosts/distant-shore-hardware.nix deleted file mode 100644 index 3c52633..0000000 --- a/nixos/hosts/distant-shore-hardware.nix +++ /dev/null @@ -1,18 +0,0 @@ -# Do not modify this file! It was generated by ‘nixos-generate-config’ -# and may be overwritten by future invocations. Please make changes -# to /etc/nixos/configuration.nix instead. -{ config, lib, pkgs, modulesPath, ... }: - -{ - imports = - [ (modulesPath + "/installer/scan/not-detected.nix") - ]; - - boot.initrd.availableKernelModules = [ "xhci_pci" "thunderbolt" "nvme" ]; - boot.initrd.kernelModules = [ ]; - boot.kernelModules = [ "kvm-intel" ]; - boot.extraModulePackages = [ ]; - - nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; - hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware; -} diff --git a/nixos/hosts/distant-shore.nix b/nixos/hosts/distant-shore.nix deleted file mode 100644 index 33a7026..0000000 --- a/nixos/hosts/distant-shore.nix +++ /dev/null @@ -1,114 +0,0 @@ -# NixOS server on a ThinkPad X13 Gen 2 (Intel i5-1145G7, 16 GB). -# WiFi-only, headless, unattended auto-rebuild via clan dm-pull-deploy. -# No LUKS (mirrors sunken-ship) so reboots don't block on a passphrase. -# -# Blank-slate server for now — no application services. Give it a purpose -# later (just add services here and let dm-pull-deploy roll it out). -{ config, lib, pkgs, ... }: - -{ - imports = [ - ./distant-shore-hardware.nix - ../disko-distant-shore.nix - ]; - - boot.loader.systemd-boot.enable = true; - # Secure Boot is enforced and the BIOS supervisor password is unknown, so - # we can't enrol our own SB keys. Instead, shim (MS-signed) is placed on - # the ESP and chain-loads systemd-boot; the NVRAM boot entry points at - # shim. We manage that entry imperatively via efibootmgr; letting bootctl - # touch EFI variables would replace it on every rebuild. - boot.loader.efi.canTouchEfiVariables = false; - boot.loader.efi.efiSysMountPoint = "/boot"; # matches disko ESP mountpoint - - # --- Secure Boot via shim + MOK (no firmware key enrolment possible) ------ - # The firmware trusts Microsoft-signed shim; shim trusts our enrolled MOK. - # On every bootloader install we: (1) sign systemd-boot with the MOK and - # drop it where shim chain-loads it (grubx64.efi), (2) install shim as the - # firmware-booted binary (+ MokManager), (3) MOK-sign every kernel image - # systemd-boot will hand off to (shim verifies them via the shim-lock - # protocol). Re-runs on each nixos-rebuild so auto-deployed generations - # stay bootable. Keys + shim live in /etc/secrets (outside the repo). - boot.loader.systemd-boot.extraInstallCommands = '' - # NixOS's bootloader-install systemd unit runs with a minimal PATH that - # doesn't include coreutils, so use absolute paths for cp/mv. - KEY=/etc/secrets/MOK.key - CRT=/etc/secrets/MOK.crt - sb() { ${pkgs.sbsigntool}/bin/sbsign --key "$KEY" --cert "$CRT" --output "$2" "$1"; } - # systemd-boot -> shim's chain-load target - sb /boot/EFI/systemd/systemd-bootx64.efi /boot/EFI/BOOT/grubx64.efi - # shim (MS-signed) is what the firmware boots; MokManager beside it - ${pkgs.coreutils}/bin/cp -f /etc/secrets/shimx64.efi /boot/EFI/BOOT/BOOTX64.EFI - ${pkgs.coreutils}/bin/cp -f /etc/secrets/mmx64.efi /boot/EFI/BOOT/mmx64.efi - # MOK-sign each kernel (skip already-signed; never touch initrds) - for k in /boot/EFI/nixos/*bzImage.efi; do - [ -e "$k" ] || continue - if ! ${pkgs.sbsigntool}/bin/sbverify --cert "$CRT" "$k" >/dev/null 2>&1; then - ${pkgs.sbsigntool}/bin/sbsign --key "$KEY" --cert "$CRT" --output "$k.tmp" "$k" \ - && ${pkgs.coreutils}/bin/mv -f "$k.tmp" "$k" - fi - done - ''; - - networking.hostName = "distant-shore"; - # WiFi via NetworkManager. The wpa_supplicant stack hit two issues on this - # box: (1) it strips CAP_CHOWN so wpa couldn't create its ctrl_interface, - # and (2) dhcpcd didn't grab a lease after the (late) association at boot, - # needing a manual restart — fatal for an unattended headless server. NM - # handles association + DHCP atomically and connected first-try here. - # The PSK stays out of the repo: it's substituted from /etc/secrets/nm.env - # ($PSK_INTENO) into the declared profile at activation. - networking.networkmanager.enable = true; - networking.networkmanager.ensureProfiles.environmentFiles = [ "/etc/secrets/nm.env" ]; - networking.networkmanager.ensureProfiles.profiles."Inteno-89FE" = { - connection = { id = "Inteno-89FE"; type = "wifi"; autoconnect = true; }; - wifi = { ssid = "Inteno-89FE"; mode = "infrastructure"; }; - wifi-security = { key-mgmt = "wpa-psk"; psk = "$PSK_INTENO"; }; - ipv4.method = "auto"; - ipv6.method = "auto"; - }; - hardware.enableRedistributableFirmware = true; # iwlwifi for the Intel AX201 WiFi - time.timeZone = "Europe/Copenhagen"; - - # It's a laptop acting as a server: keep running with the lid shut. - services.logind.settings.Login.HandleLidSwitch = "ignore"; - services.logind.settings.Login.HandleLidSwitchExternalPower = "ignore"; - - # Reduce screen burn-in / power: blank the TTY after a minute. - boot.kernelParams = [ "consoleblank=60" ]; - - nix.settings.experimental-features = [ "nix-command" "flakes" ]; - programs.nix-ld.enable = true; # run dynamically linked binaries (e.g. Claude Code remote CLI) - nix.settings.trusted-users = [ "root" "danny" ]; - system.stateVersion = "25.11"; - - users.users.danny = { - isNormalUser = true; - extraGroups = [ "wheel" "video" "audio" ]; - openssh.authorizedKeys.keys = [ - # Mac admin / fleet key (~/.ssh/id_ed25519_sunken_ship) — the key the - # Mac uses to reach the fleet; clan machines update relies on it. - "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKW/akfIiVU5o63YrTAJVZhMj7kXfYHOnXDtlpVFW7pf danny@mac-admin" - # Per-host key (~/.ssh/id_ed25519_distant_shore) — plain `ssh distant-shore`. - "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIH61JOiOOPrAXekakAwTJg5yCSDfOIjlSvMYkpXrarAf distant-shore" - # sunken-ship (dm-pull-deploy push node) — reach distant-shore over ZT. - "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIB9t4YAaoHvVouqp+qyFOq8o3SAtXMiAmjF6J0ldyx4g danny@sunken-ship" - ]; - }; - users.users.root.openssh.authorizedKeys.keys = - config.users.users.danny.openssh.authorizedKeys.keys; - - services.openssh = { - enable = true; - settings = { - PasswordAuthentication = false; - KbdInteractiveAuthentication = false; - }; - }; - - security.sudo.wheelNeedsPassword = false; - - # mokutil — manage MOK enrolment for the shim chain; sbsigntool — inspect - # signatures on bootloader/kernel images when debugging. - environment.systemPackages = with pkgs; [ git mokutil sbsigntool ]; -} diff --git a/nixos/hosts/phantom-ship.nix b/nixos/hosts/phantom-ship.nix index e018a73..f232e63 100644 --- a/nixos/hosts/phantom-ship.nix +++ b/nixos/hosts/phantom-ship.nix @@ -241,37 +241,18 @@ in # Code deployed out-of-band via rsync to /home/danny/shipyard/ # (staying in-tree in ~/python-projects/26_shipyard/ until spun out to its own repo). # Bot token (not in repo): ~danny/.secrets/telegram-bot-token-shipyard - # Data (feedback.jsonl, feedback.db, pointer cache, feedback_media/): - # ~danny/.local/share/shipyard/ - # - # Feedback now accepts photos / voice / video / docs / stickers etc. - # Phase A captures + stores raw files; Phase B derives OCR text - # (tesseract), speech transcripts (whisper-cpp), poster frames - # (ffmpeg) and PDF text (pdftotext) — all via subprocess, so each - # tool degrades gracefully if missing. + # Data (feedback.jsonl, pointer cache): ~danny/.local/share/shipyard/ systemd.services.shipyard = let pythonEnv = pkgs.python3.withPackages (ps: with ps; [ python-telegram-bot httpx - pillow # EXIF strip on captured photos ]); - # tesseract with English + Russian tessdata — vyscul writes in - # Russian, screenshots can land in either language. - tesseractWithLangs = pkgs.tesseract.override { - enableLanguages = [ "eng" "rus" ]; - }; in { description = "Shipyard Telegram bot (mini-app launcher + feedback)"; after = [ "network-online.target" ]; wants = [ "network-online.target" ]; wantedBy = [ "multi-user.target" ]; - path = [ - pythonEnv - pkgs.ffmpeg # video/animation posters, sticker decode - tesseractWithLangs # photo OCR - pkgs.whisper-cpp # voice/audio transcription - pkgs.poppler-utils # pdftotext (document handling) - ]; + path = [ pythonEnv ]; environment = { SHIPYARD_BOT_TOKEN_FILE = "/home/danny/.secrets/telegram-bot-token-shipyard"; # Owner-only commands (/admin, /grant, /revoke) — anyone else gets ignored.