Self-host eve, Vercel's agent framework
Vercel's agent framework, off Vercel, with sessions and schedules on your own disk.
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/stdineve 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 --printanswers from ~/my-agent- The morning-brief schedule, which emails the owner five Hacker News stories at 08:00 UTC
What's running, and where
ssh my-eve.exe.xyz
the agent's chat, its project, and logs
https://my-eve.exe.xyz:3000/eve/v1/
eve's HTTP API, behind exe.dev login
https://my-eve.exe.xyz/eve/v1/slack
Slack's events, only if you add Slack and make port 8000 public
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/stdinWhat 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 -fshows the server's log; build errors appear innpx eve build, not there. Drop| tail -n 1to see all of it.401 unauthorizedfromchator the API: the request lacked exe.dev's login headers. Use./chaton 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 thatagent/agent.tshasstore: falseandreasoning.encrypted_content.Connection erroror401from the model: check.env. The LLM integration is reachable only from exe.dev VMs.DefaultSandbox has no sandbox provider for this build:eve buildcouldn't reach Docker. Runsudo systemctl enable --now docker.serviceand rebuild.- Slack never verifies the Request URL:
curl -s -X POST localhost:8000/eve/v1/slackshould giveunauthorized, andssh exe.dev share show my-eveshould say public. A401in the eve log for Slack's requests means the signing secret in.envis 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