From a1b6950e93d113e3e8875929bf35ed46c9bb0b9f Mon Sep 17 00:00:00 2001 From: "sascha.koenig" Date: Wed, 15 Apr 2026 11:38:25 +0200 Subject: [PATCH] fix: pi settings sync chore: eigent update --- .pi-lens/cache/jscpd.meta.json | 2 +- .pi-lens/cache/knip.meta.json | 2 +- .../cache/session-start-guidance.meta.json | 2 +- .pi-lens/cache/todo-baseline.meta.json | 2 +- .pi-lens/turn-state.json | 2 +- .td-root | 1 - PLAN.md | 98 ------------------- modules/nixos/pi-agent.nix | 30 +++++- pkgs/eigent/default.nix | 4 +- 9 files changed, 35 insertions(+), 108 deletions(-) delete mode 100644 .td-root delete mode 100644 PLAN.md diff --git a/.pi-lens/cache/jscpd.meta.json b/.pi-lens/cache/jscpd.meta.json index 617ce93..666cf25 100644 --- a/.pi-lens/cache/jscpd.meta.json +++ b/.pi-lens/cache/jscpd.meta.json @@ -1,3 +1,3 @@ { - "timestamp": "2026-04-14T15:35:06.339Z" + "timestamp": "2026-04-15T09:30:34.459Z" } \ No newline at end of file diff --git a/.pi-lens/cache/knip.meta.json b/.pi-lens/cache/knip.meta.json index a2ab51d..d80ceea 100644 --- a/.pi-lens/cache/knip.meta.json +++ b/.pi-lens/cache/knip.meta.json @@ -1,3 +1,3 @@ { - "timestamp": "2026-04-14T15:35:07.218Z" + "timestamp": "2026-04-15T09:30:35.667Z" } \ No newline at end of file diff --git a/.pi-lens/cache/session-start-guidance.meta.json b/.pi-lens/cache/session-start-guidance.meta.json index 89ffef9..42b8956 100644 --- a/.pi-lens/cache/session-start-guidance.meta.json +++ b/.pi-lens/cache/session-start-guidance.meta.json @@ -1,3 +1,3 @@ { - "timestamp": "2026-04-14T05:13:57.102Z" + "timestamp": "2026-04-15T09:28:51.987Z" } \ No newline at end of file diff --git a/.pi-lens/cache/todo-baseline.meta.json b/.pi-lens/cache/todo-baseline.meta.json index be6b111..f2588fa 100644 --- a/.pi-lens/cache/todo-baseline.meta.json +++ b/.pi-lens/cache/todo-baseline.meta.json @@ -1,3 +1,3 @@ { - "timestamp": "2026-04-14T05:11:47.088Z" + "timestamp": "2026-04-15T09:28:16.965Z" } \ No newline at end of file diff --git a/.pi-lens/turn-state.json b/.pi-lens/turn-state.json index 79bfea8..3e1b99d 100644 --- a/.pi-lens/turn-state.json +++ b/.pi-lens/turn-state.json @@ -2,5 +2,5 @@ "files": {}, "turnCycles": 0, "maxCycles": 3, - "lastUpdated": "2026-04-14T15:35:07.218Z" + "lastUpdated": "2026-04-15T09:30:35.668Z" } \ No newline at end of file diff --git a/.td-root b/.td-root deleted file mode 100644 index a81e2a7..0000000 --- a/.td-root +++ /dev/null @@ -1 +0,0 @@ -/home/sascha.koenig/p/NIX/nixpkgs diff --git a/PLAN.md b/PLAN.md deleted file mode 100644 index 73dafcf..0000000 --- a/PLAN.md +++ /dev/null @@ -1,98 +0,0 @@ -# PLAN - -## Context -- Target implementation is confirmed as `m3ta.pi-agent` (no container mode). -- You want a **fresh-from-scratch rewrite** of `modules/nixos/pi-agent.nix` and to ignore prior behavior as design baseline. -- Required behavior: - - dedicated isolated Unix user/group for Pi (`pi-agent` defaults) - - host UX stays `pi` - - bypass prevention (wrapper should be the canonical executable path) - - per-host-user project root policy (different roots per user) - - no writable/access scope beyond isolated Pi home/state + explicitly allowed project roots - - isolated environment must include user Pi config from HM (`modules/home-manager/coding/agents/pi.nix`) and support Nix-managed settings/env merging. -- Repo findings: - - `modules/nixos/default.nix` + `flake.nix` already import/export `pi-agent` module. - - `modules/home-manager/coding/agents/pi.nix` already renders Pi config files under a configurable relative path (`coding.agents.pi.path`, default `.pi/agent`). - -## Approach -- Fully replace `modules/nixos/pi-agent.nix` with a new design centered on: - 1. **Dedicated runtime identity** (`user/group/createUser/stateDir`). - 2. **Policy-driven wrapper flow** (`pi` -> privileged runner -> isolated execution). - 3. **Per-user project allowlists** (cwd must be under roots assigned to invoking host user). - 4. **Config + env convergence**: - - sync user HM Pi config directory (e.g. `~/.pi/agent`) into isolated state, - - merge Nix-managed Pi settings into isolated `settings.json`, - - merge Nix-managed env vars + env files into isolated runtime env source, - - make merged results visible to the isolated runtime every invocation (without container recreation semantics). - 5. **Hard isolation defaults** with `systemd-run` sandboxing and explicit bind/read-write paths only for state + allowed projects. -- Keep wrapper command as `pi`, and avoid exposing direct package binary on PATH when wrapper mode is enabled. - -## Files to modify -- `modules/nixos/pi-agent.nix` (full rewrite) -- `modules/nixos/default.nix` (only if import list changes) -- `flake.nix` (only if output export attrs change) -- `docs/guides/pi-agent-isolation.md` (update option model + merge behavior) -- `docs/guides/using-modules.md` (update examples/options) - -## Reuse -- Module/user/service patterns: - - `modules/nixos/mem0.nix` - - `templates/nixos-module/default.nix` -- Pi config rendering contract to consume/sync: - - `modules/home-manager/coding/agents/pi.nix` (`coding.agents.pi.path`, `settings.json`, `mcp.json`, agent docs) - -## Steps -- [ ] Define the new `m3ta.pi-agent` option schema for fresh module behavior, including: - - base runtime options (`package`, `binaryName`, `user`, `group`, `createUser`, `stateDir`), - - wrapper controls (`enable`, `commandName`, runner name, hide-direct-binary behavior), - - per-user policy map (allowed users and each user’s allowed project roots), - - host-config sync knobs (source path relative/absolute), - - Nix-managed settings/env options for merge. -- [ ] Implement new wrapper script: - - identify invoking user, - - validate user exists in policy map, - - expand/resolve that user’s roots, - - deny out-of-policy cwd, - - escalate only to the dedicated runner. -- [ ] Implement new privileged runner script: - - enforce root-only execution, - - resync host Pi config into isolated config dir, - - merge managed settings into isolated settings file, - - merge managed env + env files into isolated env file/export source, - - prepare deterministic project mount aliases under isolated home, - - launch Pi through hardened transient `systemd-run` unit as isolated user. -- [ ] Apply hardening policy in execution profile: - - `ProtectSystem=strict`, `ProtectHome=yes`, `NoNewPrivileges=yes`, - - explicit `ReadWritePaths` limited to state + mounted allowed projects, - - bounded runtime PATH and writable tool/cache locations under `stateDir`. -- [ ] Add assertions for misconfiguration (e.g., empty per-user roots, wrapper enabled without authorized users). -- [ ] Add tightly scoped sudoers rule for runner command only. -- [ ] Ensure bypass prevention in packaging/PATH behavior when wrapper mode is enabled. -- [ ] Update docs with new option examples (per-user roots + settings/env merge + HM sync expectations). - -## Verification -- Static/eval: - - `nix flake check` - - host config eval/build with new module options. -- Policy checks: - - authorized user in authorized root: succeeds - - authorized user outside authorized root: denied - - unauthorized user: denied -- Isolation checks: - - runtime identity is isolated service user (`pi-agent`) - - no unintended write access outside `stateDir` + allowed project binds - - direct binary bypass unavailable when wrapper mode is enabled -- Merge checks: - - HM-rendered Pi files are present in isolated config dir - - Nix-managed settings are merged into effective isolated `settings.json` - - env values from declarative attrs + env files are present in isolated runtime environment. - -## Open questions -- None. - -## Resolved decisions -- Merge precedence is confirmed as: - 1) synced host Pi config/env, - 2) Nix-managed settings/env override synced values, - 3) wrapper/runtime shell env does not implicitly override managed values. -- Per-user host config source defaults to `.pi/agent` for all users, with optional per-user override support in the policy map. diff --git a/modules/nixos/pi-agent.nix b/modules/nixos/pi-agent.nix index 851039d..f6bc04d 100644 --- a/modules/nixos/pi-agent.nix +++ b/modules/nixos/pi-agent.nix @@ -154,6 +154,7 @@ with lib; let ${escapeShellArg cfg.stateDir} \ ${escapeShellArg "${cfg.stateDir}/.pi"} \ ${escapeShellArg "${cfg.stateDir}/.pi/agent"} \ + ${escapeShellArg "${cfg.stateDir}/.pi/agent/sessions"} \ ${escapeShellArg "${cfg.stateDir}/.project-mounts"} \ ${escapeShellArg "${cfg.stateDir}/projects"} \ ${escapeShellArg "${cfg.stateDir}/.npm"} \ @@ -174,7 +175,13 @@ with lib; let then "1" else "0" }" = "1" ] && [ -d "$source_dir" ]; then - ${pkgs.rsync}/bin/rsync -a --delete "$source_dir/" ${escapeShellArg "${cfg.stateDir}/.pi/agent/"} + ${pkgs.rsync}/bin/rsync -a --delete \ + --exclude='auth.json' \ + --exclude='mcp-oauth' \ + --exclude='sessions' \ + --exclude='bin' \ + --exclude='mcp-cache.json' \ + "$source_dir/" ${escapeShellArg "${cfg.stateDir}/.pi/agent/"} ${pkgs.coreutils}/bin/chown -R ${escapeShellArg "${cfg.user}:${cfg.group}"} ${escapeShellArg "${cfg.stateDir}/.pi/agent"} fi @@ -346,7 +353,7 @@ with lib; let -p RestrictRealtime=yes -p RestrictNamespaces=yes -p MemoryDenyWriteExecute=no - -p UMask=0077 + -p UMask=0007 -p ReadWritePaths=${cfg.stateDir} -p EnvironmentFile=${cfg.stateDir}/.pi/.env -E HOME=${cfg.stateDir} @@ -358,6 +365,10 @@ with lib; let -E PI_AGENT_INVOKING_USER="$invoking_user" ) + ${optionalString (cfg.projectGroup != null) '' + cmd+=( -p SupplementaryGroups=${cfg.projectGroup} ) + ''} + # Only mark existing top-level paths inaccessible; systemd fails namespace # setup if InaccessiblePaths points to a non-existent path on this host. for p in /home /root /mnt /media /srv; do @@ -596,6 +607,19 @@ in { description = "Extra packages added to isolated runtime PATH."; }; + projectGroup = mkOption { + type = types.nullOr types.str; + default = null; + description = '' + When set, the pi-agent user is added to this group and the group is + passed as SupplementaryGroups to the systemd-run sandbox. This allows + pi-agent to write to project directories that grant group write access. + The user must ensure project directories have appropriate group ownership + and permissions (e.g. setgid + group write). + ''; + example = "users"; + }; + wrapper = { enable = mkOption { type = types.bool; @@ -682,6 +706,7 @@ in { "${cfg.user}" = { isSystemUser = true; group = cfg.group; + extraGroups = mkIf (cfg.projectGroup != null) [cfg.projectGroup]; description = "Isolated Pi agent user"; home = cfg.stateDir; createHome = true; @@ -693,6 +718,7 @@ in { "d ${cfg.stateDir} 0750 ${cfg.user} ${cfg.group} - -" "d ${cfg.stateDir}/.pi 0750 ${cfg.user} ${cfg.group} - -" "d ${cfg.stateDir}/.pi/agent 0750 ${cfg.user} ${cfg.group} - -" + "d ${cfg.stateDir}/.pi/agent/sessions 0750 ${cfg.user} ${cfg.group} - -" "d ${cfg.stateDir}/.project-mounts 0750 ${cfg.user} ${cfg.group} - -" "d ${cfg.stateDir}/projects 0750 ${cfg.user} ${cfg.group} - -" "d ${cfg.stateDir}/.npm 0750 ${cfg.user} ${cfg.group} - -" diff --git a/pkgs/eigent/default.nix b/pkgs/eigent/default.nix index afa7a92..bf6a909 100644 --- a/pkgs/eigent/default.nix +++ b/pkgs/eigent/default.nix @@ -8,10 +8,10 @@ nix-update-script, }: let pname = "eigent"; - version = "0.0.89"; + version = "0.0.90"; src = fetchurl { url = "https://github.com/eigent-ai/eigent/releases/download/v${version}/Eigent-${version}.AppImage"; - hash = "sha256-9KuiFjegfXhCu1W/FCinWX4ae/DsNPudeBcXFfW18Hc="; + hash = "sha256-mwCBx+D6mgGqQa8bDuUpo3h49EwFVkwasJwaYc6aXFE="; }; appimageContents = appimageTools.extractType2 {inherit pname version src;}; in -- 2.53.0