Installation¶
shed-desktop ships two clients from one shared Rust core: a native macOS app (Apple Silicon) and a Tauri/WebKitGTK Linux app.
macOS (DMG)¶
The macOS app is an Apple Silicon (arm64) menu-bar app and requires macOS 14 or newer.
Grab the latest ShedDesktop-<version>.dmg from the
releases page, open it, and drag
ShedDesktop.app to Applications.
Official release DMGs are Developer-ID-signed and notarized when the release
pipeline has signing credentials, and then launch without a Gatekeeper prompt.
(An ad-hoc-signed, non-notarized build ships a FIRST-LAUNCH.txt with the
one-time bypass steps.)
Updates¶
The app updates through Sparkle, served from the appcast at
https://charliek.github.io/shed/appcast.xml and verified by an EdDSA signature independent
of Apple notarization. Updates are user-invoked only — there are no automatic background
checks. Trigger one from the menu-bar dropdown (or the tray popover) → Check for Updates…;
if a newer build is published, Sparkle offers it and applies it in place.
| Behavior | Value |
|---|---|
| Trigger | User-invoked (Check for Updates…); no automatic/scheduled checks |
| Feed | https://charliek.github.io/shed/appcast.xml (stable channel) |
| Verification | EdDSA signature (SUPublicEDKey), independent of Apple notarization |
| Channels | Stable by default; prerelease (rc) builds subscribe to a beta channel |
The Tauri macOS app is on a beta rollout — prerelease (vX.Y.Z-rc.N) tags publish a
beta-channel Tauri DMG while stable users keep receiving the Swift app. Both share the same
feed, EdDSA key, and bundle identity (ai.stridelabs.ShedDesktop), so the eventual promotion
is a seamless in-place update. See
RELEASING.md.
Locally built DMGs (make -C desktop dmg) are ad-hoc signed, so Gatekeeper blocks the
first launch. Clear the quarantine once, after copying it in:
(Or double-click it, dismiss the warning, then System Settings → Privacy & Security → "Open Anyway".)
A Homebrew cask for the macOS app is planned (a later monorepo phase) but not available yet — install from the DMG for now.
Linux (apt)¶
The Linux client is the Tauri app, distributed as the shed-desktop .deb (amd64 + arm64)
through charliek/apt-charliek:
The binary installs to /usr/bin/shed-desktop, with a headless shedctl and the polkit
action alongside. The .deb declares its WebKitGTK runtime dependencies
(libwebkit2gtk-4.1-0, libgtk-3-0, libayatana-appindicator3-1, librsvg2-2,
libsoup-3.0-0) and recommends polkitd. See the
shed apt repo for the repository setup.
Build from source¶
The desktop app lives under desktop/ in the shed monorepo;
its shared Rust core is the sibling crates/ workspace. Every make target below runs from
the monorepo root via the desktop- passthrough (make desktop-<target>) or directly with
make -C desktop <target>.
macOS — prerequisites: Xcode 16+ (Swift 6 toolchain), Rust stable ≥1.85.
git clone https://github.com/charliek/shed
cd shed
make -C desktop bundle # builds desktop/build/ShedDesktop.app (ad-hoc signed)
open desktop/build/ShedDesktop.app
The bundle embeds the shedctl CLI at
desktop/build/ShedDesktop.app/Contents/Resources/bin/shedctl. make -C desktop dmg packages
a release bundle into desktop/build/ShedDesktop-<version>.dmg. To produce a notarizable
build locally, set the signing identity:
SHED_DESKTOP_DEVELOPER_ID_IDENTITY="Developer ID Application: …" ./desktop/scripts/bundle.sh release
Linux — the .deb is built (in Docker, to pin the WebKitGTK toolchain) with:
make -C desktop deb # → desktop/out/shed-desktop_<version>_<arch>.deb
make -C desktop deb-validate # build + install-validate in a clean ubuntu:24.04 container
What it needs at runtime¶
~/.shed/config.yaml— the shed-server host list (created by theshedCLI). The app reads this read-only and watches it for changes.- A reachable
shed-serveron at least one configured host. Unreachable hosts are shown as a degraded state, never a hard failure.
Nothing else — for the Tauri client. brew install shed (or apt install shed) plus the
Tauri desktop app is a complete install — its embedded broker handles credential approvals
(SSH sign, AWS, Docker) itself, no extra daemon needed. The stable Swift macOS client
still requires the separately-installed shed-host-agent daemon for credential approvals
(Homebrew formula, brew services start shed-host-agent). See below for how that differs
between the two clients.
Credential broker¶
The app's headline feature — SSH-sign approvals, AWS/Docker credential gating, and the Activity audit feed — needs a credential broker in the loop. How that broker runs differs by client:
| Client | Broker |
|---|---|
| Tauri (Linux; macOS beta) | Embedded — runs in-process, on by default. No extra install. |
| Swift (macOS stable) | Separate daemon — a standalone shed-host-agent process (Homebrew formula, brew services start shed-host-agent). See Credential approvals. |
The Tauri client can also run against a standalone daemon instead of its embedded broker
— useful if you already run shed-host-agent headless on a server, or want one broker
shared across tools. A Preferences → Credential broker setting (Automatic /
In-app (embedded) / External daemon) controls it, default Automatic:
| Mode | Behavior |
|---|---|
Automatic (default) |
Probes for a running shed-host-agent at startup: a full daemon present ⇒ external (dials it, unchanged from today); a headless daemon (status socket only, no desktop socket) ⇒ coexist (the app doesn't start its own broker — no namespace conflicts — and mints its own secure-server tokens, but gets no in-app approvals, since the headless daemon owns those); neither present ⇒ embedded. |
In-app (embedded) |
Always starts the in-process broker, regardless of any running daemon. If a daemon is also running, the two race for each server's credential-bus namespace — the loser gets a per-namespace 409, surfaced (not hidden) in broker.status; no double approval prompts. |
External daemon |
Always dials the standalone daemon, never starts an in-process broker. |
The resolved mode is fixed for the app's process lifetime — changing the preference
takes effect on the next launch, not live. The active mode, the probe evidence that
produced it, and (for embedded) the resolved SSH backend and per-server connection state
are visible in Preferences and over IPC (broker.status, identify.broker_mode). Full
mechanics — extensions.yaml handling, the synthesized fresh-install default, and the
fail-closed behaviors — are documented in
Architecture → The embedded credential broker.