macOS setup
macOS has no Guix, so wraptool up uses the devcontainer runtime. That needs a container engine (Docker Desktop) and the devcontainer CLI. This page walks the whole path from a clean Mac to a running container.
1. Install Docker Desktop
The devcontainer runtime builds and runs the project container through Docker. Install Docker Desktop for Mac (Apple-silicon and Intel builds are both provided), launch it once, and confirm the engine is up:
docker versionBoth the client and server sections must report a version. Leave Docker Desktop running whenever you use wraptool up.
2. Install Node with a user prefix
The devcontainer CLI ships as an npm package. Install Node (via the installer, Homebrew’s brew install node, or a version manager), then point npm’s global prefix at a directory you own so global installs need no sudo and land on your PATH:
npm config set prefix ~/.local
export PATH="$HOME/.local/bin:$PATH" # add to ~/.zshrc to persist~/.local/bin is the same directory the wraptool install script uses, so one PATH entry covers both.
3. Install the Dev Containers CLI
npm install -g @devcontainers/cli
devcontainer --versionwraptool up shells out to this devcontainer command on non-Guix hosts; without it the devcontainer runtime cannot start.
4. Install wraptool
With Homebrew:
brew tap pti/wraptool https://forge.snamellit.com/pti/homebrew-wraptool.git
brew trust pti/wraptool
brew install pti/wraptool/wraptool
wraptool versionLater upgrades are then brew update && brew upgrade pti/wraptool/wraptool.
Do not skip brew trust. Homebrew does not trust a third-party tap by default, and an untrusted tap is skipped with a warning during brew update rather than reported as an error — so upgrades silently stop arriving. See Trusting the tap for what trust grants and how to verify the formula before granting it.
Or follow Installation — the Linux/macOS release script detects darwin and installs to ~/.local/bin. Then:
wraptool versionThe binary is the same one either way, so the notarization note below applies to both. The Killed: 9 warning does not apply to Homebrew: brew upgrade installs into a new Cellar directory and relinks instead of overwriting an executable in place, and a formula download is never quarantined.
macOS release binaries are not Apple-notarized. If local policy requires notarized software, build from source (see Installation).
Killed: 9 after copying over the old binary
When upgrading, don’t cp the new binary over the existing ~/.local/bin/wraptool — on Apple silicon the kernel caches code-signing information per inode, and an in-place overwrite leaves a stale signature that makes exec fail with Killed: 9 (the shell’s hash -r won’t help; it’s a kernel cache, not a path cache). Remove the old file first (rm then cp), use install(1) as in the install script, or repair an already-broken copy with codesign -s - -f ~/.local/bin/wraptool.
5. Where your config lives on macOS
wraptool follows the XDG base-directory spec, and on macOS the XDG config home defaults to ~/Library/Application Support — not ~/.config. So your config is:
~/Library/Application Support/wraptool/config.yaml
A ~/.config/wraptool/config.yaml is ignored on macOS unless you export XDG_CONFIG_HOME=~/.config. Editing the wrong copy is a common first-run trap — run wraptool config validate (with no path it resolves the real one) to see which file is actually loaded.
You normally don’t write this file by hand: on first run wraptool up scaffolds a commented starter there (and never clobbers an existing one). Keep the keys exactly as scaffolded — the loader silently ignores unknown keys, so a typo like auth_token (the real key is auth_token_file) or 0,0,0,0 (should be 0.0.0.0) fails quietly. A minimal, correct config:
mcp:
transport: sse
listen: 127.0.0.1:8717
tools:
git:
binary: /usr/bin/git
timeout: 30s
allow:
- subcommand: [push]
flags: ["--set-upstream"]
deny_flags: ["--force", "--force-with-lease"]
deny:
- subcommand: [remote, set-url]Leave mcp.listen on loopback — wraptool up rebinds it to 0.0.0.0 for the devcontainer bridge and auto-generates the MCP auth token for you. You do not set 0.0.0.0 or any auth_token_file by hand.
Validate before launching:
wraptool config validate6. Scaffold the devcontainer with your harness
Pick which coding agent runs in the container and which languages the image needs. This example wires the agy (Antigravity) harness with Go and Java. Install it into the shared pool once first — this is a host-side, developer-toolbox step, not something the container config does:
wraptool harness install agy
wraptool init devcontainer --harness agy --lang go,javaThat writes a .devcontainer/devcontainer.json with the base image, the go and java language features, and the wiring-only wraptool feature (it wires agy’s MCP config to the host wraptool at container create; the binary itself comes from the pool mount, not the feature). It refuses to overwrite an existing file — pass --force to replace one.
Antigravity speaks the Streamable HTTP MCP transport, so its generated config targets the server’s /mcp endpoint; wraptool up handles that automatically.
7. Launch
From the repo root:
wraptool upThis starts the shared server (widening the bind and generating the token for the bridge), brings up the container via devcontainer up, mounts the pool-installed agy binary read-only, wires its MCP config to the host, and drops you into a shell. Your host credentials (SSH keys, cloud tokens, kubeconfig) are not mounted — privileged git (push, private fetch) goes through wraptool’s policed MCP git tool on the host, while local git works inside the box.
Stop it with wraptool down (add --rm to remove the container); the shared server keeps running for other projects.
Next steps
- Getting started — the full model: policy, the review UI, runtime selection, and every
updetail. - Dev Containers — every feature option and the container wiring.
- Configuration reference — every config field.