# 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.