upload-artifact@v4 (@actions/artifact v2+) n'est pas supporté par Gitea Actions. Le publish skip désormais une version déjà publiée (re-push de tag sans échec).
A self-hosted web dashboard for your git worktrees and the Claude Code sessions running on them — from any device.
English · Français
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), and work groups (run several related repos at once) 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 @johanleroy/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.
- Work groups — bundle related repos (e.g. an API, its web frontend and a shared library) into a named group to work on them at once: a unified view of all their worktrees and sessions, a side-by-side multi-terminal grid, and a one-click "cross-repo feature" that creates the same branch worktree (and optionally a Claude session) across every repo in the group.
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
Two paths, depending on what you want:
- Just use it (most people). Arboretum is a published npm package — you don't need to clone this repo. Point npm at the registry and run it (below). Do this on the machine where your Claude Code sessions run.
- Run from source. Clone the repo only to hack on Arboretum or run an unreleased build.
Run it (recommended)
Arboretum is published to a self-hosted 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/
No token needed — the package is publicly readable. 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.
npx fetches and runs the latest published version each time. To install it once — and get the arboretum command on your PATH, which the background service relies on — install it globally instead:
npm i -g @johanleroy/git-arboretum
arboretum # identical to the npx command, from the installed binary
Run from source
Only needed to develop Arboretum or run an unreleased build — not required just to use it. Clone the repo, install dependencies, build, then start the daemon:
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
The quickest way to run Arboretum as a service that survives logout and restarts on boot is the built-in installer. Install a pinned version globally, then run install — it detects your OS, writes the service file, starts it, and prints the one-time token:
npm i -g @johanleroy/git-arboretum
arboretum install --allow-origin https://MACHINE.TAILNET.ts.net
This sets up a systemd user service on Linux (~/.config/systemd/user/arboretum.service) or a launchd LaunchAgent on macOS (~/Library/LaunchAgents/fr.lidge.arboretum.plist). Every daemon flag (--port, --allow-origin, --db, …) is propagated to the service. Manage it with:
arboretum status # service status (+ where to read logs)
arboretum uninstall # stop and remove the service
Logs live in journalctl --user -u arboretum -f (Linux) or ~/Library/Logs/arboretum/ (macOS). Run arboretum install --dry-run … first to print the unit/plist and the exact commands without touching anything.
Prefer to set up systemd by hand? (Linux)
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 by
arboretum install(and on the very first manual run with an empty database). The token is hashed and never shown again — store it safely.
Configuration
Commands: arboretum starts the daemon (the default), arboretum serve is an explicit alias, arboretum install / uninstall / status manage the background service, and arboretum help prints usage.
Daemon 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. |
arboretum install accepts every daemon flag above (propagated verbatim to the service) plus:
| Flag | Description |
|---|---|
--bin-path <path> |
Use this binary in the service instead of node + the bundled script. |
--label <id> |
launchd label (macOS only, default fr.lidge.arboretum). |
--dry-run |
Print the unit/plist and commands without applying anything. |
--no-enable |
Write the service file but do not enable/start it. |
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,
HttpOnlyandSameSite=Strict, and it automatically gains theSecureflag when the request arrives over HTTPS (e.g. behind Tailscale Serve). Login is rate-limited with exponential backoff. - Sends hardened HTTP headers (CSP,
X-Frame-Options,nosniff,Referrer-Policy, conditional HSTS,no-storeon the API), restricts the data directory to0o700and the database to0o600, and encrypts sensitive secrets at rest (AES-256-GCM). - Keeps an audit log of sensitive operations and offers GDPR data export/erasure (Settings → Security & compliance).
Tailscale Serve is the way to reach Arboretum from other devices — not just a recommendation: valid HTTPS, tailnet identity, no open ports. The --i-know-this-exposes-a-terminal flag is an escape hatch, not a deployment mode; never expose Arboretum directly to the internet.
See SECURITY.md for the full threat model and docs/ENTERPRISE_DEPLOYMENT.md for hardening in regulated environments.
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
node packages/server/scripts/acceptance-p5.mjs # work groups: CRUD + WS broadcast + CASCADE
Support
Arboretum is a free, self-funded side project. If it saves you time, you can support its development:
License
MIT — see LICENSE.
