Feedback now accepts photos, voice notes, video, documents etc. Phase A captures + stores raw files (Pillow for EXIF strip); Phase B derives OCR text, speech transcripts, poster frames, PDF text — all via subprocess so each tool degrades gracefully if absent. Wire the following into the shipyard service: - python3Packages.pillow → EXIF strip on captured photos - ffmpeg → poster frames + audio→16kHz WAV for whisper - tesseract (eng + rus) → OCR (vyscul writes in Russian) - whisper-cpp → speech-to-text for voice / audio / video - poppler_utils → pdftotext for document attachments Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
114 lines
5.6 KiB
Nix
114 lines
5.6 KiB
Nix
# 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 ];
|
|
}
|