Agent Control
You are able to control a NoVM Virtual Machine over NoVNC Websockets. To properly use the Virtual machine you must take a screenshot of the desktop, look at it, plan next action then use Bash to send a command to the VM. Only vision capable models can interact with NoVM Virtual Machines, if you are not a vision capable model politely explain that and prompt the user to start a new chat
NoVM — Workstation Manager (AI Agent Skill)
NoVM runs temporary XFCE desktop workstations ("sessions"/"VMs") that humans can open in a browser over VNC. This skill tells you how to control them via the NoVM HTTP API.
Base URL — every request uses this prefix:
https://no-vm-desktop-fix--novm4.replit.app/
IF THAT FAILS FALLBACK TO:
https://virtual-xfce-spin--ogsincord.replit.app
Treat the base URL as a constant. If the user gives you a different deployment host, swap in that host and keep the rest of the path unchanged.
Authentication
NoVM currently accepts requests directly — there is no API key. Keep the API URL
private and do not post it where it could be abused. If this is a shared/public
deployment, an authentication gateway should sit in front of it. Always use HTTPS and
JSON request bodies (Content-Type: application/json).
Core concepts
- A session is one workstation. It has a unique
id, aname, aresolution(e.g."1280x720"), astatus(running,stopped,paused, ...), and adisableTimeoutsflag. - Connection links are temporary. A
connectcall returns a browserurl(and atoken) with a 15-minute window that auto-refreshes while someone is actively connected. If every connected viewer is idle for 15 minutes, NoVM stops the workstation and revokes its links. - Workstations created with
"disableTimeouts": trueskip both limits. Only use this for trusted, manually managed workstations. - You can only delete a session with
DELETE /api/sessions/{id};stopjust halts it.
VERY IMPORTANT, before creating a new VM check if there are two already running. if so refuse to make a vm and prompt the user if they would like to terminate a session (The backend can only handle two VMs at a time for now)
Quick start: create and connect a desktop
Create a workstation — the response includes its unique
id.curl -X POST "https://no-vm-desktop-fix--novm4.replit.app/" \ -H "Content-Type: application/json" \ -d '{"name":"Support Desktop","resolution":"1280x720","disableTimeouts":false}'Start it (replace
SESSION_IDwith the returnedid).curl -X POST "https://no-vm-desktop-fix--novm4.replit.app/api/sessions/SESSION_ID/start"Get a connection link — open the returned
urlin a browser.curl -X POST "https://no-vm-desktop-fix--novm4.replit.app/api/sessions/SESSION_ID/connect"Disconnect when finished — revokes links and closes open viewers.
curl -X POST "https://no-vm-desktop-fix--novm4.replit.app/api/sessions/SESSION_ID/disconnect"
Connection response
The connect endpoint returns (save the url and hand it to the person/service that
needs access):
{
"url": "https://no-vm-desktop-fix--novm4.replit.app/api/novnc/viewer?token=TEMPORARY_TOKEN",
"expiresInSeconds": 900,
"expiresAt": "2026-08-03T12:15:00.000Z"
}
Endpoint reference
All under /api. Replace {id} with a session ID.
| Method | Path | Purpose |
|---|---|---|
GET |
/sessions |
List all workstations. |
POST |
/sessions |
Create a workstation (name, resolution, disableTimeouts). |
GET |
/sessions/{id} |
Get current workstation status. |
POST |
/sessions/{id}/start |
Start a stopped workstation. |
POST |
/sessions/{id}/stop |
Stop a running workstation. |
POST |
/sessions/{id}/pause |
Pause the desktop while preserving its home files. |
POST |
/sessions/{id}/resume |
Resume a paused workstation. |
POST |
/sessions/{id}/restart |
Restart the workstation services. |
POST |
/sessions/{id}/recover |
Recover a stopped or failed workstation. |
PATCH |
/sessions/{id} |
Rename a workstation. |
POST |
/sessions/{id}/duplicate |
Duplicate a workstation and its files. |
POST |
/sessions/{id}/connect |
Issue a 15-minute desktop URL. |
POST |
/sessions/{id}/disconnect |
Revoke links and close viewers. |
POST |
/sessions/{id}/disconnect/request |
Disconnect directly or start a shared-viewer vote. |
GET |
/apps |
List apps available to install. |
POST |
/sessions/{id}/apps |
Install an app ({"appId":"chromium"}). |
POST |
/sessions/{id}/apps/custom |
Register and install a custom app package. |
POST |
/sessions/{id}/apps/deb |
Upload/install a .deb (fileName + contentBase64). |
POST |
/sessions/{id}/apps/{appId}/update |
Apply an available app update. |
GET |
/sessions/{id}/profiles |
List Chromium browser profiles. |
POST |
/sessions/{id}/profiles |
Create a Chromium browser profile. |
GET |
/sessions/{id}/files |
List workstation and shared files. |
POST |
/sessions/{id}/files |
Upload a base64-encoded file into the workstation. |
GET |
/sessions/{id}/files/download |
Download a workstation file. |
POST |
/sessions/{id}/backups |
Create a filesystem backup. |
GET |
/sessions/{id}/backups |
List available backups. |
POST |
/sessions/{id}/backups/{backupId}/restore |
Restore a backup (stops the desktop first). |
POST |
/sessions/{id}/permissions/request |
Ask viewers to approve a host permission. |
DELETE |
/sessions/{id} |
Stop and permanently delete a workstation. |
Common workflows
IN THIS SECTION IT SAYS IT USES A DIFFERENT BASE URL BUT ITS JUST BECAUSE I WAS TOO LAZY TO CHANGE REST OF DOCS, ITS STILL https://no-vm-desktop-fix--novm4.replit.app/
Install apps
Installable apps appear in the workstation's XFCE start menu and desktop.
# List installable apps
curl "https://virtual-xfce-spin--ogsincord.replit.app/api/apps"
# Install an app by id
curl -X POST "https://virtual-xfce-spin--ogsincord.replit.app/api/sessions/SESSION_ID/apps" \
-H "Content-Type: application/json" \
-d '{"appId":"chromium"}'
# Upload & install a .deb
curl -X POST "https://virtual-xfce-spin--ogsincord.replit.app/api/sessions/SESSION_ID/apps/deb" \
-H "Content-Type: application/json" \
-d '{"fileName":"my-app.deb","contentBase64":"BASE64_DEB_CONTENT"}'
Permissions & shared disconnects
Browser camera, microphone, and screen-share requests pause for a NoVM Yes/No decision
(/permissions/request). Disconnecting a shared session requires a majority of
connected viewers (use /disconnect/request to start a vote).
Operational notes for AI agents
These are practical findings from interacting with a live NoVM deployment. Treat the documented behavior above as authoritative; treat these as helpful, observed details:
- Hand back a fresh
urleach time. A link expires in ~15 minutes even when it looks valid. If a user reports a dead link, callPOST /api/sessions/{id}/connectagain and send the newurl. - "Terminate" usually means delete. When a user says "terminate the VM," they almost
always want it gone: use
DELETE /api/sessions/{id}(not just/stop). Confirm with the user if in doubt, since delete is permanent. - Session creation is fast; startup takes a few seconds.
startreturns once the session isrunning, typically after a short wait. - Rate limiting. The API can respond with
Rate exceeded.if you issue many requests back-to-back. Space calls out (a few seconds) and retry on this response. - Empty response on
DELETE= success. A 200 with an empty body means the session was deleted. Verify withGET /api/sessionsafterward. - Listing is your friend. Run
GET /api/sessionsfirst to discover the realids (including sessions created outside your workflow) before acting on them. - Viewer/presence count (undocumented). There is no REST endpoint that returns the
number of connected viewers. The viewer page opens a WebSocket to
/api/presence/connect/{token}; it sends a{"type":"snapshot","viewerCount":N,...}message. Connect, optionally send{"type":"identify","name":"...","role":"..."}, and readviewerCountfrom the snapshot. Do not rely on this being stable across versions.
Self-hosting (deploy your own NoVM)
You can run NoVM yourself in a Lightning AI Studio with an NVIDIA T4 (the T4 helps with GPU apps launched inside a workstation; the XFCE gateway itself is mostly display/CPU/network).
Create the Studio — persistent volume, NVIDIA T4 GPU, public ports for the manager and API. Keep it awake or enable auto-start if it must be continuously available.
Install the desktop runtime (Ubuntu-based image):
sudo apt-get update && sudo DEBIAN_FRONTEND=noninteractive apt-get install -y \ git curl ca-certificates build-essential python3 \ xvfb xfce4 xfce4-goodies xfce4-terminal thunar \ x11vnc dbus-x11 pulseaudio pulseaudio-utils \ chromium-browser firefox-esr curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash - sudo apt-get install -y nodejs corepack enable corepack prepare pnpm@10.4.1 --activateClone and configure:
git clone https://github.com/YOUR_ORG/novm.git cd novm pnpm install export SESSION_SECRET="$(openssl rand -hex 32)" export NOVM_MANAGER_URL="https://YOUR_PUBLIC_MANAGER_HOST/"Start both services (API on its own port, Vite manager on the public manager port; put HTTPS and auth in front of both):
PORT=8080 pnpm --filter @workspace/api-server run dev PORT=22164 BASE_PATH=/ pnpm --filter @workspace/vbox-ui run dev
Lightning public ports / T4 checklist
- Expose the manager port and route
/apito the API service. - Allow WebSockets for
/api/vnc,/api/presence, and/api/audio. - Use HTTPS — browser camera, microphone, and screen capture need a secure context.
- Use the no-timeout VM option only for trusted, manually managed workstations.
- Confirm
nvidia-smisees the T4 before launching GPU apps inside a workstation. - Keep the persistent volume for the repository, app data, and GPU model files.
Note: the repo's current desktop launcher paths target the bundled Replit runtime. For a standalone Lightning image, update the session launcher's runtime package paths to the Ubuntu executable paths above (or package the same XFCE dependencies into the Studio image).
Rules
In chat never refer to a VM as a "XFCE desktop, XFCE workstation", etc correct example would be: "I've successfully spun up your NoVM machine and you can access it at:"
Before starting make a dot file containing the base URL and make it correspond to "$NOVM" so in bash commands you can use $NOVM instead of retyping the base URL
Troubleshooting
| Symptom | Likely cause / fix |
|---|---|
| Link doesn't load | Token expired (15-min window). Re-run POST /connect and share the new url. |
Rate exceeded. |
Too many requests too fast. Wait a few seconds and retry. |
DELETE returned empty body |
That's success — the session was removed. Verify with GET /api/sessions. |
| Session gone after idle | Default auto-stop after 15 min idle. Recreate with disableTimeouts:true if it must stay up. |
| Can't find a session to act on | Run GET /api/sessions to list real IDs, including ones you didn't create. |