Control

Security

What leaves your machine, what an agent can reach, which credentials exist, and where the boundaries end.

On this page

What leaves the machine#

From a machine of your own, the results of tool calls and what describes the project. Exeora does not upload, clone or index a repository that lives on your machines. A file's contents cross the network only when an agent reads that file, and only as far as the client that asked.

What describes the project is sent when it is registered: its name, the path of its directory on that machine, and, for the top of a checkout, the address of its remote and its default branch. The address is what lets another location clone the same repository. It is stored in its https form, without any user name or token that was written into it.

Exeora Cloud is the exception, and it is one you choose: a project put there is cloned onto instances Exeora runs.

The audit log records what ran and how it ended, never arguments or output. It knows that run_command was called and that it failed; it does not know the command.

Nothing dials in#

The CLI opens the connection outward and holds it. There is no listening socket on your machine, no port to forward, and no tunnel. Revoking a machine from the dashboard closes its connection immediately rather than at the end of whatever it was doing.

Projects are isolated at the token#

Each project is a separate OAuth resource, so a token minted for one is not accepted at another. Ownership is checked against the database as well, so there are two independent checks rather than one, and an agent connected to one project's URL has no way to name another: the project comes from the path, never from anything the client sends.

The account URL, https://exeora.dev/mcp, is a separate OAuth resource of its own, and its token is refused at every project URL just as a project's token is refused there. What it does not do is decide the project from the path, so the boundary moves to the consent screen: you tick which projects it may reach, and it reaches those and nothing else. Ticking one gives the same reach as a project URL. Which of the ticked projects a call lands in is state the agent can change, and that is the whole of the difference.

A client cannot widen that by asking. If a request names both URLs at once, the project URL wins and you are shown the screen that names the project, so the consent you give and the token you hand out cannot describe different things.

A grant is for a project, in every location#

What a client is given is a project, and a project can live in several locations. The grant reaches all of them. A client authorized for a project can work in its project root, in every workspace of every location, and can create a workspace in any of those locations, which on Exeora Cloud starts an instance. There is no grant for one machine only.

A grant outlasts the locations. A project that has a repository and loses its last location lives nowhere and keeps its clients and its policy. Its clients reach it again, under the same policy, as soon as it is given a place. To take a project away from its clients for good, remove the project or revoke the clients.

The project's policy is one policy and applies wherever the call lands. An exeora.toml belongs to a checkout, so it narrows what may happen in that copy and says nothing about the copies in other locations.

Every path is confined#

Paths are resolved and checked against the directory the call lands in before anything touches the disk: the project root in the default location, or the working copy of the workspace the call names. Absolute paths, and anything climbing out with .., are refused rather than resolved. Symlinks are resolved before the check, so a link pointing outside the project does not become a way through it.

Policy is checked twice#

The gateway checks it because it is the only side holding the account's setting, and because an older CLI would ignore a field it does not know. The executor checks it because it is the authority on the machine, and the only side that can read a project's own exeora.toml. Both run the same code, which is the only way "checked twice" is worth more than "checked once".

Nothing is queued#

With no executor connected, a call fails at once with LOCAL_EXECUTOR_OFFLINE, and every call carries an absolute deadline the executor re-checks on arrival. A command landing hours after it was asked for, when a laptop wakes up, is the hazard this refuses to accept. An instance on Exeora Cloud that was asleep is woken before the call is sent. One that does not come up in time answers EXECUTOR_WAKING, and the call is dropped rather than held for it. So is the call that finds Exeora Cloud with no instance for the project root: it asks for the instance and is not kept waiting for it.

The same rule runs the other way. When the client hangs up or the connection drops, the CLI kills the command's whole process group, and processes started with start_command die with the connection. Nothing keeps working once there is nobody left to read the answer.

Which credentials exist, and where each one lives#

CredentialWhere it livesWhat it opens
Your CLI sessionThe keychain of your machine, or a 0600 file where there is noneYour account, as far as the CLI acts on it
The git credentials of your machineOn that machine, wherever git keeps them. Exeora never reads or copies them.Whatever you gave git
Machine tokenOn the disk of one instance, in a file only the CLI's user can read. The gateway keeps only its SHA-256.That instance's relay socket, and the git credential and the gh token of the one project it was made for. Nothing else on the API, and nothing once the instance is destroyed.
GitHub installation tokenNowhere. It is made when git asks, handed to git by the credential helper, and stored by neither.One repository, for one hour, with no more than you can do on GitHub yourself
GitHub user tokenIn the gateway's database, encrypted with AES-256-GCM under a key held as a secret of the gateway, together with the token that renews it. An instance of a project connected to GitHub is handed the token when gh runs there, and never the token that renews it. On the instance it is kept in memory, under /dev/shm, for 30 minutes at most, and in the environment of the gh process. It is not written to the instance's disk. A machine of yours is never handed it.Asking GitHub, as you, which repositories you can reach. On an instance, what gh does as you: every repository the Exeora GitHub App is installed on that you can access, not only the project's, until the token expires, eight hours after GitHub issued it.
Repository token for another Git serverIn the gateway's database, encrypted the same way, and on each instance of that project, in a file only the CLI's user can read. Never on a machine of yours. Deleted with the project.What the token was issued for on that server

A project connected to GitHub has no long-lived repository token on any machine. That does not put the repository out of an agent's reach: a command an agent runs can run git, and git can ask for a token, on an instance or on a machine of yours alike. What it removes is a secret that sits on a disk for months.

The token gh runs with on an instance is a different matter, because it is yours and not the repository's. It has one caller: the instance, with its machine token, for the one project it was made for. Your own CLI and the dashboard are not answered. See gh on Exeora Cloud.

Where the boundaries end#

Worth being plain about, because a boundary you misjudge is worse than one you know:

  • A project is a directory, not an operating-system sandbox. A command running inside it runs with the connected machine account's environment, network and process permissions. It can reach anything that account can reach. The confinement is on the paths the tools take, not on what a program does once it starts.
  • An allow list with shell = true is a suggestion. Turn it on only for a project you would have left open regardless.
  • A new project allows everything. Nothing is restricted until you restrict it.
  • An instance is a machine Exeora runs. With Exeora Cloud the repository is cloned onto a microVM operated by Exeora on Fly.io, so the code is on infrastructure that is not yours. A repository token you give is stored encrypted at the gateway and, inside the instance, is readable by the user the agent's commands run as. Commands there are capped in memory, not in what they may reach on the network.
  • gh on an instance acts as you, beyond the project. The token it is handed reaches every repository the Exeora GitHub App is installed on that you can access, not only the project's. Anything that runs in the instance as its user can use gh with it, and that includes the commands an agent runs and the project's scripts. The token is kept in memory and expires on its own, which limits how long a copy of it is worth, not what it reaches while it lasts. To narrow it, give the app fewer repositories on GitHub, and use the project's policy to decide which commands an agent may run.
  • Scripts run in an instance, outside the policy. The install and resume scripts of a project are not tool calls. The policy and its approvals do not apply to them, and the audit log does not record them. A script kept in the repository is written by whoever can push to the branch, so they run code in the instance as its user, with gh signed in as you, even in a project set to read only. The switch Run the scripts found in the repository, in the card Cloud scripts on the project's page, turns those files off. Turn it off for a repository where people you would not hand that to can push.
  • A grant does not stop at a machine. Authorizing a client for a project that lives on your laptop and on Exeora Cloud authorizes it in both, and in any location you add later.
  • Confirmation only reaches a person who is there. A machine with no terminal and nobody watching the dashboard will refuse the call after ninety seconds, which is safe but is not the same as being asked.

Open source#

The full source is on GitHub under the AGPL-3.0, so you can audit every line. How to run your own gateway is in self-hosting.

Reporting something#

Mail hello@exeora.dev, the same address as everything else. Please do not open it anywhere public first. See also SECURITY.md in the repository.

Type to search every page.