Security & Privacy
How Daintree sandboxes processes, hardens IPC, enforces Trusted Types, scrubs secrets, contains the daintree-file protocol, hardens git, filters environments, handles skip-permissions, and stores your data.
Daintree runs coding agents with real filesystem and network reach, on your machine, inside your repositories. That is the whole point of the habitat, and it is also the reason this page exists. Everything below describes a boundary that is enforced in code, plus the handful of places where the honest answer is that no boundary exists and you are trusting something.
The boundaries
Four areas carry enough detail to have their own page. They fail in unrelated ways, so they are documented apart rather than as one long list:
- Desktop and IPC. Renderer sandboxing, the separation between the UI and the processes with real system access, IPC validation and rate limiting.
- Browser and content. Content
Security Policy, Trusted Types, the embedded browser, permission lockdown, the
daintree-file://protocol, and what happens to an external link. - Git, process and environment. Git command and pathspec hardening, lifecycle script injection, secret scrubbing, environment filtering, and what skip-permissions actually turns off.
- Privacy and data. What is stored and where, what telemetry does and does not send, and macOS folder access.
Plugin trust
Plugins are the one surface where third-party code runs inside Daintree, and the trust model is deliberately explicit about what it does and does not promise.
What it does: user-installed plugins run out of process, in a utility worker rather than in the renderer or the main process. Capabilities are declared in the manifest and carry load-bearing policy effects rather than being documentation: host.process.spawn is hard-gated on shell:exec and rejects without it, filesystem and git host APIs are realpath-contained to the paths the manifest declares, and holding any high-risk capability raises every action the plugin registers to require confirmation. Consent for a plugin's MCP tools is gathered per tool at first use and keyed to the plugin id, and it is revoked on update so a new version re-prompts rather than inheriting the old version's approvals. A remote blocklist can disable a known-bad plugin at startup, and it fails open so a network problem never bricks your install.
Plugins are not sandboxed. A plugin's entry point is un-sandboxed Node inside the worker and can call node:fs, child_process or node:net directly, whatever its manifest declares. There is also no signing and no publisher identity: a SHA-256 archive hash gives you integrity, not authenticity, so it proves the file was not altered in transit and proves nothing about who wrote it. Install plugins the way you would install anything else that runs as your user.
The full capability list, the compound-capability lattice, the scope semantics and the install pipeline are documented in Plugin trust and capabilities.
MCP server
Daintree exposes an opt-in local MCP server for external tools, and it also provisions per-session MCP wiring for the agents it launches itself. The two have separate trust boundaries.
Network and auth
The local server is disabled by default and binds to 127.0.0.1 only, so it is never reachable from the network. On first start it generates a bearer key in the format daintree_<32-hex>, compared timing-safely on every request, and the transport additionally requires loopback Host and Origin headers. Rotating the key from Settings > MCP server invalidates every external client holding the old one.
Four properties are worth repeating here because they are access-control boundaries rather than conveniences: destructive calls confirm in Daintree, never through client-declared elicitation; project.getSettings does not return decrypted secrets over MCP; introspection is scoped to the calling session's own tier; and the external fullToolSurface opt-in was removed outright: it used to expose 335 of 426 actions to any API-key caller, and nothing replaces it. The full model is on the MCP server page.
Cross-agent token isolation
When Daintree launches an agent that needs MCP wiring, it mints a per-session token scoped to a single agent type. A token issued for a Claude session is not valid for a Codex, Gemini or Copilot spawn, and the reverse holds. Token reuse across agent types is the pattern this guards against.
Enforcement is at the spawn-argument builder: each agent's builder returns nothing when handed a token from a different agent type, and the caller throws rather than falling back. This is a hard spawn failure (the process never starts), not a silent downgrade.
Transport is agent-shaped and never a command-line argument. Codex reads its token from the PTY environment; Gemini and Copilot pick theirs up from a session-scoped config file. Because the token never appears in argv, it cannot leak through ps output, shell history or a stray log line.
App-level guards
Two smaller behaviors that exist to prevent accidental loss rather than to stop an attacker:
- Single instance. Only one copy of Daintree runs at a time; opening a second focuses the existing window. This avoids conflicts over file locks and terminal processes.
- Window close protection. Command-W Control-W, or Control-F4 Control-W closes the focused panel, not the application window, so a reflexive keystroke does not take a set of running agent sessions with it.