Sparehand

Docs › Security model

Security model

Sparehand lets one computer (the hub) run commands on others. That is exactly what an attacker would want, so the design keeps the trust in one direction and in as few places as possible.

Who trusts whom

What leaves the hub

sparehand run copies the current git checkout to the worker: every file git tracks plus untracked files git doesn't ignore. Ignored files stay home, which keeps node_modules, build output and most secrets off the workers.

**The .git folder stays home**, with its config (remote URLs can carry tokens), hooks, other branches, stash and reflogs. So that git works in the job, the worker gets the commit you have checked out and its history (as git bundles, only what it doesn't have yet, shared by the checkouts of one repository there), on the same branch name; the changes you haven't committed show there as not staged. That history is everything reachable from that commit, so a secret that was committed once and removed later still goes with it: clean it out of the history, or keep such a repository off workers you don't control. .git folders inside the checkout (submodules, nested repositories) stay home too: their files go, but git doesn't work inside them on the worker. Copies of .git made by older versions of Sparehand are deleted at the next job from that checkout. sparehand add and sparehand doctor warn when a remote URL of the checkout you run them in has a password or token in it.

.env files are the exception people ask about. By default they are not copied (SPAREHAND_SYNC_ENV=0), and an ignored .env* copied by an earlier run with sync on is deleted from the worker on the next run. Set SPAREHAND_SYNC_ENV=1 only for workers you trust as much as the hub, since a stolen .env usually means stolen credentials.

A git worktree is copied the same way: its own files, and git set up there as above.

What reaches outside services

Jobs, files and the dashboard stay between your own machines. Sparehand itself contacts only Lemon Squeezy (your licence), the product's website (a daily update check) and ollama.com (the daily list of new models, off with "aiLatest": false). Everything else happens because you set it up: Studio sends a production's scripts and pictures to the services you pick for it, Crew sends a task and its code to the agents you add, People invites go through Tailscale's API, notifications through the browsers' push services. The privacy policy names every one and what it gets.

Installing on a machine downloads from the publishers at that moment. sparehand ai install takes Ollama's latest GitHub release and checks it against the release's own SHA-256 list. sparehand studio install runs uv's install script, takes ComfyUI and most Python and npm packages at their newest version, and downloads models from Hugging Face over HTTPS without checksums of Sparehand's own; only the frame-interpolation nodes, Chatterbox and the Inter font are pinned to a version. Install Studio only on machines where you accept that trust in those publishers. What it downloads, with each licence, is listed in studio.md.

The on/off switch lives on the hub

sparehand disable NAME writes the name to ~/.config/sparehand/disabled on the hub. A disabled worker isn't contacted at all: no status probes, no jobs, no files. Because the switch is on the hub, a compromised worker can't switch itself back on. sparehand pause is different: its flag lives on the worker, so the person at that machine can pause it, but it's not a security control.

If a worker may be compromised (malware found, stolen and recovered, pirated software installed), sparehand disable it first, investigate, and only sparehand enable it when you're satisfied. If secrets were ever synced to it, rotate them.

Tunnels and shared folders

Worker mode

Worker mode closes apps on a worker so builds get its memory. It only ever asks apps to quit (like ⌘Q or clicking ✕), so apps can prompt to save; on Windows, game launchers that merely hide in the tray are closed firmly after a few seconds, browsers and chat apps never are. Apps are reopened as your normal (non-administrator) user. The default is SPAREHAND_WORKER_MODE=manual: nothing closes unless you ask.

GitHub Actions runners

sparehand runner add makes a worker run a repository's workflows (ci-runners.md). That lets whoever can change that repository's workflows run code on the worker, so:

The app

The Sparehand app can pause, switch and stop things, so it's locked down the same way:

This network

This network (remote-access.md) opens the dashboard to devices on the same home or office network that you paired at the hub, without Tailscale. It is off until you switch it on, at the hub itself; "lan": false in app.json takes it away. What it does and what it leaves open:

What a hostile device on the same network can do: find the port, see that a Sparehand dashboard answers there and read its certificate, load the pairing page, keep the port busy, and stand in the middle of a device's first visit as described above. What it can't: read or change anything without a token, guess a code or a token, learn a token by listening, use a paired browser from another site (SameSite, Origin), or reach the dashboard's loopback address or the AI hub. Someone who sees the QR code and scans it first shows up at the hub as a device you don't know, with a number your own device doesn't show: don't allow it, and make a new code.

What this mode doesn't have, compared with Tailscale: use from outside the network, a certificate browsers trust, and with that the service worker and notifications (Web Push). If the hub's address changes, devices pair again (their cookie belongs to the old address): give the hub a fixed address in the router. On a Mac with the firewall on, macOS asks once whether Node may accept incoming connections.

Reporting a problem

Please report security issues privately by email to [email protected] (see SECURITY.md).

Removing a worker

sparehand remove NAME (when the machine answers and nothing else uses it there; --clean insists) runs, as the hub's last commands there: file sharing off (the share, its account and firewall rule), Ollama's service and program removed (downloaded models stay), worker mode off, its GitHub Actions runners deregistered at GitHub (when gh is signed in on the hub) and their services stopped, ~/.sparehand deleted (run folders, dependency caches, markers), and the hub's line removed from ~/.ssh/authorized_keys. The SSH server, rsync and git stay, as does Remote Login on a Mac, and the worker's host key stays in the hub's ~/.ssh/known_hosts (remove it with ssh-keygen -R HOST if you like). On Windows the keep-alive task and the firewall rule need an administrator; the command prints them.