Skip to content

Security

nerdr runs as you, outside every agent’s sandbox. It reads what agents write, runs git and gh in their worktrees, and runs your plugins and setup commands. An agent, or text an agent read, could try to use that to do what its sandbox stops it from doing. This page is what nerdr does about it, and what is left for your sandbox to do.

  • Run agents in a sandbox if you can, and mark them sandboxed. nerdr then refuses what they ask of it on the hook socket that would get them out: a terminal, an agent outside a sandbox, a task for one.
  • Nothing runs as you without your yes. A repository’s .nerdr.toml setup and every plugin wait for you to read and allow them, and again after any change.
  • Read drafts before sending. Review comments, CI logs and issues contain text written by others, which may hold instructions for the agent.
  • Keep nerdr’s own files out of reach. A sandbox must not let its agent write to ~/.config/nerdr, ~/.local/state/nerdr, or the main checkout’s .git/config, .git/hooks, .git/config.worktree and .git/worktrees/*/config.worktree. With nerdr’s core running on its own, as it does unless NERDR_HOLDER=0, it must also deny the client socket’s folder. nerdr refuses to start an agent whose sandbox check finds it can reach that folder; if it cannot check, it refuses to start the agent too. Detected write access to the protected config, state, Git paths or other worktrees also blocks startup. The check runs before every sandboxed launch, including when NERDR_HOLDER=0. See what a sandbox must deny.
  • Closed is not stopped. The agents work on when you close nerdr or its window, with no one watching, until you quit. A setup that needs your approval waits until you connect again; a plugin run that needs it does not run when its time to be answered runs out. Nothing is approved by itself.

nerdr discards OSC 52 clipboard requests printed by programs, including the focused agent, terminal or editor. Copy text using nerdr’s own selection instead. Recent input or a visible pane does not authorize a program to replace the clipboard.

While the client is disconnected, nerdr keeps the latest 64 distinct notices, up to 64 KiB of encoded messages in total. Repeated notices move to the end of the queue; older notices are evicted when either limit is reached. Oversized notices are dropped. When you connect again, it shows the retained notices and one summary of how many were omitted or repeated.

What nerdr does not take from the worktree

Section titled “What nerdr does not take from the worktree”

nerdr runs Git and gh outside the agent’s sandbox after each turn. An agent could rewrite a worktree’s .git file to point at a repository whose config executes commands through hooks, filters or core.fsmonitor.

To avoid this, nerdr finds the Git directory through the main checkout’s .git/worktrees/*/gitdir. It sets GIT_DIR, GIT_COMMON_DIR and GIT_WORK_TREE explicitly. It ignores worktrees unknown to the main checkout. core.fsmonitor is always disabled. Git hooks run only when nerdr creates a worktree from the base branch, commits or pushes. If an agent in the tab ran in a sandbox, no hook runs when nerdr commits or pushes there: not those inside the worktree (a core.hooksPath such as .husky/_), which the agent can write, and not those in .git/hooks or a folder of your own either. Most of those run what the worktree says (pre-commit its .pre-commit-config.yaml, lefthook its lefthook.yml, a script the repository’s linter or tests), which would be the agent’s code run as you. The commit dialog says so. To have your hooks run, commit and push in a terminal of your own once you have read the change.

Executable Git drivers are another way to run code from a worktree: even protected filter, diff or merge driver definitions can name scripts the agent can edit. Git may run them during status, diff, add, checkout or merge. In repositories with sandbox history, nerdr therefore refuses host Git while executable filter, textconv, external diff or merge drivers are configured. Use Git inside the sandbox, or inspect the work and use your own terminal. Remove driver commands only when the repository no longer needs them.

Git LFS is handled separately so a global git lfs install does not block ordinary repositories. In repositories with sandbox history, nerdr disables the LFS filter commands and requires conversion to succeed: operations that need LFS conversion therefore fail, rather than silently storing unfiltered content or executing a worktree-controlled program. nerdr no longer invokes LFS uploads itself. In these repositories it also refuses pushes when the standard pre-push hook is recognized as using LFS, so it does not publish pointers without their objects. This hook check does not identify every custom LFS integration. Use Git inside the sandbox, or inspect the work and run LFS in your own terminal.

In repositories with sandbox history, nerdr disables host Git transports. Fetch and push are refused, including local remotes, SSH and HTTPS; URL rewrites and custom remote helpers cannot enable another transport. Run those operations inside the sandbox, or inspect the work and use your own terminal. New worktrees that require a fetch can consequently fail too. The private object-verification step has a separate restricted local transfer, with isolated configuration and executable upload hooks disabled. Moving an existing tab imports its Git bundle directly as data; it does not enable a transport or apply remote URL rewrites. Before changing the destination branch, nerdr checks that all objects reachable from the moved commits and snapshots exist. Explicit fetch.fsckObjects settings take precedence over transfer.fsckObjects; when enabled, full object validation also runs and may reject unrelated repository corruption.

Authentication and signing delegates must not run agent-written scripts through protected configuration either. nerdr suppresses executable environment overrides and blocks unsafe configured delegates. Signing programs are unavailable to host Git in these repositories: when signing is required, the commit fails instead of becoming unsigned. A global gpg.program, gpg.ssh.program (including 1Password) or credential helper does not by itself block local status, diff or unsigned commits.

Some executable settings still cause all host Git in the repository to be refused, including core.gitProxy, core.alternateRefsCommand, remote.*.uploadpack, remote.*.receivepack, remote.*.vcs, uploadpack.packObjectsHook and gc.recentObjectsHook. The notice names the setting. Remove it only if it is no longer needed, or run Git inside the sandbox or in your own terminal after inspecting the work. These restrictions do not apply to Git the agent runs inside its own sandbox.

The host executables and nerdr’s own environment must remain trusted. Keep agent-writable directories out of nerdr’s PATH, and do not let the sandbox replace Git, gh, their libraries or other host tools. These Git restrictions do not replace the approval rules for plugins, setup or hooks.

Branch-dependent Git configuration (includeIf.onbranch) is refused too, even when its condition is currently false. An agent can change HEAD to activate filters between the safety check and Git’s own configuration read. Configuration selected by a protected repository path can use includeIf.gitdir instead when branch-dependent settings are unnecessary.

Sandbox history is saved for each repository in the session, including after its tabs and worktrees are removed. Imported sandboxed work is recorded before the destination agent starts, including when that start fails. Older sessions that still contain recorded worktree paths conservatively mark every known repository once when migrated. Sessions saved in the new format do not repeat that migration when their repository history is empty. Older versions could erase those paths when worktrees disappeared. If all that history was already removed, migration cannot detect or recover the earlier sandbox use; the current agent configuration is not proof of where an agent ran in the past. Repository history protects host Git and setup; it does not by itself mark every tab in the repository as having run a sandboxed agent. Hook and move restrictions follow the individual tab and its worktrees.

A repository whose .git/config has extensions.worktreeConfig on, as git sparse-checkout turns it on, has Git also read a config.worktree in each worktree’s own Git directory: .git/worktrees/<name>/config.worktree, or .git/config.worktree for the main checkout. An agent must be able to write .git/worktrees/<name>/ to commit, so it may be able to write that file too, with a diff.external or a filter that Git would run as you the next time nerdr looks at the tab. Checking the file before each call would not stop an agent that rewrites it between the check and Git’s read, and the extension cannot be turned off from outside the repository. So once an agent has run in a sandbox in any worktree of a repository, nerdr does not run Git in that repository’s worktrees or main checkout while the extension is on, and tells you so once, naming any setting in a config.worktree other than sparse-checkout’s own. This extension restriction does not affect repositories where no agent has run in a sandbox. Repositories without the extension are still subject to the Git driver restrictions above.

This is the cost for sparse-checkout users who run agents in a sandbox: in such a repository nerdr shows no changes, diffs, branch or pull request state, cannot commit, push, open a pull request, make a new worktree or close one with its worktree, until you turn the extension off. Run git sparse-checkout disable in each sparse worktree, which makes it a full checkout again, then git config --unset extensions.worktreeConfig in the main checkout. The agents themselves can still use Git in their sandbox.

nerdr records origin’s push destination when it opens a worktree, or on first start for older tabs. Pushes and new pull requests fail if that destination changes, and pull requests go to the recorded repository, which nerdr passes to gh.

The sandbox must not let the agent write to ~/.config/nerdr or ~/.local/state/nerdr (which holds plugins-allowed.json, setup-allowed.json and sessions/): a plugin or setup command written or approved there would run outside the sandbox. See Plugins.

The sandbox must still protect the main checkout’s .git directory. Committing requires some access there, but agents should not be able to write .git/config, .git/hooks, .git/config.worktree, .git/worktrees/*/config.worktree, or files included by Git configuration.

nerdr reads .nerdr.toml from the main checkout. Agents working there can edit it. Its setup runs outside the sandbox, and copy exposes files to the agent. nerdr therefore asks for approval on first use and after every change. Approvals are saved in ~/.local/state/nerdr/setup-allowed.json. Copying does not follow symlinks outside the repository. setup runs only in worktrees on a new branch from the base, never on a branch’s own code: see Getting a new worktree ready.

When a repository has sandbox history and copy contains any patterns, nerdr copies the files but skips automatic setup, even on a verified base commit. Copied local files may contain agent-written code that setup would execute, and approving .nerdr.toml does not approve their contents. This restriction applies even when no pattern currently matches a file. Run setup inside the sandbox, or inspect the copied files and run it in your own terminal.

The base is the commit origin itself names, asked for when the worktree is made, not the copy of origin/main in the main checkout, which an agent that can write .git/refs could move. An agent that commits can also write .git/objects, and so put files of its own under the ids of origin’s commit, or name others with a ref under refs/replace. Where a sandboxed agent has run in the repository, or asked for the tab, nerdr therefore first copies the commit into an object store of its own, which fails when an object is not what its id says, and checks the new worktree’s files out from that copy. Such a tab is not made when the check fails; run git fsck in the repository then. A repository with no origin has no commit to ask for: a tab a sandboxed agent asks for there gets no setup.

nerdr also writes to the data folder (data_dir), where agents store their notes, review results, walkthroughs and handoffs. When it writes to data_dir or reads files from a worktree, nerdr does not follow symbolic links or paths that lead outside the folder, so an agent cannot use a link to make nerdr overwrite or show files elsewhere.

Source navigation skips special files and sources larger than 512 KiB. It also limits directory traversal and the aggregate index, so navigation results may be incomplete in very large projects. The review panel marks incomplete results instead of treating missing references as proof that there are none. Worktree file content and walkthrough anchor checks refuse regular files larger than 16 MiB. Git index snapshots accept regular files of at most 128 MiB and reject named pipes and symbolic links at the index path. Larger indexes cannot be snapshotted or staged through nerdr.