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.
The short version
Section titled “The short version”- 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.tomlsetup 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.worktreeand.git/worktrees/*/config.worktree. With nerdr’s core running on its own, as it does unlessNERDR_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 whenNERDR_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.