Privileged Automation
The documented default for autonomous use is passwordless sudo for the
narrow nix-agent commands, scoped to one user and one flake directory.
nix-agent invokes sudo -n with the resolved store path of
nixos-rebuild and nix-env, so a missing NOPASSWD rule fails
immediately with a privilege diagnosis instead of hanging on a
password prompt. NOPASSWD rules must match that argv form (the command
after sudo -n, as in the examples below). Standalone Home Manager mode
does not use sudo.
To lower trust (sudo password, or host prompts on activation), see Lower trust. Do not grant a wildcard flake ref.
The first generation that installs these rules still needs a TTY if
NOPASSWD is not already present. The installer tries sudo -n first;
nix-agent bootstrap-rebuild prints one tty_command when that fails.
$NIX_AGENT_FLAKE (and the module option that wraps it into the binary) is
an anti-footgun, not a security boundary. The agent can still edit the
pinned tree and, if it can edit MCP config, the pin in .mcp.json. The
privilege boundary that actually runs as root is sudoers. Narrow those rules
to the pinned flake directory. Do not grant a wildcard flake ref.
The autonomous install enables this. Prefer the NixOS module over a
pasted extraRules block when nixosModules.default is already imported:
programs.nix-agent.enable = true;
programs.nix-agent.flake = /home/alice/nixos; # absolute working-tree path
programs.nix-agent.privilegedAutomation.enable = true;
programs.nix-agent.privilegedAutomation.user = "alice";
When programs.nix-agent.flake is set, the module wraps the binary so
NIX_AGENT_FLAKE is in its environment, and emits NOPASSWD rules narrowed
to that directory. When privileged automation is enabled but flake is
unset, the module emits only rollback / generation-switch rules (no
rebuild or switch from a flake). Set the flake path, or skip privileged
automation; do not widen the flake argument.
Commands to allow
Allowlist only these commands for the local trusted user.
Flake-scoped (only when the flake directory is known; substitute that absolute path, no trailing slash):
nixos-rebuild dry-activate --flake <flake_dir>*nixos-rebuild switch --flake <flake_dir>*
Generation / rollback (no flake ref):
nixos-rebuild switch --rollbacknix-env -p /nix/var/nix/profiles/system --switch-generation */nix/var/nix/profiles/system/bin/switch-to-configuration switch
(build, diff, and check("dry-build") use nix build and do not need sudo.)
Activate through the profile path
(/nix/var/nix/profiles/system/bin/switch-to-configuration). Never grant
NOPASSWD on a store-path binary, and never write a sudoers wildcard over
/nix/store/*/bin/switch-to-configuration.
Equivalent extraRules
If you are wiring sudoers by hand instead of
programs.nix-agent.privilegedAutomation, the same narrowed rules look
like this. Replace /home/alice/nixos with the real flake directory.
security.sudo.extraRules = [
{
users = [ "alice" ];
commands = [
{
command = "${pkgs.nixos-rebuild}/bin/nixos-rebuild dry-activate --flake /home/alice/nixos*";
options = [ "NOPASSWD" ];
}
{
command = "${pkgs.nixos-rebuild}/bin/nixos-rebuild switch --flake /home/alice/nixos*";
options = [ "NOPASSWD" ];
}
{
command = "${pkgs.nixos-rebuild}/bin/nixos-rebuild switch --rollback";
options = [ "NOPASSWD" ];
}
{
command = "${pkgs.nix}/bin/nix-env -p /nix/var/nix/profiles/system --switch-generation *";
options = [ "NOPASSWD" ];
}
{
command = "/nix/var/nix/profiles/system/bin/switch-to-configuration switch";
options = [ "NOPASSWD" ];
}
];
}
];
If the flake directory is unknown, omit the two --flake rebuild/switch
rules entirely. Keep only --rollback, nix-env --switch-generation, and
the profile switch-to-configuration rule.
This is intentionally broader than per-command host prompts and is the
default for a trusted local machine. It is not a reason to widen the
flake argument or to wildcard /nix/store/*/bin/switch-to-configuration.
Lower trust
Keep programs.nix-agent.enable and the flake pin. Then:
- Sudo password: omit
programs.nix-agent.privilegedAutomation(the Nix option defaults to off) or setenable = false. Privileged MCP tools fail fast withprivilege(sudo -n) until a human authenticates. - Host prompts on activation: omit
switch/generations(and matching Bashnixos-rebuildswitch / dry-activate / rollback) from the host allow list. See agent-install.md § Lower trust. Do this only when the user asked for human-in-the-loop guards.
Remote flake refs (humans only)
Privileged MCP tools (switch and check("dry-activate")) reject remote
flake refs (github:, git+, https://, …) and, when a pin is set, refuse
a different local directory (target_locked). That classifier is an
anti-footgun so the MCP server is not the confused-deputy path to root
activation. It does not make the pinned tree trusted.
NIX_AGENT_ALLOW_REMOTE=1 (same truthy set as NIX_AGENT_USAGE_LOG:
1 / true / yes / on) skips the remote-ref rejection only. It does
not skip target_locked. Off by default. Set it yourself in the MCP
server environment if you understand the trust decision; do not ask an
agent to enable it, and do not put it in tool-envelope hints.