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#
- Open a project in the Command Center.
- Click the Terminal button in the project header bar, or open the bottom panel and select the Terminal tab.
- The terminal connects automatically. If no container is running, the server starts one and shows a status message while it initializes.
- 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:
| Type | Direction | Description |
|---|---|---|
input | Client → Server | Terminal keystrokes (base64-encoded) |
resize | Client → Server | Terminal dimensions (cols, rows) |
pong | Client → Server | Silently accepted (no-op); the terminal endpoint does not send ping messages |
output | Server → Client | Terminal output (base64-encoded) |
status | Server → Client | Status updates (e.g., "Starting preview container…") |
session | Server → Client | Session ID assigned to this connection (session_id field); not sent when the server falls back to a terminal-only container (empty workspace) |
error | Server → Client | Rate-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#
| Symptom | Likely cause | Resolution |
|---|---|---|
| "Connection failed" error after connecting | Container failed to start or WebSocket closed unexpectedly | Click Reconnect to retry |
| Status spinner never clears | Preview container is slow to initialize | Wait 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 Owner | Contact your workspace Owner to upgrade your role |
| Commands produce no output | Container may have exited | Reconnect; if the issue recurs, restart the preview container |
| Terminal renders garbled characters | Window resize event missed on initial load | Wait a moment for the ResizeObserver to fire and call fitAddon.fit() automatically; if it persists, disconnect and reconnect |