Skip to content

Self-host eve, Vercel's agent framework

Vercel's agent framework, off Vercel, with sessions and schedules on your own disk.

Deploy on exe.dev

Shelley handles the setup. About 10 minutes.

Prefer a terminal?
curl -fsSL https://exe.dev/deploy/eve/prompt.txt | ssh exe.dev new --prompt=/dev/stdin

eve is Vercel's open-source TypeScript framework for agents. An agent is a directory: instructions in Markdown, tools in TypeScript, skills, schedules, and channels such as Slack. Each conversation is a durable session that survives restarts, and the agent runs commands in a sandbox. eve is built to deploy on Vercel, but eve build also makes a plain Node.js server that runs anywhere.

An exe.dev VM runs that server around the clock under systemd, private behind exe.dev login, with sessions and schedules in files on its disk and each session's sandbox in Docker on the same VM. Models come from exe.dev's LLM integration, so no API key is on the VM, and the agent can email you without mail credentials.

What you get#

  • Node.js 26 and an eve project from eve init in ~/my-agent, a git repository, with eve pinned to 1.0.0-rc.14
  • eve.service enabled, serving on 127.0.0.1:3000, private behind exe.dev login, and back after a reboot
  • Model calls through exe.dev's LLM integration, with no OpenAI API key on the VM
  • Commands the agent runs in a Docker container per session, not on the VM itself
  • ./chat --print answers from ~/my-agent
  • The morning-brief schedule, which emails the owner five Hacker News stories at 08:00 UTC
What's running, and where
Private ssh my-eve.exe.xyz the agent's chat, its project, and logs
Private https://my-eve.exe.xyz:3000/eve/v1/ eve's HTTP API, behind exe.dev login
Public https://my-eve.exe.xyz/eve/v1/slack Slack's events, only if you add Slack and make port 8000 public
my-eve.exe.xyz exe.dev VM
Your agent An eve 1.0.0-rc.14 project from eve init, a git repository you edit ~/my-agent/agent
eve server eve start on Node.js 26, the eve systemd service, on 127.0.0.1 only :3000
Sessions and schedules Workflow's local world, files on the VM's disk ~/my-agent/.eve/.workflow-data
Sandbox A Docker container per session, from Vercel's ghcr.io/vercel/eve image
GPT models OpenAI, through exe.dev's LLM integration
Slack proxy (optional) nginx, passing only Slack's route through :8000

Deploy with Shelley#

Click Deploy and Shelley will set up eve on a new VM, then check that it works. No config-file wrangling required.

Prefer a terminal?
curl -fsSL \
  https://exe.dev/deploy/eve/prompt.txt \
  | ssh exe.dev new --prompt=/dev/stdin
What Shelley is told
Set up eve on this VM by following the exe.dev recipe at https://exe.dev/deploy/eve.md from top to bottom.

The recipe's example VM name is my-eve. This VM's name is the output of `hostname`: use it wherever the recipe writes my-eve. Skip the step that creates the VM; you are on it.

You are done when all of these are true. Check each one yourself:
- Node.js 26 and an eve project from eve init in ~/my-agent, a git repository, with eve pinned to 1.0.0-rc.14
- eve.service enabled, serving on 127.0.0.1:3000, private behind exe.dev login, and back after a reboot
- Model calls through exe.dev's LLM integration, with no OpenAI API key on the VM
- Commands the agent runs in a Docker container per session, not on the VM itself
- `./chat --print` answers from ~/my-agent
- The morning-brief schedule, which emails the owner five Hacker News stories at 08:00 UTC

Run "Set up eve by hand" from "On the VM", skipping VM creation and
SSH, then "Email yourself a morning brief". Wait for /eve/v1/health
before each request. Don't wait for 08:00: the hello email is the
check. Skip "Answer in Slack" unless the owner asks and
gives you the token and signing secret; never make port 8000 public
yourself. Finish with `ssh -t <vm>.exe.xyz my-agent/chat`, using this
VM's name.

Finish with the URLs I should open.

Also at /deploy/eve/prompt.txt. The recipe it follows is /deploy/eve.md.

Set up eve#

Deploy sets it up on a new VM. To do it yourself:

Set up eve by hand

On your computer (pick any free VM name):

ssh exe.dev new --name=my-eve
ssh my-eve.exe.xyz

On the VM, install Node.js 26, start Docker at boot, and make a project with eve's starter agent, pinned to this version (about a minute):

curl -fsSL https://deb.nodesource.com/setup_26.x | sudo -E bash -
sudo apt-get install -y nodejs
sudo systemctl enable --now docker.service
npx -y eve@1.0.0-rc.14 init my-agent --non-interactive
cd ~/my-agent
npm install --save-exact eve@1.0.0-rc.14

Point OpenAI models at the LLM integration, let exe.dev login stand in for eve's auth, and add the chat script. These replace the starter's agent/agent.ts and agent/channels/eve.ts, whose placeholder auth refuses everyone in production:

(umask 077; printf 'OPENAI_BASE_URL=https://llm.int.exe.xyz/v1\nOPENAI_API_KEY=exe.dev\n' > .env)
curl -fsSL -o agent/agent.ts https://exe.dev/deploy/eve/agent.ts
curl -fsSL -o agent/channels/eve.ts https://exe.dev/deploy/eve/eve.ts
curl -fsSL -o chat https://exe.dev/deploy/eve/chat && chmod +x chat
npx eve build | tail -n 1

The build ends with built output at /home/exedev/my-agent/.output. The first one also builds the sandbox image, about 550 MB.

Run it under systemd on 127.0.0.1:3000. Port 3000 is private through exe.dev's HTTPS proxy, and binding to 127.0.0.1 keeps the sandboxes from reaching it:

sudo tee /etc/systemd/system/eve.service >/dev/null <<'EOF'
[Unit]
Description=eve agent
After=network-online.target docker.service
Wants=network-online.target docker.service

[Service]
User=exedev
WorkingDirectory=/home/exedev/my-agent
EnvironmentFile=/home/exedev/my-agent/.env
Environment=NODE_ENV=production
ExecStart=/home/exedev/my-agent/node_modules/.bin/eve start --host 127.0.0.1 --port 3000
Restart=on-failure

[Install]
WantedBy=multi-user.target
EOF
sudo systemctl enable --now eve

Check it after a few seconds with curl -s localhost:3000/eve/v1/health. Expect {"ok":true,"status":"ready",...}. Then ask it something that needs the sandbox:

./chat --print "Write /workspace/primes.py to print the first 10 primes, run it, and reply with its output only."

Expect 2 3 5 7 11 13 17 19 23 29 after about ten seconds.

Chat with your agent#

On your computer:

ssh -t my-eve.exe.xyz my-agent/chat

That opens eve's terminal chat against the server on the VM. The starter agent answers with GPT through the LLM integration and runs bash, reads and writes files, and fetches web pages in its sandbox, a Docker container of its own for each session. /help lists the chat's commands, and sessions carry on where they left off after a restart or a reboot.

The project is yours to change: the agent's instructions in agent/instructions.md, tools in agent/tools/, skills in agent/skills/, and schedules in agent/schedules/. Shelley, or any coding agent, can make the changes; the project's AGENTS.md tells it how. After a change, on the VM:

cd ~/my-agent
npx eve build | tail -n 1 && sudo systemctl restart eve

Email yourself a morning brief#

email_owner.ts is a tool that emails you, the VM's owner, through exe.dev's email gateway: no address to configure and no mail credentials. morning-brief.md is a schedule that has the agent read Hacker News at 08:00 UTC every day and email you the five stories that matter most to someone building with agents. On the VM:

cd ~/my-agent
curl -fsSL --create-dirs -o agent/tools/email_owner.ts https://exe.dev/deploy/eve/email_owner.ts
curl -fsSL --create-dirs -o agent/schedules/morning-brief.md https://exe.dev/deploy/eve/morning-brief.md
npx eve build | tail -n 1 && sudo systemctl restart eve

eve start fires schedules itself, so nothing else needs a cron job. Try the tool now:

./chat --print "Email me a one-line hello with email_owner."

Expect the email within a minute. To change the time or the prompt, edit agent/schedules/morning-brief.md (cron is in timezone), then rebuild and restart. Each run is a session of its own; the agent's reply is discarded, so a schedule acts through tools such as email_owner.

Answer in Slack#

eve's Slack channel takes Slack's events over HTTPS, so Slack must reach the VM. Port 8000 is the only port exe.dev can make public, so put nginx there, passing through only /eve/v1/slack. eve checks every request's Slack signature; the rest of eve stays behind exe.dev login on port 3000.

In Slack's app settings, choose Create New App, From a manifest, pick your workspace, and paste this, with your VM's name:

display_information:
  name: eve
features:
  bot_user:
    display_name: eve
    always_online: true
  app_home:
    messages_tab_enabled: true
    messages_tab_read_only_enabled: false
oauth_config:
  scopes:
    bot: [app_mentions:read, chat:write, channels:history, groups:history, im:history, mpim:history, files:read, files:write]
settings:
  event_subscriptions:
    request_url: https://my-eve.exe.xyz/eve/v1/slack
    bot_events: [app_mention, message.channels, message.groups, message.im, message.mpim]
  interactivity:
    is_enabled: true
    request_url: https://my-eve.exe.xyz/eve/v1/slack

Slack can't verify the Request URL yet; that comes last. Install the app to the workspace, then copy the Bot User OAuth Token (xoxb-..., under OAuth & Permissions) and the Signing Secret (under Basic Information). On the VM, add the Slack channel and both secrets, and rebuild:

cd ~/my-agent
npx eve add channel/slack --non-interactive --skip-install --answer 'slack-credentials="portable"'
read -rsp 'Bot token: ' T; echo; read -rsp 'Signing secret: ' S; echo
printf 'SLACK_BOT_TOKEN=%s\nSLACK_SIGNING_SECRET=%s\n' "$T" "$S" >> .env; unset T S
npx eve build | tail -n 1 && sudo systemctl restart eve

Then serve only Slack's route on port 8000. exeuntu's default nginx site also listens there, so remove it:

sudo tee /etc/nginx/sites-available/eve-slack >/dev/null <<'EOF'
# Port 8000 is the only port exe.dev can make public.
# Serve Slack's route there and nothing else; eve stays private on :3000.
server {
    listen 8000 default_server;
    listen [::]:8000 default_server;
    location = /eve/v1/slack {
        proxy_pass http://127.0.0.1:3000/eve/v1/slack;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-Proto https;
    }
    location / {
        return 404;
    }
}
EOF
sudo ln -sf /etc/nginx/sites-available/eve-slack /etc/nginx/sites-enabled/eve-slack
sudo rm -f /etc/nginx/sites-enabled/default
sudo nginx -t && sudo systemctl enable nginx && sudo systemctl restart nginx

Once eve is back up, check the route:

curl -s -o /dev/null -w '%{http_code}\n' -X POST localhost:8000/eve/v1/slack

Expect 401, eve refusing an unsigned request; 502 means eve is still starting. Then, on your computer, make port 8000 public:

ssh exe.dev share set-public my-eve

Back in Slack, under Event Subscriptions, click Retry next to the Request URL; expect Verified. Invite the bot to a channel (/invite @eve) and mention it. It replies in a thread, and each thread is a session. ssh exe.dev share set-private my-eve takes it off the internet again.

Back up and restore#

Everything is in ~/my-agent: the project in git, .env (its secrets), and .eve/.workflow-data (sessions, their history, and schedule state). Stop eve for a consistent copy. On the VM:

cd ~
sudo systemctl stop eve
tar czf eve-backup-$(date +%F).tgz --exclude=my-agent/node_modules --exclude=my-agent/.output my-agent
sudo systemctl start eve

Copy the file off the VM, for example with scp my-eve.exe.xyz:eve-backup-*.tgz . on your computer. The sandbox containers aren't in it; a restored session starts a fresh one.

To restore on a VM set up as above, with the same eve version:

sudo systemctl stop eve
cd ~ && rm -rf my-agent && tar xzf eve-backup-*.tgz
cd my-agent && npm ci && npx eve build | tail -n 1
sudo systemctl start eve

Update#

eve is a release candidate and changes often; read the releases before you move. A server on a new eve version can't continue sessions started on the old one: they answer 409 session_stranded, so finish or abandon them first. Rebuilding on the same version resumes them. On the VM, after a backup:

cd ~/my-agent
npm install --save-exact eve@<version>
npx eve build | tail -n 1 && sudo systemctl restart eve
curl -s localhost:3000/eve/v1/health

Troubleshooting#

  • sudo journalctl -u eve -f shows the server's log; build errors appear in npx eve build, not there. Drop | tail -n 1 to see all of it.
  • 401 unauthorized from chat or the API: the request lacked exe.dev's login headers. Use ./chat on the VM, or a token from your computer.
  • Item with id 'rs_...' not found: the model's reasoning was sent by reference, which the LLM integration can't keep. Check that agent/agent.ts has store: false and reasoning.encrypted_content.
  • Connection error or 401 from the model: check .env. The LLM integration is reachable only from exe.dev VMs.
  • DefaultSandbox has no sandbox provider for this build: eve build couldn't reach Docker. Run sudo systemctl enable --now docker.service and rebuild.
  • Slack never verifies the Request URL: curl -s -X POST localhost:8000/eve/v1/slack should give unauthorized, and ssh exe.dev share show my-eve should say public. A 401 in the eve log for Slack's requests means the signing secret in .env is wrong.

Frequently asked questions#

Do I need Vercel, or an OpenAI API key?

Neither. eve build makes a plain Node.js server and eve start runs it, with no Vercel account. openai() from eve/models/openai sends requests to OPENAI_BASE_URL, which .env points at exe.dev's LLM integration; it bills your exe.dev account, and the key in .env is a placeholder. A model given as a plain string, such as "openai/gpt-5.5", goes to Vercel's AI Gateway instead and needs AI_GATEWAY_API_KEY.

Who can talk to the agent?

You, over SSH or after exe.dev login. eve listens on 127.0.0.1, and agent/channels/eve.ts admits only requests that carry the login headers exe.dev's HTTPS proxy adds, so a request without them gets 401. Port 3000 is private; ssh exe.dev share add my-eve ann@example.com lets someone else in, with the same power over the agent as you.

Can I connect from my own computer?

Yes, with an exe.dev HTTPS token for this VM: ssh exe.dev ssh-key generate-api-key --vm=my-eve --label=eve, then npx eve@1.0.0-rc.14 remote connect --url https://my-eve.exe.xyz:3000 -H "X-Exedev-Authorization: Bearer <token>". The proxy checks the token and adds the login headers. Your own code can call the HTTP API the same way.

Where does the agent run its commands?

In a Docker container for each session, from Vercel's ghcr.io/vercel/eve image (Ubuntu, Node.js 26, Python 3), in /workspace. eve uses Docker because eve build found it. The container stops when the agent is idle and is deleted when the session ends, after 30 idle days by default. It can reach the internet, but not eve or anything else listening on the VM's 127.0.0.1.

How is this different from deploying eve on Vercel?

It's one always-on machine: eve start runs the server, its schedules, and the sandboxes, and keeps sessions in files on its disk, with no Vercel Sandbox, Queues, or database. That also means one process: for replicas, eve's Postgres world shares sessions between servers. There's no autoscaling; a bigger VM is the way up.

Does eve send telemetry?

The CLI does, on by default. npx eve telemetry disable in ~/my-agent turns it off, as does EVE_TELEMETRY_DISABLED=1.

Updated Nov 13, 2026 · tested end to end Nov 13, 2026 · Markdown · manifest.json · Something wrong? Tell us