Server Edition
Server Edition runs the same nodeterm canvas from a headless Linux server and serves it to your browser. It boots the same core services the desktop app uses and ships the same built UI, so the React interface is unchanged — you’re just reaching it over the network instead of through a desktop window.
What it is
Section titled “What it is”The server serves the same built renderer the desktop app runs and speaks a WebSocket protocol to it. A browser-side shim stands in for the desktop’s native bridge, so the canvas, the nodes and the panels behave the same. Terminals get the same tmux session continuity — they survive an app restart, and cold-restore after a reboot.
The server is a plain Node process — it runs on any Linux box: an Ubuntu or Debian server, a VPS, a homelab machine, a container host. There are two ways to run it.
What you need
Section titled “What you need”On a bare Ubuntu/Debian server:
- Node 22 (the version the official image runs)
- git and tmux —
sudo apt install tmux git. tmux is what gives terminals their session continuity; without it they fall back to a plain shell. - The agent CLIs you want (
claude,codex,gemini) installed for the server user — agents run on the server, so this is where they live and authenticate.
Docker (recommended for a server)
Section titled “Docker (recommended for a server)”The repo ships a multi-stage Dockerfile that builds everything and produces a slim runtime
image with tmux, git and curl included:
docker build -t nodeterm-server .docker run -d -p 8443:8443 \ -e NODETERM_SERVER_PASSWORD='choose-a-strong-one' \ -v nodeterm-data:/data \ nodeterm-server/data must be a volume — auth, sessions, workspace and scrollback snapshots live there;
without it every restart forgets the password and the canvas. On Dokploy (or any
Dockerfile-based PaaS): create an app from the repo, attach a volume at /data, set
NODETERM_SERVER_PASSWORD, and point a domain at container port 8443 — the platform’s proxy
terminates TLS.
Agent CLIs aren’t baked into the image — install them into the running container (or extend the image) and sign in from a terminal node.
From a checkout
Section titled “From a checkout”npm run server:devThat builds the renderer and the server bundle, then starts it. On a repeat run where the
renderer is already built, npm run server:start skips straight to launching. Useful flags:
--port (default 8443), --host (default 127.0.0.1), --data-dir (default
~/.nodeterm-server).
First run prints a one-time setup URL to the console:
Setup: http://127.0.0.1:8443/setup?token=<token>Open it, choose a password (minimum 8 characters), and you’re signed in. The setup token is
single-use and lives only in memory. For containers or CI, seed the password out-of-band with
NODETERM_SERVER_PASSWORD on first boot and skip the setup URL entirely.
Auth is single-user: one password, hashed with scrypt and stored on the server; on
login the server sets an HttpOnly session cookie scoped to the browser. There are no
accounts or roles.
What works in the browser
Section titled “What works in the browser”Most of the app runs in the browser:
- Terminals — spawn, I/O, resize, and tmux continuity.
- Editor & diff nodes — Monaco reads and writes files; ⌘S saves.
- Source Control — stage / unstage / discard, diff, branch switch, commit + push, and the ✦ AI commit message.
- Explorer & folder picker — there’s no native OS dialog in a browser, so “Open folder…” uses an in-app server-directory browser that navigates the server’s own filesystem.
- Agent observability — agent-status badges, subagent cards, and the context-window meter all work, driven by the same hook pipeline as the desktop app.
Two surfaces are not yet wired into the browser bridge:
- Chat nodes — the SDK-driven Claude chat node is deferred.
- Canvas control — the agent canvas-control verbs are not wired over the server.
Security notes
Section titled “Security notes”- Single user. One password; sessions are per-browser, with a lockout after repeated failed logins.
- Origin check. The WebSocket upgrade requires a valid session cookie and, when the
browser sends one, an
Originthat matches the host — this blocks cross-site WebSocket hijacking. - Loopback hook server. The agent-status hook server binds only
127.0.0.1, and a forged hook POST is defended by jailing the transcript path it carries before anything reads it.