Au quotidien

Votre propre client

Brancher Claude Code, Codex, votre terminal et votre éditeur sur un serveur Pupitre en SSH, avec la clé privée que l’app a générée.

Une fois la machine prête, Pupitre n’est pas l’endroit où vous devez travailler. Le serveur est une machine Ubuntu ordinaire, jointe en SSH sous le compte dev, et tout ce qui parle SSH s’y branche — l’app Claude, l’app ChatGPT et son Codex, votre terminal, votre éditeur.

Cette page est la partie pratique : où est la clé, comment déclarer l’hôte une fois pour toutes, et ce qu’il faut remplir pour chaque client.

Ce que le serveur offre à un client

Rien de particulier, et c’est voulu :

  • un accès SSH sur le port 22 — et sur le 443 aussi, si vous l’avez demandé à core.hardening ;
  • l’utilisateur dev, dont authorized_keys porte la moitié publique de chaque appareil de votre compte autorisé sur ce serveur ;
  • vos projets sous le dossier que vous avez choisi, possédés par dev ;
  • les runtimes activés dans les shells non interactifs également, pour qu’un client qui lance une commande sans shell de connexion trouve les mêmes node, python et claude que vous ;
  • tmux, systemd, la commande dev, et les bases sur 127.0.0.1.

Ce dernier point sur les shells non interactifs est ce qui décide qu’un client distant sera agréable ou insupportable. Les modules de runtime écrivent leur activation dans .zshenv, que zsh lit pour tous les shells, exactement pour cette raison.

Où l’app garde la clé

Pupitre génère une clé ed25519 par serveur et la garde dans son propre dossier de données, en mode 0600. Elle ne réécrit jamais votre ~/.ssh/config : sur votre demande, elle y pose une ligne, une seule, décrite plus bas.

SystèmeDossier
macOS~/Library/Application Support/@pupitre/desktop
Windows%APPDATA%\@pupitre\desktop
Linux~/.config/@pupitre/desktop

À l’intérieur :

ssh/config        un bloc Host par serveur, passé à ssh avec -F
ssh/known_hosts   les clés d’hôte que l’app a épinglées
keys/device       la clé de cet ordinateur, celle qui ouvre un serveur confié par votre organisation
keys/srv-xxxxxxx  la clé d’un serveur que vous avez ajouté vous-même, en mode 0600

Chaque clé a son .pub à côté d’elle. Ouvrez ssh/config pour savoir quel serveur est lequel : chaque bloc porte son adresse, son port, son utilisateur et le chemin de sa clé — keys/device pour un serveur confié, keys/srv-… pour un serveur que vous avez ajouté.

Host pupitre-srv-m9k2p1
  HostName 203.0.113.10
  Port 22
  User dev
  IdentityFile /Users/vous/.pupitre/desktop/keys/srv-m9k2p1
  IdentitiesOnly yes

Le chemin passe par ~/.pupitre/desktop, un lien que l’app garde pointé sur son dossier de données. Le dossier lui-même porte une espace dans son nom sur macOS, et souvent sur Windows ; OpenSSH lit un chemin entre guillemets, mais JetBrains Gateway analyse le même fichier avec son propre code et coupe IdentityFile et UserKnownHostsFile sur l’espace, guillemets ou non. Le lien est ce qui rend le bloc de l’app lisible par les deux.

Pointez vos clients sur ce fichier plutôt que d’en faire une copie ailleurs. Supprimer le serveur dans l’app emporte sa clé, et une copie posée ailleurs serait une orpheline qui ouvre toujours votre machine.

Déclarer l’hôte une fois

Tout ce qui suit demande que votre propre ~/.ssh/config connaisse le serveur : ssh, les éditeurs et les agents de code ne lisent que ce fichier, pas celui que l’app garde dans son dossier. L’app le fait pour vous. Dans Réglages › SSH, cochez Laisser ssh, mes éditeurs et mes agents de code joindre mes serveurs par leur nom : une ligne s’ajoute en tête de ~/.ssh/config, et rien d’autre n’y bouge.

Include "/Users/vous/Library/Application Support/@pupitre/desktop/ssh/config"

Chaque serveur de l’app répond dès lors à son nom SSH — le mot choisi à côté de son nom quand vous l’ajoutez, atelier par défaut pour un serveur nommé « Atelier », modifiable depuis la fiche du serveur dans Réglages › Serveurs, le réglage SSH le montre — avec la clé de l’app, IdentitiesOnly et la clé d’hôte épinglée. Un serveur dont le nom SSH est déjà celui d’un Host de votre fichier garde son identifiant seul, pupitre-srv-…. Décocher la case retire la ligne. Vérifiez :

ssh atelier "dev status"

Une machine qui répond avec ses projets est une machine que tous les clients de cette page peuvent désormais joindre. Si elle demande un mot de passe, la clé n’est pas celle que dev accepte : révélez la clé publique sur l’écran des serveurs et comparez-la à ~/.ssh/authorized_keys sur la machine.

Si vous préférez écrire l’entrée vous-même, recopiez les valeurs depuis le bloc de l’app, lien compris : un chemin qui contient une espace demande des guillemets à OpenSSH et casse JetBrains Gateway dans tous les cas.

Host atelier
  HostName 203.0.113.10
  Port 22
  User dev
  IdentityFile /Users/vous/.pupitre/desktop/keys/srv-m9k2p1
  IdentitiesOnly yes

Claude Code

Deux chemins, qui arrivent au même endroit : ai.claude a déjà installé Claude Code pour dev, avec le contexte machine et les skills Pupitre.

Depuis l’app Claude. Ouvrez le menu d’environnement avant de démarrer une session et choisissez Add SSH connection. Elle demande quatre choses :

ChampCe qu’on met
NameLe nom que vous donnez à la machine
SSH Hostpupitre, ou dev@203.0.113.10
SSH Port22, ou celui que vous avez choisi
Identity FileLe chemin de la clé de l’app, d’après le tableau plus haut

C’est ce champ Identity File qui fait que la clé de l’app fonctionne ici sans cérémonie : donnez le chemin, rien d’autre n’a à bouger. Laissez-le vide si l’hôte se résout déjà par votre ~/.ssh/config.

Depuis un terminal. Ouvrez une session, gardez-la dans tmux pour qu’elle survive au réseau, et lancez l’outil là où est le projet :

ssh atelier
tmux new -A -s claude
cd ~/projects/votre-projet && claude

Dans les deux cas vous vous connectez une fois, depuis la session où vous êtes : l’outil affiche une URL, vous l’approuvez dans votre navigateur. C’est votre abonnement ; Pupitre ne revend rien et ne stocke aucun token pour vous.

Codex

ai.codex installe Codex par mise, et les shims sont sur le PATH de tous les shells — y compris le shell non interactif qu’ouvre un client distant. C’est exactement ce que l’app exige de la machine : elle démarre Codex sur l’hôte distant à travers votre shell de connexion.

Depuis l’app ChatGPT. Codex y ajoute un projet distant depuis un hôte SSH : elle lit les alias d’hôte concrets de votre ~/.ssh/config et les résout avec OpenSSH, si bien que le nom du serveur, déclaré plus haut, est ce qu’elle vous propose. Il n’y a pas de champ pour une clé — c’est la ligne IdentityFile du bloc de l’app qui désigne la sienne.

Depuis un terminal. Même forme que pour Claude Code :

ssh atelier
tmux new -A -s codex
cd ~/projects/votre-projet && codex

Claude Code et Codex cohabitent sans discussion. Ils voient les mêmes projets, les mêmes runtimes et les mêmes bases, parce qu’il n’y a qu’une machine.

Votre terminal

ssh atelier suffit, et le binaire de l’agent est aussi lié sous le nom dev : les opérations que l’app effectue existent dans un terminal nu.

dev status                    ce qui tourne, sur quel port, avec quelle mémoire
dev up mon-projet             le démarre dans sa session tmux
dev restart all
dev logs mon-projet -f
dev sync mon-projet           git pull, puis les dépendances
dev attach mon-projet         affiche la ligne tmux à coller dans votre terminal
dev branch                    la branche de chaque projet
dev branch mon-projet main    change ce projet de branche
dev db url                    la chaîne de connexion, pour un client sur votre poste
dev doctor                    un diagnostic court de la machine

L’agent lit son rattachement et sa licence dans des fichiers que seul root ouvre : la commande parle donc à sudo -n pupitred serve, que sudo lance sans mot de passe. dev db dump, dev db import et dev backup now passent par une session privilégiée, pour laquelle sudo demande le mot de passe sudo de dev dans le terminal. Taper sudo dev … vous-même ne change rien.

dev db accepte aussi shell, dump et import, et le moteur va de soi quand la machine n’a qu’une base — dev db url postgres quand elle en a deux.

Ajoutez --json à n’importe laquelle et vous obtenez la réponse de l’agent plutôt qu’un rendu de celle-ci, ce qui rend ces commandes utilisables par un script, ou par un agent qui travaille dans un terminal.

Votre éditeur

VS Code, Cursor et Windsurf se connectent en Remote SSH, Zed par zed://ssh, les IDE JetBrains par Gateway. Ils lisent la même entrée de ~/.ssh/config, et les modules editor.* préinstallent les backends distants pour que la première connexion soit immédiate plutôt qu’un téléchargement de dix minutes. Voir éditeurs distants.

Quand ça n’ouvre pas

  • On vous demande un mot de passe. La clé n’est pas dans le authorized_keys de dev. Recopiez la moitié publique depuis l’écran des serveurs, dans l’app.
  • Permissions 0644 for ... are too open. La clé privée ne doit être lisible que par vous : chmod 600 sur macOS et Linux. Sur Windows, la garder dans %APPDATA% suffit — une copie posée dans un dossier partagé, non.
  • Rien ne répond sur le port 22. Si votre réseau le filtre, core.hardening peut mettre SSH sur le 443 également ; changez Port dans votre entrée.
  • La clé d’hôte a changé. Réinstaller une machine la change. L’app vous le demande de son côté ; dans votre ~/.ssh/known_hosts, retirez l’ancienne ligne.
  • Gateway dit Could not verify host key sans rien vous demander. Le bloc de l’app pointe sur son propre known_hosts en vérification stricte, et Gateway n’a pas pu lire le chemin. Vérifiez que ~/.pupitre tient un lien au nom du dossier de l’app et que rien d’autre n’a pris sa place ; l’app le repose à son prochain démarrage.
  • codex: command not found depuis un client distant. Le client a ouvert un shell qui n’a pas lu .zshenv. Vérifiez avec ssh atelier 'which codex'.

La révocation fonctionne toujours

Un appareil retiré dans la console perd sa clé sur le serveur au prochain rendez-vous de l’agent — quoi que cette clé servait à ouvrir —, sauf si c’était la dernière clé du serveur. C’est la raison de pointer vos clients sur la clé de l’app plutôt que sur une copie.

Ce qui reste dans l’app

Pupitre reste l’endroit des choses qu’un client de conversation n’a pas à faire : inspecter la machine, installer et configurer les services, sécuriser, le rapport d’installation en direct, l’enregistrement des projets, les secrets, l’état des services que l’agent rapporte, les mises à jour et les alertes.

Son terminal est là le jour où vous en voulez un — la première connexion, des logs à regarder, une commande que vous préférez lancer là où l’installation a eu lieu. Ce n’est pas le prix d’entrée.