Skip to content

macOS Quickstart

The opinionated, packaged path to a working shed on macOS, with the shed-desktop menu-bar app (Tauri, the shipped macOS client) handling credential approvals. Everything installs from Homebrew plus one DMG.

Building from source, custom images, or hacking on shed itself? See macOS Developer Setup instead.

What you get

  • A local shed-server running VMs through Apple's Virtualization.framework (via vfkit, installed by Homebrew).
  • shed-host-agent brokering your real credentials (SSH agent, Docker registry auth, optionally AWS) into sheds — keys never leave the host.
  • shed-desktop: a menu-bar app (Tauri) that lists/creates sheds and shows a Touch ID approval prompt whenever a shed asks to use a credential.

Prerequisites

  • macOS 14 (Sonoma) or newer, Apple Silicon (arm64).
  • Homebrew.
  • No host Docker needed — the full shed image ships its own Docker daemon.

1. Install the CLI, server, and host agent

brew install charliek/tap/shed             # shed CLI + shed-server (+ vfkit)
brew install charliek/tap/shed-host-agent  # credential broker
brew services start shed
brew services start shed-host-agent

brew install charliek/tap/shed installs the shed CLI and shed-server, pulls vfkit (the Apple Virtualization bridge), generates a default VZ server config, installs shed-server inside a signed app bundle (ShedServer.app, so macOS privacy grants such as external-drive access survive upgrades), and registers a launchd service.

2. Configure the server

The server config lives at /opt/homebrew/etc/shed/server.yaml. A minimal setup shares a couple of host credential directories and enables the credential extensions:

# /opt/homebrew/etc/shed/server.yaml
# Full reference: https://charliek.github.io/shed/reference/configuration/
name: my-mac
http_port: 8080
ssh_port: 2222
default_backend: vz

# Local-only by default: bind_address defaults to loopback (127.0.0.1), so the
# server is reachable only on this Mac. You reach it as `shed server add localhost`.
# To reach it from another machine, see the Security Configuration guide.

# Host directories shared into every shed (VirtioFS). Add only what you need.
mounts:
  claude:
    source: ~/.claude
    target: /home/shed/.claude
    readonly: false
  gh:
    source: ~/.config/gh
    target: /home/shed/.config/gh
    readonly: false

# Credential brokering via shed-host-agent (configured in step 3).
extensions:
  enabled:
    - ssh-agent
    - docker-credentials
    # - aws-credentials

# default_image / image_aliases are omitted, so they're synthesized from the
# server version — upgrades move to the matching images with no config edit.

Restart after editing: brew services restart shed.

3. Configure credential brokering (shed-host-agent)

The host agent holds your real keys and decides each request; its config is at /opt/homebrew/etc/shed/extensions.yaml. To route approvals to shed-desktop, set each provider's policy to shed-desktop (the agent always serves the local socket the app connects to — there's nothing to enable):

# /opt/homebrew/etc/shed/extensions.yaml
# Full reference: https://charliek.github.io/shed/extensions/configuration/
discovery:
  servers: all            # broker for every server in ~/.shed/config.yaml
  watch: fsnotify         # pick up `shed server add/remove` live

# Optional: how long to wait for a shed-desktop decision before failing closed (default 25s)
# approval_timeout: 25s

ssh:
  approval:
    policy: shed-desktop  # SSH sign approvals decided in shed-desktop
                          # no-GUI alternative: biometrics-or-password (native Touch ID)

docker:
  approval:
    policy: shed-desktop  # live Allow/Deny toggle in shed-desktop
  registries:             # registries the agent brokers creds for
    - ghcr.io
    - docker.io
    # - registry.example.com   # add your private/org registries here
  allow_all: false        # set true to broker for ANY registry (ignores the list)

# AWS credential vending (inactive until you set a role)
aws:
  approval:
    policy: shed-desktop  # or deny-all until a role is configured
  source_profile: default
  default_role: ""        # set an IAM role ARN to enable AWS vending
  session_duration: 1h
  cache_refresh_before: 5m
  # Per-shed role overrides:
  # sheds:
  #   my-shed:
  #     role: arn:aws:iam::123456789012:role/ShedRole

logging:
  enabled: true
  path: ~/.local/share/shed/extensions-audit.log

Restart and verify. status queries the running agent directly and prints the config it loaded, so there's no need to point it at a path:

brew services restart shed-host-agent
shed-host-agent status

It should report config: /opt/homebrew/etc/shed/extensions.yaml, each delegated provider's policy reading shed-desktop, and the approval channel (its consumer shows as connected once shed-desktop is running, next step). Policies, roles, and registries are off by default — see the full reference.

4. Install shed-desktop

Download the latest ShedDesktop-<version>.dmg from the releases page, open it, and drag ShedDesktop.app to Applications.

Release builds are signed and notarized, so they open normally. If you built the app yourself it is signed ad-hoc instead, and macOS quarantines it until you clear the flag once:

xattr -dr com.apple.quarantine /Applications/ShedDesktop.app

Launch it (it lives in the menu bar, no Dock icon). It reads your ~/.shed/config.yaml host list and connects to the host-agent socket. Open Preferences from the menu-bar dropdown to set approval behavior:

Section What
SSH approvals Method (Touch ID or password / Touch ID / Prompt), default decision (Time Based Allow, etc.), and duration.
AWS / Docker credentials A live Allow / Deny toggle (default Deny).
Per-shed overrides Always-allow / always-deny rules keyed by (server, shed).

Credentials fail closed: when shed-desktop isn't running, delegated requests are denied. See the shed-desktop installation and approvals docs for the full policy model.

5. Register the server and create a shed

shed server add localhost --name my-mac   # registers the local server
shed create hello-world                   # a small shed to verify with
shed console hello-world                  # drop into a shell inside it

(You can also create from a repo or a local directory: shed create demo --repo charliek/your-repo, or shed create demo --local-dir ~/projects/app.)

6. Verify credential brokering

Run these inside the shed — the shed console hello-world shell from step 5. The first SSH or Docker credential use raises a Touch ID approval in shed-desktop; approve it (pick a policy like Time Based Allow so you aren't asked again for a while).

# SSH — signs with your host key via the forwarded agent (-> approval in shed-desktop).
# Success prints: Hi <you>! You've successfully authenticated...
ssh -T git@github.com

# Docker — pull a small public image (anonymous pulls work with credsStore=shed):
docker pull postgres:16-alpine
# ...and a private registry to exercise credential brokering (-> approval):
# docker pull ghcr.io/your-org/your-image:tag

# AWS — only if you set default_role + aws.approval.policy in step 3:
aws sts get-caller-identity

Back on the host, check each shed's extension health and connection state:

shed list -vv

Remove the test shed when you're done: shed delete hello-world.

Next steps