web-terminal-server

command module
v1.6.26 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 15, 2026 License: GPL-2.0, GPL-3.0 Imports: 25 Imported by: 0

README

web-terminal-server

Image Size Platforms Test coverage OpenSSF Best Practices OpenSSF Scorecard SBOM

A small, generic web terminal: it runs a configured command in a PTY and serves the @cplieger/web-terminal-ui front end over HTTP + WebSocket, built on the github.com/cplieger/web-terminal-engine engine. A native-touch terminal in the browser for any command — phone and desktop alike.

Published as a multi-arch (amd64 + arm64) container image on GHCR (ghcr.io/cplieger/web-terminal-server) and Docker Hub (cplieger/web-terminal-server).

⚠️ Security: this is a remote shell

Anyone who can reach the server and pass auth (if configured) gets an interactive process running WT_CMD with this server's privileges. Treat it like exposing SSH.

  • The binary binds 127.0.0.1 by default. Reachable only from the same host until you change WT_ADDR.
  • The container image binds :7681 (it has to, to be reachable via a published port) and so is unauthenticated and network-exposed by default. Before exposing it beyond a trusted host, do one of:
    • set WT_PASSWORD (enables HTTP Basic auth on every route, including the WebSocket handshake), and/or
    • front it with an authenticating reverse proxy (Caddy + forward-auth, oauth2-proxy, Authentik, …), and/or
    • keep the published port bound to loopback / a private network only.
  • The server logs a loud warning at startup when it is listening on a non-loopback address without WT_PASSWORD set.

Built-in Basic auth is a convenience for simple setups; a reverse proxy with real identity is the recommended posture for anything internet-facing. The process runs as the container user (root by default) — restrict it with a non-root WT_CMD target, a read-only root filesystem, dropped capabilities, and a scoped work directory as your threat model requires.

Run

docker run --rm -p 127.0.0.1:7681:7681 \
  -e WT_PASSWORD=changeme \
  -v "$PWD":/work -e WT_WORKDIR=/work \
  ghcr.io/cplieger/web-terminal-server

Open http://127.0.0.1:7681. The example binds the published port to loopback and sets a password; adjust for your environment.

Configuration

All configuration is via environment variables:

Variable Default (binary / image) Purpose
WT_ADDR 127.0.0.1:7681 / :7681 Listen address. The binary defaults to loopback; the image must listen on all interfaces.
WT_CMD /bin/bash Command to run in the PTY, whitespace-split (use a wrapper script for complex commands).
WT_WORKDIR (process default) Working directory for the command. Must be an existing directory if set.
WT_SCROLLBACK 5000 Lines of scrollback the server retains for reconnect replay.
WT_IDLE_REAPER (unset → disabled) Go duration (e.g. 30m); when > 0, idle sessions are reaped after this long.
WT_USERNAME admin Basic-auth username (only used when WT_PASSWORD is set).
WT_PASSWORD (unset → no auth) Basic-auth password. When set, every route (including /ws) requires it.
WT_TRUSTED_PROXIES (unset → socket peer) Comma-separated reverse-proxy CIDRs / bare IPs whose X-Forwarded-For the access log trusts to resolve client_ip. See Client IP logging.

Endpoints: / (UI), /ws?session=<id> (per-session terminal WebSocket), /api/sessions (create/list/close), /api/sessions/events (status SSE), /healthz (readiness).

Client IP logging

The access log records a client_ip per request. By default (WT_TRUSTED_PROXIES unset) it logs the direct socket peer and ignores any X-Forwarded-For header, so the logged IP cannot be spoofed; that's the correct choice when the server is directly exposed. Behind a reverse proxy the socket peer is the proxy, not the user, so set WT_TRUSTED_PROXIES to the proxy's address(es), a comma-separated list of CIDRs or bare IPs (e.g. WT_TRUSTED_PROXIES=10.0.0.0/8,192.0.2.10), and the log resolves the real client from a trusted X-Forwarded-For. Only a request whose socket peer is inside the set has its X-Forwarded-For trusted (spoof-safe); a malformed entry is logged and skipped rather than aborting startup. Log timestamps are UTC regardless of the container's TZ, so lines stay zone-stable for ingest.

How it fits together

github.com/cplieger/web-terminal-engine   (Go engine: PTY + VT screen + wire protocol)
        │
        ├── terminal.NewSessionManager ─────►  this server (main.go)
        │
@cplieger/web-terminal-engine  +  @cplieger/web-terminal-ui   (TS engine + UI)
        └── compiled to static/vendor/ at image build, served to the browser

The server is deliberately thin: env parsing, terminal.NewSessionManager, the session REST API + status SSE, a create rate limit, static file serving, optional Basic auth, and graceful shutdown. All terminal behavior lives in the engine and UI packages.

The web-terminal family:

Apps built on the same engine:

  • vibekit — a chat-first browser front end for the Kiro CLI (chat history, MCP, editor, git/forge workflows).
  • web-terminal-kiro — a touch-first, multi-tab browser terminal wired to the Kiro CLI (kiro-cli), on desktop or phone.

Disclaimer

This project is built with care and follows security best practices, but it is intended for personal / self-hosted use. No guarantees of fitness for production environments. Use at your own risk.

This project was built with AI-assisted tooling using Claude Opus and Kiro. The human maintainer defines architecture, supervises implementation, and makes all final decisions.

License

GPL-3.0-or-later. See LICENSE.

Documentation

Overview

Command web-terminal-server is a thin, generic web terminal: it runs a configured command in a PTY and serves the @cplieger/web-terminal-ui front end over HTTP + WebSocket, using the github.com/cplieger/web-terminal-engine engine.

SECURITY: this is a remote shell. Anyone who can reach the listen address and pass auth (if any) gets an interactive process running WT_CMD with this server's privileges. It binds loopback (127.0.0.1) by default; only expose it on a public interface behind an authenticating reverse proxy, or set WT_PASSWORD. See README.md.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL