When no WebSocket client is connected, the session's readLoop still processes every byte of terminal output through the go-te VT parser (tracker.Feed), Screen.Draw grapheme segmentation, and string allocations — even though nobody is consuming the screen state. For programs like btop inside tmux that produce continuous full-screen redraws, this causes sustained CPU usage and GC pressure over hours. Fix: after a 10-second idle threshold (no client connected), skip tracker.Feed() and only maintain the replay buffer. When a client reconnects (UpdateConnector) or a screenshot is requested (GetScreenSnapshot), rebuild the tracker by replaying the buffer through a fresh VT parser instance. Changes: - Add idleSince atomic timestamp + MarkIdle() to Session interface - handleOutput() skips tracker.Feed when idle > threshold - UpdateConnector() clears idle flag and rebuilds tracker from replay - GetScreenSnapshot() rebuilds stale tracker on-demand for screenshots - Wire MarkIdle() call into handleWebSocket cleanup (client disconnect) - Add TestIdleTrackerPauseAndRebuild covering the full lifecycle
webterm
webterm serves terminal sessions over HTTP/WebSocket, with a dashboard mode for multiple sessions and live-updating terminal tiles that is perfect for running multiple AI coding agents in agentbox.
This repository is the Go port of the original Python implementation, which is preserved in the python branch.
https://github.com/user-attachments/assets/62c52183-83a3-4fb5-97b1-ed001de4f53a
Features
- Typeahead find for quickly finding and launching sessions with minimal friction
- Web terminal with reconnect support
- Ghostty WebAssembly terminal engine for fast rendering from
ghostty-web - Session dashboard with live SVG screenshots from
go-te - Docker watch mode (
webterm-command/webterm-themelabels) - Docker compose manifest ingestion
- CPU sparkline tiles for compose services
- SSE activity updates for fast dashboard refresh
- Mobile/touch support with virtual keyboard + draggable keybar
- Theme/font controls for terminal rendering
- Vendored Nerd Font assets with full glyph/icon support (no external font fetch required)
Install
Quick install
go install github.com/rcarmo/webterm/cmd/webterm@latest
Build from source
git clone https://github.com/rcarmo/webterm.git
cd webterm
mkdir -p bin
go build -o ./bin/webterm ./cmd/webterm
The command above produces bin/webterm; you can also build it from repo root with make build-go.
Quick start
Run a default shell session:
go run ./cmd/webterm
Run a specific command:
go run ./cmd/webterm -- htop
Then open http://localhost:8080.
Dashboard modes
Landing manifest
- name: Logs
slug: logs
command: docker logs -f my-service
theme: nord
go run ./cmd/webterm -- --landing-manifest ./landing.yaml
Docker watch
go run ./cmd/webterm -- --docker-watch
Containers with these labels become tiles:
webterm-command: command string, orautofor Docker execwebterm-theme: theme name (fallback isxtermpalette)
Available themes: tango, xterm, monokai, monokai-pro, ristretto, dark, light, dracula, catppuccin, nord, gruvbox, solarized, tokyo, miasma, github, gotham
Compose manifest
go run ./cmd/webterm -- --compose-manifest ./docker-compose.yaml
Environment variables
WEBTERM_STATIC_PATH: override static asset directoryWEBTERM_DOCKER_USERNAME: user for Docker exec sessionsWEBTERM_DOCKER_AUTO_COMMAND: override auto command (/bin/bashdefault)WEBTERM_SCREENSHOT_FORCE_REDRAW: force redraw before screenshots (true/1/yes/on)DOCKER_HOST: Docker daemon endpoint override
Development (Makefile-first)
make install-dev
make check
make race
make test
make bump-patch
Frontend bundle tasks:
make build
make build-fast
make bundle-watch
Docker
docker build -t webterm .
docker run -v /var/run/docker.sock:/var/run/docker.sock -p 8080:8080 webterm --docker-watch
Web assets are embedded in the Go binary by default (you can still override with WEBTERM_STATIC_PATH).
The Dockerfile uses a minimal Alpine runtime stage and only installs ca-certificates plus docker-cli.