Claude Code Web: aus einer Shell ein mobiles Power-Terminal bauen
Stell dir vor: Auf deinem Server läuft eine Claude-Code-Session und arbeitet an einem Refactoring. Du bist unterwegs, willst kurz nachsehen, wie weit sie ist – und eine Freigabe erteilen. Per SSH vom Handy? Winzige Schrift, keine Pfeiltasten, Verbindung weg beim Bildschirm-Sperren, Session tot.
Es geht besser. Dieser Beitrag zeigt, wie aus einer schlichten Shell ein mächtiges, mobiles Werkzeug wird: eine Web-Konsole, die sich an eine dauerhafte Terminal-Session hängt und sie mit Datei-Browser, Push-Benachrichtigungen, SSH-Tabs und einem ganzen Homelab-Cockpit umgibt. Das Projekt heißt CCW – Claude Code Web. Und das Beste: Es ist kein Hexenwerk – der Tech-Stack ist überschaubar, und du kannst dir Schritt für Schritt etwas Ähnliches bauen.
Dieser Beitrag beschreibt das Muster und die Bausteine, nicht meine konkrete Instanz. Alle Adressen, Hostnamen und Zugänge sind bewusst generisch gehalten.
Die Grundidee in einem Satz
Ein Browser-Terminal (xterm.js) spricht über einen WebSocket mit einem kleinen Server, der ein Pseudo-Terminal (PTY) an eine tmux-Session koppelt. Mehr ist der Kern nicht.
flowchart LR
P["📱 Browser / PWA<br/>xterm.js"] <-->|WebSocket| S["Server<br/>FastAPI / uvicorn"]
S <-->|PTY| T["tmux-Session<br/>-A -s claude"]
D["🖥️ Desktop-Terminal"] <-->|derselbe tmux-Socket| T
T --> C["Claude Code<br/>/ Shell / vim / …"]
Der Clou steckt im tmux: Der Befehl tmux new-session -A -s claude heißt „verbinde dich mit der Session – oder lege sie an, falls es sie noch nicht gibt”. Dadurch teilen sich Desktop-Terminal und Handy dieselbe Session. Was du am Schreibtisch startest, läuft am Handy weiter – und umgekehrt.
Und weil beim Trennen der Verbindung nur der tmux-Client stirbt, nicht die Session, überlebt alles: Browser zu, Handy gesperrt, Zug im Tunnel – die Claude-Session tuckert weiter.
Warum tmux das Herzstück ist
Ein reines Web-Terminal ohne Persistenz wäre nutzlos: Jeder Verbindungsabbruch würde die laufende Arbeit killen. tmux löst gleich drei Probleme auf einmal:
- Persistenz – die Session lebt unabhängig von jedem Client.
- Teilen – mehrere Clients (Desktop, mehrere Handys) sehen denselben Bildschirm in Echtzeit.
- Fenster = Tabs –
tmux-Fenster lassen sich als Tabs in der Oberfläche darstellen.
Betreibe den
tmux-Server auf einem dedizierten Socket (tmux -S ~/pfad/ccw.sock) und starte ihn einmal beim Boot über einen eigenen systemd-Dienst. So kollidiert er nicht mit deinem normalentmuxund der Server-Prozess kann sich jederzeit neu anhängen.
Der Feature-Rundgang
Über viele Iterationen ist aus dem MVP („tippen + Ausgabe sehen”) eine erstaunlich komplette Konsole geworden. Hier die wichtigsten Ausbaustufen.
Terminal & Session
- Mehrere Fenster als Tabs – responsive (bei Platzmangel ins ▾-Overflow-Menü), Long-Press zum Umbenennen, Ziehen zum Umsortieren.
- Aktivitäts-Punkt je Tab: leuchtet, wenn neue Ausgabe kommt – und wird pink, wenn das Fenster auf eine Eingabe von dir wartet (z. B. eine Claude-Freigabe).
- Pixelgenaues Fit via
ResizeObserver+document.fonts.ready, damit das Terminal exakt in den Container passt und keine Leiste überlappt. - Nur-Lesen-Modus für den Fall, dass jemand nur zuschauen soll.
Mobile Eingabe – der eigentliche Knackpunkt
Auf dem Handy fehlt einer Shell fast alles. Diese Bausteine machen sie bedienbar:
- Keybar mit den Tasten, die Handytastaturen nicht haben: Pfeile,
Esc,Tab,^C,^D,^L,| ~ / -. - Compose-/Chat-Leiste zum bequemen Tippen oder Diktieren langer Prompts an Claude – mit Auto-Resize und gespeichertem Entwurf pro Fenster.
- 🎤 Spracheingabe über die Web Speech API.
- Command-Palette (
Strg/⌘+K): Fuzzy-Suche über Fenster, Hosts, Befehle und Snippets. - Auswahl-/Kopier-Modus mit sichtbarem Rahmen, solange er aktiv ist.
Dateien, Browser & Viewer
- Anhängen aus Kamera, Galerie, Datei, Zwischenablage oder per Drag&Drop → ein Notiz-Modal schickt Anweisung + Pfad direkt an Claude.
- Datei-Browser über das ganze Host-Dateisystem und jeden Remote-Host: navigieren, Vorschau, Up-/Download, Löschen (mit Nachfrage), inline Umbenennen, Ordner anlegen, Mehrfachauswahl, Ordner als
.zip, Suche nach Name/Inhalt. - Viewer für Bilder (Zoom/Pan), PDF (lazy per Seite), Video/Audio, Markdown und Code mit Syntax-Highlighting – inklusive Inline-Editor für schnelle Korrekturen.
- Ablage/Dropbox – Claude legt eine Datei ab, du lädst sie per Tap herunter.
Snippets, Favoriten & Rituale
- ⚡ Snippets/Makros mit Pinning direkt in der Keybar; Platzhalter wie `` werden vor dem Senden abgefragt.
- ★ Favoriten für Web-Links und Host-Dateien.
- Rituale-Menü: definierte Code-Wörter (z. B. „Bearbeitungsstand?”, „Aufräumen”) auf Knopfdruck an die Session senden.
Alles, was auf mehreren Geräten gleich sein soll – Snippets, Favoriten, Einstellungen – liegt server-seitig und atomar gespeichert (tmp-Datei +
os.replace+.bak). Der Client bleibt dünn, der Server hält den Zustand.
Remote-Konsolen per SSH
Ein + öffnet eine SSH-Konsole zu jedem hinterlegten Host – als eigener Tab, in eigener Farbe (Tab, Kopfzeile, dezenter Terminal-Tint), damit man immer sieht, auf welcher Maschine man tippt. Der Datei-Browser folgt dem aktiven Tab und transferiert Dateien per scp mit.
Das Homelab-Cockpit
Hier wächst das Terminal-Tool über sich hinaus – es wird zur Steuerzentrale fürs ganze Homelab:
- Monitoring – liest eine Uptime-Statuspage aus und zeigt alle Monitore gruppiert mit Ampel + 24-h-Uptime. Alles grün → zugeklappt; Gruppe mit Ausfall → automatisch offen. Oben eine Backup-Status-Zeile.
- Container – ein Cockpit über eine Docker-API: Status je Host, Start/Neustart/Stop (mit Bestätigung), Logs und sogar eine Shell (
docker execals neues Tab). - System – TLS-Zertifikatsablauf (Ampel bei < 14 / < 30 Tagen) und Speicherbelegung je Host.
- Dienste – ein Icon-Raster deiner selbst gehosteten Dienste, gruppiert nach Tags.
Jedes Panel hat einen Deep-Link ins jeweilige Original-Tool. Zugangsdaten liegen ausschließlich server-seitig (in einer .env bzw. ~/.netrc), nie im Client.
Benachrichtigungen & Workflow
- Web-Push „Claude wartet” – der Server erkennt server-seitig, wann die Ausgabe zur Ruhe kommt und niemand mehr verbunden ist, und schickt dann einen echten Push. Das funktioniert auch bei geschlossener App und gesperrtem Handy.
- Ruhezeiten und „nur bei Freigabe-Frage” lassen sich einstellen.
- Permission-Prompt-Erkennung – erkennt Claudes Freigabe-Menü und blendet ein Overlay mit ✓ Ja / Immer / ✗ Nein ein. Antworten geht sogar direkt aus der Push-Notification.
Zugang & PWA
- Geteiltes Login über ein signiertes HMAC-Cookie – gilt für Web-Konsole und Desktop-Terminal gleichermaßen.
- Biometrie-Entsperren per Passkey (WebAuthn) – braucht HTTPS und ist pro Domain gebunden.
- Installierbar als PWA (Home-Screen, Vollbild), tastatursicheres Layout über
visualViewport. - Service-Worker network-first mit Auto-Update: eine einzige Versionsnummer im Backend steuert den Cache-Key, neue Assets landen ohne manuelles Leeren beim Nutzer.
Die kniffligen Stellen (damit du sie nicht selbst erleiden musst)
Drei Fallen kosten beim Nachbau am meisten Zeit. Wer sie kennt, spart Tage.
1. Claude läuft im Alternate-Screen. Vollbild-Programme wie Claude, vim oder less nutzen den Alternate-Screen – xterm.js hat dort keinen eigenen Scrollback, und der Maus-Modus verschluckt die Text-Auswahl.
- Scrollen: Touch-Drag wird in SGR-Mausrad-Sequenzen übersetzt und an die PTY geschickt – die Vordergrund-App scrollt dann selbst.
- Kopieren: ein expliziter Auswahl-Modus setzt die Maus-Weiterleitung kurz aus, dann markiert man normal.
2. Die Bildschirmtastatur. Keybar-Taps dürfen den Fokus nicht verändern (pointerdown mit preventDefault), sonst klappt bei jedem Tastendruck die Tastatur auf und zu.
3. Das verzögerte Enter. Schickt man Text und Zeilenumbruch zusammen, landet die Nachricht als Paste in Claudes Eingabefeld statt abgeschickt zu werden. Lösung: den Text senden und das \r ~80 ms später als separates Frame nachschicken.
Und die wichtigste Infrastruktur-Falle: Der Reverse Proxy muss das WebSocket-Upgrade durchreichen. Ohne die passenden
Upgrade/Connection-Header verbindet sich das Terminal nie. Das früh abzusichern erspart viel Rätselraten.
Der Tech-Stack – bewusst schlank
Das Beeindruckende: Für all das braucht es keine schwere Maschinerie.
| Schicht | Wahl | Warum |
|---|---|---|
| Backend | Python 3 + FastAPI/uvicorn | Das PTY kommt aus der stdlib (pty, fcntl, termios) – keine schwere Abhängigkeit |
| Terminal | xterm.js + Addons (fit, web-links, unicode11) | De-facto-Standard fürs Browser-Terminal |
| Frontend | Vanilla JS, kein Build-Step | Eine Handvoll .js-Dateien, direkt ausgeliefert |
| Session | tmux | Persistenz + Teilen + Fenster-als-Tabs |
| Auslieferung | Reverse Proxy mit WS-Upgrade + systemd | Ein uvicorn-Prozess, nativ statt Docker |
Der minimale Nachbau-Pfad
Die Reihenfolge ist so gewählt, dass nach jedem Schritt etwas Lauffähiges existiert:
- PTY/WS-Bridge + statisches xterm.js an
tmux new-session -Ahängen – erst nur Tippen und Ausgabe. Resize überTIOCSWINSZ. - Auth – HMAC-Cookie, Middleware, Login-Seite.
- Mobile-Feinschliff – Fit-Härtung, Keybar, Compose-Leiste (inklusive der Alternate-Screen-Tricks und des verzögerten Enter).
- Datei-Workflow – Upload, Ablage, Viewer (Markdown/Bild zuerst, PDF und Code-Highlighting später).
- tmux-Tabs – Fenster anlegen/wechseln/umbenennen/schließen.
- Remote – SSH-Konsolen, dann der Datei-Browser (erst lokal, dann remote).
- Extras – Snippets/Favoriten server-seitig, Web-Push mit Idle-Erkennung, PWA + Service-Worker.
- Deployment – Reverse Proxy mit WS-Upgrade, dedizierter tmux-Socket, geteiltes Login.
Die drei kritischsten Dinge – dedizierter tmux-Socket, host-eigener tmux-Server und WS-Upgrade im Proxy – früh richtig aufsetzen. Sie kosten sonst am meisten Zeit.
Fazit
CCW hat als 50-Zeilen-Bridge angefangen und ist zu einer vollwertigen mobilen Kommandozentrale gewachsen – ohne Framework-Zoo, ohne Build-Pipeline, ohne Cloud-Abhängigkeit. Der rote Faden: der Server hält Zustand und Geheimnisse, der Client bleibt dünn, und tmux sorgt dafür, dass die eigentliche Arbeit jeden Verbindungsabbruch überlebt.
Das Schöne an so einem Projekt ist, dass jeder Schritt für sich schon nützlich ist. Fang mit der PTY↔WebSocket↔tmux-Bridge an – und wenn du das erste Mal vom Sofa aus eine Claude-Freigabe per Tap erteilst, verstehst du, warum sich der Rest lohnt.
Es ist wirklich kein Hexenwerk. Es ist nur eine Shell – gut verpackt.