From 830cb5d6798ea27053bbd8b5bf35d82e5e6845c4 Mon Sep 17 00:00:00 2001 From: Claude Code Bot Date: Mon, 27 Jul 2026 07:28:02 +0000 Subject: [PATCH] Mode operatoire : session Claude Code persistante dans un navigateur Stack ttyd + tmux + Caddy, reproduction d'une installation en production. Inclut le frontend personnalise et les correctifs deja valides (FitAddon, refit apres changement de police, double collage Ctrl+V, OSC 52, window.open). Co-Authored-By: Claude Opus 5 (1M context) --- MODE-OPERATOIRE.md | 949 +++++++++++++++++++++++++++++++++++++++++++++ README.md | 47 +++ ttyd-custom.html | 325 ++++++++++++++++ 3 files changed, 1321 insertions(+) create mode 100644 MODE-OPERATOIRE.md create mode 100644 README.md create mode 100644 ttyd-custom.html diff --git a/MODE-OPERATOIRE.md b/MODE-OPERATOIRE.md new file mode 100644 index 0000000..3a48740 --- /dev/null +++ b/MODE-OPERATOIRE.md @@ -0,0 +1,949 @@ +# Mode operatoire : acces web a une session Claude Code persistante + +Document destine a etre donne tel quel a une instance Claude Code disposant d'un acces root +sur l'hyperviseur / le serveur cible. Reproduit une stack deja en production. + +Fichier joint indispensable : `ttyd-custom.html` (frontend personnalise, etape 6). +Si tu ne l'as pas, la section 6.3 contient son contenu integral. + +--- + +## 0. Resultat vise et architecture + +Depuis n'importe quel navigateur, on ouvre `https://claude.mondomaine.fr`, on saisit un +identifiant, et on retombe sur **la meme session Claude Code**, exactement la ou elle en +etait, y compris apres fermeture du navigateur, redemarrage du PC ou changement d'appareil. + +``` +navigateur + -> https://claude.mondomaine.fr (443, TLS Let's Encrypt) + -> reverse proxy Caddy (basic_auth + websocket) + -> conteneur/VM claude-code : 7681 (jamais expose publiquement) + -> ttyd 1.7.7 (frontend xterm.js personnalise) + -> tmux attach-session -t claude (persistance de la session) + -> claude (Claude Code CLI) +``` + +Points de conception qui comptent : + +- La persistance vient de **tmux**, pas de ttyd. ttyd n'est qu'un pont websocket vers un PTY. +- tmux tourne dans un service systemd **distinct** de ttyd. Redemarrer ttyd ne tue donc jamais + la session. C'est ce qui rend les mises a jour du frontend sans risque. +- L'authentification est faite par le **reverse proxy**, pas par ttyd. Le port 7681 doit rester + inaccessible depuis l'exterieur. + +Reference materielle de l'installation existante (largement suffisant) : +LXC Proxmox non privilegie, Ubuntu 24.04, 4 vCPU, 4 Go RAM, disque 20 Go. + +--- + +## 1. Prerequis + +| Element | Detail | +|---|---| +| Un domaine | ex. `mondomaine.fr`, avec acces a la zone DNS | +| Une IP publique | fixe, ou dynamique + client DDNS | +| Acces au routeur/box | pour rediriger les ports 80 et 443 | +| Un serveur Linux | conteneur LXC, VM ou machine physique, x86_64 | +| Un compte Claude | abonnement Claude ou cle API Anthropic | + +Choisir des maintenant : +- le sous-domaine : `claude.mondomaine.fr` +- l'IP interne du serveur Claude : ex. `192.168.1.123` +- l'IP interne du reverse proxy : ex. `192.168.1.2` + +Le reverse proxy et le serveur Claude peuvent etre la meme machine. Le document les separe +car c'est plus propre, mais l'etape 7 indique comment tout regrouper. + +--- + +## 2. Etape 1 : creer le conteneur + +Sous Proxmox (adapter les identifiants et le pont reseau) : + +```bash +pct create 123 local:vztmpl/ubuntu-24.04-standard_24.04-2_amd64.tar.zst \ + --hostname claude-code \ + --cores 4 --memory 4096 --swap 512 \ + --rootfs local-lvm:20 \ + --net0 name=eth0,bridge=vmbr0,ip=192.168.1.123/24,gw=192.168.1.1 \ + --unprivileged 1 --features nesting=1 \ + --onboot 1 --start 1 +``` + +`nesting=1` n'est necessaire que si tu comptes faire tourner Docker ou des outils qui montent +des systemes de fichiers dans le conteneur. Sans cela, ce n'est pas requis. + +Sur une VM ou une machine physique : installer Ubuntu 24.04, IP fixe, c'est tout. + +Toute la suite s'execute **dans** ce serveur, en root. + +--- + +## 3. Etape 2 : base systeme + +```bash +apt update && apt install -y \ + curl git tmux ca-certificates locales build-essential \ + python3 jq unzip + +# Locale UTF-8 : indispensable, sinon les accents et les caracteres de dessin +# de l'interface Claude Code s'affichent en carres. +sed -i 's/^# *fr_FR.UTF-8/fr_FR.UTF-8/' /etc/locale.gen +locale-gen +update-locale LANG=fr_FR.UTF-8 + +# Node.js 22 LTS +curl -fsSL https://deb.nodesource.com/setup_22.x | bash - +apt install -y nodejs + +# Claude Code +npm install -g @anthropic-ai/claude-code +claude --version +``` + +Configuration tmux minimale mais **obligatoire** pour le confort de l'interface web : + +```bash +cat > /root/.tmux.conf <<'EOF' +set -g mouse on +set -g set-clipboard on +EOF +``` + +- `mouse on` : molette = defilement de l'historique, selection a la souris. +- `set-clipboard on` : tmux emet des sequences OSC 52, que le frontend convertit en + ecriture dans le presse-papier du navigateur. Sans cela, la copie depuis le terminal + distant ne remonte pas jusqu'a la machine cliente. + +Authentification Claude : lancer `claude` une premiere fois en SSH et suivre le flux de +connexion. Les identifiants sont stockes dans `/root/.claude/.credentials.json`. + +Notification de fin de commande (facultatif, utilise par l'etape 6) : + +```bash +cat >> /root/.bashrc <<'EOF' + +# Notification terminal : emet OSC 777 a chaque prompt (capte par le JS de ttyd) +__ttyd_prompt_cmd() { + local code=$? + printf '\033]777;%d\007' "$code" +} +PROMPT_COMMAND="${PROMPT_COMMAND:+$PROMPT_COMMAND; }__ttyd_prompt_cmd" +EOF +``` + +--- + +## 4. Etape 3 : installer ttyd + +Binaire statique, pas de compilation : + +```bash +curl -fL -o /usr/local/bin/ttyd \ + https://github.com/tsl0922/ttyd/releases/download/1.7.7/ttyd.x86_64 +chmod +x /usr/local/bin/ttyd +ttyd --version # doit afficher 1.7.7 +``` + +Verifier le nom exact de l'asset sur la page des releases si le telechargement echoue ; +les noms de fichiers changent selon les versions. Rester en **1.7.7** : le frontend +personnalise de l'etape 6 a ete corrige contre ce bundle xterm.js precis, une autre version +peut deplacer les points d'accroche. + +Transfert de fichiers dans le navigateur (facultatif, active par `-t enableTrzsz=true`) : + +```bash +curl -fL https://github.com/trzsz/trzsz-go/releases/download/v1.1.8/trzsz_1.1.8_linux_x86_64.tar.gz \ + | tar xz -C /tmp +install -m755 /tmp/trzsz_1.1.8_linux_x86_64/tr[sz]* /usr/local/bin/ +``` + +--- + +## 5. Etape 4 : session tmux persistante et service ttyd + +### 5.1 Script de session + +```bash +cat > /usr/local/bin/claude-session.sh <<'EOF' +#!/bin/bash +# Maintient la session tmux "claude" toujours en vie + +tmux has-session -t claude 2>/dev/null || \ + tmux new-session -d -s claude -c /root -x 220 -y 50 + +echo "[claude-session] Session tmux 'claude' active" + +while true; do + if ! tmux has-session -t claude 2>/dev/null; then + echo "[claude-session] Session perdue, recreation..." + tmux new-session -d -s claude -c /root -x 220 -y 50 + fi + sleep 5 +done +EOF +chmod +x /usr/local/bin/claude-session.sh +``` + +Les dimensions `-x 220 -y 50` ne sont qu'une valeur de depart : le premier client qui +s'attache redimensionne la session. + +### 5.2 Script de lancement ttyd + +```bash +cat > /usr/local/bin/claude-start.sh <<'EOF' +#!/bin/bash +export LANG=fr_FR.UTF-8 +export LC_ALL=fr_FR.UTF-8 +exec /usr/local/bin/ttyd --port 7681 --writable --index /etc/ttyd/index.html \ + -t enableTrzsz=true tmux attach-session -t claude +EOF +chmod +x /usr/local/bin/claude-start.sh +``` + +Ne pas ajouter `--credential` ici : l'authentification est geree par le reverse proxy. +Si le serveur Claude est expose autrement que derriere le proxy, alors il faut la remettre. + +### 5.3 Unites systemd + +```bash +cat > /etc/systemd/system/claude-session.service <<'EOF' +[Unit] +Description=Claude Code - Session tmux persistante +After=network-online.target +Wants=network-online.target + +[Service] +Type=simple +User=root +Environment=HOME=/root +ExecStart=/usr/local/bin/claude-session.sh +Restart=always +RestartSec=5 + +[Install] +WantedBy=multi-user.target +EOF + +cat > /etc/systemd/system/ttyd.service <<'EOF' +[Unit] +Description=Claude Code - Web Terminal (ttyd) +After=claude-session.service +Requires=claude-session.service + +[Service] +Type=simple +User=root +Environment=HOME=/root +ExecStart=/usr/local/bin/claude-start.sh +Restart=always +RestartSec=5 + +[Install] +WantedBy=multi-user.target +EOF + +systemctl daemon-reload +systemctl enable --now claude-session.service ttyd.service +systemctl status ttyd.service --no-pager +``` + +Le couple `After=` + `Requires=` garantit l'ordre au demarrage. La separation en deux unites +est **le point structurant** : `systemctl restart ttyd` ne touche pas au serveur tmux, donc la +session Claude survit a toutes les manipulations du frontend. + +Test local avant d'aller plus loin : + +```bash +curl -sI http://127.0.0.1:7681/ | head -3 +``` + +--- + +## 6. Etape 5 : frontend personnalise + +C'est ici que se trouve tout le travail d'ergonomie. Le frontend stock de ttyd est un terminal +nu : pas de barre d'outils, pas de themes, pas de recherche, copier-coller approximatif. + +### 6.1 Ce que le bloc personnalise apporte + +- barre d'outils fixe de 30 px : indicateur de connexion websocket, taille de police +/-, + effacer, recherche, selecteur de theme (5 themes), boutons de split et de navigation tmux +- taille de police et theme persistes en localStorage +- recherche dans l'historique du buffer, `Ctrl+F`, `Entree` / `Maj+Entree` pour naviguer +- menu contextuel au clic droit (copier, coller, rechercher, effacer, police) +- `Ctrl+C` copie s'il y a une selection, sinon envoie SIGINT ; `Ctrl+V` colle +- `Ctrl+molette` pour zoomer +- OSC 52 : le presse-papier de tmux remonte dans celui du navigateur +- OSC 777 : notification desktop quand une commande longue se termine, onglet en arriere-plan +- les liens cliquables s'ouvrent dans un nouvel onglet au lieu de remplacer la page + +### 6.2 Installation + +Recuperer d'abord le `index.html` stock que ttyd embarque, puis y injecter le bloc : + +```bash +mkdir -p /etc/ttyd +# instance jetable sur un autre port, uniquement pour recuperer la page embarquee +/usr/local/bin/ttyd --port 7699 tmux ls >/dev/null 2>&1 & P=$! +sleep 1 +curl -s http://127.0.0.1:7699/ > /etc/ttyd/index.stock.html +kill $P +wc -c /etc/ttyd/index.stock.html # 729693 octets attendus en 1.7.7 +``` + +Le service `ttyd` en production n'a pas besoin d'etre arrete : le port est different. + +Placer `ttyd-custom.html` (fourni avec ce document) dans `/root/`, puis : + +```bash +python3 - <<'EOF' +stock = open('/etc/ttyd/index.stock.html', encoding='utf-8').read() +custom = open('/root/ttyd-custom.html', encoding='utf-8').read() +tail = '' +assert stock.rstrip().endswith(tail), "structure du index.html stock inattendue" +out = stock.rstrip()[:-len(tail)] + custom + tail + '\n' +open('/etc/ttyd/index.html','w',encoding='utf-8').write(out) +print(len(out), 'octets ecrits') +EOF +``` + +Verifier la syntaxe avant de recharger la page (une erreur JS ici donne un terminal blanc) : + +```bash +python3 -c " +s=open('/etc/ttyd/index.html',encoding='utf-8').read() +js=s[s.rindex('')] +open('/tmp/check.js','w',encoding='utf-8').write(js)" +node --check /tmp/check.js && echo "JS OK" +``` + +**Aucun redemarrage n'est necessaire.** ttyd relit le fichier `--index` sur le disque a chaque +requete HTTP et repond `cache-control: no-store` : un simple rechargement de page suffit, la +session n'est jamais coupee. Toujours garder une sauvegarde avant modification : + +```bash +cp /etc/ttyd/index.html /etc/ttyd/index.html.bak-$(date +%Y%m%d) +``` + +### 6.3 Contenu de `ttyd-custom.html` + +Si le fichier joint est absent, creer `/root/ttyd-custom.html` avec exactement ce contenu. +Les sequences `\x02` (prefixe tmux Ctrl+B) et `\x1b` (ESC) sont des echappements JavaScript +volontaires : ne pas les convertir en caracteres bruts. + +```html + +
+
+ + 13 + + + + + + + + + + + +
+ Ctrl+C=copier/inter.   Ctrl+V=coller   clic droit=menu +
+ +
+
+ +``` + +--- + +## 7. Etape 6 : reverse proxy, domaine et TLS + +Caddy est le choix retenu : HTTPS automatique par Let's Encrypt, websocket natif sans +configuration, fichier de conf de trois lignes. Nginx ou Traefik fonctionnent aussi, mais +demandent une configuration websocket explicite (`Upgrade` / `Connection`), source classique +de terminal qui ne se connecte pas. + +### 7.1 Installer Caddy + +Sur une machine dediee (ex. `192.168.1.2`) ou sur le serveur Claude lui-meme : + +```bash +apt install -y debian-keyring debian-archive-keyring apt-transport-https curl +curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' \ + | gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg +curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' \ + | tee /etc/apt/sources.list.d/caddy-stable.list +apt update && apt install -y caddy +caddy version +``` + +### 7.2 Generer le hash du mot de passe + +```bash +caddy hash-password +# saisir le mot de passe deux fois, copier le hash bcrypt affiche ($2a$14$...) +``` + +Stocker le mot de passe en clair dans un gestionnaire de mots de passe : le hash bcrypt n'est +pas reversible, un mot de passe perdu impose d'en regenerer un. + +### 7.3 Caddyfile + +``` +claude.mondomaine.fr { + basic_auth { + claude $2a$14$REMPLACER_PAR_LE_HASH_GENERE + } + reverse_proxy 192.168.1.123:7681 +} +``` + +```bash +caddy validate --config /etc/caddy/Caddyfile +systemctl reload caddy +``` + +Notes : + +- `basic_auth` est la directive de Caddy >= 2.8. Sur une version anterieure, ecrire `basicauth`. +- Le hash contient des `$`. Dans un Caddyfile ce n'est pas un probleme, mais si tu generes ce + fichier depuis un script shell, protege-le des expansions (guillemets simples). +- Les websockets passent seuls : ne surtout pas ajouter de `header_up Upgrade` manuel. +- Si le proxy et le serveur Claude sont sur la meme machine, remplacer par + `reverse_proxy 127.0.0.1:7681`. + +### 7.4 DNS et ouverture de ports + +1. Enregistrement DNS public : `claude.mondomaine.fr` -> IP publique (type A). Si l'IP est + dynamique, utiliser un client DDNS. +2. Redirection sur le routeur : **80 -> proxy:80** et **443 -> proxy:443**. Rien d'autre. + Le port 80 est requis pour la validation Let's Encrypt en HTTP-01. +3. **Ne jamais rediriger le port 7681.** Il donne un shell root sans authentification. +4. Si le reseau local resout deja `*.mondomaine.fr`, ajouter une entree interne + `claude.mondomaine.fr -> 192.168.1.2` pour eviter de sortir sur internet. +5. Pare-feu du serveur Claude : n'autoriser 7681 que depuis l'IP du proxy. + +```bash +# sur le serveur claude-code, si ufw est utilise +ufw allow from 192.168.1.2 to any port 7681 proto tcp +ufw deny 7681 +``` + +Le premier acces en HTTPS declenche l'emission du certificat, ce qui prend quelques secondes. +En cas d'echec, `journalctl -u caddy -f` donne la raison exacte (port 80 non joignable depuis +l'exterieur dans 90 % des cas). + +--- + +## 8. Verification + +Dans l'ordre, en s'arretant a la premiere etape qui echoue : + +```bash +# 1. ttyd repond en local +curl -sI http://127.0.0.1:7681/ | head -1 # 200 + +# 2. la session tmux existe +tmux ls # claude: 1 windows + +# 3. le proxy demande bien une authentification +curl -sI https://claude.mondomaine.fr | head -1 # 401 + +# 4. avec les identifiants +curl -sI -u claude:MOTDEPASSE https://claude.mondomaine.fr | head -1 # 200 +``` + +Puis dans un navigateur, controler point par point : + +- [ ] le terminal occupe toute la hauteur, la derniere ligne n'est pas coupee +- [ ] la pastille de connexion en haut a gauche est verte +- [ ] `Ctrl+V` colle **une seule fois** le contenu du presse-papier +- [ ] selectionner du texte puis `Ctrl+C` affiche le toast "Copie" et le texte est + recuperable dans une autre application +- [ ] les boutons A+ / A- changent la taille **et** la grille se recalcule (le nombre de + lignes visibles change) +- [ ] le clic droit ouvre le menu contextuel +- [ ] fermer l'onglet, le rouvrir : la session est retrouvee intacte +- [ ] `systemctl restart ttyd` puis recharger : la session est toujours la + +--- + +## 9. Pieges connus et bugs deja corriges + +Cette section est la raison d'etre du document. Ces defauts ont tous ete rencontres et +diagnostiques en production ; les correctifs sont **deja integres** dans `ttyd-custom.html`. +Elle sert a ne pas les reintroduire lors d'une modification ulterieure. + +### 9.1 Bas du terminal tronque (deux causes cumulees) + +**Cause A : le padding du parent est compte comme espace disponible par le FitAddon.** +Poser `#terminal-container{padding-top:30px}` pour degager la barre d'outils semble naturel. +C'est structurellement faux. `proposeDimensions()` du FitAddon calcule : + +```js +r = parseInt(getComputedStyle(parentElement).getPropertyValue("height")) +a = r - (padding-top + padding-bottom de .terminal) // le padding du PARENT n'est jamais retranche +rows = floor(a / cell.height) +``` + +Il mesure donc 30 px de trop, soit environ deux lignes de trop a chaque fit. + +Regle : **ne jamais poser de padding, de bordure ou de decoration sur `#terminal-container`.** +C'est l'element mesure par le FitAddon, il doit contenir exactement l'espace utilisable. +Pour degager de la place, positionner le conteneur, ne pas le rembourrer : + +```css +#terminal-container{position:absolute !important;top:30px;left:0;right:0;bottom:0; + height:auto !important;padding-top:0 !important;} +``` + +**Cause B : police changee apres l'initialisation, sans refit.** Le frontend applique la +taille de police memorisee en localStorage alors que le fit initial a deja tourne avec la +cellule d'origine, plus petite, donc avec trop de lignes. De plus, `term.fit()` **n'existe +pas** sur un Terminal xterm.js : `fit()` appartient au FitAddon. L'appel est ignore +silencieusement et les boutons A+ / A- ne recalculent jamais la grille. + +Regle : **tout changement de `term.options.fontSize` doit etre suivi de** + +```js +window.dispatchEvent(new Event('resize')); +``` + +C'est l'evenement que ttyd ecoute reellement (`g(window,"resize",()=>t.fit())` dans son +bundle). Le meme appel est place en differe de 50 ms apres l'application de la police +initiale, dans `waitForTerm()`. + +Mesures reelles avant / apres, fenetre de 980 px, police 14, dpr 1.25 : + +| | padding conteneur | rows | bas de .xterm-screen | +|---|---|---|---| +| avant, au chargement | 30 | 62 | 1027 (deborde de 69 px) | +| apres, au chargement | 0 | 61 | 1011 | +| apres, refit a +50 ms | 0 | 58 | **963, tient dans 980** | + +### 9.2 Ctrl+V collait en double + +Le gestionnaire de touches faisait `readClip(t => term.paste(t)); return false;` sans +`preventDefault()`. `return false` empeche seulement xterm.js de traiter la touche : le +navigateur emettait quand meme son evenement `paste` natif vers le textarea cache, ou xterm +l'ecoute (`handlePasteEvent` -> `getData("text/plain")` -> `paste`). Deux insertions. + +Correctif : `e.preventDefault()` dans les branches `Ctrl+V` et `Ctrl+Maj+V`. `return false` +est conserve pour que xterm n'envoie pas `\x16` au PTY. Le seul chemin de collage restant est +`readClip`. + +```js +if (e.code==='KeyV') { + e.preventDefault(); + readClip(function(t){ term.paste(t); }); + return false; +} +``` + +Meme defaut latent sur la branche `Ctrl+C`, laisse tel quel car sans consequence visible : +la copie native et `writeClip` copient la meme selection. + +### 9.3 Copier depuis le terminal distant ne remontait pas + +Il faut les deux moities : `set -g set-clipboard on` cote tmux (emission de l'OSC 52) et le +handler `registerOscHandler(52, ...)` cote frontend, qui decode le base64 et appelle +`navigator.clipboard.writeText`. L'un sans l'autre ne donne rien. + +`navigator.clipboard` n'existe qu'en contexte securise : cela fonctionne en HTTPS ou sur +`localhost`, **jamais en HTTP simple sur une IP**. C'est une raison de plus de toujours passer +par le proxy TLS, meme pour tester. Un repli `document.execCommand('copy')` est present pour +les navigateurs anciens. + +### 9.4 Les liens ouvraient une page blanche + +Le WebLinksAddon appelle `window.open('')` puis assigne `location.href`, comportement que les +navigateurs recents bloquent ou traitent mal. Le bloc personnalise remplace `window.open` : +quand l'URL est vide, il retourne un objet dont le setter `href` cree un `` et le clique. Ne pas retirer ce patch. + +### 9.5 Pieges d'exploitation + +- **Ne pas mettre l'authentification a deux endroits.** ttyd `--credential` **et** basic_auth + Caddy donnent une double invite. Une seule, au niveau du proxy. +- **Modifier `index.html` ne demande pas de redemarrage.** ttyd relit le fichier a chaque + requete. Redemarrer ttyd est neanmoins sans danger pour la session (elle appartient a + l'autre unite systemd). +- **Une erreur de syntaxe JS donne un terminal blanc et muet.** Toujours passer + `node --check` avant de recharger, et garder la sauvegarde datee. +- **Ne pas suivre les mises a jour de ttyd sans verifier.** Le bloc personnalise s'accroche a + des details du bundle xterm.js de la 1.7.7 (`window.term`, l'ecoute de `resize`, + `#terminal-container`). Apres une montee de version, rejouer toute la checklist du 8. +- **Methode de debug du frontend, sans rien installer.** ttyd journalise le **chemin** de + chaque requete HTTP. Il suffit d'encoder les mesures dans un chemin bidon : ttyd repond 404 + et la valeur atterrit dans le journal, lisible cote serveur. + + ```js + fetch('/diag/win'+innerHeight+'/rows'+term.rows+'/sBot'+rect.bottom); + ``` + ```bash + journalctl -u ttyd -f | grep diag + ``` + + Aucun endpoint a creer, aucun port a ouvrir, et le retour est lisible directement au lieu + d'etre dicte par l'utilisateur. Penser a retirer l'instrumentation ensuite. +- **Lecon de methode.** Sur le bug du bas tronque, la premiere hypothese, purement CSS, etait + fausse et n'a rien corrige. Le signal d'alerte a ete une prediction contredite par + l'observation. Sur ce frontend, mesurer avant de conclure coute moins cher que corriger au + jugé. + +--- + +## 10. Options utiles + +**Redemarrage propre de la session Claude** sans toucher aux services : + +```bash +tmux send-keys -t claude C-c +tmux send-keys -t claude 'claude' Enter +``` + +**Editeur web** sur le meme serveur, pratique pour editer les fichiers de config : + +```bash +curl -fsSL https://code-server.dev/install.sh | sh +systemctl enable --now code-server@root # port 8080 +``` + +Ajouter un bloc Caddy dedie, avec la meme protection : + +``` +code.mondomaine.fr { + basic_auth { claude $2a$14$LE_HASH } + reverse_proxy 192.168.1.123:8080 +} +``` + +**Sauvegarde de la configuration** : versionner `/etc/ttyd/index.html`, les unites systemd et +les scripts `/usr/local/bin/claude-*.sh` dans un depot git prive. C'est ce qui permet de +rejouer l'installation ou de revenir en arriere apres une modification ratee du frontend. + +Attention en versionnant : filtrer tout secret avant le commit. Un fichier d'environnement +pousse par erreur reste dans l'historique meme apres correction, et la seule remediation +reelle est de **regenerer les identifiants concernes**. + +--- + +## 11. Rappel de securite + +`claude.mondomaine.fr` donne un **shell root persistant** sur le serveur, expose sur internet. +Le niveau de protection doit etre a la hauteur : + +- mot de passe long et unique, stocke dans un gestionnaire de mots de passe +- port 7681 jamais redirige, et filtre au pare-feu sur l'IP du proxy +- HTTPS obligatoire, jamais d'acces en HTTP simple +- evolution recommandee : remplacer basic_auth par une authentification SSO avec second + facteur (forward_auth vers Authentik ou Authelia), ou restreindre l'acces a un VPN +- si un service d'upload de fichiers est ajoute a cote, verifier qu'il ne tourne pas avec un + mot de passe par defaut et qu'il n'ecoute pas sur `0.0.0.0` sans authentification diff --git a/README.md b/README.md new file mode 100644 index 0000000..a9222a9 --- /dev/null +++ b/README.md @@ -0,0 +1,47 @@ +# claude-web-terminal + +Mode operatoire pour exposer une session **Claude Code persistante** dans un navigateur, +derriere un nom de domaine et un reverse proxy. + +``` +navigateur -> https://claude.mondomaine.fr -> Caddy (basic_auth) -> ttyd -> tmux -> claude +``` + +La session survit a la fermeture du navigateur, au redemarrage du poste client et au +changement d'appareil : on retombe toujours sur le meme contexte Claude Code. + +## Contenu + +| Fichier | Role | +|---|---| +| `MODE-OPERATOIRE.md` | Le document complet, a donner tel quel a une instance Claude Code disposant d'un acces root sur le serveur cible | +| `ttyd-custom.html` | Le frontend personnalise a injecter dans la page de ttyd (etape 6) | + +## Utilisation + +```bash +git clone https://gitea.8grangettes.fr/sylfrd/claude-web-terminal.git +cd claude-web-terminal +claude +``` + +Puis : `lis MODE-OPERATOIRE.md et deroule l'installation`. + +## Points notables + +Le document ne se limite pas a l'installation. Sa section 9 documente les defauts rencontres +en production et deja corriges dans `ttyd-custom.html` : + +- bas du terminal tronque : le padding du parent est compte comme espace disponible par le + FitAddon, et tout changement de taille de police exige un refit explicite +- `Ctrl+V` qui collait en double, faute de `preventDefault()` +- copie depuis le terminal distant qui ne remontait pas (OSC 52, des deux cotes, en HTTPS) +- liens cliquables qui ouvraient une page blanche + +Ainsi qu'une methode de debug du frontend sans rien installer, en encodant les mesures dans +des chemins HTTP journalises par ttyd. + +## Avertissement + +Cette stack expose un **shell root persistant** sur internet. Lire la section 11 avant de +rediriger le moindre port. diff --git a/ttyd-custom.html b/ttyd-custom.html new file mode 100644 index 0000000..7911211 --- /dev/null +++ b/ttyd-custom.html @@ -0,0 +1,325 @@ + +
+
+ + 13 + + + + + + + + + + + +
+ Ctrl+C=copier/inter.   Ctrl+V=coller   clic droit=menu +
+ +
+
+ \ No newline at end of file