Documentation

Everything Cleat can do, from first install to the security internals. Current as of v1.2.4.

Why Cleat #

AI coding agents are at their best with full permissions. They install, build, test and iterate without stopping to ask. That is exactly what --dangerously-skip-permissions unlocks in Claude Code. On your actual machine it is a disaster waiting to happen: one wrong command can overwrite system files, read your credentials, or reach into every other project you have. Cleat gives the agent that same unrestricted flag, safely, inside a container that cannot touch your host.

Without isolation

  • modifies /usr/local, overwrites /etc/hosts
  • reads ~/.ssh, tokens, every secret you have
  • reaches into your other projects

Secrets leaked. Home folder wiped. No undo.

Inside Cleat

  • install, build, delete, run anything
  • cannot touch your host filesystem
  • cannot reach your other projects

8 hours later · 47 files changed · host untouched.

Why not just write a Dockerfile?

You could. But then you handle UID/GID mapping, clipboard bridging, auth sharing, session persistence, per-project container naming and a dozen edge cases. Cleat does all of it in one command and keeps your files owned by you, not root.

Install #

Requirements

  • Docker. Cleat starts it for you when it's down and offers to install it when it's missing. Prefer to set it up yourself? Get Docker, then confirm with docker run hello-world.
  • macOS or Linux (Windows via WSL2). On macOS, Docker Desktop is the easy way to get Docker. Cleat also runs on plain Docker Engine, so it doesn't require Desktop.
  • An Anthropic account (Pro, Max, Team, or Enterprise) or an API key. Already signed into Claude Code on this machine? Cleat reuses that login automatically.
  • git, used by the installer and by cleat update.
$ curl -fsSL https://cleat.sh/install | bash

This clones Cleat to ~/.cleat, checks out the latest stable tag and symlinks cleat onto your PATH.

Prefer to read it first? curl -fsSL https://cleat.sh/install -o install.sh, inspect it, then run bash install.sh, or clone the repo and run ./install.sh yourself. If you cloned or moved the script, cleat install symlinks it onto your PATH and cleat uninstall removes it. Then drop into any project and go:

$ cd ~/code/my-app$ cleat

The first run downloads a prebuilt image once (later runs reuse it instantly), starts the container, shares your Claude auth and launches Claude, locked down by default:

✔ Image ready (pulled v1.2.4)

✔ Container started

✔ Auth shared

✔ Claude launched

  Container:  cleat-my-app-a1b2c3d4

  Project:    ~/code/my-app /workspace

No git identity, no SSH keys, no env files, no Docker socket: just your repo at /workspace and Claude's auth. You add access deliberately with cleat config (next sections).

That → /workspace is the locked-down default. Grant the docker cap and the Project line instead reads (same path, sandboxed). Your repo mounts at its real host path so in-container docker commands resolve on the host daemon. That's the only thing that moves it. See what a configured start shows.

Signing in

If you already use Claude Code on this machine, you're set. Cleat mounts your existing ~/.claude login, so nothing else is needed. If Claude asks you to log in, run cleat login once the container is up: the OAuth page opens in your browser and the callback is proxied straight back into the container. You only log in once and it's shared across every project and box.

If the first run fails

  • Docker isn't running: Cleat starts it for you (Docker Desktop, OrbStack, or Colima on macOS. Desktop or a rootless engine on Linux. The Windows-side Desktop from WSL2), waits, then continues your command. No docker CLI at all? Cleat offers to install one. Where it can't start Docker (root-owned engine, script, CLEAT_NO_AUTOSTART=1), it prints the exact command to run instead.
  • "permission denied … /var/run/docker.sock" (Linux): your user isn't in the docker group. Run sudo usermod -aG docker $USER, then log out and back in.
  • Claude asks you to log in: run cleat login (see above).

Quickstart #

The daily loop is six commands. They all act on the current directory.

$ cleat # start the sandbox + launch Claude (the default command)$ cleat resume # continue the last session, with full context (alias: r)$ cleat stop # stop the container, keep it for next time$ cleat shell # open a bash shell inside the container (alias: sh)$ cleat ps # list every Cleat container across all projects$ cleat rm # remove the container; your session survives on the host

Sessions live on your host, not in the container, so cleat rm never loses your work. cleat resume recreates a fresh container and picks up exactly where you left off. Once a container is running, cleat login signs you in: the OAuth page opens in your browser and the callback is proxied straight back into the container.

Capabilities #

By default the sandbox is sealed. Capabilities are opt-in grants that extend what it can reach on your host. Each one is off until you turn it on. Manage them with cleat config:

$ cleat config # interactive picker (space to toggle, enter to save)$ cleat config --enable git # turn on a capability, globally$ cleat config --project --enable env # write it to this project's .cleat instead$ cleat config --list # show what's enabled$ cleat --cap ssh # enable for one run only, no config change
git mount

Mounts your ~/.gitconfig read-only. Commits carry your name and email. Identity only: it grants no repo access.

ssh mount

Mounts ~/.ssh read-only and forwards your SSH agent ($SSH_AUTH_SOCK). push, pull and clone private repos work. Reaches every host your keys can. A real privilege grant, which is why it is separate from git.

env mount

Loads variables from ~/.config/cleat/env and .cleat.env. With it off, those files are ignored entirely.

hooks mount

Runs your Claude Code hooks on the host through a bridge. With it off, hooks are stripped from the container so it cannot run host commands.

gh mount

Mounts ~/.config/gh read-write so gh auth login persists. The token lives on the host and survives rm, nuke and rebuild.

docker sandbox

Mounts the host Docker socket so the agent can run docker compose and docker run. Breaks isolation: socket access is root-equivalent on the host. Off by default, loud warning on every start.

mount capabilities give the container something from your host (files for git, ssh, gh and hooks, plus environment variables for env), while sandbox capabilities change the isolation boundary itself. There is exactly one of the latter, docker. Cleat warns loudly on every start.

What a configured start shows

Turn caps on and the summary lists exactly what's mounted, grouped by category. The docker cap also mounts your workspace at its real host path, so the Project line reads (same path, sandboxed) instead of → /workspace. A loud warning also prints on every start:

✔ Image ready (cached)

✔ Container started

✔ Auth shared

  ! Docker socket mounted. Container can create host-level processes

✔ Claude launched

  Container:  cleat-my-app-a1b2c3d4

  Project:    ~/code/my-app (same path, sandboxed)

  Caps:

    mount:    git, ssh

    sandbox:  docker (breaks isolation)

Changing a capability recreates the container. Docker mounts are fixed at create time, so after a config change the next cleat notices the drift and asks to recreate. Your session survives the recreate.

Upgrading Cleat does not. A new CLI version only prompts to refresh the image when the image's contents actually change (the entrypoint, the bridges, the base). A version-only release leaves your container and everything you installed in it untouched.

Boxes #

new in v0.14.0

You can skip this until you need it. A single default sandbox is all most people ever use. Boxes are here for when one project needs more than one posture at once.

A box is a named sandbox for the current project. Every box mounts the same live /workspace (the same uncommitted files), but each gets its own capabilities, trust, writable layer and Claude session. The grammar is cleat <verb> [box]. The token after the verb is always a box name, never a path.

This buys you least privilege within a single repo. Run a locked-down editing box beside a cloud-capable one over the same code. The editing box genuinely cannot reach what the other holds:

# A locked-down everyday box: identity only$ cleat config dev --enable git$ cleat start dev # A separate box with the Docker socket, over the same files$ cleat config az --enable docker$ cleat start az --desc "cloud box: az login lives here"

A named box reads .cleat.<box> if present, otherwise it falls back to .cleat. This is replace, not merge: a box can declare fewer capabilities than the project default, which is the whole point. Trust, env files (.cleat.<box>.env) and Claude sessions are per-box too: approving the az box never grants dev those caps, while cleat resume dev never crosses into az's conversation.

$ cleat shell dev # bash into the dev box$ cleat status # list this project's boxes, caps, descriptions$ cleat ps # every box across every project$ cleat describe az "prod only" # update a description, never recreates the box$ cleat rm scratch # remove just the scratch box

A box is not an auth boundary for your Claude account. Your Anthropic credentials (~/.claude) are shared read-write into every box so you log in once. Sign in from any box (or the default) and they're all authenticated. Boxes isolate host-capability blast radius, the writable layer and per-project Claude state, not your login. cleat, cleat start and cleat start main all mean the same default container.

Kits #

new in v1.2.0

A kit is a curated Claude Code pre-configuration (a CLAUDE.md policy plus custom subagents) enabled for one box with one command: the setup power users hand-build and the first feature that makes a Cleat box a better place to run Claude, not just a safer one. The flagship, plan-big-execute-small, adapts the coordinator pattern from Anthropic's cookbook (big models for planning, small models for execution): your session's model (pick Fable 5 once with /model) plans and reviews every result while worker and scout subagents (Sonnet 5 by default) carry the mechanical bulk at the worker model's rate, each in its own context window, so the session stays lean and heavy work burns your rate limit far slower. It merges over your own config, never touches your host and cleat kit show prints every line before you enable it. To verify it is routing, check /usage after a heavy session: the bulk of tokens should sit on the worker model.

$ cleat kit # interactive picker: kit, then models$ cleat kit plan-big-execute-small # or enable directly, for the default box$ cleat kit plan-big-execute-small dev # or for the dev box only$ cleat kit show plan-big-execute-small # read every line it injects, first$ cleat kit off # back to your own config next session

A kit merges on top of your own setup inside the box. Your global CLAUDE.md content comes first, byte for byte, then a short marked Cleat box notes section every box carries (the clipboard-bridge rules: copying to your host clipboard works, paste-back does not), with the kit's section clearly marked below it. Your own agents sit beside the kit's (a same-named agent of yours wins). Your commands, skills and settings pass through untouched. And it is per box: main can stay vanilla while a refactor box runs the planner kit on the same repo.

Your host is never written. The kit exists only as read-only files masked over ~/.claude/CLAUDE.md, ~/.claude/agents and ~/.claude/commands inside the container. Native claude on your machine never sees it and the caged agent cannot rewrite its own instructions. Kits contain instructions and subagents only. No hooks, no settings, nothing that executes as a program and cleat kit show prints every line before you enable one.

Kit changes take effect at the next session (Claude reads its config at session start) and enabling, switching, or disabling one never recreates the box. One exception: a box created before kits existed lacks the mount points, so the first enable there offers a one-time rebuild. Inside any box created since kits (kitted or not), your user-level ~/.claude memory, agents and slash commands are read-only views (a hardening side effect: the caged agent still reads them but can no longer rewrite any of the three instruction surfaces your host claude obeys). Author agents and commands you make in a box at project level (.claude/agents/, .claude/commands/) instead.

Security model #

The boundary is the container itself. The agent writes only /workspace and the container's throwaway layer. Everything else on your machine is out of reach unless you grant it.

Protects against

  • Host filesystem damage: writes are confined to your repo and the ephemeral layer.
  • Credential theft: SSH keys and gitconfig are absent by default, read-only when shared.
  • Malicious repo config: a project's .cleat cannot silently grant capabilities (see trust, below).
  • Cross-project bleed: sessions and per-project .claude.json are isolated, so trust and MCP approvals never leak between repos.

Deliberately allows

  • Inside the box, the agent has full power: install, build, delete, run anything.
  • Claude runs with permissions auto-approved (--dangerously-skip-permissions), safe because the container, not a prompt, is the boundary.
  • ssh reaches every host your keys can. A real grant: opt in knowingly.
  • docker is root-equivalent on the host. It explicitly breaks the sandbox.
  • Shared by design, not a boundary: your Anthropic auth and host Claude settings.

Workspace trust

A .cleat file lives in the repo, so it is untrusted input. Cleat never applies its capabilities silently. The first time a project requests them, you get a prompt that defaults to no. In non-interactive contexts (CI), project caps are dropped unless you opt in. Your global config and --cap flags are always trusted, because you wrote them.

$ cleat trust # approve this project's .cleat caps$ cleat trust --list # review approvals (green ● ok, amber ● = .cleat changed)$ CLEAT_TRUST_PROJECT=1 cleat # approve + record non-interactively (CI)

Resource limits & honesty

Each box runs with --init (a real PID 1 that reaps orphaned processes, so long sessions can't wedge on zombie buildup and cleat stop is instant), a memory ceiling sized to your Docker VM (a quarter of VM memory, clamped to 4–8 GB, with swap off so a runaway process OOMs inside its own box instead of freezing every session) and --pids-limit 4096 (a fork-bomb guard) on Docker's default bridge network. Your host is reachable at host.docker.internal. Files the agent creates are owned by you, via UID/GID remapping. Need more headroom for a heavy build, or want a box pinned to fewer cores? Set it per project or per box. Repo-supplied values are capped (8g memory, your machine's core count for cpus) so an untrusted .cleat can't overcommit your machine:

# ~/.config/cleat/config (or <project>/.cleat, .cleat.<box>)$ [resources]$ memory = 4g$ cpus = 2 # optional; decimals OK; unset means all cores

If a session is ever killed by that memory ceiling, Cleat says so (and what to do) instead of leaving an unexplained crash. The usual culprit is a test runner: jest and vitest start one worker per CPU and a box sees every host core, so a small box can spawn a dozen Node workers and blow its ceiling. Cap the workers (jest --maxWorkers=2), give the box more memory, or set cpus so the runner self-limits.

Prebuilt images are multi-arch (Intel and Apple Silicon both run natively, never under emulation). cleat prune clears cleat's own stale images (superseded builds and pulls, never your boxes or other projects). Cleat watches for this itself: when stale images pile up it offers the prune on start. Closing a terminal ends the session but leaves the box running, still reserving its memory ceiling, so on every start Cleat first stops other idle boxes that are safe to stop (detached, no agent running, idle past a grace window) and tells you what it freed. A box working unattended (terminal left open) is never touched. If running sessions still reserve more memory than the Docker VM has, Cleat names how many are running and your real RAM and the VM, before the freeze does. A box and a worktree are the same thing (one session, one memory ceiling), so Cleat sizes the VM with comfortable headroom for parallel work (a ≈16 GB default, capped at half your RAM) and, when yours is smaller, tells you the exact value to set, the click-path and your machine's safe max, naming the Docker Desktop knobs the CLI can't touch (memory, swap, VirtioFS) and keeps reminding you until you fix it. That's a default, not a cap on sessions: a ceiling is a limit, not a reservation, so you can run many sessions at once and the VM only swaps if a lot of them go genuinely heavy on memory at the same time (rare and no different from Claude Code outside Docker). When the VM is already as large as your RAM can safely back, it says so and steers you to running fewer sessions instead of asking for memory that isn't there. It checks swap too: memory and swap are separate Docker Desktop sliders, so if you bump memory (often on Cleat's advice) but leave swap at the default, Cleat flags just the swap, so a brief spike pages out instead of freezing a session. And when your VM is sized right, Cleat says so on every start with a one-line nod (✔ Docker tuned for Cleat) right above Image ready, so a good config gets confirmed, not just a bad one flagged. The wording is engine-aware: on a native Linux engine there is no VM and no slider, so the nod reads ✔ Docker ready for Cleat (16 GB RAM, ...) instead. A small host (under 8 GB) states its size without the parallel-headroom claim.

To be candid: these are plain Docker containers, sharing your host kernel, not microVMs with a separate kernel. Cleat trades that stronger boundary for a true one-command experience, session persistence and the clipboard bridge. For most local agent work, that is the right trade. For genuinely hostile code, a separate VM is the stronger boundary.

Recipes #

Copy-paste starting points for the things people actually do.

Explore an untrusted repo

Let the agent loose in code you don't trust. A malicious .cleat can't grant itself the Docker socket. You'll be asked first and the default is no.

$ git clone https://github.com/some/repo && cd repo$ cleat

Commit as you, without push access

git is identity. ssh is reach. Enable only the first and commits carry your name while the agent still can't push anywhere.

$ cleat config --enable git

Pass secrets without committing them

Enabling env scaffolds a .cleat.env in your current directory (gitignore it). Even a global enable drops the file where you run it. Then declare keys: a bare key inherits from your shell. KEY=value sets it explicitly.

$ cleat config --enable env
.cleat.env
# .cleat.env: project environment variables for CleatGH_TOKEN # inherit from hostDATABASE_URL=postgres://localhost:5432/app

Run Docker from inside the sandbox

For Docker-first apps. Containers you launch run as siblings on the host daemon. Use $(pwd) or . for mounts. The daemon sees host paths. Remember: this breaks isolation.

$ cleat config --enable docker
$ docker compose up -d$ docker compose exec app npm test

Two postures, one repo

A least-privilege editing box beside a cloud box, over the same files. The dev box cannot reach the socket the az box holds.

$ cleat config dev --enable git$ cleat config az --enable docker$ cleat start dev # locked down$ cleat start az # cloud-capable, separate session

Authenticate gh inside the container

The browser opens on your host and the token writes back to ~/.config/gh, so it survives rm, nuke and rebuild.

$ cleat config --enable gh$ cleat# then, inside the container:$ gh auth login

Upgrade the bundled Claude Code

Cleat owns Claude's version. Upgrade in place (fast, no full rebuild), then it offers to recreate this project's container.

$ cleat upgrade-claude # latest$ cleat upgrade-claude stable$ cleat upgrade-claude 2.1.156 # pin a version

Notes & gotchas #

  • Docker down? Cleat starts it. Missing? Cleat offers to install it. A session verb that finds the daemon unreachable launches your own Docker (Docker Desktop, OrbStack, or Colima on macOS, Docker Desktop or a rootless engine on Linux, the Windows-side Desktop from WSL2), waits with a bounded spinner, then continues your command. A root-owned Linux engine gives you the exact one-line command instead. A remote tcp:// endpoint is named so you start it where it runs. No docker CLI at all? An interactive, consent-first install offer (macOS picks Desktop / OrbStack / Colima via Homebrew's official packages, Linux runs Docker's official script under sudo only after you say yes, WSL2 uses winget), with the exact command always shown and No as the default. Interactive terminals only, so scripts and CI never pop a GUI or an installer. CLEAT_NO_AUTOSTART=1 turns all of it off.
  • Root-only hosts work. On a stock VPS image (or under sudo) the box user maps to uid 0. Claude Code itself refuses --dangerously-skip-permissions as root, which used to end every session with "Claude exited with code 1". Cleat now sets IS_SANDBOX=1 (Claude Code's own escape hatch for sandboxed containers) on each session for root hosts only, so sessions launch normally and existing boxes heal on their next attach.
  • Enabling a cap recreates the container. Mounts are fixed at create time, so the next cleat after a config change prompts to recreate. Sessions survive via --continue.
  • Only a real config change prompts a recreate. Resizing your Docker VM or upgrading the CLI never does. The recreate prompt fires only when a capability, an environment key, or a configured [resources] limit actually differs from how the box was created, never because a value Cleat derives from the VM moved underneath you.
  • A box from before the read-only ~/.claude masks says so. Bind mounts are baked at create, so an older box keeps those surfaces writable until recreated. Every start notes it until you run cleat rm && cleat.
  • The VM size Cleat shows matches your slider. docker info reports the guest kernel's memory, which sits under the Docker Desktop slider by a reserve that grows with VM size (a 24 GB VM reads ~23.4 GiB), so Cleat reads the slider value straight from the Docker Desktop settings (the same way it reads swap), falling back to rounding the kernel's memory when that can't be read. The display and the undersized check share that one value everywhere (the advisory, the tuned nod, cleat status), so a correctly sized VM is never falsely flagged as too small and the number you see is the slider you set.
  • Ad-hoc installs survive stop and reboot. Per-container state lives under ~/.config/cleat/run, not /tmp, so macOS file rotation can't trigger a surprise recreate that wipes what you installed inside the box.
  • An ssh box survives a reboot. macOS regenerates your SSH agent socket path on every restart, so the path baked into the box goes stale. Cleat spots the vanished mount before starting and recreates the box transparently. No cryptic mount … not a directory error, no lost session.
  • Trust and MCP approvals are per-project. Each project keeps its own isolated .claude.json, which fixed the old corruption crash, but approvals no longer carry across repos. Approve once per project. Your login is unaffected.
  • Use host.docker.internal, not localhost, to reach a service on your host (an MCP server, a local proxy). Inside the container, localhost is the container. On Linux, Cleat wires up host.docker.internal for you automatically.
  • With the docker cap on long sessions, a Docker Desktop restart can renumber the socket group. Cleat re-heals access on every attach. If the socket itself was replaced, it tells you to run cleat stop && cleat resume rather than killing your live session.
  • Prompts treat redirected stdin as a decline. Every [Y/n] read from /dev/null or a wrapper means no, never a silent yes. Update checks are throttled and never downgrade.
  • Cloud CLI caps were removed. az, aws and gcloud are gone. Install the CLI on your host and pass credentials through the env cap instead. Old aliases like --cap aws now fail with "Unknown capability" rather than silently doing nothing.

Reference #

Commands

Daily loop

cleat

Start the sandbox and launch Claude for the default box. The default command.

cleat start [box]

Build, start and launch Claude, the all-in-one. Same as bare cleat, for a named box.

cleat run [box]

Bring the container up without attaching Claude, for scripting, CI, or pre-warming a box.

cleat resume [box]

Resume the last session with --continue. Alias: r.

cleat stop [box]

Stop the container and keep it for resume.

cleat rm [box]

Remove the container. Host sessions are preserved. Resume recreates it.

cleat shell [box]

Open a bash shell in the container. Alias: sh.

cleat claude [box]

Launch Claude in a running container. Fresh session, recreates on config drift.

cleat login [box]

Run claude login with the browser + OAuth callback bridge.

Inspect

cleat ps

List every Cleat container across all projects.

cleat status

Show this project's container, image, auth, capabilities and boxes. Alias: st.

cleat logs [box]

Follow container logs (docker logs -f).

cleat describe [box] [text]

Show or set a box's one-line description. Never recreates the container.

Capabilities & trust

cleat config [box]

Open the capability picker, or use --enable / --disable / --list.

cleat trust [path]

Approve this project's .cleat capabilities. --list to review approvals.

cleat untrust [path]

Remove a project's trust approval.

Kits

cleat kit

Interactive picker, like cleat config: pick a kit (a detail pane shows what it does), then set each agent model with the arrow keys, enter to enable.

cleat kit list

Plain kit library and this project's per-box selections.

cleat kit <name> [box]

Enable a curated Claude pre-configuration for a box. Merges on top of your own config. Takes effect next session (one exception: a pre-kits box is offered a one-time rebuild).

cleat kit off [box]

Disable the box's kit. Back to your own config next session.

cleat kit show <name>

Print every line a kit injects, before you enable it.

Image & version

cleat build

Pull or build the image if it is missing.

cleat rebuild

Rebuild the image from scratch (--no-cache).

cleat upgrade-claude [channel|version]

Upgrade the bundled Claude Code in place. Default channel: latest.

cleat update

Update the CLI to the latest tag and pull the matching image.

cleat version

Print the version. Aliases: -v, --version.

Setup

cleat install

Symlink the cleat script onto your PATH (/usr/local/bin), for manual or cloned installs.

cleat uninstall

Remove the cleat symlink from your PATH. Docker state is left alone (see nuke for that).

Teardown

cleat stop-all

Stop and remove every Cleat container.

cleat prune

Remove stale cleat images: superseded builds and pulls. Boxes and other projects' images are never touched.

cleat clean

Remove all Cleat containers and the image.

cleat nuke

Remove everything Cleat in Docker. Asks you to type 'nuke' to confirm.

Flags

--cap <name>

Enable a capability for this run only: git, ssh, env, gh, hooks, docker.

--env KEY=VALUE

Set an env var in the container. Works even without the env cap.

--env KEY

Pass a host env var through by name.

--env-file PATH

Load env vars from a file.

--project

With config: write to ./.cleat instead of the global config.

--enable / --disable <cap>

With config: toggle a capability non-interactively.

--list

With config: print enabled capabilities.

--trust-project

Approve this project's .cleat for this run and record it.

--desc "<text>"

Set a box's description when it is created.

Configuration files

~/.config/cleat/config

Global capabilities, [resources] limits and [kits] agent-model overrides. Trusted, because you wrote it.

~/.config/cleat/env

Global env vars (loaded when the env cap is on).

<project>/.cleat

Project capabilities. Require a trust approval before they apply.

<project>/.cleat.env

Project env vars.

<project>/.cleat.<box>

Per-box capabilities. Replaces .cleat for that box.

<project>/.cleat.<box>.env

Per-box env vars.

~/.config/cleat/trust

Recorded trust approvals (mode 0600).

Environment variables

CLEAT_TRUST_PROJECT=1

Approve this project's .cleat capabilities non-interactively (for CI / scripts).

CLEAT_CLAUDE_CHANNEL

Claude Code update channel: stable, latest (default), or a pinned version.

CLEAT_NO_UPDATE_CHECK=1

Disable the CLI self-update check (no network call to GitHub on start).

CLEAT_NO_CLAUDE_UPDATE_CHECK=1

Disable the Claude Code update check (no network call on start).

CLEAT_BROWSER_BRIDGE

Browser-bridge open policy: auto (default: defer plain links to the terminal so a click opens one tab, not two), always (open every URL via the bridge), or off (never auto-open, though the login callback proxy still runs).

CLEAT_NO_IDLE_SWEEP=1

Disable the on-start idle-session sweep (which stops detached boxes that no longer have an agent running, to free Docker VM memory).

CLEAT_IDLE_GRACE_MINS

Minutes a box must be detached and idle before the sweep may stop it (default 30).

CLEAT_NO_AUTOSTART=1

Disable Docker autopilot (session verbs normally auto-start a down Docker Desktop / OrbStack / Colima and wait, but with this set, cleat just prints the exact start (or install) command and exits).

CLEAT_AUTOSTART_TIMEOUT_SECS

How long autopilot waits for the started daemon before giving up (default 90, sized for a Docker Desktop cold boot).

All ~/.config/cleat paths honor $XDG_CONFIG_HOME if it is set.

Full release history is on the changelog. The source lives on GitHub.

Get started

Drop into any project and type cleat.

$ curl -fsSL https://cleat.sh/install | bash
Install command copied Paste it in your terminal to install Cleat