diff --git a/.sops.yaml b/.sops.yaml index 3d6df3b..0935176 100644 --- a/.sops.yaml +++ b/.sops.yaml @@ -16,7 +16,7 @@ keys: # User key — always included so secrets can be edited from any machine. - - &ewan age1xl8ptkqm03skrdadqgprnez3trrc0k9t0ex052lweewqre2zc9qq7ljm3z + - &ewan age17ulnk7akn9zfwtc87vsexrr809xj6gkkcp2rkez6xtzyrqclpshqfew5wy # Host keys — derived from /etc/ssh/ssh_host_ed25519_key on each machine. # sops-nix decrypts at activation time using the host's own key. diff --git a/home/default.nix b/home/default.nix index 531bc8c..75231ac 100644 --- a/home/default.nix +++ b/home/default.nix @@ -41,6 +41,7 @@ in ] ++ lib.optionals (cfg.isDesktop) [ ./programs/starship.nix + ./programs/ghostty.nix ] ++ [ ./programs/fastfetch.nix @@ -94,6 +95,50 @@ in programs.home-manager.enable = true; + # Disable the home-manager manual — avoids a known upstream warning about + # options.json referencing store paths without proper context (HM issue #7935). + manual.manpages.enable = false; + manual.html.enable = false; + manual.json.enable = false; + + # ── nix-config git repo ──────────────────────────────────────────────────── + # Ensures ~/.config/nix-config is always a git repo with the correct remotes. + home.activation.nixConfigGitRepo = lib.hm.dag.entryAfter [ "writeBoundary" ] '' + nix_config="${config.home.homeDirectory}/.config/nix-config" + github_remote="git@github.com:ewanc26/nix" + tangled_remote="git@tangled.org:ewancroft.uk/nix" + + if [ ! -d "$nix_config" ]; then + echo "nix-config: directory not found, skipping git setup" + else + cd "$nix_config" + + if [ ! -d ".git" ]; then + $DRY_RUN_CMD ${pkgs.git}/bin/git init + $DRY_RUN_CMD ${pkgs.git}/bin/git checkout -b main + echo "nix-config: initialised git repo" + fi + + current_origin=$(${pkgs.git}/bin/git remote get-url origin 2>/dev/null || echo "") + if [ -z "$current_origin" ]; then + $DRY_RUN_CMD ${pkgs.git}/bin/git remote add origin "$github_remote" + echo "nix-config: added origin -> $github_remote" + elif [ "$current_origin" != "$github_remote" ]; then + $DRY_RUN_CMD ${pkgs.git}/bin/git remote set-url origin "$github_remote" + echo "nix-config: updated origin -> $github_remote" + fi + + current_tangled=$(${pkgs.git}/bin/git remote get-url tangled 2>/dev/null || echo "") + if [ -z "$current_tangled" ]; then + $DRY_RUN_CMD ${pkgs.git}/bin/git remote add tangled "$tangled_remote" + echo "nix-config: added tangled -> $tangled_remote" + elif [ "$current_tangled" != "$tangled_remote" ]; then + $DRY_RUN_CMD ${pkgs.git}/bin/git remote set-url tangled "$tangled_remote" + echo "nix-config: updated tangled -> $tangled_remote" + fi + fi + ''; + # ── Nextcloud desktop client ───────────────────────────────────────────── # Both activation scripts below patch nextcloud.cfg in-place so that # credentials/tokens already written by the client are preserved. diff --git a/home/programs/ghostty.nix b/home/programs/ghostty.nix new file mode 100644 index 0000000..ca1fd1d --- /dev/null +++ b/home/programs/ghostty.nix @@ -0,0 +1,49 @@ +# Ghostty terminal — all desktop hosts (Darwin + Linux). +# On Darwin, uses the pre-built ghostty-bin package (avoids building from +# source / Xcode requirement). On Linux, uses the full nixpkgs package. +{ + pkgs, + osConfig, + isDarwin, + ... +}: +let + cfg = osConfig.myConfig; + d = cfg.desktop; +in +{ + programs.ghostty = { + enable = true; + + package = if isDarwin then pkgs.ghostty-bin else pkgs.ghostty; + + enableZshIntegration = true; + + settings = { + # ── Font ──────────────────────────────────────────────────────────── + font-family = d.monoFontFamily; + font-size = d.monoFontSize; + + # ── Theme ─────────────────────────────────────────────────────────── + # Inline Catppuccin Mocha colours — no external theme file needed + background = "1e1e2e"; # base + foreground = "cdd6f4"; # text + + # ── Window ────────────────────────────────────────────────────────── + window-decoration = if isDarwin then "auto" else "none"; + background-opacity = 0.95; + background-blur-radius = 20; + + # ── Cursor ────────────────────────────────────────────────────────── + cursor-style = "bar"; + cursor-style-blink = true; + + # ── Scrollback ────────────────────────────────────────────────────── + scrollback-limit = 10000; + + # ── Misc ──────────────────────────────────────────────────────────── + confirm-close-surface = false; + copy-on-select = false; + }; + }; +} diff --git a/home/programs/ssh.nix b/home/programs/ssh.nix index 0650a61..da3ebc1 100644 --- a/home/programs/ssh.nix +++ b/home/programs/ssh.nix @@ -93,7 +93,9 @@ in config = { ProgramArguments = [ "/usr/bin/ssh-add" - "--apple-load-keychain" + "-q" + "--apple-use-keychain" + "/Users/${cfg.user.username}/.ssh/id_ed25519" ]; RunAtLoad = true; StandardOutPath = "/tmp/ssh-add-keychain.log"; diff --git a/home/scripts/verify-ssh-external b/home/scripts/verify-ssh-external new file mode 100644 index 0000000..8f24ac0 --- /dev/null +++ b/home/scripts/verify-ssh-external @@ -0,0 +1,52 @@ +#!/usr/bin/env bash +# Check SSH connectivity to external services +# Usage: verify-ssh-external + +set -uo pipefail + +PASS=0 +FAIL=0 + +check() { + local label="$1" + local host="$2" + local user="$3" + local expected="$4" + + echo -n " $label... " + result=$(ssh -o ConnectTimeout=5 \ + -o BatchMode=yes \ + -o StrictHostKeyChecking=accept-new \ + "${user}@${host}" 2>&1) || true + + if echo "$result" | grep -qi "$expected"; then + echo "✓" + ((PASS++)) + else + echo "✗ ($result)" + ((FAIL++)) + fi +} + +echo +echo "=== External SSH Verification ===" +echo + +check "GitHub (github.com)" "github.com" "git" "successfully authenticated" +check "Forgejo (git.ewancroft.uk)" "git.ewancroft.uk" "git" "successfully authenticated\|welcome" +check "Tangled (tangled.sh)" "tangled.sh" "git" "successfully authenticated\|welcome" +check "Tangled (tangled.org)" "tangled.org" "git" "successfully authenticated\|welcome" + +echo +if [ "$FAIL" -eq 0 ]; then + echo " All $PASS connections OK" +else + echo " $PASS passed, $FAIL failed" + echo + echo " Troubleshooting:" + echo " - Check your key is loaded: ssh-add -l" + echo " - GitHub keys: https://github.com/settings/keys" + echo " - Forgejo keys: https://git.ewancroft.uk/user/settings/keys" + echo " - Tangled keys: https://tangled.sh/settings/keys" +fi +echo diff --git a/modules/options.nix b/modules/options.nix index b0e0a3a..7688a0d 100644 --- a/modules/options.nix +++ b/modules/options.nix @@ -736,44 +736,11 @@ in brews = mkOption { type = listStr; default = [ + # MediaInfo — standalone GUI/CLI media analyser "libmediainfo" "media-info" "libzen" - "aribb24" - "dav1d" - "rav1e" - "svt-av1" - "x264" - "x265" - "xvid" - "webp" - "aom" - "jpeg-xl" - "highway" - "flac" - "lame" - "opus" - "vorbis-tools" - "libsndfile" - "libsamplerate" - "rubberband" - "speex" - "theora" - "mpg123" - "little-cms2" - "leptonica" - "rtmpdump" - "srt" - "librist" - "libmms" - "lzo" - "snappy" - "xxhash" - "yyjson" - "freetds" - "unixodbc" - "summarize" - "goat" + # MAS helper — required for masApps below "mas" ]; }; @@ -801,10 +768,7 @@ in "firefox" # Gaming "steam" - "epic-games" "prismlauncher" - "roblox" - "ea" # Virtualisation "utm" # Networking / remote @@ -823,7 +787,6 @@ in "microsoft-powerpoint" "microsoft-teams" "microsoft-word" - "libreoffice" "nextcloud-vfs" ]; }; @@ -832,8 +795,7 @@ in default = { "Amphetamine" = 937984704; "OneDrive" = 823766827; - "OP Auto Clicker" = 6754914118; - "Steam Link" = 1246969117; + # Steam Link removed — requires Rosetta 2, incompatible with Apple Silicon "TestFlight" = 899247664; "The Unarchiver" = 425424353; "WhatsApp" = 310633997; diff --git a/modules/ssh-keys.nix b/modules/ssh-keys.nix index ca2881c..485c5e3 100644 --- a/modules/ssh-keys.nix +++ b/modules/ssh-keys.nix @@ -1,7 +1,7 @@ # Authorised SSH public keys for ewan, keyed by machine name. # Each host automatically excludes its own key at build time. { - macmini = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPcEH7Belx/vpnmuspyAc/3iIAFqtxKGeftG5z5vBsUv git@ewancroft.uk"; + macmini = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGVVr3laZlNUZ+g5DuHEf5VsOpdg52WLK38kBmZzSbJm git@ewancroft.uk"; laptop = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAwl17Pm/CCbcCw4pboqP3V3iJTWKHggow0Qpt3vENNZ git@ewancroft.uk"; server = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICUA55QI9vU2TCTJobFSkTZ4xnl3s7CXdnARxkh6/QzT git@ewancroft.uk"; } diff --git a/scripts/check-secrets.sh b/scripts/check-secrets.sh index 9352921..f7924d4 100644 --- a/scripts/check-secrets.sh +++ b/scripts/check-secrets.sh @@ -2,7 +2,8 @@ # Audits all sops secrets in the nix-config repo. # Reports: format, recipient count, whether server key is present, and decryptability. -cd "$(git rev-parse --show-toplevel)" || exit 1 +REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && cd .. && pwd)" +cd "$REPO_ROOT" SERVER_KEY="age1xvny7h8cahajamj4lz9cew5w0dqlge0yy6tys7szj42grcrl95jqsrutsu" PASS=0 @@ -13,16 +14,29 @@ check_secret() { local expected_format="$2" echo "── $file ──────────────────────────────" - if ! jq empty "$file" 2>/dev/null; then - echo " ✗ Not valid JSON — may be corrupted" - ((FAIL++)); return + # sops metadata is always in a JSON sidecar regardless of file format. + # For dotenv/binary files, extract the sops block from the file directly. + local sops_json + if [[ "$expected_format" == "json" ]]; then + if ! jq empty "$file" 2>/dev/null; then + echo " ✗ Not valid JSON — may be corrupted" + ((FAIL++)); return + fi + sops_json=$(jq '.sops' "$file" 2>/dev/null) + else + # dotenv/binary: sops appends a JSON block at the end after a blank line + sops_json=$(awk '/^sops:/{found=1} found{print}' "$file" 2>/dev/null || true) + if [ -z "$sops_json" ]; then + # fallback: try treating whole file as JSON anyway + sops_json=$(jq '.sops' "$file" 2>/dev/null || echo "{}") + fi fi local count - count=$(jq '.sops.age | length' "$file" 2>/dev/null) + count=$(echo "$sops_json" | jq '.age | length' 2>/dev/null || echo "0") echo " Recipients: $count" - if jq -r '.sops.age[].recipient' "$file" 2>/dev/null | grep -q "$SERVER_KEY"; then + if echo "$sops_json" | jq -r '.age[].recipient' 2>/dev/null | grep -q "$SERVER_KEY"; then echo " ✓ Server key present" else echo " ✗ Server key MISSING" @@ -30,7 +44,7 @@ check_secret() { fi local modified - modified=$(jq -r '.sops.lastmodified' "$file" 2>/dev/null) + modified=$(echo "$sops_json" | jq -r '.lastmodified' 2>/dev/null || echo "unknown") echo " Last modified: $modified" local plaintext diff --git a/settings/darwin/default.nix b/settings/darwin/default.nix index dddc602..a73b918 100644 --- a/settings/darwin/default.nix +++ b/settings/darwin/default.nix @@ -55,7 +55,7 @@ "/Applications/Spotify.app" "/Applications/Firefox.app" # ── System ───────────────────────────────────────────────────── - "/System/Applications/Utilities/Terminal.app" + "/Users/ewan/Applications/Home Manager Apps/Ghostty.app" ]; };