Run the thing you would have run locally, on another machine you own, and get the result back: same command, same working tree, logs streaming to your terminal, real exit code. The heat, watts, and minutes are spent elsewhere.
Status: milestone 3. The transactional core works end to end over a tailnet, including durable detached jobs, restart reconciliation, and content-addressed snapshot reuse. The v0 design is frozen in docs/DESIGN.md.
# on a runner (Linux box on your tailnet)
errand serve # config: ~/.config/errand/errandd.toml
# on a caller
errand info # measured facts: arch, kvm, tools
errand -- python3 -m unittest # runs your working tree over there
errand --no-snapshot -- uname -a # runs in a fresh empty remote workspace
# Ctrl-D detaches without stopping the remote job; Ctrl-C interrupts it
job=$(errand --detach -- make build)
errand ps # list your jobs across configured peers
errand attach "$job" # replay logs and follow to completion
Runner config authorizes callers by tailnet identity (whois): an ACL app
capability or a local allow_users list. Destination-scoped capability
checks require Tailscale 1.100 or newer. No keys, no credentials stored.
Run errand from a Git worktree for automatic snapshot selection. A non-Git
directory requires an explicit .errandignore policy or --include-all.
Errand always refuses a filesystem root, and snapshotting your home directory
requires --include-all. The client prints the selected file count and byte
total before remote admission. Use --no-snapshot when a command needs no
local files; Errand then skips local inspection and runs it in an empty remote
workspace. Because that workspace is empty, --workdir may only name its root.
ssh gives you a remote shell and assumes the remote already has your project state: a checkout on the right branch, your uncommitted changes, no stale artifacts. Every ssh target is a pet you maintain.
errand ships the workspace with the job, so targets are stateless with respect to your projects: nothing checked out, nothing drifting, any peer equally valid at any moment. Around that round-trip it wraps a transaction ssh doesn't attempt. The implemented milestones provide:
- At-most-once admission: network retries cannot run a command twice.
- Durable execution: an admitted job survives caller disconnect, with ordered logs that the protocol can resume after a dropped connection.
- Honest cleanup: errand removes the temporary workspace and inherited process scope it owns, then records whether cleanup completed.
- A receipt: an append-only record of what was asked, who asked, what ran, and what happened.
The remaining v0 work includes declared output transfer with conflict detection, explicitly declared named caches, fact-based peer selection, and direct LAN pairing.
One binary, symmetric peers, no controller. Transport and identity come from your tailnet (WhoIs + ACL capability grants; errand stores zero credentials) or, later, from direct LAN pairing with pinned device keys. The current execution backend runs directly on the host. Rootless containers and Nix devshells are planned.
errand -- cargo test # configured default peer
errand --on buildbox -- cargo test # named configured peer
An attached terminal can detach at any time with Ctrl-D and later resume with
errand attach HANDLE. Ctrl-C keeps its Unix meaning: it sends SIGINT to the
remote command, and a second Ctrl-C force-kills it. Interactive detachment
returns 0 for the detach action; it is not the unfinished job's exit status.
Non-terminal EOF is ignored, so scripts remain attached unless they request
--detach explicitly.
Detached jobs, ps, attach, kill, snapshot-cache inspection, and cache GC
are implemented. Planned v0 commands still include fact-based peer selection,
output fetching, named-cache management, and pairing.
Capability-based runners must grant manage-caches to use errand caches and
errand gc; the frozen design's ACL example includes the complete action set.
Linux and macOS first; Windows is a design constraint, not yet a deliverable.
Not a CI system, not interactive (no PTY in v0), not a security boundary
against hostile code, no web UI, and no arbitrary-host discovery or scheduler
fan-out. errand ps only queries explicitly configured peers. The design
resists becoming ansible, nomad, or a scheduler on purpose.
MIT