Terminal

The terminal provides interactive shell access inside a running preview container via xterm.js.

Requirements#

  • Admin or Owner role required — the terminal WebSocket closes with code 4003 ("Insufficient permissions") if your role is Member or below
  • A preview container is started automatically on connect if one is not already running

Accessing the Terminal#

  1. Open a project in the Command Center.
  2. Click the Terminal button in the project header bar, or open the bottom panel and select the Terminal tab.
  3. The terminal connects automatically. If no container is running, the server starts one and shows a status message while it initializes.
  4. Type commands directly in the shell. The working directory defaults to /workspace.

Features#

  • Full terminal emulation — xterm.js provides complete terminal behavior including keyboard shortcuts, cursor movement, and control sequences
  • Base64 I/O — All input and output is transmitted as base64 over WebSocket for binary safety
  • Manual reconnect — If the WebSocket connection closes unexpectedly (any close code other than 1000/1001), a Reconnect button appears to re-establish the session; a normal clean disconnect shows a "disconnected" status without a Reconnect button
  • Auto-resize — Terminal adapts to panel and window size changes
  • Sandbox isolation — Commands run inside the preview container (or a dedicated terminal container), isolated from the host system
  • Multiple terminals — Open up to 4 terminal tabs within the panel; close individual tabs when done
  • Split view — Click the split icon in the terminal toolbar to display two terminals side by side

Session Tracking#

Terminal sessions are recorded in the database for audit purposes:

  • Who opened the session (user ID)
  • When the session started and ended
  • Which project the session was for

Session records are readable by any workspace member via the API (GET /api/projects/{project_id}/terminal/sessions).

WebSocket Protocol#

The terminal WebSocket (/api/ws/terminal/{project_id}) supports these message types:

TypeDirectionDescription
inputClient → ServerTerminal keystrokes (base64-encoded)
resizeClient → ServerTerminal dimensions (cols, rows)
pongClient → ServerSilently accepted (no-op); the terminal endpoint does not send ping messages
outputServer → ClientTerminal output (base64-encoded)
statusServer → ClientStatus updates (e.g., "Starting preview container…")
sessionServer → ClientSession ID assigned to this connection (session_id field); not sent when the server falls back to a terminal-only container (empty workspace)
errorServer → ClientRate-limit or protocol error before the connection is closed

If no preview container is running when you connect, the server first attempts to start the project's preview container. If that is unavailable (e.g., empty workspace), it falls back to a lightweight terminal-only container (sleep infinity) scoped to the connection.

Troubleshooting#

SymptomLikely causeResolution
"Connection failed" error after connectingContainer failed to start or WebSocket closed unexpectedlyClick Reconnect to retry
Status spinner never clearsPreview container is slow to initializeWait a few seconds; if it persists, stop and restart the preview from the header bar
"Insufficient permissions" (close code 4003)Your role is Member — terminal requires Admin or OwnerContact your workspace Owner to upgrade your role
Commands produce no outputContainer may have exitedReconnect; if the issue recurs, restart the preview container
Terminal renders garbled charactersWindow resize event missed on initial loadWait a moment for the ResizeObserver to fire and call fitAddon.fit() automatically; if it persists, disconnect and reconnect