in short
- nerd sends no telemetry.
- The server runs in a macOS Seatbelt sandbox that denies the network.
nerd --sandbox-selftestshows that the profile refuses it. - Each way out starts from something you do or approved, and writes a line to
daemon.log. The list is below. - Plugins each run in a sandbox and write or reach the network only as their manifest names. Like any program you run, a plugin can read most of your files and the command lines and environments of your other programs.
- Programs in your panes, agents included, run as you unless their isolation says otherwise. A pane left at Everything and Open has your full access to files and the network; Isolation… narrows that with a sandbox of its own, a proxy for Only these providers, or another account.
nerd --sandbox-selftestprivacy and security
- The core. nerd sends no telemetry, and checks for updates only if you allowed it, and logs every check. The server locks itself into a macOS Seatbelt sandbox that denies it the network:
nerd --sandbox-selftestshows the profile refusing it, andnerd --listshows whether each server has it. nerd's only network code is the proxy it runs for a plugin with network, or for a pane set to Only these providers: outside the server's sandbox, in a sandbox of its own, it connects only to the hosts and ports that the plugin's manifest or the chosen agents' manifests name. Everything that can leave nerd is in What leaves nerd; SECURITY.md says what each protection covers and what it does not. - Terminal output is contained. Escape sequences printed by programs are interpreted inside nerd and never reach your terminal.
- What is stored. Everything is under
~/Library/Application Support/nerd/, private to your user: - the layout, working directories, commands, and each agent's session id with the agent it belongs to;
- the resume command of each agent you confirmed when its hook was installed;
- whether nerd may check for a new release once a day, and when it last did (
update-check,update-checked), the release you skipped (release-skipped), each server's last answer and the release its window's install click left (release.json,release-install), and while a check or an install runs, its download folder (release-<pid>, removed afterwards); - pane text, written when the server stops and deleted when it starts again, kept out of Time Machine and Spotlight. It holds whatever your panes printed. Set
restore_screenstofalseto turn it off.
Outside that folder, a terminal preset writes only on your word (Settings… → Terminal preset…, nerd --preset): it adds one marked block or line to that terminal's config, writes iTerm2's profile named nerd, and sets iTerm2's margins and default profile. It shows every change before making it, and undo takes them back.
- Dependencies are permissively licensed and checked with
cargo deny.
How it works
nerd is a client and a server. nerd starts a server in the background if needed and connects to it. The server owns the panes, runs their programs and draws the screen; the client passes your keys and mouse to it and shows what it draws. Closing the window only disconnects the client.
Each pane is a pseudo-terminal read by a terminal emulator library. nerd reads each pane's process tree once a second to see which agent runs there; the hook reports a supported agent's state directly.
What leaves nerd
Each way out starts from something you do or approved, and writes a line to daemon.log.
| Way out | When | Where to | What it carries | Logged as |
|---|---|---|---|---|
git | You add a plugin, update it or check for updates, or refresh the catalog (Plugins…, nerd --plugin-add, --plugin-update, --plugin-catalog). Never in the background. | The public GitHub address shown in the window. | git's own requests for that repository, from a git run with none of your git config, no home folder, no credentials and no proxy settings. | git clone … |
curl to GitHub | You choose Check for a new release… or run nerd --check-update; or once a day, only after you answered yes to Check for new releases (asked once on each Mac, changed in Settings…). A release is installed only on your click in nerd's own window on that Mac, or your y in a terminal outside nerd's panes. A window inside a pane cannot install one, though a program running as you could ask for the install itself: it would get only the newest release, checked against its checksum: nothing it could not already do as you. | api.github.com for nerdterm/nerd's latest release; to install, its archive and .sha256 from github.com/nerdterm/nerd/releases/download/…, which redirects to release-assets.githubusercontent.com. /usr/bin/curl with no .curlrc, https only, redirects included, and no proxy. | The request: your IP address and curl's version; nothing about you or your panes. The archive is checked against the .sha256 published with the same release, which proves it arrived intact, not who made it; signed releases are tracked separately. | release check: …; release … installed … |
ssh | You connect to another computer (Connect to another computer…, nerd --remote). | The host you named in remotes, reached through your own ~/.ssh/config, keys and agent, so a ProxyJump there decides the path. | nerd's protocol to the nerd on that machine, including what you type and what its panes print. | remote: ssh … |
| A plugin with network | A plugin you installed with a network permission runs; approving it at install is what lets it reach the network later. | "localhost": services on this Mac only, and whatever they pass on. Hosts and ports: only those hosts on those ports, through a proxy nerd runs for that plugin alone in a sandbox of its own; the plugin itself reaches only that proxy's port on this Mac's addresses and looks up no name. A name is reached at whatever public address its owner gives it, never this Mac or a network it is on; an IP address in the manifest is reached as written, and one on your local network or this Mac is named so when you are asked. The plugin must honour HTTPS_PROXY; plain http:// does not work. | Anything the plugin can read, the command line and environment of your other programs included (macOS lets any of your processes read those). | plugin … started: network …; each connection and refusal as plugin … proxy: … |
| A pane set to Only these providers | You choose Isolation… → Network: Only these providers for a pane, and its program starts. | Only the chosen agents' or chosen providers' hosts, on the ports their manifests name, through a proxy nerd runs for that pane alone in a sandbox of its own; the pane's programs reach only that proxy's port on this Mac and look up no names. The agent must honour HTTPS_PROXY; one that does not gets no network. | Anything the pane's programs send. | pane N proxy: … |
| A link or a file | You open a link in a pane, or a file with Open in its app. | http(s) in your browser, mailto: in your mail app, a file in its app. Files that hold an address of their own (.webloc, .inetloc, .url, .fileloc, .ftploc, .afploc, .vncloc, .mailloc) are not opened; a page opened in the browser may still load addresses of its own, and a file opened in its app runs as that app does (a .command or an app runs code as you). | The address. | link opened …, with the host for http(s) and only the kind otherwise |
| The clipboard | You copy. | The macOS clipboard, and through OSC 52 the terminal you run nerd in, which may be on another machine over ssh. | The text you copied. | copied N characters |
| Claude Code's inbox | A message from another pane, or from you, is delivered to a Claude Code pane whose session reported its inbox socket. | That session's own socket on this Mac, used only after nerd has checked that a process of that pane listens on it. | The message, its first line naming the sender: [message from <name> via nerd, id <id>; …]. | message … via inbox: delivered |
Outside the table, and said here so nothing is implied:
- Panes. The programs in your panes, agents included, run as you unless their isolation says otherwise (see Isolation), outside the server's sandbox and outside this table: a shell you open, a log viewer nerd opens from a menu (
lesson its own log), or an agent's resume command you confirmed once, which a restored pane types for you. nerd also hands a message to an agent in a pane: at the end of its turn as its hook's own answer (block,denyorfollowup_message), through the pi or OpenCode extension, or, for an idle agent with no better path, typed in with nerd's frame line first and a carriage return after it; never into a pane waiting for your answer. - The sandbox off. With
sandboxset tofalse, or when the sandbox fails to start (the server keeps running;nerd --listand every window say so), the server is not kept off the network until the sandbox is back.
Links are found in the text a pane prints; OSC 8 hyperlinks are not read. Anything else that leaves nerd is a bug: report it as a vulnerability (see SECURITY.md).
What a program inside a pane can do
An agent in a pane runs shell commands as you, so it could type nerd --stop or nerd --update. The server refuses a connection from a process that descends from its own panes when it asks to stop, update, disconnect everyone or take the screen; the command prints refused: … from inside a pane, exits non-zero, and the server logs it. nerd --stop, --update and --disconnect without a name, run inside any nerd pane, refuse too: name the server, or set NERD_SOCKET or NERD_STATE_DIR to target a test server. Programs in panes may stop or update nerd, in Settings… → Messages, turns both refusals off. Plugins follow the same rule: from a pane, nerd --plugin-add only fetches, and the question comes up in nerd's own window, where only a click installs; --yes, --plugin-on and --plugin-remove are refused.
The way in is the message socket. Panes of admitted accounts reach it through /tmp/nerd-<uid>/, a folder only nerd's owner can write, checked before use, and reach nothing else of nerd. No path in nerd lets an agent change names, routes, isolation or that switch while nerd runs; from an isolated pane, no file can either.
This guards against accidents, not against code that means it: a program that detaches from the pane (a double fork) is no longer its descendant, and any program running as you can stop the server with a signal anyway.
report a vulnerability
Supported versions
Only the latest release receives security fixes.
Reporting a vulnerability
Report it privately through GitHub: Security → Report a vulnerability on this repository. Do not open a public issue, pull request or discussion.
Include what is affected, the steps to reproduce, and the impact you expect. You will get an answer when the report has been read; there is no guaranteed response time. Please allow a fix to be released before you disclose the issue publicly.
Scope
In scope:
- escape sequences or other pane output that reach the user's terminal or trigger actions in nerd;
- ways around the server's network sandbox;
- anything that leaves nerd other than what "What leaves nerd" in docs/privacy-and-security.md lists, or a plugin reaching more of the network than its permission says;
- a pane reaching more than its isolation allows (Files: This folder only or Another account; Network: Only these providers or None), or an agent changing names, routes, isolation or accounts while nerd runs;
- files written by nerd with wrong permissions or readable by other users;
- the installer, the release archive and its checksum.
Out of scope:
- what programs in a pane left at Everything and Open do, which nerd deliberately does not restrict;
- attacks that require local access as the same user;
- the binary not being notarized by Apple, which is documented.
What the protections cover
- The server's sandbox denies the network, and nothing else. The Seatbelt profile starts from
(allow default): the file system and Mach services stay open to the server. Unix-domain sockets stay allowed, except mDNSResponder's, which is denied by name so that name lookups fail; the server can reach any other local socket its user can. - A plugin reaches only its network tier. Its own Seatbelt profile allows nothing, or
localhostonly. With hosts and ports it reaches only the port of a proxy nerd runs for it alone, and the proxy connects only to those hosts on those ports: a name only at its public internet addresses, never this Mac or a network it is on; an IP address as the manifest writes it. A plugin never opens a terminal device, and does not read the credential folders and~/Librarylisted in docs/plugins.md; it can read the command line and environment of the user's other programs, which macOS does not let a sandbox hide. - The broker runs what the server asks. It starts panes and plugins outside the sandbox from the server's requests, so the server's sandbox is a guarantee about nerd's own code (no network, no telemetry), not a wall around a compromised server.
- A nerd reached over ssh is trusted like a local one. What the remote server sends reaches your terminal as it would from a local server.
- Panes are outside it. A helper started before the sandbox launches every pane. A pane left at Everything and Open keeps your full access to files and the network; its isolation (Isolation…) narrows that with a Seatbelt profile of its own, a proxy for Only these providers, or another account. An agent's own Seatbelt sandbox cannot run inside an isolated pane.
openis outside it. Opening a file or link "the way the system would", and opening a new terminal window, go through LaunchServices, which starts those programs outside the sandbox.- A sandbox that fails to start does not stop the server. The failure is written to
daemon.log, shown bynerd --list, and shown in the window of every client that attaches. - The
.sha256proves integrity, not origin. It comes from the same release as the archive, so it catches a damaged or truncated download, not a release replaced by someone who can replace both. The installer refuses an archive without one unlessNERD_INSTALL_UNVERIFIED=1is set. - Both ends check the other's user on every connection. The server talks only to processes of its own user, and a client refuses a server socket another user created first, for example at a
NERD_SOCKETin a shared directory, before it sends anything. Such a socket still keeps nerd from starting there: keepNERD_SOCKETin a directory only you can write.