Français : docs/fr/README.md
zsh, git, mise, tmux, Starship and Tabby, split by machine: Windows 11, XPS, dev machine and server.
| Machine | Contents | Check with |
|---|---|---|
| Windows 11 | Tabby + Bitwarden (SSH agent) + ~/.ssh/config: you connect to the other machines from here |
Tabby opens the default connection |
| XPS (Debian) | Everything the dev machine has + Tabby + Bitwarden (SSH agent) + ~/.ssh/config |
just doctor-dev + Tabby opens the default connection |
| Dev machine (Debian) | Shell + every mise tool + git + gh + Claude Code config |
just doctor-dev |
| Server / VPS (Debian) | Shell only. No private key, no GitHub account, nothing to steal | just doctor-server |
- Where things live
- Once only: the SSH key
- Windows 11
- Dev machine
- XPS
- Server / VPS
- Add a connection
- Commands
- Shell
- Tabby
- tmux
- Claude Code
- Security
- MOTD
| What | Where | In the repo |
|---|---|---|
| Private SSH key | Bitwarden, served by its SSH agent on Windows 11 and the XPS | Never |
| SSH connections | ~/.ssh/config on Windows 11 and the XPS, copy in a Bitwarden note |
Never |
| Tabby settings (theme, font, shortcuts) | tabby/config.yaml |
Yes |
| Git identity | Each repo (.git/config), or ~/.gitconfig.local as an option |
Never |
| GitHub access | gh token on each dev machine (the XPS included) |
Never |
| Claude Code config (CLAUDE.md, RTK.md, agents, skills, base settings) | claude/ |
Yes |
| Claude Code credentials, MCP tokens, memory, history | ~/.claude/ and ~/.claude.json on each dev machine |
Never |
The repo is public: it is cloned over HTTPS, with no key or account.
-
In Bitwarden, create an SSH key item (Ed25519). The private key never leaves the vault.
-
Create a secure note named ssh config with the list of connections, in
~/.ssh/configformat:Host mon-serveur HostName 203.0.113.10 Host * User sonny ServerAliveInterval 5 ServerAliveCountMax 10No
IdentityFile:sshand Tabby ask the Bitwarden agent for the key.
-
Install Bitwarden Desktop, sign in, then enable the SSH agent in the settings.
-
Turn off the Windows SSH agent, which conflicts with Bitwarden's (PowerShell admin):
Stop-Service ssh-agent Set-Service ssh-agent -StartupType Disabled
-
Create
C:\Users\<user>\.ssh\config(no extension) with the content of the ssh config note. -
Tabby looks for this file through the
HOMEvariable, which is not set by default on Windows:[Environment]::SetEnvironmentVariable("HOME", $env:USERPROFILE, "User")
-
Install Tabby, launch it once, close it completely (taskbar icon included), then, from Windows Terminal (not from Tabby):
irm https://raw.githubusercontent.com/Sonny93/dotfiles/main/tabby/install.ps1 | iex
-
Verify:
ssh-add -Lshows the Bitwarden key, and Tabby opens the chosen connection at launch.
To update: see Tabby.
-
Base packages:
sudo apt install git curl zsh
-
Clone the repo (the
~/dotfilespath is hard-coded) and install mise:git clone https://github.com/Sonny93/dotfiles ~/dotfiles curl https://mise.run | sh export PATH="$HOME/.local/bin:$PATH"
-
Shell:
cd ~/dotfiles mise exec just -- just shell chsh -s "$(command -v zsh)" exec zsh
-
Everything else:
just dev
just devrunsapt,build-deps(packages to build tmux),shell,mise(all the tools),tmux(links for the tmux config and the micro theme),git(config + gitleaks hook),gh-auth,notes(git repo for the notes),claude(Claude Code config), thendoctor-dev, which must print onlyOK/INFOlines.No default git identity: set it in each repo (
git config user.name/user.email). For a default identity on the machine, put it in~/.gitconfig.local([user]section).
To chain Windows 11 or XPS → dev machine → server (for example Claude Code on the dev machine working on a VPS), the dev machine borrows the client's Bitwarden agent instead of having its own key:
-
In the ssh config note and in
~/.ssh/configon Windows 11 and the XPS, enable agent forwarding on the dev machine'sHost, and on that one only:Host ma-machine-de-dev HostName 203.0.113.20 ForwardAgent yes -
On the dev machine, create
~/.ssh/configwith the servers to manage (the same note, without the dev machine itself).
Each connection started from the dev machine asks for approval in Bitwarden, on the client. It only works while an SSH session from the client is open.
cd ~/dotfiles && git pull && just dev
exec zshThe XPS is both a dev machine and a client.
-
Install as a Dev machine, running
just xps(alias ofjust dev) at step 4. -
Install Bitwarden Desktop as a
.deb(not Flatpak or Snap: the SSH agent does not create its socket there), sign in, enable the SSH agent. Then announce it to the whole graphical session (Tabby included), and log out / log back in:mkdir -p ~/.config/environment.d echo 'SSH_AUTH_SOCK=${HOME}/.bitwarden-ssh-agent.sock' > ~/.config/environment.d/bitwarden-ssh-agent.conf
-
Create
~/.ssh/configwith the content of the ssh config note. -
Install Tabby, launch it once, close it completely, then from another terminal:
bash ~/dotfiles/tabby/install.sh -
On KDE Plasma Wayland, Tabby's global shortcut (
Ctrl+Shift+Space, show / hide) goes through the global shortcuts portal, which Electron only uses with a flag. Add that flag to Tabby's launcher:sed 's|^Exec=/opt/Tabby/tabby|& --enable-features=GlobalShortcutsPortal|' /usr/share/applications/tabby.desktop > ~/.local/share/applications/tabby.desktop
Relaunch Tabby from the launcher, then in System Settings → Keyboard → Shortcuts → org.chromium.Chromium, assign
Ctrl+Shift+Spaceto "Electron shortcut Ctrl+Shift+Space" (once only, KDE remembers it). Thehacks.globalHotkey: falseintabby/config.yamlforces Tabby to declare this shortcut, which it otherwise ignores on Plasma Wayland before 6.6. -
Verify:
ssh-add -Lshows the Bitwarden key, Tabby opens the chosen connection at launch, andCtrl+Shift+Spaceshows / hides Tabby.
cd ~/dotfiles && git pull && just xps
exec zshTabby: see Tabby.
-
Give the public key to the server. It is the lock, not the key: it can be read safely.
-
New VPS: paste the public key (copied from Bitwarden) into the host's form.
-
Existing server, from Windows 11 (PowerShell) or the XPS, with a password connection:
ssh-add -L | ssh user@host "umask 077; mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys"
-
-
Add the connection (see Add a connection).
-
On the server:
sudo apt install git curl zsh git clone https://github.com/Sonny93/dotfiles ~/dotfiles curl https://mise.run | sh export PATH="$HOME/.local/bin:$PATH" cd ~/dotfiles mise exec just -- just shell chsh -s "$(command -v zsh)" exec zsh
-
Everything else:
just server
just serverrunsapt,shellanddoctor-server.just shellonly installs the tools the shell needs (starship, fzf, just, fastfetch), not the whole mise config.
Never do on a server: put the private key on it, sign in to GitHub from it, or enable ForwardAgent to it (root on the server could use your key while you are connected). Agent forwarding is reserved for your own dev machines.
cd ~/dotfiles && git pull && just server
exec zsh- Give the public key to the server (see Server / VPS).
- Add the
Hostto the Bitwarden ssh config note, then to~/.ssh/configon Windows 11 and the XPS (and on the dev machine if it must reach this server). Tabby shows it at next launch.
just or just help lists the commands. Windows 11 uses none: Tabby goes through its script (see Tabby).
| Command | Purpose | Dev / XPS | Server |
|---|---|---|---|
just dev (alias just xps) |
Install / update a dev machine: apt, build-deps, shell, mise, tmux, git, gh-auth, notes, claude, doctor-dev |
✓ | |
just server |
Install / update a server: apt, shell, doctor-server |
✓ | |
just apt |
Update the system packages | ✓ | ✓ |
just build-deps |
Install the system packages to build tmux (build-essential, libncurses-dev, bison) |
✓ | |
just shell |
zsh, Starship and the shell's mise tools | ✓ | ✓ |
just mise |
Install / update every tool in mise/config.toml |
✓ | |
just tmux |
Link the tmux config (~/.config/tmux/tmux.conf) and the micro Cobalt2 theme (~/.config/micro/colorschemes/cobalt2.micro) |
✓ | |
just git |
The repo's git config + gitleaks hook | ✓ | |
just notes |
Turn ~/notes into a git repo linked to the private GitHub repo notes (created on the first machine, cloned elsewhere) |
✓ | |
just gh-auth |
GitHub sign-in through gh, used by git over HTTPS |
✓ | |
just claude |
Link the Claude Code config (CLAUDE.md, RTK.md, agents, skills) and merge the base settings | ✓ | |
just doctor-server |
Check the shell | ✓ | ✓ |
just doctor-dev |
Check the whole dev machine (also runs doctor-server) |
✓ | |
just uninstall-omz |
Remove an old Oh My Zsh / Powerlevel10k install |
zsh + zinit (autosuggestions, syntax-highlighting) + Starship (prompt) + fzf (Ctrl+R / Ctrl+T / Alt+C) + fastfetch (alias ff). The update command updates the system (apt), then the tools (mise upgrade), then restarts idle Claude Code instances in tmux (tmux/restart-claude.sh). Keyboard shortcuts (Ctrl+arrows, Home/End, Delete…) come from Oh My Zsh's key-bindings.zsh file, loaded on its own through zinit: it also handles the sequences Tabby sends on Windows. Every tool comes from mise, with a single config for all machines.
Each machine gets a colored badge (emoji) computed from its hostname, shown in the tab title and in the prompt: no configuration. If two machines collide, rename one of them or add export MACHINE_BADGE=🟢 to ~/.zshenv on that machine.
tabby/config.yaml only contains what differs from Tabby's defaults. It is the reference: a setting is changed in the repo (commit + push), then reinstalled on Windows 11 and the XPS. A setting changed in Tabby's interface is lost at the next install.
Install or update, with Tabby closed, from another terminal:
- Windows:
irm https://raw.githubusercontent.com/Sonny93/dotfiles/main/tabby/install.ps1 | iex(tabby/install.ps1) - XPS:
cd ~/dotfiles && git pull && bash tabby/install.sh(tabby/install.sh)
The script:
- backs up the local config (
config.backup-<date>.yaml, next to it); - replaces it with the repo's;
- keeps the already known hosts (no new fingerprint validation);
- keeps the default profile if it points to a
Hostof~/.ssh/config, otherwise asks which one to open at launch (aHostor the local shell).
Connections do not go through the Tabby config: it reads them from ~/.ssh/config and shows them as mon-serveur (.ssh/config). Their shared settings come from profileDefaults.ssh in tabby/config.yaml: tab title sent by the remote shell (colored badge and machine name, then the current folder or command) and new tab / SFTP opened in the current folder. The .zshrc sends both at every prompt, no profile to edit by hand.
On dev machines, tmux keeps sessions alive when the SSH connection drops. mise builds tmux from source (mise-tmux plugin, with build-essential, libncurses-dev and bison installed by just build-deps): the prebuilt binaries from tmux/tmux-builds lag weeks behind tmux releases. tmux/tmux.conf (linked by just tmux) only contains what differs from tmux's defaults. The prefix is Ctrl+A (Ctrl+B is used by Claude Code to send a command to the background).
t opens or attaches a tmux session. t . (or any path, for example t ~/dev/app), run from a project (any subfolder of a git repo), opens or attaches the project's session, named after the git root folder, with the "Claude + shells" layout in the first tab: Claude Code on the left, two stacked shells on the right (tmux/workspace.sh). Claude resumes the folder's last conversation there (claude --continue), for example after a tmux restart; a "Claude + shells" tab added to an existing session starts a fresh conversation, so two tabs never share the same one. t <name> attaches the session of that name, or creates it if <name> is a known project (~/dev/<name> or ~/dotfiles); Tab completes sessions and projects. t alone attaches the last session, or opens the session picker when already inside tmux; with no session at all, it opens the picker directly in the terminal, limited to projects: Enter creates the chosen project's session and attaches to it, Esc creates nothing. When Claude exits, its pane stays open on a shell. Inside tmux, t switches to the session without nesting another one.
tmux boils down to two notions: the session (usually a project) and the tab. Alt+n or Ctrl+A then c opens the "New tab" menu: Claude + shells (w), Claude alone (c) or shell alone (s), in the current pane's folder (tmux/new-tab-menu.conf, defined once and reused by the palette).
Ctrl+A then s opens a single picker (fzf in a popup): the open sessions as a tree (the current one is marked), each followed by its tabs with the same label as the top bar, then the folders of ~/dev and ~/dotfiles without a session. The current tab is preselected, the preview shows the targeted tab's pane, and the session name stays filterable from each tab. Enter switches to the session or tab, creating the session with "Claude + shells" for a project, Esc closes without doing anything (tmux/session-picker.sh). The native tree of sessions and tabs remains in the palette.
Ctrl+A then n opens the project's free-form notes in a popup (micro with the Cobalt2 theme, micro/colorschemes/cobalt2.micro, linked by just tmux; VS Code-style shortcuts: Ctrl+S saves, Ctrl+Q closes, Ctrl+C copies the selection to the workstation's clipboard (OSC 52), autosave, Ctrl+Z history kept after closing). The file is ~/notes/<project>.md, outside the repos; the project is the git root folder of the current pane, created with a title on first use (tmux/notes.sh). ~/notes is a git repo: opening does a pull, each close commits then pushes to the private GitHub repo notes created by just notes. Commits are signed notes <notes@localhost>, an address unknown to GitHub: they do not inflate the profile's contribution graph. On conflict or failure, a tmux message flags it: ask Claude to sync ~/notes. This key replaces tmux's native next-window: Alt+1 … Alt+9 switch tabs.
The status bar spans two lines, in Cobalt2 colors like Tabby: the first lists the sessions (the current one in yellow), the second the tabs, each named after the title of its Claude pane. A bell in any pane of the tab turns its tab pink; claude/settings.json sets preferredNotifChannel to terminal_bell so Claude rings when it has finished or is waiting for an answer. Alt+1 … Alt+9 switch tabs without the prefix, and so do Alt+& … Alt+ç (the AZERTY number row).
Ctrl+A then Ctrl+K opens a command palette (fzf in a popup): the actions of tmux/palette.tsv (label, shortcut, tmux command; one action = one line), Enter runs the command on the pane the palette was opened from, Esc closes (tmux/command-palette.sh).
After update, Claude Code instances that are already open keep the old binary: the "Restart idle Claude Code" palette action restarts the idle ones with claude --resume, skipping those that are working and those already up to date (tmux/restart-claude.sh).
The shortcuts live in tmux/cheatsheet.md; Ctrl+A then h opens it in tmux.
tmux ls lists the sessions. The mouse is enabled: dragging selects text within a single pane without copying it, Ctrl+C or Enter copies it to the workstation's clipboard, a click or Esc cancels.
claude/ holds the Claude Code user config, applied by just claude (run by just dev):
- Linked: the top-level
.mdfiles ofclaude/(CLAUDE.md,RTK.md), each file ofclaude/agents/and each folder ofclaude/skills/are linked one by one into~/.claude/(~/.claude/CLAUDE.md,~/.claude/RTK.md,~/.claude/agents/<name>.md,~/.claude/skills/<name>). The rest of~/.claude/skills/(synced/, skills installed bynpx skills, plugins) is left untouched. - Merged:
claude/settings.jsonis a base merged (jq) into~/.claude/settings.json, never linked: Claude Code rewrites that file itself. The repo's keys win, the machine's own keys (model,theme…) stay. - rtk:
rtk(installed byjust mise) compresses command output before it reaches Claude. The base settings declare itsPreToolUsehook (rtk hook claude) andCLAUDE.mdimportsRTK.md: nortk initto run. Do not rerunrtk init -g(it would modifysettings.jsonand write through theRTK.mdlink); if a new rtk version changesRTK.md, regenerate it elsewhere and copy it toclaude/RTK.md. claude/statusline.shis referenced directly by the base settings: nothing to install.- Doc and code skill guard:
claude/hooks/require-skill.sh, referenced by the base settings (PreToolUsehook onEdit|Write), refuses any change to aCLAUDE.md,CLAUDE.local.md,AGENTS.mdor.claude/rules/*.mduntil theclaude-mdskill has been loaded, to aREADME.md,docs/**/*.md,CONTRIBUTING.md,DEPLOY.md,PUBLISHING.mdorSECURITY.mduntil theproject-docsskill has been, and to TypeScript files untiltypescript-conventionshas been (plusreact-conventionsfor.tsx,adonis-conventionsfor.tsinside an AdonisJS project, found by itsadonisrc.ts). Declaration files,dist/,build/,.adonisjs/andnode_modules/are skipped, and so areCHANGELOG.mdanddocs/adr/. A skill counts as loaded when it was invoked in the editing agent's session or preloaded by that agent's definition (skills:inagents/<name>.md). - Context7 guard:
claude/hooks/require-context7.sh, referenced by the base settings (SessionStarthook, asynchronous, andUserPromptSubmithook), checks withclaude mcp get plugin:context7:context7that the Context7 server is connected. When its status is notConnected(typically a pending OAuth login), the first prompt of the session is blocked with a message asking the user to run/mcp; sending the message again goes through. It blocks once per session, so a Context7 outage never locks the user out, and does nothing when the status cannot be determined (noclaudeon thePATH, error, 10 s timeout).
Before replacing anything, just claude moves what exists to ~/.claude/backups/dotfiles-<date>/ (same relative path) and prints each backup. Nothing is deleted, except links to the repo that became orphaned (skill or agent removed from the repo).
- Removing a setting: the merge only adds and overwrites. A key removed from
claude/settings.jsonis removed by hand from~/.claude/settings.json. - Adding a skill or an agent: create it in
claude/skills/<name>/SKILL.mdorclaude/agents/<name>.md, thenjust claude. - Outside the repo: skills installed by
npx skillsor by plugins stay outside the repo. - Only machine-independent, non-sensitive settings go in
claude/settings.json.
The repo is public and never contains a secret, an identity or server info:
- SSH key: only in Bitwarden, on Windows 11 and the XPS. Locked vault = no connection possible. A lost machine = sign out of Bitwarden on it.
- Connections: in Bitwarden and in
~/.ssh/configon Windows 11 and the XPS, never in the repo. - Git identity: set in each repo.
.gitconfigalso includes~/.gitconfig.local, optional and specific to each machine, for a default identity. - GitHub:
ghtoken on dev machines only (the XPS included). It goes into the system keyring if there is one, otherwise in plain text in~/.config/gh/hosts.yml. No SSH key for GitHub:.gitconfigrewritesgit@github.com:remotes to HTTPS, so repos cloned over SSH also go throughgh, without changing their remote. - gitleaks runs as a pre-commit hook (
githooks/pre-commit, enabled byjust git) and blocks any secret trying to get in.
sudo nano /etc/update-motd.d/99-custom
#!/bin/bash
LAST_IP=$(last -n 2 $USER | awk 'NR==2{print $3}')
LAST_DATE=$(last -n 2 $USER | awk 'NR==2{print $4, $5, $6, $7}')
echo "$(figlet $(logname | sed 's/./\u&/'))"
echo -e "\e[44m\e[97m 🔐 Dernière connexion : $LAST_DATE depuis $LAST_IP \e[0m"
echo ""
echo "📅 $(date)"
echo "🖥️ $(hostname | sed 's/./\u&/') — Linux $(uname -r)"
echo "💾 RAM : $(free -h | awk '/Mem/{print $3"/"$2}') ($(free | awk '/Mem/{printf "%.0f%%", $3/$2*100}'))"
echo "💿 Disque : $(df -h / | awk 'NR==2{print $3"/"$2" ("$5")"}')"
echo "🌡️ Uptime : $(uptime -p)"
echo ""