A web-based terminal application that provides browser-based access to shell sessions through WebSocket.
- Multi-session support: Create and manage multiple terminal sessions
- Default session on startup: A
defaultsession is created automatically at server start - WebSocket-based: Real-time terminal I/O using xterm.js
- RESTful API: Manage sessions via HTTP endpoints
- Browser-based: Access terminals from any modern web browser
- Cookie-based Authentication: Secure session management with web login
- File downloads: Download files directly from the terminal using OSC escape sequences
Linux/macOS:
curl -sL "https://github.com/iwanhae/terminal-hub/releases/download/v1.3.1/terminal-hub_1.3.1_$(uname -s | tr '[:upper:]' '[:lower:]')_$(uname -m | sed 's/x86_64/amd64/' | sed 's/aarch64/arm64/').tar.gz" | tar xz && ./terminal-hubWindows (PowerShell):
Invoke-WebRequest -Uri "https://github.com/iwanhae/terminal-hub/releases/download/v1.3.1/terminal-hub_1.3.1_windows_amd64.zip" -OutFile "terminal-hub.zip"
Expand-Archive -Path "terminal-hub.zip" -DestinationPath .
.\terminal-hub.exeNote: This command detects your OS/architecture, downloads the release version embedded in the URL, and runs it. Access the terminal at http://localhost:8081.
docker run -p 8081:8081 ghcr.io/iwanhae/terminal-hub:latestTerminal Hub Docker image is designed to persist data via the container user's HOME directory (/home/ubuntu).
Mount a volume to /home/ubuntu to keep files, tool installations, and shell config across container recreation.
Recommended (named volume):
docker volume create terminal-hub-home
docker run -d --name terminal-hub \
-p 8081:8081 \
-v terminal-hub-home:/home/ubuntu \
ghcr.io/iwanhae/terminal-hub:latestBind mount (host directory):
mkdir -p ./terminal-hub-home
docker run -d --name terminal-hub \
-p 8081:8081 \
-v "$(pwd)/terminal-hub-home:/home/ubuntu" \
ghcr.io/iwanhae/terminal-hub:latestdocker-compose example:
services:
terminal-hub:
image: ghcr.io/iwanhae/terminal-hub:latest
ports:
- "8081:8081"
volumes:
- terminal-hub-home:/home/ubuntu
volumes:
terminal-hub-home:Important notes:
- Persisted: files under
/home/ubuntu(shell profile, git config, downloaded files, user-installed tools). - Not persisted: in-memory terminal sessions themselves (session processes end when container/app stops).
- First run initializes the volume from image defaults. If you need re-initialization, remove
/home/ubuntu/.terminal-hub-initializedinside the volume and restart with care. - If no volume is mounted, data is ephemeral and lost when the container is removed.
- If you have any suggestion on default supported tools, PR is welcomed.
PATH tip (${HOME}/.local/bin):
The image includes ${HOME}/.local/bin in PATH. If you want binaries to be available permanently across restarts, place them in:
/home/ubuntu/.local/binThis works best with a persistent /home/ubuntu volume.
# Clone repository
git clone https://github.com/iwanhae/terminal-hub.git
cd terminal-hub
# Build the application
make build
# Run the server
./build/terminal-hub
# Access the terminal at http://localhost:8081Terminal Hub supports cookie-based session authentication for secure access. When enabled, users must log in via a web form to access the terminal interface.
Set the following environment variables:
TERMINAL_HUB_USERNAME- Username for authenticationTERMINAL_HUB_PASSWORD- Password for authenticationTERMINAL_HUB_SESSION_TTL(optional) - Session duration (default: "24h")
If both username and password are set, authentication is required for all access. If either is missing or empty, the application runs in open mode.
Login brute-force protection is enabled on the login endpoint: 10 failed attempts from the same IP triggers a 1-hour temporary ban.
Using environment variables:
export TERMINAL_HUB_USERNAME=admin
export TERMINAL_HUB_PASSWORD=your-secure-password
./build/terminal-hubWith custom session duration:
export TERMINAL_HUB_SESSION_TTL=12h
./build/terminal-hubUsing Docker:
docker run -p 8081:8081 \
-e TERMINAL_HUB_USERNAME=admin \
-e TERMINAL_HUB_PASSWORD=your-secure-password \
ghcr.io/iwanhae/terminal-hub:latestUsing docker-compose:
services:
terminal-hub:
image: ghcr.io/iwanhae/terminal-hub:latest
ports:
- "8081:8081"
environment:
TERMINAL_HUB_USERNAME: admin
TERMINAL_HUB_PASSWORD: your-secure-password
TERMINAL_HUB_SESSION_TTL: "24h"- HttpOnly Cookies: Prevents JavaScript access (XSS protection)
- Secure Flag: Only sent over HTTPS when available
- SameSite=Strict: Prevents CSRF attacks
- Sliding Expiration: Sessions extend with activity (up to TTL)
- Cryptographic Tokens: 256-bit random session tokens
- Background Cleanup: Expired sessions removed every 5 minutes
- IP Fail2Ban: 10 failed logins from one IP are blocked for 1 hour
- HTTPS Recommended: Session cookies are more secure over HTTPS. Enable HTTPS in production environments.
- Strong Passwords: Use strong, unique passwords for
TERMINAL_HUB_PASSWORD. - Environment Variable Security: Be careful how you set environment variables:
- Don't commit credentials to git
- Use secrets management in production (Docker secrets, Kubernetes secrets, etc.)
- Use
.envfiles with proper file permissions (add to.gitignore)
- Session Management: Users are automatically logged out after the session TTL period of inactivity.
Terminal Hub supports downloading files directly from the terminal to your browser using OSC (Operating System Command) escape sequences. The terminal uses REST API endpoints for the actual file transmission, providing browser-native download support with progress indicators.
- An OSC escape sequence is emitted in the terminal output
- The frontend detects the sequence and extracts the file path
- A REST API call is made to
/api/downloadto retrieve the file - The browser downloads the file using its native download manager
You can emit the OSC sequence directly from your shell:
# Syntax: printf '\033]FILE;download:path=<absolute-path>,name=<filename>\007'
printf '\033]FILE;download:path=/tmp/myfile.txt,name=myfile.txt\007'Source the helper script in your shell:
source /path/to/scripts/download-helper.sh
# Download with default filename (uses original filename)
download-file /path/to/file.txt
# Download with custom filename
download-file /path/to/file.txt custom-name.txt
# Using the alias
dl /path/to/file.txt custom-name.txtGET /api/download?path=<file-path>&filename=<optional-filename>- Download a file
- Path validation: Only absolute paths are allowed
- Path traversal protection: Directory traversal attacks are blocked
- File size limits: Configurable via
TERMINAL_HUB_MAX_DOWNLOAD_SIZE(default: 100MB) - Directory prevention: Cannot download directories
- Filename sanitization: Dangerous characters are removed from filenames
- Authentication: Uses the same authentication as other endpoints
# Maximum download size in bytes (default: 100MB)
export TERMINAL_HUB_MAX_DOWNLOAD_SIZE=104857600# Create a test file
echo "Hello World" > /tmp/test.txt
# Trigger download
printf '\033]FILE;download:path=/tmp/test.txt,name=test.txt\007'
# The file will appear in your browser's download managerDownload not starting:
- Verify the file exists and is readable by the server
- Check that the path is absolute (starts with
/) - Ensure the file size is within the configured limit
- Check browser console for errors
Error message in terminal:
- Red
[Download Error]messages indicate server-side errors - Check the server logs for details
See CLAUDE.md for detailed development documentation including:
- Build commands
- Testing instructions
- Architecture overview
- Code organization
POST /api/auth/login- Login with username/password (sets session cookie, returns429when IP is temporarily banned)POST /api/auth/logout- Logout (clears session cookie)GET /api/auth/status- Get current authentication status
GET /api/sessions- List all sessionsPOST /api/sessions- Create a new sessionPUT /api/sessions/:id- Update session nameDELETE /api/sessions/:id- Delete a session
GET /api/download?path=<file-path>&filename=<optional-name>- Download a file
WS /ws/:sessionId- Connect to a terminal session
Bug Fixes:
- Fixed authentication status API to correctly return
authenticated: truewhen authentication is not configured. Users were incorrectly redirected to the login page when running in open mode (withoutTERMINAL_HUB_USERNAMEandTERMINAL_HUB_PASSWORDset).
Initial Release:
- Multi-session terminal support via WebSocket
- Cookie-based authentication with session management
- RESTful API for session management
- File download support via OSC escape sequences
- Embedded React frontend with xterm.js
[Add your license here]