NickCis/remote-development-environment
remote-development-environment
A small Node.js web app (node-pty + xterm.js) that runs bash in a PTY and renders it in the browser. Optional passkey (WebAuthn) login and CLI token auth for exposing the service securely.
Installation (Ubuntu server)
Requirements: Node.js (LTS 18+). On Ubuntu:
sudo apt update && sudo apt install nodejs npmOr use NodeSource / nvm for a recent Node version.
Install the app:
cd /path/to/remote-development-environment
chmod +x install.sh # if needed after clone
./install.shThis runs npm install in the project directory.
Run as a systemd user service (so it keeps running under your user, survives reboots when you use linger):
./install.sh --systemd
# Or install in a specific directory and set up the service:
./install.sh /home/you/repos/remote-development-environment --systemdThen enable and start the service:
systemctl --user daemon-reload
systemctl --user enable --now remote-development-environmentTo have the service run when the server is up but no one is logged in:
loginctl enable-linger $USERUseful commands:
systemctl --user status remote-development-environmentjournalctl --user -u remote-development-environment -f
By default the systemd service uses your home directory as the PTY cwd and ~/.config/remote-development-environment-auth.json as the auth file. Override with CWD and/or AUTH_FILE:
CWD=/path/to/cwd AUTH_FILE=/path/to/auth.json ./install.sh --systemdAuthentication
The server can run with or without authentication.
Without auth (development / local only)
node server.js --no-authNo passkey or token is required. Anyone who can reach the server can use the terminal.
With auth (exposed server)
When you run without --no-auth, the server expects either:
- Browser: sign in with a passkey (WebAuthn). After login you get a session cookie and can use the web terminal.
- CLI / tunnel: use a token (see “CLI token” below). The token is sent via
--tokenorTERMINAL_TOKEN.
First-time setup (create a passkey):
-
Run the server in setup mode (web UI is only for registering a passkey):
node server.js --setup-passkey --cwd . --auth-file ./auth-data.json --all -
Open the app in the browser. You’ll see “Register passkey”. Click Create passkey and complete the flow (e.g. Touch ID, Windows Hello, or a security key).
-
After “Passkey created”, stop the server and run it without
--setup-passkey:node server.js --cwd . --auth-file ./auth-data.json --all
Normal use (browser):
- Open the app in the browser.
- You’ll see “Sign in”. Click Sign in with passkey and authenticate.
- You’re in the terminal. Sessions work as described below.
CLI token (for connect.js and tunnel.py):
The CLI cannot do passkey login. You use a token that the server issues after you’ve logged in in the browser:
-
Log in in the browser (passkey) as above.
-
Open the toolbox (⌘, top right) and click Generate CLI token.
-
The token is copied to the clipboard (or shown in the console). Use it with the CLI:
node connect.js --token YOUR_TOKEN new # or TERMINAL_TOKEN=YOUR_TOKEN node connect.js new python tunnel.py --token YOUR_TOKEN 8080
Tokens are stored (as hashes) in the auth file (same file as passkey credentials). They don’t expire unless you delete them from that file.
Summary:
| Mode | Flag / behaviour | Browser | CLI / tunnel |
|---|---|---|---|
| No auth | --no-auth |
No login | No token |
| Setup (first time) | --setup-passkey |
Register passkey only | N/A |
| Normal (auth on) | default | Passkey login | Token (--token) |
Run (quick start)
npm install
npm startThen open http://localhost:3847 in your browser (default port is 3847). You get a full bash session; resize the window and the PTY dimensions follow.
Server options (see node server.js --help):
-p, --port— port (default 3847)-H, --host/--all— bind address (--all= 0.0.0.0)--no-auth— no passkey or token--setup-passkey— web UI only for passkey registration--cwd <path>— working directory for the server / PTY (default: current directory)--auth-file <path>— path to auth JSON file (default:auth-data.jsonin cwd)
Sessions (UUID)
Sessions are identified by a UUID and are shared between web and CLI.
- Web: Use
?session=<uuid>to attach to a session, or?session=newto create a new one. If you omitsession, you attach to the last used session (or a new one). The page URL updates to show the current session ID. - CLI:
node connect.js <uuid>to attach, ornode connect.js newto create. With no argument, attaches to the last session. The script printsSession: <uuid>to stderr on connect.
Examples:
npm run connect # last session, or new
npm run connect new # new session
npm run connect abc-123 # attach to session abc-123
npm run connect --list # list active session IDs (then exit)
npm run connect --resize new # new session and reset PTY size
npm run connect --token TOKEN new # with auth tokenSame session from the web: open http://localhost:3847?session=abc-123. Set TERMINAL_URL=ws://host:port if the server is not on localhost:3847.
Web UI
- Sessions (≡, top left): Menu with “New session” and all active session IDs; click one to switch (page reloads into that session).
- Toolbox (⌘, top right): “Resize to screen”, modifier toggles (Ctrl, Alt, Meta), Esc/Tab/Enter/arrow keys, “Show keyboard” (bar of special keys below the terminal, handy on mobile). When auth is on, Generate CLI token appears here.
Stack
- Server: Express,
node-pty(spawns bash),ws(WebSocket),@simplewebauthn/server(passkeys) - Client: xterm.js + xterm-addon-fit, WebSocket,
@simplewebauthn/browser(passkeys)