Self-hosted web UI for Claude Code CLI — access AI-powered development from any browser
Flask + xterm.js. No build step. Vendored dependencies. Bundled Python 3.12 runtime. One command to run.
- Overview
- Quick Start
- Features
- Requirements
- Installation
- Configuration
- Remote Servers
- Architecture
- Security
- Troubleshooting
- Disclaimer
- License
QN Code Assistant is a self-hosted web interface that wraps the Claude Code CLI, giving you browser-based access from any device on your network — laptops, phones, tablets.
Why this exists: Claude Code is a powerful CLI tool, but it chains you to a local terminal. QN Code Assistant breaks that constraint. Run it on a server and work from any browser, on any device, from anywhere on your network.
Key capabilities:
- Browser-based Claude Code access — full PTY-backed terminal emulation via xterm.js, with tmux session persistence that survives disconnects and browser refreshes
- AI agent team deployment — launch and orchestrate 20+ specialized Claude Code agents (Architect, Builder, Tester, SecOps, DevOps) directly from the UI
- Autonomous mode — unattended Claude Code sessions with configurable auto-restart and task management
- Remote server management — guided 5-step SSH wizard for adding remote hosts, with autonomous key generation and exchange
- Multi-user auth with RBAC — password-protected access, multiple user accounts, role-based permissions
- Pure Python, zero Node.js — 14 Python packages vendored in
vendor/, no pip install at runtime, no npm, no build step
# 1. Download the latest self-extracting installer
curl -LO https://github.com/rayketcham-lab/qn-claude-web/releases/latest/download/qn-code-assistant-installer.sh
# 2. Install
bash qn-code-assistant-installer.sh
# 3. Access
# Open http://<server-ip>:5001 in your browserOr clone and run directly (requires system Python 3.12+):
git clone https://github.com/rayketcham-lab/qn-claude-web.git
cd qn-claude-web
python3 app.pyOpen http://localhost:5001. For LAN access: http://<server-ip>:5001
| Feature | Description |
|---|---|
| PTY Terminal | Full xterm.js terminal with PTY support and multiple tabs |
| tmux Persistence | Sessions survive browser disconnects and page refreshes |
| Autonomous Mode | Unattended Claude Code sessions with auto-restart |
| Agent Teams | Launch Architect, Builder, Tester, SecOps, DevOps agents |
| Git Integration | Branch and status display in the terminal toolbar |
| Feature | Description |
|---|---|
| Persistent Sessions | Conversations saved to disk, searchable by content |
| Streaming | Responses stream in real time with markdown rendering |
| Export | Save any chat session as a markdown file |
| Session Search | Full-text search across all saved conversations |
| Feature | Description |
|---|---|
| Project Browser | Directory navigation with auto-detection of project types |
| File Browser | Syntax-highlighted file viewing with breadcrumb navigation |
| CLAUDE.md Wizard | One-click deploy of project instructions and agent configs |
| Feature | Description |
|---|---|
| Remote Servers | SSH and mount-based remote host support with guided setup wizard |
| Multi-user Auth | Password-protected access with RBAC — admin and user roles |
| HTTPS | Auto-generated self-signed certs or custom certificate paths |
| PWA | Installable as a standalone app on mobile devices |
| Themes | Dark, Midnight Blue, Solarized Dark, Light |
| Usage Tracking | Token usage monitoring with weekly reset counters |
- tmux —
tmux -V(for session persistence) - Claude Code CLI — installed and available in
PATH
The installer ships a bundled Python 3.12 runtime (musl, ~90MB compressed) — no system Python required. Python dependencies are vendored — no pip installs required.
For the clone-and-run path, the bundled runtime is not present; you need Python 3.12+ on the host.
Download the installer for your architecture and run it:
ARCH=$(uname -m)
curl -LO "https://github.com/rayketcham-lab/qn-claude-web/releases/latest/download/qn-code-assistant-v2.0.0-linux-${ARCH}.sh"
bash "qn-code-assistant-v2.0.0-linux-${ARCH}.sh"Direct downloads:
- x86_64 (amd64):
qn-code-assistant-v2.0.0-linux-x86_64.sh - aarch64 (arm64):
qn-code-assistant-v2.0.0-linux-aarch64.sh
The installer verifies SHA-256 hashes, extracts the app and bundled Python 3.12 runtime to the install directory, and runs interactive setup. No system Python required.
git clone https://github.com/rayketcham-lab/qn-claude-web.git /opt/claude-web
python3 /opt/claude-web/app.pyNote: the clone-and-run path uses whatever python3 is on PATH. For the bundled musl runtime, use the self-extracting installer above.
sudo cp /opt/claude-web/qn-code-assistant.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable qn-code-assistant
sudo systemctl start qn-code-assistantCheck logs:
sudo journalctl -u qn-code-assistant -fSettings are managed through the web UI (Settings icon in the sidebar) and persisted in config.json.
| Setting | Default | Description |
|---|---|---|
port |
5001 |
Server listening port |
projects_root |
/opt |
Root directory for project browsing |
allowed_paths |
["/opt"] |
Directories allowed for file access |
max_concurrent_terminals |
5 |
Maximum simultaneous terminal sessions |
max_concurrent_chats |
10 |
Maximum simultaneous chat processes |
process_timeout_minutes |
60 |
Watchdog timeout for stale processes |
auth.enabled |
false |
Enable password authentication |
ssl_enabled |
false |
Enable HTTPS |
| Variable | Description |
|---|---|
QN_SECRET_KEY |
Flask session secret key — set this in production |
QN_PORT |
Override server port (also settable in config.json) |
QN_CONFIG_PATH |
Path to config.json (default: config.json in app directory) |
Production note: Always set QN_SECRET_KEY to a long random value. Do not rely on the auto-generated key across restarts if you need session continuity.
export QN_SECRET_KEY="$(python3 -c 'import secrets; print(secrets.token_hex(32))')"
python3 /opt/claude-web/app.pyAdd remote hosts via Settings > Remote Hosts > Add Remote Host. The 5-step wizard handles:
- Method — Choose SSH or mount-based connection
- Connect — Enter host details or import from
~/.ssh/config - SSH Key Setup — Auto-generates an ed25519 key and pushes it via sshpass
- Verify — Tests the SSH connection and detects Claude CLI on the remote host
- Save — One-click save with auto-suggested display name
Two connection modes are supported:
- SSH mode — runs Claude Code on the remote host over SSH
- Mount mode — runs local Claude Code on a remotely mounted filesystem
+----------------------------------------------------------+
| Browser (Any Device) |
| xterm.js Terminal | Chat UI | File Browser |
+----------------------------------------------------------+
| WebSocket (SocketIO) |
+----------------------------------------------------------+
| Flask + SocketIO |
| async_mode='threading' |
+---------------+----------+-------------+----------------+
| PTY / tmux | Claude | SSH Manager | File API |
| Terminal Mgr | Code CLI | Remote Hosts| Project Detect |
+---------------+----------+-------------+----------------+
| Vendored Python Packages |
| Flask, SocketIO, Werkzeug, MarkupSafe, ... |
+----------------------------------------------------------+
- Backend: Flask + Flask-SocketIO (
async_mode='threading'), single-fileapp.py - Web server: Werkzeug built-in WSGI server via
socketio.run()— no gunicorn / uvicorn needed; threading mode owns the event loop for SocketIO long-polling and WebSockets - Frontend: Vanilla JS (
ClaudeCodeWebclass instatic/js/app.js), no framework, no build step - Terminal: xterm.js with PTY backend (
pty.fork()+select), tmux for session persistence - Runtime: python-build-standalone (PBS) Python 3.12 musl, bundled in
runtime/— no system Python dependency - Dependencies: 14 Python packages vendored in
vendor/— no runtime pip installs
qn-claude-web/
├── app.py # Flask backend + WebSocket handlers
├── templates/
│ ├── index.html # Single-page app
│ └── login.html # Auth page
├── static/
│ ├── css/style.css # Themed styling
│ ├── js/app.js # Client-side application
│ ├── sw.js # Service worker (PWA)
│ └── manifest.json # PWA manifest
├── .claude/ # Claude Code project configuration
│ ├── agents/ # Agent definitions
│ ├── rules/ # Language and project standards
│ └── settings.json # Permission and environment config
├── vendor/ # Vendored Python dependencies
├── tests/ # Unit and integration tests
├── sessions/ # Saved chat sessions (gitignored)
├── config.json # Runtime configuration (gitignored)
├── build-installer.sh # Installer generator with SHA-256 hashes
├── build-release.sh # Self-extracting installer builder
└── qn-code-assistant.service # systemd unit file
| Control | Implementation |
|---|---|
| Authentication | bcrypt-hashed passwords, multi-user, RBAC |
| Rate Limiting | 10 login attempts per 5 minutes per IP |
| Security Headers | CSP, X-Frame-Options, X-Content-Type-Options, HSTS, Referrer-Policy |
| Cookie Security | HttpOnly, SameSite=Lax, Secure flag with SSL |
| XSS Prevention | HTML sanitization, escaped output, Content Security Policy |
| Path Traversal | Null byte rejection, symlink resolution, allowed-path enforcement |
| App Directory | Excluded from file browser — config, source, and session files cannot be accessed via the API |
| SSH Sanitization | Hostname/username character filtering, port range validation |
| CSRF Protection | SameSite cookies + origin checking on WebSocket connections |
| Message Limits | Chat messages capped at 1MB |
| Thread Safety | Lock-protected session storage and terminal registry |
| Version Hiding | Server header reports QN Code Assistant — no framework or Python version exposed |
| systemd Hardening | NoNewPrivileges, PrivateTmp, ProtectSystem, capability restrictions |
| Disconnect Handling | 30-second grace period with browser correlation, orphan PTY cleanup |
To report a security vulnerability, open a GitHub issue marked [SECURITY] or contact the maintainer directly.
Port already in use:
lsof -ti:5001 | xargs kill -9Claude command not found:
which claude
# Add to PATH if missing:
export PATH="$PATH:/path/to/claude"Service won't start:
sudo journalctl -u qn-code-assistant -n 50tmux sessions not persisting:
tmux -V # confirm tmux is installed
which tmuxSSL certificate errors in browser:
Self-signed certificates require a browser trust exception. For production use, configure a real certificate via ssl_cert_path and ssl_key_path in config.json.
We welcome contributions. Before submitting a PR:
ruff check . # Lint
ruff format --check . # Format
python -m pytest # TestsAll commits follow Conventional Commits: feat:, fix:, refactor:, test:, docs:, ci:, security:.
This project is not affiliated with, endorsed by, or sponsored by Anthropic, PBC. "Claude" and "Claude Code" are trademarks of Anthropic, PBC. This is an independent, community-developed web interface that works with the Claude Code CLI. Use requires a valid Claude subscription and the Claude Code CLI installed locally.
Apache-2.0 — see LICENSE.