Run
Exeora Cloud
A location Exeora runs for you, so an agent can work on a project with no machine of yours switched on.
On this page
Exeora Cloud is one more location a project can live in. Your machines hold a project as a checkout with the CLI beside it. Cloud holds it as instances: machines Exeora runs, each with a clone of the repository and the CLI connected. Clients connect the same way, the policy works the same way, Source Control and the terminal open the same way. The one difference you notice is that an instance sleeps.
Cloud is behind a flag while there is no billing: administrators have it, and they can turn it on for an account from that account's page in the administration panel. Everyone else sees what it is and who to ask.
Putting a project on Cloud#
A project needs a repository with an https address to live on Cloud, because an instance starts from a clone. There are three ways to put it there:
- Add the project there. In the dashboard, Add project under Projects, pick the repository and choose Exeora Cloud as the place it lives first. A repository that is already a project of yours is not made again: it gains Cloud as one more location.
- Add the location to a project you have. Add location on the project's page, or
exeora project locations add. This costs nothing and starts nothing: it records where to clone from and with what. - Ask for a workspace there. Creating a workspace on Cloud puts the project there on the way, from the dashboard, from the CLI with
--on cloud, or from an agent withcreate_workspaceandwhereset tocloud.
One instance per workspace#
Every workspace on Cloud is an instance of its own, with its own clone on its branch. Instances never share a disk, so a build on one branch cannot starve another, and removing a workspace is removing exactly one instance.
An instance for the project root exists only when the project was created on Cloud or Cloud is its default location, because those are the only cases where a call that names no workspace lands there. A project that has Cloud as a second location holds only workspaces there. Making Cloud the default, with Make default on the project's page or exeora project default, is what asks for the instance of the project root, checked out on the default branch.
A workspace branch that exists on the remote is checked out as it is. One that does not is created from the base you name, or from the default branch, and stays local until you push it. list_workspaces reports the status of each instance: creating, ready, error with why, or destroying. The dashboard shows the same, with the step an instance that is being created has reached.
What an instance comes with#
An instance starts from an image that has Node.js, Python, Go, Ruby, Rust, Java, Bun, Git and curl. While it is set up, in the step Installing tools, Exeora adds the tools below. Each is looked for before it is installed, so an instance that already has one keeps its own, in the version it came with.
| Tool | Command | Version installed | Installed from |
|---|---|---|---|
| GitHub CLI | gh | 2.101.0 | Its release, checked against a SHA-256 |
| uv | uv | 0.12.19 | Its release, checked against a SHA-256 |
| jq | jq | 1.8.2 | Its release, checked against a SHA-256 |
| ripgrep | rg | 15.2.0 | Its release, checked against a SHA-256 |
| yq | yq | 4.53.6 | Its release, checked against a SHA-256 |
| Git LFS | git-lfs | 3.8.0 | Its release, checked against a SHA-256 |
| pnpm | pnpm | 12.6.0 | npm |
| Yarn | yarn | 1.22.22 | npm |
| unzip | unzip | The system's | apt |
| zip | zip | The system's | apt |
| make | make | The system's | apt |
| A C toolchain | cc | The system's | apt, as build-essential |
| tmux | tmux | The system's | apt |
Every tool is attempted every time, and one that fails does not stop the others. Only gh blocks. When it cannot be installed the instance is failed, with the reason, and Retry sets it up again. Any other tool that could not be installed leaves the instance ready: its row says how many are missing, and About this instance lists every tool as already there, installed, failed or skipped, with the reason beside the last two.
A tool is skipped when the instance has nothing to install it with: no npm for pnpm and Yarn, or no apt-get or no root without a password for the five that come from apt. Install it from the install script in another way.
Docker and database servers are not installed, and neither is anything only one repository needs. The install script is the place for those.
When an instance fails#
An instance that could not be set up says why and what to do about it, in a sentence, with what the instance actually said folded behind it. The repository refused access, no repository was found at the address, the branch does not exist, Cloud could not start the instance, gh could not be installed, or setting it up took too long. Retry on the instance sets it up again, from the project's page or from Machines under the Exeora Cloud tab. When the cause is a token, set a new one first: a retry uses it.
A script that fails is not one of these. It leaves the instance ready, with a warning.
Sleeping and waking#
An instance sleeps a minute or so after it stops working. Its disk keeps everything: the checkout, uncommitted changes, whatever a terminal left behind. The gateway wakes it before the next call it routes there, which costs the first call after a pause about a second, and the CLI keeps it awake as long as a call is in flight, a command started with start_command is running, or a terminal is open. Nothing keeps an instance awake for being looked at, except an open Source Control or terminal tab, which polls it.
A cold start, after a longer sleep, restarts the CLI rather than resuming it. Background processes and terminal sessions are gone then, the way they are after a laptop reboots; files are not. A call that arrives while an instance is still coming up is answered with EXECUTOR_WAKING, and is worth sending again.
An instance that is asleep runs nothing and still takes one place of the plan, because its disk is kept. Destroying the instance frees that place.
Scripts#
A project can run two scripts inside its instances. They are the place for what the repository needs beyond the tools above: its dependencies, a database, a service that has to be running.
| Script | When it runs | Limit |
|---|---|---|
| Cloud Install script | Once when an instance is set up, after the clone and before the instance is shown as ready. Again when the script changes, and when somebody presses Run again. | 20 minutes |
| Cloud Resume script | Every time an instance resumes: after a short sleep that froze its processes, and after a longer one that ended them. On a new instance it runs once the install script has ended. | 2 minutes |
A script that reaches its limit is stopped and reported as having run out of time. One resume can, rarely, run the resume script more than once, so write it to be safe to run twice.
Where they are written#
Each script can be written in two places. On the project's page, the card Cloud scripts has a field for each. In the repository they are the files .exeora/cloud_install.sh and .exeora/cloud_resume.sh.
The page replaces the repository, script by script. Text in a field means the file for that script is ignored: it does not run before the text or after it. An empty field means the file runs when it exists. Under each field the card says which of the two will run as it stands.
The switch Run the scripts found in the repository turns the files off. With it off, a script whose field is empty runs nothing.
A script written on the page can hold 16 384 bytes. A larger one is refused when it is saved: shorten it, or have it call a file of the repository. A file in the repository can hold 1 048 576 bytes.
How they run#
A script is run with bash, as the instance's user, in the checkout. There is no terminal to ask anything on, and CI=1 is set so that what it calls does not try. One script runs at a time, the install script before the resume script, and the instance stays awake while one runs. The end of what a script printed is kept, 8 000 bytes of it.
EXEORA_HOOK is install or resume, and EXEORA_HOOK_SOURCE is dashboard or repository. For the resume script EXEORA_RESUME_KIND is warm after a sleep that froze the processes and cold after one that ended them.
An edit made on the page reaches the instances that already exist the next time each resumes. Nothing has to be made again. An install script that changed runs again at that moment. One that failed and did not change is not run again at every resume: change it, or press Run again.
While a script runs, what an agent starts waits for it: run_command, start_command, a tool of a proxied MCP server, and a terminal that is being opened. The wait is 30 seconds at most, because a client gives up on a call after about a minute. Past it the command goes ahead and the script goes on beside it. A command that ran beside an install script may find dependencies missing: run it again once the script has ended. Reading and editing files never wait.
When a script fails#
A script that fails does not fail the instance. The instance is ready, with a warning on its row: which script failed or ran out of time, its exit code, and where it came from. Details shows the end of what it printed, and Run again runs it once more. The instance is also listed under Needs your attention on the Overview. Fix the script, then run it again.
Which instances run them#
Scripts run only on instances made with CLI 0.19.0 or later. An older instance keeps working, and About this instance says that scripts do not run there. Destroy it and start it again to get them, after pushing what it holds: the instance is the only copy of work that was never pushed.
Scripts are outside the policy#
A script is not a tool call. The project's policy and its approvals do not apply to it, and the audit log does not record it. A script on the page is written by you. A script kept in the repository is written by whoever can push to the branch: they run code in the instance, even in a project set to read only. Turn off Run the scripts found in the repository to stop that. See security.
Private repositories#
A project connected to GitHub keeps no repository token on its instances. Git there is wired to exeora git-credential, which asks the gateway for a token that lasts an hour and reaches that one repository, each time git needs one. gh is signed in another way, with a token that reaches more: see gh.
Any other private repository needs a token with read and write access, given when the project is put on Cloud. The gateway stores it encrypted, hands it to each instance of that project as it is set up, and deletes it with the project. A token that was wrong or has expired is replaced from the menu of the Cloud location on the project's page or with exeora project credential; instances created or retried from then on clone with the new one, and an instance already running keeps the one it was set up with. Inside the instance the token lives in a file only the CLI's user can read, wired to git for that one host; an agent running commands there runs as that user and could read it, which is the same as on any machine where an agent is allowed to run commands.
The instance's own credential is separate: a machine token that does three things and nothing else. It opens that instance's relay socket, it asks for the git credential of the one project the instance was made for, and it asks for the token gh runs with in that project. A token taken from one instance reaches no other project and nothing else on the API. Through the token for gh it reaches what you reach on GitHub, as described below.
gh#
Inside an instance of a project connected to GitHub, gh is signed in as you, the person who owns the project, through the Exeora GitHub App. gh auth status, gh pr create, gh pr list --author @me, gh issue list, gh run list and gh pr checks work, from a command an agent runs, from a script and from a terminal.
On an instance gh is a small wrapper, and the GitHub CLI itself is kept in a place of its own. The wrapper asks the gateway for your token and hands it to the GitHub CLI in the environment of that one process. The token is never written to the instance's disk or to the configuration of gh. It is kept in memory, under /dev/shm, in a folder only the instance's user can open, for 30 minutes at most, so that not every run of gh asks the gateway. It expires on its own: GitHub issues it for eight hours. The token that renews it stays at the gateway.
Git is separate. It still uses a token that reaches only the project's repository and lasts an hour.
A project that is not connected to GitHub may have a repository token set by hand. There gh uses that token when the repository is on github.com. When it is on another host gh is not signed in, because a token made for another host is not shown to GitHub: set GH_TOKEN for the command when gh is needed there. A command that sets GH_TOKEN or GITHUB_TOKEN itself runs gh with that token on any instance.
What the token reaches. It is your token, so it 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. That includes the commands an agent runs and the project's scripts. To narrow it, give the app fewer repositories with Change repositories under Settings, and use the project's policy to decide which commands an agent may run. See security.
gh is signed in only on instances made with CLI 0.19.0 or later. What it can read also depends on the permissions the installation has accepted.
Limits#
An instance has 8 vCPUs, about 8 GB of memory and a large disk. Commands the CLI runs are capped at 6 GB of memory as a group: a build that exceeds it is killed with exitCode: null and a note in stderr, and the instance stays up. Instances have their own cap on the plan, separate from machines you register yourself.
From the CLI#
Cloud has no commands of its own. It is a location, so the commands are the ones every location takes, with --on cloud, run on any machine that is signed in:
exeora project add owner/repo --on cloud exeora project add https://git.example.com/you/private.git --on cloud --token-stdin < token.txt exeora project locations add api --on cloud exeora project default api --on cloud exeora workspace create feature/name --project api --on cloud --from main exeora workspace remove feature-name --project api exeora project credential api --token-stdin < token.txt exeora project locations remove api --on cloud exeora machine list
project add and workspace create wait until the instance is ready, or report why it is not. The token only ever arrives on standard input, never as an argument, so it stays out of shell history and process lists. Commands that destroy an instance ask first unless -y is given, which --json requires. The older exeora cloud commands still work as aliases, so scripts written for them keep running.
Removing#
Removing a workspace destroys its instance. From the dashboard and from remove_workspace it is refused while the instance holds work the remote does not have, unless you force it, because the instance is the only copy. From the CLI it asks first and says so. Taking a project off Cloud destroys every instance of it there and leaves the project in its other locations. While the project lives somewhere else as well, it is refused until another location is the default. When Cloud is the only location, the project stays and lives nowhere. Removing a project destroys every instance of it, the one of the project root last, and the stored token with them.
Destroy on an instance under Machines does the same as removing its workspace. On the instance of the project root it destroys that instance and nothing else: the project stays, with its MCP URL, its policy, its clients and its workspaces. What is lost is only what was never pushed from the instance. The repository on its host is never touched: Exeora only ever clones, fetches and pushes to it as you ask.
When the project lives on a machine of yours as well, the default location moves there. Otherwise the project lives nowhere, and Cloud stays its default location in state no instance. The instance is made again by the next call to the project root, or by Start instance on the project's page. The call that makes it is answered with EXECUTOR_WAKING: send it again in about a minute.
Where it runs#
Instances are microVMs on Fly.io, operated by Exeora, one per workspace. On a self-hosted gateway they are yours: the self-hosting guide names the two secrets that turn Cloud on. Because an instance dials the gateway from outside, the gateway needs a public address; a development server on localhost cannot host Cloud.