This file records the exe.dev API documentation that this repository depends on. It was last checked on 2026-05-18 from these source pages:
- https://exe.dev/docs/api
- https://exe.dev/docs/login-with-exe
- https://exe.dev/docs/https-api
- https://exe.dev/docs/https-api-local-key
- https://exe.dev/docs/https-tokens-for-vms
exe.dev exposes two programmatic access styles:
-
SSH command automation:
ssh exe.dev ls --json ssh exe.dev new --json
-
HTTPS command automation:
POST https://exe.dev/exec
POST /exec is not a resource-style REST API. The request body is the same
exe.dev command that would be typed into ssh exe.dev or run through
ssh exe.dev <command>. API responses enable JSON output by default, equivalent
to passing --json.
Minimal HTTPS request:
curl -X POST https://exe.dev/exec \
-H "Authorization: Bearer $EXE_DEV_API_KEY" \
-d 'whoami'Operational limits:
- Only
POSTis accepted. - There is no stdin and no pty.
- Request bodies are limited to 64KB.
- Commands time out after 30 seconds.
- Commands that require interactive input do not fit this API.
The exe.dev HTTPS API uses bearer tokens. A token can be generated by exe.dev:
ssh exe.dev ssh-key generate-api-key --exp=30dTokens can also be created locally by signing permissions JSON with an SSH private key. For automation, prefer a dedicated SSH key so the API token family can be revoked without disrupting normal user SSH access.
ssh-keygen -t ed25519 -C api -f ~/.ssh/exe_dev_api
cat ~/.ssh/exe_dev_api.pub | ssh exe.dev ssh-key addLocal token creation flow:
b64url() { tr -d '\n=' | tr '+/' '-_'; }
export PERMISSIONS='{}'
export PAYLOAD=$(printf '%s' "$PERMISSIONS" | base64 | b64url)
export SIG=$(printf '%s' "$PERMISSIONS" | ssh-keygen -Y sign -f ~/.ssh/exe_dev_api -n v0@exe.dev)
export SIGBLOB=$(echo "$SIG" | sed '1d;$d' | b64url)
export EXE_DEV_API_KEY="exe0.$PAYLOAD.$SIGBLOB"PERMISSIONS is embedded in the token as plaintext JSON. Do not put secrets in
it.
Supported top-level permission fields:
exp: UTC Unix timestamp after which the token is invalid.nbf: UTC Unix timestamp before which the token is not valid yet.cmds: exe.dev command names this token may run.ctx: signed JSON context uninterpreted by exe.dev.
The empty object {} uses defaults. For least-privilege automation, specify
cmds explicitly. Subcommands must be granted explicitly; allowing ssh-key
does not allow ssh-key list.
Useful default commands include:
helplsnewwhoamissh-key listshare showexe0-to-exe1teamteam members
Commands such as rm, rename, restart, share port, share set-public,
and share set-private should be added only when automation needs them.
Permission JSON restrictions:
- Compact JSON is recommended.
- No leading/trailing whitespace.
- No newlines or null bytes.
- No duplicate keys.
- Only
exp,nbf,cmds, andctxare allowed at the top level. expandnbfmust be integer timestamps between 2000-01-01 and 2100-01-01 UTC.- The whole token must not exceed 8KB.
Short opaque exe1 tokens can be issued from a valid exe0 token:
ssh exe.dev exe0-to-exe1 "$EXE_DEV_API_KEY"For a VM-scoped token, validate the source token against the VM:
ssh exe.dev exe0-to-exe1 --vm=vm-name "$EXE_DEV_API_KEY"Common responses from POST /exec:
400: request body is empty, missing, or syntactically invalid.401: token is malformed, expired, signed by an unknown key, or fails signature verification.403: token permissions do not allow the requested command.404: unknown exe.dev command.405: non-POST method.413: request body exceeds 64KB.422: command ran and returned a non-zero exit code.429: per-key rate limit.500: unexpected server-side error.504: command exceeded the 30-second timeout.
Debug checklist for 401:
- Confirm the signing public key is listed with
ssh exe.dev ssh-key list. - Confirm
expis not in the past. - Confirm the exact compact JSON payload was signed.
- Use a private key file with
ssh-keygen -Y sign -f; an agent-only key is not enough for this command.
Debug checklist for 403:
- Decode the base64url payload and inspect
cmds. - Remember that subcommands are exact command-name grants.
- Add only the missing command, then regenerate the token.
The exe.dev HTTPS auth proxy can accept bearer tokens for programmatic access to
individual VM HTTPS endpoints. This is separate from authenticating to
POST https://exe.dev/exec.
Generate a VM token through exe.dev:
ssh exe.dev ssh-key generate-api-key --vm=my-vm --label=deployGenerate a VM token locally by changing the SSH signing namespace from
v0@exe.dev to v0@VMNAME.exe.xyz:
export SIG=$(printf '%s' "$PERMISSIONS" | ssh-keygen -Y sign -f ~/.ssh/exe_dev_api -n v0@myvm.exe.xyz)VM token differences:
- The signing namespace scopes the token to one VM.
- The token payload
ctxis forwarded to the VM HTTP server asX-ExeDev-Token-Ctx.
Preferred VM proxy authentication header:
X-Exedev-Authorization: Bearer <token>The proxy consumes and strips that header before forwarding to the VM.
Other supported forms:
Authorization: Bearer <token>is supported but deprecated for new integrations.- Basic auth can be used for tools such as
git; the username is ignored and the password is the token. This applies to VM proxy access, not/exec.
When authenticated by token, the VM server receives:
X-ExeDev-UserIDX-ExeDev-EmailX-ExeDev-Token-Ctx, if present in the token
Git HTTPS example:
echo "$TOKEN" > ~/.ssh/exe_dev_token
git config credential.helper '!f() { echo "password=$(cat ~/.ssh/exe_dev_token)"; }; f'
git clone https://myvm.exe.xyz/repo.gitApplications served through the exe.dev HTTP proxy can use exe.dev authentication instead of managing their own login system.
When a request is authenticated by exe.dev, the proxy adds:
X-ExeDev-UserID: stable exe.dev user identifier.X-ExeDev-Email: authenticated user email address.
For public sites, unauthenticated requests may not include these headers. For private sites, access requires authentication, so the headers are expected.
Special URLs:
https://vmname.exe.xyz/__exe.dev/login?redirect={path}redirects the user to log in, then back to{path}.POST https://vmname.exe.xyz/__exe.dev/logoutlogs the user out for that domain.
Local development can simulate these headers with a reverse proxy, for example:
mitmdump \
--mode reverse:http://localhost:8000 \
--listen-port 3000 \
--set modify_headers='/~q/X-Exedev-Email/user@example.com' \
--set modify_headers='/~q/X-Exedev-Userid/usr1234'VM services must not trust user-controlled copies of these headers when traffic can bypass the exe.dev proxy. Bind private services to localhost or firewall direct VM ports if the header values are part of authorization.
This repository uses the exe.dev HTTPS command API through
EXE_DEV_API_KEY for non-interactive VM management:
exedev-ctlwraps individual exe.dev commands.exedev-k8suses exe.dev VM commands as the infrastructure layer for k3s fleet bootstrapping.
For local scripts and manual debugging, prefer:
ssh exe.dev <command> --jsonFor repo automation and services, prefer:
curl -X POST https://exe.dev/exec \
-H "Authorization: Bearer $EXE_DEV_API_KEY" \
-d '<command>'For new automation tokens, grant only the commands that the workflow needs. A typical VM lifecycle token for this repository needs at least:
{
"cmds": [
"ls",
"new",
"rm",
"whoami",
"share show",
"share port",
"share set-public",
"share set-private"
],
"exp": 1798761600
}Use shorter-lived tokens for CI and long-running automation where replay risk is important.