Arboretum

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, sessions on your main branch or any worktree, live session states, the web terminal, mobile supervision (installable PWA, Web Push when a session needs you, answer a prompt without opening a terminal), and work groups (drive several related repos from a single Claude session) are implemented and tested. The latest milestone turns Arboretum into a real **worktree IDE**: an in-browser workspace (file tree, Monaco editor, inline diffs, integrated terminal), detailed git status backed by a real-time file-system watcher, selective staging / discard / amend / fetch / pull, automatic session archival, real-time settings sync across browsers, and encrypted remote git services (GitHub / GitLab / Gitea) with HTTPS clone. --- ## 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 list` across multiple repos is a chore, worktrees pile up, each needs its `node_modules` and `.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. Hide the old ones that clutter the list (one click clears the whole external history; they stay resumable). - **Web terminal** — full xterm.js terminal to every managed session, surviving browser disconnects; truly fullscreen, with the prompt pinned to the bottom and full scrollback above. - **Worktree IDE** — open any worktree in a full in-browser workspace: a file tree, a Monaco editor, an inline per-file diff viewer and the worktree's session terminal, side by side. Edit files, stage changes selectively, commit (or amend), fetch/pull and push — all without leaving the dashboard. A real-time file-system watcher keeps the view live as the agent edits. - **Supervision from your phone** — installable PWA with push notifications when a session needs you; answer a prompt (its options, or deny) without opening a terminal. - **Work groups** — bundle related repos (e.g. an API, its web frontend and its docs) into a named group, then launch **one Claude session that spans all of them at once** (via the CLI's `--add-dir`): a single conversation with one shared context working across every repo, plus a unified view of all their worktrees and a side-by-side multi-terminal grid. Group sessions can either create the same branch worktree in each repo first, or run straight on the main checkouts. - **Remote git services** — connect your GitHub, GitLab or Gitea accounts (personal access token or app password), stored **encrypted at rest** (AES-256-GCM); browse your remote repositories and clone them over HTTPS with live progress, straight from the dashboard. - **Automatic archival** — finished sessions are archived automatically after a configurable retention window (30 days by default), so the list stays focused on what's live while the history is one toggle away. - **VS Code extension** — a native extension (not a webview) that brings the live tree, native session terminals, waiting alerts and git actions right into your editor. See [VS Code extension](#vs-code-extension). --- ## 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. (`.nvmrc` pins `22`.) - **The `claude` CLI** on your `PATH` if 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: ```bash 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): │ └──────────────────────────────────────────────────────────────────┘ 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](#running-it-as-a-background-service) relies on — install it globally instead: ```bash 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: ```bash 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 1. **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. 2. **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. 3. **Start or resume a session.** Launch a Claude Code session on the repo's main branch or any worktree, or resume one that was started in your terminal — Arboretum discovers existing sessions automatically and always resumes them in their original working directory. 4. **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). 5. **Supervise from your phone.** Install the PWA, and when a session flips to *waiting* you get a push notification. Answer the prompt — pick one of its options or deny it — straight from the dashboard, no terminal required. ## Workspace & git operations Beyond supervising, Arboretum lets you actually *work* on a worktree from the browser. Open any worktree's **workspace** (`/workspace`) for an IDE-like, three-pane view: - **File tree & editor.** Browse the worktree, open a file in a **Monaco editor** (syntax highlighting, language detection), edit and save it. Binary and oversized files are handled gracefully. - **Per-file diffs.** A unified diff viewer shows exactly what changed, line by line, for the working tree or the index — additions and deletions colour-coded, with a refusal to render binary or huge diffs. - **Selective staging & commit.** Stage or unstage individual files, discard changes you don't want, write a message and **commit** — either everything (`git add -A`) or just what's staged. **Amend** the last commit (refused once it's been pushed), then **fetch**, **pull** (fast-forward or rebase) and **push**. - **Detailed git status.** Every worktree reports staged / unstaged / conflict counts and its last commit, kept current by a **real-time file-system watcher** so the diff and counts update the moment a Claude session touches a file. - **Integrated terminal.** The worktree's correlated Claude session terminal sits right next to the editor, so you can read the diff and talk to the agent in one place. Like every git action in Arboretum, these run **as you** (the daemon runs under your account) — consistent with the security model: a web terminal is remote code execution by design. ## VS Code extension Prefer to stay in your editor? Arboretum ships a **native VS Code extension** (`packages/vscode`) — not a webview. It connects to the same daemon and surfaces it with VS Code's own primitives: - A live **Repositories** and **Groups** tree (repos → worktrees → sessions) in the Activity Bar, updated in real time over the daemon's WebSocket. - **Native terminals**: attach to (or observe) any session in a real VS Code terminal — you get VS Code's rendering, scrollback and copy/paste for free. - A **status-bar** counter and native **notifications** when a session is waiting, with Yes/No answers without opening a terminal. - Git mutations (create worktree, commit, push, promote, **fetch / pull**) with **detailed git status** in the tree (staged / unstaged / conflicts and the last commit), and **workspace awareness** — the worktree for your open folder is highlighted, with one-click "start session / create worktree here". - **Open in the web IDE** — jump from any worktree straight to its full `/workspace` view in the browser. The extension stays a lightweight, visual worktree manager; the heavy editing lives in the web IDE. It is distributed as a **private VSIX**. Build and package it from the monorepo: ```bash npm run build:vscode cd packages/vscode && npx @vscode/vsce package --no-dependencies # → git-arboretum-0.2.0.vsix ``` Then install it via **Extensions: Install from VSIX…** (or `code --install-extension git-arboretum-0.2.0.vsix`), run **Arboretum: Sign In** and paste a token. Full details in [`packages/vscode/README.md`](packages/vscode/README.md). ## 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](https://tailscale.com/kb/1242/tailscale-serve)** — valid HTTPS, tailnet identity, no open ports: ```bash # 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): ```bash npx @johanleroy/git-arboretum --allow-origin https://..ts.net ``` Open `https://..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: ```bash 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: ```bash 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`: ```ini [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 ``` ```bash 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 ` | `7317` | Port to listen on. | | `--bind ` | `127.0.0.1` | Bind address. Non-loopback is refused unless `--i-know-this-exposes-a-terminal` is set. | | `--allow-origin ` | — | Additional allowed `Origin` (repeatable). Needed for Tailscale/HTTPS access. | | `--db ` | `/arboretum.db` | SQLite database path. | | `--vapid-contact ` | `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 ` | Use this binary in the service instead of `node` + the bundled script. | | `--label ` | 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`). Settings beyond CLI flags — the directories Arboretum scans for repos and how often, the `claude` binary path and home, and the session retention / purge windows — live in **Settings** in the UI. They are broadcast over the WebSocket, so every connected browser reflects a change in real time, no reload needed. ## Remote git services & clone Arboretum can connect to your git hosting providers so you can browse and clone repositories without leaving the dashboard: - **Providers & auth.** GitHub, GitLab and Gitea, authenticated with a **personal access token** or an **app password** (SSH keys and OAuth are planned). Add a connection from **Settings → Git services**, give it a label, and test it in place — Arboretum reports `ok`, `auth failed`, `rate limited` or `unreachable`. - **Secrets stay secret.** Credentials are **encrypted at rest** (AES-256-GCM, `SecretBox`) and **never** returned in clear by the REST API — responses carry only a `…last4` hint and a "has secret" flag. - **Browse & clone.** List the repositories a connection can see, pick one, and clone it over HTTPS into a destination of your choice. The clone runs as a tracked operation with live **progress and phase** pushed over the WebSocket, and the new repo is registered automatically when it completes. ## Security model A web terminal is remote code execution *by design*. Arboretum's guardrails are structural: - Binds to `127.0.0.1` by default; refuses non-loopback binds without an explicit flag. - Authenticates **every** `/api/**` request **and** every `/ws` upgrade with revocable tokens, and applies a **strict `Origin` check** (the `SameSite=Strict` cookie 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, `HttpOnly` and `SameSite=Strict`, and it automatically gains the `Secure` flag 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-store` on the API), restricts the data directory to `0o700` and the database to `0o600`, 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`](SECURITY.md) for the full threat model and [`docs/ENTERPRISE_DEPLOYMENT.md`](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), `@arboretum/web` (the Vue 3 SPA), and `git-arboretum` (the VS Code extension — built separately with `npm run build:vscode`). ```bash 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): ```bash 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 node packages/server/scripts/acceptance-p7.mjs # git engine, file API & real-time FS watcher node packages/server/scripts/acceptance-p8.mjs # workspace IDE: changes / diff / staging / commit node packages/server/scripts/acceptance-p9.mjs # advanced commit/push: selective staging, amend, fetch/pull node packages/server/scripts/acceptance-p10.mjs # automatic session archival node packages/server/scripts/acceptance-p11.mjs # real-time settings sync node packages/server/scripts/acceptance-p12.mjs # remote git services + HTTPS clone ``` The protocol grew (additively, no version bump) to carry the new surface: client `watch` / `unwatch` messages and the targeted `worktree_changes` signal (P7), plus `session_archived` (P10), `settings_update` (P11) and `clone_update` (P12) broadcasts. Server-side, the work is backed by `core/git.ts` (the pure git engine), `core/fs-watcher.ts` (chokidar), `core/git-credentials.ts` + `core/clone-manager.ts` (encrypted credentials & clone), and the session-archive and settings services. ## Support Arboretum is a free, self-funded side project. If it saves you time, you can support its development: [![Buy Me a Coffee](https://img.shields.io/badge/Buy%20Me%20a%20Coffee-johanleroy-FFDD00?logo=buymeacoffee&logoColor=black)](https://buymeacoffee.com/johanleroy) ## License MIT — see [LICENSE](LICENSE).