- renomme le paquet en @johanleroy/git-arboretum (scope routé vers le registre npm Gitea privé) - embarque @arboretum/shared via bundleDependencies (scripts/vendor-shared.mjs au prepack) - joint README/LICENSE au tarball (scripts/copy-meta.mjs) + metadata, keywords, publishConfig - CI pack-smoke en mono-tarball avec assertion bundleDep ; nouveau workflow release.yml (publish sur tag v*) - version 1.0.0 ; README mis à jour (install scopé + service systemd)
A self-hosted web dashboard for your git worktrees and the Claude Code sessions running on them — from any device.
Status: MVP. The worktree-first dashboard, session discovery & resume, multi-repo worktree lifecycle, live session states, the web terminal, and mobile supervision (installable PWA, Web Push when a session needs you, approve/deny without opening a terminal) are implemented and tested.
The problem
Working with AI coding agents changed how we use git: one feature = one worktree = one Claude Code session, several of them in parallel. But the tooling didn't follow:
git worktree listacross multiple repos is a chore, worktrees pile up, each needs itsnode_modulesand.env.- Claude Code sessions are scattered: some running in terminals, some resumable from history, with no consolidated view of which one is waiting for your input.
- When you step away from your desk, a session blocked on a permission prompt stays blocked.
What Arboretum does
A single Node.js daemon you run on your dev machine (npx git-arboretum), serving a web UI usable from your desktop, phone or tablet:
- Worktree-first, multi-repo dashboard — every worktree of every registered repo, with its git state (branch, ahead/behind, dirty files) and the state of its Claude Code session (busy / waiting for input / idle / resumable).
- Full worktree lifecycle — create (with per-repo post-create hooks:
npm ci, copy.env…), adopt worktrees created by hand, delete with guardrails, prune orphans. - Session discovery & resume — sessions you launched in your own terminal show up automatically; resume dead ones, observe or fork live ones. Never corrupts a live session.
- Web terminal — full xterm.js terminal to every managed session, surviving browser disconnects.
- Supervision from your phone — installable PWA with push notifications when a session needs you; approve/deny a prompt without opening a terminal.
Requirements
- Node.js ≥ 22.16 — required, not just recommended. Arboretum persists state with
node:sqlite(DatabaseSync), which is native and stable only from this version. (.nvmrcpins22.) - The
claudeCLI on yourPATHif you want Arboretum to launch and manage Claude Code sessions. Arboretum wraps the interactive CLI you already use — install and authenticate it as usual. - A git repository (or several) you want to manage.
Quick start
Run it (recommended)
Arboretum is published to a private Gitea npm registry. Point the @johanleroy scope at it once per machine — add to ~/.npmrc:
@johanleroy:registry=https://git.lidge.fr/api/packages/johanleroy/npm/
//git.lidge.fr/api/packages/johanleroy/npm/:_authToken=<your-gitea-token>
(The _authToken line is only needed if the package is private.) Then run it from anywhere:
npx @johanleroy/git-arboretum
On first start, Arboretum prints a one-time access token and the URL to open:
┌──────────────────────────────────────────────────────────────────┐
│ First start — your access token (shown once, store it safely): │
└──────────────────────────────────────────────────────────────────┘
<your-token-here>
Login at: http://127.0.0.1:7317/
Open the URL, paste the token to log in, and you're in. The token is stored hashed — it is shown only once, so save it somewhere safe (a password manager). You can manage tokens later from Settings.
Run from source
git clone https://git.lidge.fr/johanleroy/arboretum.git
cd arboretum
nvm use # or ensure Node ≥ 22.16
npm install
npm run build # builds shared → server → web (order matters)
node packages/server/dist/index.js
Using Arboretum
- Add a repository. From the dashboard, register a local git repo by its path. Optionally configure post-create hooks (e.g.
npm ci,cp ../.env .env) that run automatically every time you create a new worktree for that repo. - Create or adopt worktrees. Spin up a new worktree + branch in one click (hooks run for you), or adopt a worktree you created by hand. Each worktree shows its branch, ahead/behind, and dirty-file count.
- Start or resume a session. Launch a Claude Code session on a worktree, or resume one that was started in your terminal — Arboretum discovers existing sessions automatically and always resumes them in their original working directory.
- Watch the live states. Each session reports whether it's busy, waiting for your input, or idle. Open the web terminal to interact directly; it survives browser disconnects (closing the tab does not kill the session).
- Supervise from your phone. Install the PWA, and when a session flips to waiting you get a push notification. Approve or deny the prompt straight from the notification UI — no terminal required.
Remote access from your phone
Arboretum binds to 127.0.0.1 by default and refuses to bind to a non-loopback address without an explicit override. The recommended (and safe) way to reach it from other devices is Tailscale Serve — valid HTTPS, tailnet identity, no open ports:
# Expose the local daemon over HTTPS inside your tailnet
tailscale serve --bg 7317
Then start Arboretum allowing your tailnet origin (the strict Origin check needs to know about it):
npx @johanleroy/git-arboretum --allow-origin https://<machine>.<tailnet>.ts.net
Open https://<machine>.<tailnet>.ts.net from any device on your tailnet. Web Push requires HTTPS, so Tailscale Serve (or another HTTPS front) is also what enables mobile notifications. On iOS, install the app to your home screen first, then allow notifications.
⚠️ A web terminal is remote code execution by design. Never expose Arboretum directly to the public internet.
Running it as a background service
For a daemon that survives logout and restarts on boot, run it under a systemd user service. Install a pinned version globally (npm i -g @johanleroy/git-arboretum), then create ~/.config/systemd/user/arboretum.service:
[Unit]
Description=Arboretum — git worktree & Claude Code dashboard
After=network-online.target
Wants=network-online.target
[Service]
ExecStart=%h/.local/bin/arboretum --port 7317 --allow-origin https://MACHINE.TAILNET.ts.net
Restart=on-failure
RestartSec=5
KillSignal=SIGTERM
TimeoutStopSec=10
Environment=NODE_ENV=production
[Install]
WantedBy=default.target
which arboretum # adjust ExecStart to the real path if needed
systemctl --user daemon-reload
systemctl --user enable --now arboretum
loginctl enable-linger "$USER" # start the service at boot, without an open session
journalctl --user -u arboretum -f # logs
The one-time access token is printed only on the very first run (empty database). When running as a service, capture it from
journalctl, or do one manual run before enabling the service. The token is hashed and never shown again.
Configuration
All options are CLI flags:
| Flag | Default | Description |
|---|---|---|
--port <n> |
7317 |
Port to listen on. |
--bind <addr> |
127.0.0.1 |
Bind address. Non-loopback is refused unless --i-know-this-exposes-a-terminal is set. |
--allow-origin <url> |
— | Additional allowed Origin (repeatable). Needed for Tailscale/HTTPS access. |
--db <path> |
<data>/arboretum.db |
SQLite database path. |
--vapid-contact <mailto/url> |
mailto:arboretum@localhost |
VAPID contact subject for Web Push. |
--print-token |
false |
Hint about token re-printing (tokens are hashed and cannot be re-shown). |
--i-know-this-exposes-a-terminal |
false |
Acknowledge binding to a non-loopback address. Avoid — prefer Tailscale Serve. |
State (the SQLite database) lives in $XDG_DATA_HOME/arboretum (default ~/.local/share/arboretum).
Security model
A web terminal is remote code execution by design. Arboretum's guardrails are structural:
- Binds to
127.0.0.1by default; refuses non-loopback binds without an explicit flag. - Authenticates every
/api/**request and every/wsupgrade with revocable tokens, and applies a strictOrigincheck (theSameSite=Strictcookie does not cover WebSocket upgrades — this is the anti cross-site hijacking guard). - Tokens are stored hashed (sha256) and compared in constant time; the bootstrap token is shown only once. The session cookie is an HMAC-signed payload. Login is rate-limited with exponential backoff.
The recommended way to reach Arboretum from other devices is Tailscale Serve (valid HTTPS, tailnet identity, no open ports). Never expose it directly to the internet.
What makes it different
| Arboretum | GitKraken Agent Mode / Conductor / Nimbalyst | Happy / CloudCLI | Anthropic Remote Control | |
|---|---|---|---|---|
| Web UI, any device | ✅ | ❌ desktop apps | ✅ | ✅ |
| Visual worktree management (multi-repo) | ✅ | ✅ (single repo, desktop) | ❌ | ❌ |
| Discovers & resumes existing terminal sessions | ✅ | ❌ | partial | ❌ |
| 100% self-hosted — zero traffic through third-party servers | ✅ | ✅ | relay server | ❌ relayed through Anthropic |
| Linux-first | ✅ | varies | ✅ | Desktop app has no Linux build |
| Open source | MIT | ❌ / partial | MIT / AGPL | ❌ |
Anthropic's Remote Control is great at piloting one session from your phone. Arboretum is the layer it doesn't provide: the consolidated, self-hosted board of all your worktrees and sessions across all your repos.
A note on Claude usage
Arboretum wraps the interactive Claude Code CLI in a PTY — the same thing you run in your terminal, displayed in your browser. It does not use the Agent SDK or headless mode. Anthropic's usage policies around programmatic use may evolve; Arboretum will track CLI releases and document any impact transparently.
Development
Arboretum is an npm-workspaces monorepo: @arboretum/shared (WS/REST protocol, source of truth), @johanleroy/git-arboretum (the Fastify daemon, the published package), and @arboretum/web (the Vue 3 SPA).
npm run build # build shared → server → web (order matters)
npm run typecheck # tsc -b shared + server
npm test # vitest across packages
npm run dev:server # daemon in watch mode
npm run dev:web # Vite dev server (proxies /api and /ws to the daemon on :7317)
End-to-end acceptance scripts (run npm run build first):
node packages/server/scripts/acceptance-p1.mjs # core: daemon + real WS client
node packages/server/scripts/acceptance-p2.mjs # session discovery & resume
node packages/server/scripts/acceptance-p3.mjs # worktrees & session correlation
node packages/server/scripts/acceptance-p4.mjs # Web Push + WS `answer` command
License
MIT — see LICENSE.
