diff --git a/hosts/mars/hermes-agent.nix b/hosts/mars/hermes-agent.nix index 089a5f6..5151219 100644 --- a/hosts/mars/hermes-agent.nix +++ b/hosts/mars/hermes-agent.nix @@ -492,7 +492,8 @@ in # is ever actually used. # # Provisioner invariants (shaped by the Mnemosyne + KittenTTS review rounds): - # - Build in a ROOT-OWNED staging dir (/var/lib/hermes-kittentts, 0700): + # - Build in a ROOT-OWNED staging dir (/var/lib/hermes-kittentts, 0755 so + # the uid-986 delivery step can read it; nothing in it is secret): # stamp, staging venv, uv cache and staging model copies all live there. # Root never reads or executes anything the container can write — the # container cannot symlink-takeover the stamp (review #1) or plant a @@ -533,7 +534,8 @@ in "hermes-agent-prepare-dirs.service" ]; requires = [ "hermes-agent-prepare-dirs.service" ]; - path = [ pkgs.uv pkgs.coreutils pkgs.setpriv ]; + # diffutils: `cmp` in the fast path; missing, it silently re-delivers every boot. + path = [ pkgs.uv pkgs.coreutils pkgs.diffutils pkgs.util-linux ]; serviceConfig = { Type = "oneshot"; TimeoutStartSec = 600; @@ -556,7 +558,7 @@ in # any uid-986-writable path while running as root. stageDir=/var/lib/hermes-kittentts # Root-side staging of the DELIVERED TREE (venv + full HF hub layout - # including refs/main) — everything root writes lives here, 0700. + # including refs/main) — everything root writes lives here. # review2 #4: root never writes into hermesHome; delivery happens as # the container uid via setpriv, copying from these root-owned sources. stageVenv=$stageDir/venv @@ -595,14 +597,17 @@ in # running as root; symlink-takeover of stamps/refs/install targets is # structurally impossible. ---- mkdir -p "$stageDir" "$stageSnap" "$stageVenv" "$stageHub" - chmod 0700 "$stageDir" + # 0755: the setpriv'd uid-986 delivery below must be able to read it. + chmod 0755 "$stageDir" install -m 0444 ${kittenttsModelConfig} "$stageSnap/config.json" install -m 0444 ${kittenttsModelOnnx} "$stageSnap/kitten_tts_mini_v0_8.onnx" install -m 0444 ${kittenttsModelVoices} "$stageSnap/voices.npz" # Skip the venv REBUILD when staging is still valid (review2 minor: - # damaged delivery should be a copy, not a rebuild). - if ! [ -x "$stageVenv/bin/python" ] \ + # damaged delivery should be a copy, not a rebuild) — but only for the + # CURRENT inputs, else a bump delivers the old venv under a new stamp. + if [ "$(cat "$stampFile" 2>/dev/null)" != "$inputHash" ] \ + || ! [ -x "$stageVenv/bin/python" ] \ || ! [ -f "$stageVenv/lib/python3.13/site-packages/kitten_tts_stub.py" ]; then rm -rf "$stageVenv" UV_CACHE_DIR=$stageDir/uv-cache \ @@ -652,7 +657,7 @@ in # fresh staging tree in. cp-as-986 also fixes review2 #2: rm+cp in one # step, no mv -Tf rename-replace on a non-empty directory. setpriv --reuid=${hermesUid} --regid=${hermesGid} --clear-groups \ - env HOME="$hermesHome" sh -c ' + ${pkgs.runtimeShell} -c ' set -eu rm -rf "$1" "$2" cp -a "$3" "$1"