aboutsummaryrefslogtreecommitdiffstats
path: root/modules
diff options
context:
space:
mode:
authorSimen A. W. Olsen <hello@simenandre.no>2026-10-27 11:50:21 +0100
committerSimen A. W. Olsen <hello@simenandre.no>2026-10-27 11:50:21 +0100
commit305a0e61f839de12f5aebaee6e1469e138c86fd7 (patch)
treec3c7ff3dd1180a785e02a848ad3e4655d68cea20 /modules
parent163c2dee18ab2b197fe1fcf00cf32214b804fda6 (diff)
downloaddotfiles-305a0e61f839de12f5aebaee6e1469e138c86fd7.tar.gz
dotfiles-305a0e61f839de12f5aebaee6e1469e138c86fd7.zip
feat: gate updates behind vet supply-chain checksHEADmain
update now holds inputs and skills back by a cooldown, has Claude review third-party diffs, and reports CVE changes before switching. Homebrew taps are pinned through nix-homebrew, and npm/pnpm get install policies.
Diffstat (limited to 'modules')
-rw-r--r--modules/darwin/default.nix4
-rw-r--r--modules/darwin/homebrew.nix11
-rw-r--r--modules/home/default.nix1
-rw-r--r--modules/home/supply-chain.nix26
-rw-r--r--modules/home/zsh.nix28
5 files changed, 58 insertions, 12 deletions
diff --git a/modules/darwin/default.nix b/modules/darwin/default.nix
index d23e795..1ccb7c9 100644
--- a/modules/darwin/default.nix
+++ b/modules/darwin/default.nix
@@ -144,5 +144,9 @@
# Specific toolsets
rclone
+
+ # Supply-chain checks: vet gates `update`, nvd shows what a switch changes
+ (pkgs.callPackage ../../pkgs/vet { })
+ nvd
];
}
diff --git a/modules/darwin/homebrew.nix b/modules/darwin/homebrew.nix
index 51749e1..3175e97 100644
--- a/modules/darwin/homebrew.nix
+++ b/modules/darwin/homebrew.nix
@@ -1,19 +1,18 @@
-{ ... }:
+{ config, ... }:
{
homebrew = {
enable = true;
+ # Taps are pinned in flake.lock (nix-homebrew in flake.nix), so upgrading
+ # moves to the versions vet let through and never past them.
onActivation = {
- autoUpdate = true;
+ autoUpdate = false;
upgrade = true;
cleanup = "zap";
};
- taps = [
- "simenandre/tools"
- "anomalyco/tap"
- ];
+ taps = builtins.attrNames config.nix-homebrew.taps;
# Note: most CLI tools live in nixpkgs (modules/darwin/default.nix).
# These stay in brew on purpose: shell completions, the macOS container
diff --git a/modules/home/default.nix b/modules/home/default.nix
index 5331684..e815c7c 100644
--- a/modules/home/default.nix
+++ b/modules/home/default.nix
@@ -10,6 +10,7 @@
./herdr.nix
./onepassword.nix
./starship.nix
+ ./supply-chain.nix
./tmux.nix
./lazygit.nix
./twm.nix
diff --git a/modules/home/supply-chain.nix b/modules/home/supply-chain.nix
new file mode 100644
index 0000000..43c5f3b
--- /dev/null
+++ b/modules/home/supply-chain.nix
@@ -0,0 +1,26 @@
+{ pkgs, lib, ... }:
+
+let
+ # Most registry compromises are caught and unpublished within days, so a
+ # week-old version has had time to be noticed. Lifecycle scripts are
+ # already blocked by pnpm unless a project allows them.
+ pnpmConfig = ''
+ minimumReleaseAge: 10080
+ trustPolicy: no-downgrade
+ blockExoticSubdeps: true
+ '';
+in
+{
+ # pnpm reads $XDG_CONFIG_HOME/pnpm/config.yaml, and on macOS falls back to
+ # ~/Library/Preferences/pnpm when XDG_CONFIG_HOME is unset (GUI apps, editors).
+ xdg.configFile."pnpm/config.yaml".text = pnpmConfig;
+ home.file."Library/Preferences/pnpm/config.yaml" = lib.mkIf pkgs.stdenv.isDarwin {
+ text = pnpmConfig;
+ };
+
+ # An env var rather than ~/.npmrc, which `npm login` must be able to write.
+ # pnpm ignores it. Explicit `npm run <script>` still runs.
+ home.sessionVariables.npm_config_ignore_scripts = "true";
+
+ home.packages = [ pkgs.osv-scanner ];
+}
diff --git a/modules/home/zsh.nix b/modules/home/zsh.nix
index 3cef29d..88873d8 100644
--- a/modules/home/zsh.nix
+++ b/modules/home/zsh.nix
@@ -1,4 +1,4 @@
-{ pkgs, lib, flakeAttr, ... }:
+{ config, pkgs, lib, flakeAttr, ... }:
let
# Pinned to commits, not branch names: a branch ref keeps moving while the
@@ -40,6 +40,9 @@ in
export XDG_CACHE_HOME=''${XDG_CACHE_HOME:-$HOME/.cache}
export ZDOTDIR=''${ZDOTDIR:-$XDG_CONFIG_HOME/zsh}
+ # home.sessionVariables (GOPRIVATE, SKILLS_MANAGED_BY, ...)
+ . "${config.home.profileDirectory}/etc/profile.d/hm-session-vars.sh"
+
# Ensure path arrays do not contain duplicates.
typeset -gU path fpath
@@ -120,13 +123,26 @@ in
alias tailscale='/Applications/Tailscale.app/Contents/MacOS/Tailscale'
alias rebuild='sudo darwin-rebuild switch --flake ~/ghq/simenandre.no/dotfiles#${flakeAttr}'
function update() {
- local flake=~/ghq/simenandre.no/dotfiles
- nix flake update --flake "$flake" || return
+ local flake=~/ghq/simenandre.no/dotfiles out
+ out=$(mktemp -d)
+ # vet holds inputs back by a cooldown and has Claude review third-party
+ # changes; see pkgs/vet.
+ (cd "$flake" && vet lock && vet check --report "$out/report") || {
+ echo 'update: stopped by vet, nothing was switched'
+ return 1
+ }
+ (cd "$out" && darwin-rebuild build --flake "$flake#${flakeAttr}") || return
+ nvd diff /run/current-system "$out/result"
+ (cd "$flake" && vet cves /run/current-system "$out/result" --report "$out/cves") ||
+ { echo 'update: CVE scan failed'; : >"$out/cves"; }
+ read -q '?update: switch to this system? [y/N] ' || { echo; return 1; }
+ echo
sudo darwin-rebuild switch --flake "$flake#${flakeAttr}" || return
- # Commit only after a switch succeeds, and only flake.lock, so unrelated
+ # Commit only after a switch succeeds, and only the locks, so unrelated
# work in the tree stays uncommitted.
- git -C "$flake" diff --quiet HEAD -- flake.lock && return 0
- git -C "$flake" commit -m 'chore(flake): update inputs' -- flake.lock || return
+ git -C "$flake" diff --quiet HEAD -- flake.lock skills.lock && return 0
+ printf 'chore(flake): update inputs\n\n%s\n\n%s\n' "$(<"$out/report")" "$(<"$out/cves")" |
+ git -C "$flake" commit -F - -- flake.lock skills.lock || return
git -C "$flake" push || echo 'update: lock committed but push failed'
}
alias nix-gc='sudo nix-collect-garbage --delete-older-than 14d && nix-collect-garbage --delete-older-than 14d && nix store gc'