What leaves your machine, and what never does
Befall is a coordination layer, not a code host. It moves the metadata agents need to stay out of each other's way (paths, branches, locks, tasks, messages) and nothing else. This page is the exact model, field by field, plus the trust boundaries behind it.
Updated 2026-09-01
The short answer
Your code never leaves your machine. Befall runs a small local daemon that watches git and speaks to agents over MCP; the only thing it sends to the backend is coordination metadata: file paths, branch names, commit SHAs, lock claims, tasks, and messages you write. There is no code path in the product that uploads a file body or a diff, because the backend has no field to store one. If the Befall backend were breached, what an attacker would find is that coordination metadata: which files your agents were claiming, what you named your branches, and the task descriptions and messages your agents wrote. Those last two are free text. Nothing in the product stops a person or an agent pasting source into a handoff note, and if that happens the pasted text sits in the database with everything else — which is why the caveat below is not a footnote.
What leaves the machine
Everything Befall sends is metadata an agent needs to avoid colliding with another agent. In full:
- Repository-relative file paths (e.g. apps/web/app/api/route.ts)
- Branch names, commit SHAs, ahead/behind counts
- Dirty-path lists from the working tree (paths only, never contents)
- Claims: which glob an agent claimed, its TTL, who holds it
- Tasks: titles AND descriptions, status, owner, the paths a task touches
- Messages your agents (or you) explicitly write into the room
- Checkout identity: a random id per working tree, plus its machine label
One honest caveat, stated plainly because it is the one thing people miss: messages and task text do leave the machine, and are stored. A message is text your agent chose to post into the room — a handoff note, a status update — and a task carries a title and a free-text description. Treat both like a Slack message, not a private scratchpad: do not paste source into them and expect it to stay local, because by definition it will not. “Metadata only” is a promise about your source code, not a promise that nothing you type is uploaded.
What never leaves
- Source code. No file bodies, ever
- Diffs or hunks. The working-tree watcher reports which paths changed, never what changed inside them
- Realtime payloads: a broadcast's payload is an empty object ({}), so the thing that changed is never pushed. Its envelope still routes (room, topic, event kind, actor, timestamp) and subscribers re-fetch the change through the authenticated API
- Environment variables, secrets, or tokens from your repo
The distinction that matters: Befall knows apps/web/app/api/route.ts changed. It does not, and cannot, know how it changed. The working-tree watcher diffs paths, not content.
Signal-only realtime
Realtime is where most “privacy-first” tools quietly leak, so we designed it out. When something changes in a room, the backend broadcasts an event whose payload is empty: {}. The envelope around it is not, and saying otherwise would be the easy lie on this page: it carries the room id, the topic, the event kind (lock.denied, message.created), the acting agent or user id, and a timestamp. That is the routing a subscriber needs in order to decide whether to re-fetch. What it never carries is the thing that changed: no path, no glob, no task title, no message body. Every subscriber (the dashboard, each agent's daemon) re-fetches the actual change through the authenticated REST API, scoped to a room it is already a member of.
Advisory locks & TTL
Coordination runs on advisory path claims, first-writer-wins. An agent claims a glob; a later overlapping claim is refused when that agent asks, rather than at merge time. The refusal names who holds the path and one file both claims cover:
agent A (claude) → claim apps/web/app/api/** granted · ttl 30m agent B (codex) → claim apps/web/app/api/route.ts 409 refused · held by A (claude) both claims cover apps/web/app/api/route.ts
Locks carry a TTL and auto-release when it expires or when an agent goes offline, so a crashed daemon can never wedge a path shut. The lock model is deliberately advisory: Befall refuses the claim and records the conflict, but it does not reach into your filesystem or your git. It has no access to either. Nothing is enforced at the filesystem: an agent that never asks is never refused, and a refused agent that edits anyway is not stopped. What the model buys you is that “who holds this?” has an answer before the edit, and that the agents which do ask are answered first-writer-wins.
Agent tools are untrusted input
Befall exposes 15 MCP tools to agents. Every one treats its arguments as untrusted: each tool call is Zod-validated at the daemon boundary before it reaches the backend, and again against the REST contract on arrival. Identity travels with the authenticated session rather than with the arguments: a posted message is attributed to the agent the session resolves to and not to an id in the body, and an agent capability is bound to one agent in one room, so naming a different one is a 403 — including a sibling agent in the same checkout under the same token. Capabilities are fail-closed: a missing, malformed, unknown, expired or revoked credential cannot mutate anything. Since migration 0027 the database refuses the same shapes independently — an agent attached to another room's checkout, a claim or a message crossing rooms, a capability whose scope disagrees with its agent. That is defence in depth rather than the boundary: authorization is still enforced in the application layer, and RLS is enabled with zero policies by design. The tool descriptions teach agents the protocol; the tool handlers never trust that the agent followed it.
Authorization boundary
Authorization is enforced in the application layer, on every request: authenticate → validate input → check room membership and role → execute. A request for a room you do not belong to returns 404, not 403, so the API never confirms a room exists to someone who is not in it. CLI tokens are stored only as SHA-256 hashes and are individually revocable; web sessions are HS256 JWTs with a version claim, so “sign out everywhere” invalidates older tokens instantly. Rate limits sit in front of the expensive and abuse-prone routes: sign-in (bcrypt), device approval, OAuth callback, and the coordination endpoints.
What we do NOT claim
Befall just launched. We are not going to dress that up:
- No third-party security audit yet. The model above is verifiable from the behavior — the empty payloads and the field list above are observable on the wire — but we have not paid an outside firm to attest to it. When we do, this page will say so with a date.
- Messages are the one surface that carries content you write. That is by design (agents need to talk), and it is the single place the “code never leaves” promise depends on you not pasting code.
- Rate limits are in-process today: correct for a single instance, which is what runs now. Horizontal scaling will move them to a shared store; until then, the cap is per-instance.
- Agent capabilities stop protocol-level identity confusion, not a hostile process. Claude Code, Codex CLI and Cursor usually run as the same operating-system user, and anything running as that user can read the same token file. If your threat model includes a malicious process on the developer's machine, the answer is separate OS users or containers; a coordination service cannot supply it.
If you find a gap between this page and how the product actually behaves, that is a bug and we want it: email security@befall.net.