Skip to content

Security and privacy

Your terminals run on your machines, and only you and the people you share with can reach them. This page explains how. To report a vulnerability, see SECURITY.md.

The daemon keeps your data private on the machine and lets in only you.

  • The state directory (layout, output, keys) is private to you: the folder is 0700 and its files 0600.
  • Local access needs the daemon’s local token. The CLI reaches the daemon through a socket in your private state directory, and a browser or program on the TCP port must show the token, because loopback is shared by every account on the machine (Who gets in).

The daemon asks Tailscale who each caller is, and lets in only the login that owns the machine (or --owner). Tagged nodes, Funnel and the internet never get in. A read-only link to one pane shows that pane to anyone on the tailnet who has the link and can reach the machine, nothing else, and expires.

Arugula control connects devices to machines without being able to read or change what passes between them.

  • Every connection between a device and a machine is end-to-end encrypted, including the ones control relays, and control never holds a private key.
  • A device you already have approves every new device and machine, and your devices and machines check those approvals themselves. When a machine joins, you check that it shows your account’s fingerprint.
  • Team membership is signed on members’ devices, so control can’t add anyone to a team or move a machine by itself.
  • A session’s read-only link carries its key only in the link itself, so control never sees it. Anyone with the link watches the session from then on, for an hour, until the owner removes it.
  • What you trust it with: the web client. Control serves the page your browsers, your phone and the signed-in desktop app run; a control that served a modified page could read what that page shows. The machine’s own page, arugulad and the CLI don’t come from control.

The design, and exactly what holds if control itself turns hostile: control-e2e.md. What the hosted control keeps: its privacy notice.

Blocks that show other people’s code are kept away from Arugula itself.

  • Browser blocks on ports are each served on a web origin of their own, so a page running there can’t reach Arugula or another block. The daemon’s proxy rewrites Host and Origin to localhost:{port}, so dev servers need no config. Since that switches off a dev server’s own checks, the proxy enforces its own: only the block’s own origin, no cross-site requests except page loads, framed only by Arugula, and never a header that says who you are (Blocks).
  • VS Code in an editor block has no login of its own: it listens on a private socket instead of a port, so only the daemon can reach it, after its normal checks. Only the machine’s owner can open one.
  • Files: the read-only file API refuses /proc, /sys, /dev, the daemon’s state directory and the secrets it knows of, and resolves symlinks before it reads.

Agents act through Arugula, but people decide what leaves your machine.

  • Agents draft, people send. An agent’s comment, review, merge or new issue on a forge, and an invite to a session, wait as cards a person sends (Pull requests and issues, Sharing).
  • Agent blocks’ MCP tokens reach only their own tab, and end with the block.
  • An agent working on an issue reads the issue’s text as a description, not as instructions, and starts with nothing allowed ahead of time.
  • Forge tokens (gh, glab, tea) are read from your own logins and kept in memory only, never in a block’s config or log.

A machine that pushes its history to another (--sync) has it kept encrypted at rest there (AES-256-GCM, with a key per file from a key ring only the receiving machine holds). arugula synced rotate-key re-encrypts everything under a new key. File contents are encrypted; file names and sizes are not.

The daemon, the CLI and the desktop app send no telemetry unless you say yes (and editor blocks start VS Code with its telemetry off). They connect out only for these:

  • Usage and crash reports, only after a yes to the question each machine asks once: about once a day, the daemon sends Arugula control a random install id, its version and platform, counts of what was used (terminals, agents and blocks by kind, views opened in the web client, Labs flags on), and where Arugula’s own code crashed, never the crash’s message. Every field is a number or one of a fixed list of values. arugula telemetry status shows the next report; arugula telemetry off, DO_NOT_TRACK=1 or ARUGULA_TELEMETRY=off stops it, and it never runs in CI. The privacy notice says where it goes.

  • The update check asks GitHub where the latest release is, at most twice a day, with nothing about you or the machine in it. --no-update-check turns it off.

  • The desktop app checks the app’s release for updates.

  • What you turn on: joining Arugula control, a forge block reading GitHub, GitLab or Forgejo, push notifications (through your browser’s push service), and downloads you ask for (code-server for an editor block, an agent’s adapter from npm).