Skip to main content
There are two ways to keep working when you are away from the machine that runs your Agents. Pick the one you need:
What is a headless runtime? Think of it as Vibex without a window. It owns Agent sessions, workspaces, Git, terminals, and provider profiles, and behaves exactly like the desktop app — it just runs on a server, and you operate it through a desktop or mobile client. Its program name is vibex-server.
Both paths establish trust with a one-time pairing: the first time, you need the pairing code or QR code from the machine itself. After that, long-lived credentials live on both sides and clients reconnect on their own. The steps below assume the desktop app is already installed. The Android client can be sideloaded from GitHub Releases; iOS currently ships only a simulator build. See Install and platforms for both.

1. Cloud development: run Vibex on a server

Agents read and write code on the server and run commands there, so prepare the server first and connect clients afterwards.

Before you start

  • A Linux server with Docker. For a single user, 1 vCPU and 2 GB of RAM is enough to start; running several Agents benefits from 2 vCPU and 4 GB or more.
  • Your code must be reachable on the server: either clone it there, or mount a host directory into the container (the example below mounts one).
  • Agent accounts and API keys are configured later, from the desktop client: add them under Config Center (see Config Center). Never put them in the compose file or commit them.

Step 1: start the runtime

The project repository already contains a complete docker-compose.yml deployment file — open it directly: deploy/server/docker-compose.yml. The version below is a minimal example trimmed for this walkthrough; save it as docker-compose.yml in any directory on the server:
This example only uses the variables you cannot avoid; the full list, including rate limits, event capacity, and secret storage, is in the environment reference.
Use rc to follow the newest release candidate or latest for the newest stable release; pin a specific version only if you need to. If you already cloned the repository, you can also build from source with its deployment files: docker compose -f deploy/server/docker-compose.yml up --build -d vibex-server.
Start it and read the pairing material:
The log prints two things:
  • pairing_code=123-456-789 — a one-time code that expires after five minutes by default.
  • pairing_link=vibex://pair#/code/... — a connection string that already carries the server address, the one-time code, and the server certificate (DER). Copy the whole string; the client pins that certificate before its first request, so a server with a self-signed certificate needs no system CA.
A pairing code or connection string is as sensitive as a password and works once. Do not post it in chat, an issue, or a screenshot. When it expires, mint a new one on the server.

If the server is on the public internet

A public deployment adds exactly one requirement: HTTPS. Let Caddy obtain the certificate while the runtime only listens on loopback:
Add a Caddyfile next to it:
Your firewall only needs 80, 443, and SSH. Keep 8765 on loopback and out of the public internet. After you replace the domain and address with your own, VIBEX_ALLOWED_HOSTS and VIBEX_ALLOWED_ORIGINS must match the domain clients actually use, or requests are rejected.
If you would rather not expose anything to the public internet, install Tailscale on the server and publish the runtime with tailscale serve --bg http://127.0.0.1:8765. Set VIBEX_DEPLOYMENT_MODE to public, VIBEX_TLS_MODE to trusted_https_proxy, and use the Tailscale hostname for both VIBEX_PUBLIC_HOST and VIBEX_ALLOWED_HOSTS.

Step 2: connect the desktop

  1. Select the Runtime button in the title bar to open the runtime manager.
  2. Select Add runtime…, choose the Connection string tab, paste the whole vibex://pair#/code/... value, and select Pair. This also works when the server uses a self-signed certificate, because the fingerprint travels inside the string.
  3. Alternatively, choose the Pairing code tab and enter the Server address and One-time pairing code.
  4. After pairing, the runtime’s Certificate row shows a sha256: fingerprint. Compare it with tls_fingerprint in the server log.
  5. Select Switch to this runtime to make it current. From now on, sessions, files, Git, and terminals in the desktop come from the server. To go back to your own machine, select This device in the list and switch to it. To remove the runtime, select Remove in its details — that deletes only the local device credential; the server-side authorization stays until it is revoked on the server.

Step 3: connect the mobile client

  1. Open the mobile app and choose Pair with a Cloud Server.
  2. Enter the server address (for example 192.168.1.10:8765) and the pairing code, then select Pair with Code.
  3. When the server uses a self-signed certificate, paste the vibex://pair#/code/... string into the connection string field instead; it carries the certificate too.
  4. Confirm the pairing; what this device may do comes from the server when it mints the pairing code, and defaults to Full control. To grant less, mint a new code on the server with docker exec vibex-server vibex-server pairing-code --permission read-only (choices: read-only, approve-only, full-control).

Step 4: let Agents see your code

Agents run on the server, so they only see directories inside the server (container). Your code therefore has to live there:
  • Clone it on the server into /data/repos/your-project, or
  • Mount a host directory into the container (the - /srv/repos:/data/repos line above).
Then make sure the container path is listed in VIBEX_WORKSPACE_ROOTS (the /data:/data/repos value above). In any client, open a new session, select the project directory picker, choose Choose another directory, and pick your project under /data or /data/repos.
Mounting a directory without listing it in VIBEX_WORKSPACE_ROOTS leaves it unbrowsable for clients; listing a path without mounting it leaves the directory nonexistent inside the runtime.

Step 5: day-two operations

2. Remote development: your phone reaching your desktop

Your computer stays the authority: Agents, files, Git, and terminals all run on the desktop, and the phone only gets a window to view and operate them. Use this when you are away and want to check progress, answer a question, or approve a request.

Before you start

  • The desktop app is running and the computer stays awake; sleep drops the connection.
  • The phone and the computer can reach each other: the same Wi-Fi is easiest, and Tailnet or a self-hosted Relay cover the rest.

Step 1: publish remote access from the desktop

  1. Select the pair mobile device icon in the top toolbar. The dialog has two tabs: Pair publishes a new pairing, and Paired devices (N) manages the devices that already hold a grant.
  2. Choose how the phone will connect:
    • Tailnet (recommended) — private access through Tailscale or another private network; good when you are not on the same Wi-Fi.
    • Direct HTTPS — you have your own HTTPS address that points at this computer.
    • Self-hosted Relay — the phone and computer cannot reach each other directly, so your own Relay forwards encrypted traffic.
  3. Fill in the address the dialog asks for (Direct expects https://your-address, Relay expects https://your-relay), choose what this phone may do (the default is Read only; see Permission levels), and select Publish.
  4. A QR code and a pairing link appear: scan to pair, or select Copy link and send it to the phone. The link expires quickly; select Regenerate when it does.
A pairing link or QR code is a one-time secret. Do not publish it. When the phone pairs, the desktop asks for confirmation, and pairing only succeeds if you approve it.

Step 2: pair the phone

  • On the same Wi-Fi: the mobile app discovers nearby desktops; select yours and confirm.
  • On another network: scan the QR code or open the copied vibex:// link.
  • Manually: choose Pair with Code in the mobile app and enter the address and pairing code shown on the desktop.
The permission level is chosen on the desktop when it publishes the pairing offer, and the desktop asks you to confirm the device when the phone claims it. The phone then shows the desktop’s session list.

Step 3: what the phone can do

Mobile has no Config Center, so it cannot manage Agents, providers, MCP, or Skills. Its Settings contain only Connection, Session timeline, Appearance, Notifications, and About. Every write is ultimately produced by the authoritative runtime; mobile shows its projection.

When direct access fails: self-hosted Relay

Phones and computers are often behind corporate networks or carrier NAT, so they cannot reach each other. Run the Relay on a server both sides can reach; it only forwards encrypted frames and cannot read them:
To reach it over the public internet, give it an HTTPS/WSS entry point (for example Caddy reverse-proxying to 127.0.0.1:9700), then publish remote access on the desktop with Self-hosted Relay and enter https://your-relay-domain. The Relay is pure forwarding: it does not store sessions, files, or provider profiles, and it does not decrypt content. Rooms and connections live in memory, so a restart clears them and devices rebuild them on reconnect.

Disconnects and reconnects

After a network change, screen lock, or app resume, clients re-authenticate and catch up on missing history: they show authoritative session and timeline state first, then resume live updates.

Permission levels

Choose a permission when you pair a mobile device, and grant the smallest one that works. The level is decided by the initiating side when it creates the pairing, and a client cannot raise it on its own: Revoke devices on the authoritative side:
  • When the desktop is the authority, open Pair mobile, switch to the Paired devices (N) tab, and select Revoke on the device, then confirm. Each row shows the device’s status, permission, and last activity, and Refresh devices reloads the list; more than six devices are paged.
  • When a headless runtime is the authority, run docker exec vibex-server vibex-server revoke DEVICE_ID (or vibex-server revoke DEVICE_ID on the server).
Revocation closes the device’s active connections immediately, and it has to pair again afterwards. Selecting Remove on a client runtime deletes only the local credential; the server-side device authorization stays.

Security checklist

  • Keep the runtime’s 8765 port on loopback or your private network, and always use HTTPS on the public internet. The authority listens on loopback by default; never run an unauthenticated public listener.
  • Grant the smallest permission level; use Full control only when you need it.
  • Never post pairing codes, QR codes, or connection strings publicly; each one works once, and none is a long-lived credential.
  • Revoke devices you no longer use.
  • A Relay only forwards encrypted traffic. It is not a permission boundary, and business data does not belong in its logs.

Troubleshooting first steps

Environment variables

The examples above use only the variables you cannot avoid. The full list of vibex-server, Relay, and desktop variables, with defaults and purposes, is in the environment reference. Deployment forms, commands, and health checks are in Self-hosted headless runtime, and the Relay’s endpoints, limits, and push configuration are in Self-hosted Relay. For deeper details on the pairing handshake, encryption, and sync contracts, read Remote v2 and Relay in the developer guide.