One command to launch a full dev environment per task. MIT licensed.
garth is a workspace orchestrator for autonomous coding agents. One command
gives you a git branch, worktree, Docker-sandboxed agents, and a Zellij terminal
session — all wired together and ready to go. Run multiple tasks across multiple
repos with multiple agents in parallel, each in its own isolated workspace.
garth new . feature/auth # branch + worktree + containers + session
garth new ../other-repo fix/bug # same thing, different repo
garth open -d . feature/auth # open an existing branch
garth open a1b2c3 # resume a session by ID
garth refresh-images # rebuild configured Docker images
garth ps # see everything runninggarth comes from Old Norse garðr ("enclosure"), the root of garden, yard, and guard. A garth is the protected courtyard within walls: an enclosed workspace where controlled work happens.
In monastic architecture, the garth was the cloister garden: a calm, ordered space surrounded by the structure that protects it.
Organization. Working on three features across two repos with Claude and
Codex means juggling branches, worktrees, terminal sessions, containers, editor
windows, and browser tabs. garth treats all of that as a single operation:
pick a task, get a workspace — complete with Cursor pointed at the worktree,
a Chrome window open to the GitHub repo, and agents ready in their panes.
Resume it later, or tear it down. garth ps shows everything at a glance.
Sandboxing unlocks autonomy. Agents are most productive when they can run
without constantly blocking on permission prompts. But running them with full
access to your machine — SSH keys, cloud credentials, Docker socket — means
you can't safely let them operate unattended. garth resolves this tension:
robust container isolation lets you grant agents permissive execution within
strict boundaries. Each agent gets only:
- a mounted worktree
- a short-lived GitHub App token
- an agent API key
No home directory, no SSH agent, no Docker socket. That's what makes it safe to kick off several long-running agents and walk away.
Convenience. One command handles what would otherwise be a sequence of git branch, git worktree, docker run, zellij, credential plumbing, and editor/browser setup. Token rotation, image builds, and session state are managed automatically.
brew install ealt/tap/garth
garth setupcurl -fsSL https://raw.githubusercontent.com/ealt/garth/main/install.sh | bash
garth setupgit clone https://github.com/ealt/garth.git && cd garth
./setup.sh --yesgarth new . feature/my-feature # new branch + worktree + session
garth open -d . # open default branch
garth up . # interactive launcher- One command to create a branch, worktree, containers, and terminal session
- Launches Cursor pointed at the worktree, Chrome open to the PR page, branch tree, or repo (context-aware)
- Resume or reattach to existing workspaces without recreating anything
- Interactive wizard or fully explicit flags — your choice
- Git worktree workflow for parallel branch/task execution
- Isolated Docker runtime with strict hardening defaults (
--cap-drop=ALL,--read-only, custom seccomp, PID limits) - Agent panes are containerized while shell panes stay local
- Cursor workspace terminal bridges into the sandbox automatically
- Config-driven safety defaults (
safevspermissive) and retry policy
- GitHub App auth with token rotation from 1Password-managed secrets
- Short-lived tokens — no long-lived credentials in containers
- Zellij session layout with one tab per agent (agent left, shell right)
garth psfor a dashboard of all sessions, branches, and status- Stop, resume, or tear down sessions and their resources independently
- AeroSpace workspace integration for per-project screen real estate
garth/
bin/garth # CLI entrypoint
lib/ # Shell and Python modules
docker/ # Dockerfile + seccomp profile
tests/ # Smoke test scripts
docs/ # Setup guides
templates/ # Config templates (AeroSpace)
config.example.toml # Baseline config
config.toml # Local config (gitignored)
Required:
gitpython3(on macOS, prefer Homebrew Python over/usr/bin/python3)op(1Password CLI, signed in)zellijdocker(for sandbox mode)
macOS Python note:
- if
python3resolves to/usr/bin/python3, macOS may prompt for Command Line Tools updates garth/setup.shauto-prepend/opt/homebrew/binwhen available, so once Homebrew Python is installed you do not need a shell restart for garth usage- recommended:
brew install python && echo 'export PATH="/opt/homebrew/bin:$PATH"' >> ~/.zshrc
Optional:
Cursor(macOS app launcher)Google Chrome(profile-per-project launcher)aerospace(workspace movement)
garth's core workflow is cross-platform at the host level and is exercised in
CI on Ubuntu: git/worktree management, config parsing, token handling, Docker
container orchestration, and Zellij session generation all run outside the
macOS-only GUI helpers.
- Works on macOS and Linux for core orchestration:
garth new,garth open,garth up,garth ps, session state management, Docker sandboxing, token refresh, and Zellij layout/session startup. - macOS-only integrations:
Cursor auto-launch, Chrome profile/window launch, AeroSpace workspace
placement, and the
launchctlGUI PATH/Python setup. - Linux behavior: unsupported GUI integrations are skipped with warnings, and Zellij falls back to launching in the current shell instead of using macOS terminal-app launchers.
- Config note:
the default
chrome.profiles_dirinconfig.example.tomluses a macOS path. That setting only matters if you enable Chrome launching on macOS.
./setup.shThis repo bootstrap script:
- runs
bin/garth setup(interactive by default) - creates
./config.tomlfromconfig.example.toml(if missing) - validates repo-local config and installs
garthin~/.local/bin - on macOS, if
python3is/usr/bin/python3, offers to install Homebrew Python (auto in--yeswhen Homebrew is installed) - on macOS, offers to install
aerospacevia Homebrew (auto in--yes) - skips GitHub App ref prompts when already done
- auto-builds missing default Docker images when Docker is available
- optionally installs/updates a managed weekly cron job to rebuild default
agent images with
--pull --no-cache - validates agent binaries in Docker images at launch and rebuilds if needed
For automation/non-interactive runs:
./setup.sh --yesIn --yes mode, setup attempts op signin automatically if the 1Password CLI
session is missing.
On macOS with Homebrew available, it also auto-installs Homebrew Python when
python3 resolves to /usr/bin/python3.
When crontab is available, it also auto-installs/updates the managed weekly
image-refresh cron job.
Debug launch toggles (env vars):
GARTH_SKIP_GUI=true: skip workspace move + Cursor + Chrome launchGARTH_SKIP_CURSOR=true: skip Cursor setup/launch onlyGARTH_SKIP_CHROME=true: skip Chrome launch onlyGARTH_SKIP_GUI_PATH_SET=true: skip macOS GUI PATH update (launchctl setenv PATH ...)GARTH_TRACE_PYTHON=true: log which Python runtime garth is using
If you prefer interactive setup prompts:
garth setupThis will:
- create
config.toml(repo root) fromconfig.example.toml - optionally switch safety default to
permissive - optionally guide you through setting
[github_app]1Password refs - validate config
- symlink
garthinto~/.local/bin/garth - build missing default Docker images (
garth-claude,garth-codex) when possible - optionally install/update a managed weekly cron job that rebuilds configured
default agent images (
--pull --no-cache)
Cron note:
- default schedule is
15 3 * * 0(weekly Sunday 03:15 local time) - override schedule with
GARTH_IMAGE_REFRESH_CRON_SCHEDULE, for example:GARTH_IMAGE_REFRESH_CRON_SCHEDULE="0 2 * * 1-5" garth setup
The example config uses placeholder op://... refs. You must replace them with
real refs from your vault.
See the dedicated setup guide:
Default config file:
./config.toml (repo root, gitignored)
Key sections:
[defaults]: selected agents, sandbox mode, network mode, workspace target, safety mode, optionalauth_passthrough[token_refresh]: lead time, retry window (0m..forever), backoff behavior[github_app]: 1Password refs and installation selection strategy[chrome]:profiles_dirand optionalprofile_directoryfor Chrome launches[features]: optional packages and host mounts for agent images[security]: protected read-only worktree paths, seccomp profile path, and auth passthrough mount modes (ro|rw)[agents.<name>]: command + safe/permissive args + API key ref
Validation is strict for known fields and warning-only for unknown fields.
Chrome note:
- set
chrome.profiles_dir = ""to open URLs in your default signed-in profile - set
chrome.profile_directory(for example"Default") to pin a specific Chrome profile when opening a new window
Feature notes:
- set
features.packagesto install optional tools in image builds (generic apt package names, plus special support foruvandbun) - set
features.mountsto add optional host mounts (files or directories) - string mount entries default to read-only and mount at the same absolute path
- table mount entries support explicit
host_path, optionalcontainer_path, and optionalmode(ro|rw) - mounting toolchain can help when command/rule files in
~/.claude/~/.codexare symlinks into your toolchain repo
garth new . feature/auth # branch from default branch
garth new . hotfix-login --base release/2.1 # branch from a specific refCreates a new branch, worktree, Docker containers, and Zellij session — all derived from the branch name.
garth open -d . # open the repo's default branch
garth open -d . feature/auth # open an existing feature branch
garth open a1b2c3 # resume an existing session by IDReuses existing worktrees and sessions when available. If a live session exists for the branch, reattaches to it automatically.
garth up . # full interactive wizard
garth up . --auto # non-interactive, all defaults
garth up . --branch feature/auth # skip branch step, wizard for restWhen run with no flags, presents a step-by-step wizard to select a branch, worktree, and session. Each step has smart defaults (hit Enter to accept). Typing a non-number at the branch step creates a new branch with that name.
garth ps # full table with status
garth ps -q # session IDs only (for piping)garth stop a1b2c3 # stop session (preserves worktree)
garth stop a1b2c3 --clean # stop + remove session state
garth down a1b2c3 # remove session + all resources
garth down a1b2c3 -y # remove without confirmation
garth ps -q | xargs garth stop # stop all sessionsgarth stop halts a session's Zellij session and Docker containers but
preserves the worktree and session state so it can be resumed later with
garth open. Use --clean to also remove the session state directory
(lighter than garth down — keeps worktrees). garth down removes
everything including managed worktrees (warns before deleting if there are
uncommitted changes).
garth gc # clean stopped sessions + orphans
garth gc --dry-run # preview what would be cleaned
garth gc --repos ~/Documents # also sweep branches across reposgarth gc performs a non-interactive sweep that removes:
- stopped session state directories
- orphan Zellij sessions (
garth-*with no matching state) - orphan Docker containers (labeled
garth.sessionwith no matching state) - local git branches whose upstream has been deleted (
[gone])
Without --repos, only the current repo's branches are checked. With
--repos <dir>, every git repo under <dir> is fetched and pruned.
The flag is repeatable for multiple parent directories.
garth containers a1b2c3 | xargs docker logs
garth containers a1b | xargs docker statsgarth refresh-images # rebuild defaults.agents
garth refresh-images --agents claude,codex # rebuild specific agents
garth refresh-images --agents claude --dry-run # preview docker build commandgarth refresh-images rebuilds selected agent images with
docker build --pull --no-cache and preserves configured features.packages.
These flags apply to new, open, and up:
--agents claude,codex--auth-passthrough claude,codex--sandbox docker|none--network bridge|none--safety safe|permissive--workspace 3|auto
Workspace note:
- when
defaults.workspace = "auto"(default),garthuses the next numeric AeroSpace workspace (max + 1)
Auth note:
--sandbox docker: agent API keys are required unless the agent is listed indefaults.auth_passthrough(or passed via--auth-passthrough)garth agentauto-detects localclaude/codexCLI auth state and auto-enables passthrough for those agents in Docker mode--sandbox none: local CLI login auth is supported (for exampleclaude auth login,codex login)- when
claudeis in auth passthrough,garthmounts Claude auth/state paths (~/.claude,~/.config/claude,~/.local/state/claude,~/.local/share/claude,~/.cache/claude) so login state persists across container restarts (mount mode is configurable via[security.auth_mount_mode]) ~/.claude.jsonis not mounted at/home/agent/.claude.json; instead it is mounted read-only to a side path and used as a startup seed, thengarthmerges/restores OAuth state from Claude backups as needed- container cache root (
/home/agent/.cache) is writable tmpfs so tools likeuvcan create caches even with--read-only - on interactive terminals, when a secret read requires 1Password auth,
garthauto-attemptseval "$(op signin)"and retries
garth agent . --sandbox docker # use defaults.agents
garth agent . codex --sandbox docker # single agent override
garth agent . --agents claude,codex --sandbox dockergarth token .garth caches recently minted GitHub installation tokens in
$XDG_STATE_HOME/garth/token-cache (permissions 0700/0600) and reuses them
until they are near expiry. This avoids unnecessary 1Password prompts.
garth doctor --repo .
garth doctor --repo . --deep- Secrets are not passed in process args or layout files.
- Agent API keys go through short-lived
0600env files. - GitHub token is mounted as a file (
/run/garth/github_token) and rotated atomically. - Repository mount path inside containers is
/<repo-name>-sandbox(for example/simplexity-sandbox). - Container defaults:
--cap-drop=ALL--security-opt no-new-privileges:true--security-opt seccomp=<repo>/docker/seccomp-profile.json(custom profile)--pids-limit=512--read-only- tmpfs mounts for writable transient paths
- read-only overlays for sensitive repo paths (
.git/hooks,.git/config,.github,.gitmodules)
- Session events are written to
$XDG_STATE_HOME/garth/sessions/<session>/audit.log(JSONL,0600) with secret redaction.
For implementation details (module-level security functions, seccomp profile
usage, auth mount modes), see AGENTS.md.
- macOS: full launch flow, including GUI integrations
- Linux: core orchestration path is supported; macOS-only GUI integrations are skipped with warnings
Auth/config check: rungarth doctor --repo .Runtime auth/startup probe: rungarth doctor --repo . --deepConfig not found: rungarth setup1Password CLI is not signed in: runeval "$(op signin)"1Password sign-in keeps getting requested: ensure you are runninggarthinteractively (TTY attached) so auto sign-in can prompt, and verifyop whoamisucceeds in the same shellgarth keeps prompting for 1Password: check token cache reuse withgarth token . --machine; if the token is near expiry, a freshopauth is expectedCan't connect to AeroSpace server: start app withopen -a AeroSpace"isn't a vault in this account": updateop://...refs inconfig.tomlto your actual vault/item/field namesClaude native installer mismatch: if you see messages likeinstallMethod is nativeorNative installation exists but ..., rebuildgarth-claude:latest(for examplegarth refresh-images --agents claude)bash: line 1: claude: command not found: rebuildgarth-claude:latestfrom thegarthrepo (garth refresh-images --agents claudeis simplest)Unsupported remote URL: ensure repo uses a GitHub remote URLSession already exists: rungarth stop <id>first (find the ID withgarth ps)macOS asks to install Developer Tools for python3: ensure/opt/homebrew/bin/python3exists (brew install python);garthprefers that interpreter at runtime. On macOS,garthalso updates GUI app PATH vialaunchctl setenv PATH ...; it also creates apythonshim at~/.local/state/garth/gui-bin/pythonfor tools that invokepython(notpython3). Fully quit/reopen Cursor after launching a session so the new environment is picked up.
See CONTRIBUTING.md for development setup, testing patterns,
and the PR workflow.