Repository navigation
Conversation
Pi keeps its configuration only under the user's ~/.pi/agent, so a system daemon skipped it and a device needed a second, user-mode daemon just for Pi while Claude Desktop, Codex and Grok need the system daemon. A system daemon now manages Pi when daemon.pi.models and daemon.pi.settings name that user's files (both are required). The plan hands the files, their state sidecars and any directories it creates to the owner of the nearest existing ancestor, so Pi can read them and the credential command stays private to the user. Without daemon.pi paths a system daemon still skips Pi. ReconcilePlan gains an optional plan-wide owner (Unix), applied after each write and to created directories; a write is not skipped as already in place while the file's owner differs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
🟡 Changes recommended
Privileged filesystem access introduces security vulnerabilities, and ownership recovery and authenticated one-shot validation remain incomplete.
5 open issues
Prevent privileged reads through user-controlled Pi symlinks · New Prevent symlink-following chown in user-writable Pi directories · New Reject authenticated Pi in system-mode one-shot runs · New Plan ownership-only repairs for unchanged JSON files · New Assign planned ownership before Pi directory file writes · New
What changed in this PR
This PR lets a system daemon manage Pi for one explicitly configured user, removing the need for a separate user daemon alongside system-only providers.
Changes:
- Accept paired Pi startup paths and explicitly enable system-mode Pi management.
- Add ownership handling for planned files, sidecars, and newly created directories.
- Update startup validation, ownership tests, and configuration documentation.
| File | Description |
|---|---|
schema/daemon-config.md |
Document the system-mode Pi opt-in. |
schema/daemon-config.json |
Update Pi configuration descriptions. |
crates/core/src/config.rs |
Document Pi startup paths and supported daemon modes. |
crates/agent/src/reconcile/plan.rs |
Add ownership assignment and ownership-aware write checks. |
crates/agent/src/reconcile/mod.rs |
Enable Pi management when both system-mode paths are supplied. |
crates/agent/src/provider/pi/mod.rs |
Infer the target owner and test explicit system-mode management. |
crates/agent/src/daemon.rs |
Accept paired Pi paths and reject incomplete configuration. |
Review effort: Balanced
| if for_user { | ||
| // Pi reads these as the user, so files and any directories the plan | ||
| // creates belong to whoever owns the user's Pi directory. | ||
| plan.set_owner(owner_of_nearest(models_path)); |
Comment on lines
+503
to
+504
| std::os::unix::fs::chown(path, Some(owner.uid), Some(owner.gid)) | ||
| .with_context(|| format!("set owner of {}", path.display()))?; |
Comment on lines
+237
to
+239
| (Some(models), Some(settings)) => ResolvedPiStartupConfig { | ||
| models: Some(models), | ||
| settings: Some(settings), |
| for (index, change) in self.operations.into_inner().into_iter().enumerate() { | ||
| if let Some(contents) = &change.contents | ||
| && already_in_place(&change.path, contents, change.permissions, &observed) | ||
| && owned_by(&change.path, change.owner) |
Comment on lines
+263
to
+267
| crate::secure_fs::atomic_write( | ||
| &change.path, | ||
| &contents, | ||
| change.permissions, | ||
| )?; |
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.


Why
Pi keeps its configuration only in the user's
~/.pi/agent, so a system daemon skipped it. Claude Desktop, Codex and Grok are system-only, though. A device that wants all four managed needed two daemons: a system one, plus a user-mode one just for Pi.What
daemon.pi.modelsanddaemon.pi.settingsname one user's files:.agentdesktopstate sidecars and any directories it creates to the owner of the nearest existing ancestor. Pi can read them, and the!agentdesktop … credentialcommand stays in user-private (0600) files.ReconcilePlangets an optional plan-wide owner (set_owner, Unix only). It's applied after each write and to directories the plan creates. A write isn't skipped as "already in place" while the file's owner differs.Pigetsmanage_for_user.Reconciler::newsets it only for a system daemon constructed with both paths, so the opt-in is explicit rather than inferred from the presence of paths.PiConfigandPiStartupConfigdocs are updated, plusschema/daemon-config.{json,md}.Tests
system_mode_manages_a_configured_users_pi_files(new): a system daemon writes the files, and the files and created directories have the ancestor's owner.system_mode_rejects_a_single_pi_path(new).pi_startup_paths_resolve_in_both_modes(was…_require_user_mode).skips_configured_pi_in_system_modeandsystem_mode_leaves_user_pi_files_untouchedstill cover the skip when not opted in.cargo fmt --checkandcargo clippy --workspace --all-targets -D warningsare clean.cargo test -p agentdesktop-agent -p agentdesktop-core: 473 + 11 passed, run as a non-root user. I couldn't run the testcontainers integration suite locally (no Docker socket in my build container).Manual check on an Ubuntu 24.04 VM. I ran a root
agentdesktop daemon --oncewithdaemon.pipaths for a fresh user:~/.piand~/.pi/agent,models.json(0600),settings.jsonand both sidecars, all owned by that user.Pi requires user mode (--user); skipped, as before.One note: I edited
schema/daemon-config.mdby hand, becausecargo xtask schemain my environment dropped the startup-field rows from the Markdown output. The JSON came out the same as the generator's.🤖 Generated with Claude Code