Safe use

Edit on GitHub

Hand runs its agents with their permission prompts skipped, as your own OS user, with no sandbox. This page says what that allows, what to do about it, and what no setup of today prevents. Read it before you start a supervisor.

What runs without asking

Every worker and the supervisor start with the flag that skips permission prompts. No Hand setting turns it off. hand attempt start takes --harness, --model and --effort, and no permission option.

HarnessFlags Hand passes (worker; the supervisor passes the same)
Claude Code--dangerously-skip-permissions
Codex--dangerously-bypass-approvals-and-sandbox --disable hooks --cd DIR
opencode--standalone --auto
Antigravity (agy)--dangerously-skip-permissions
GitHub Copilot (copilot; workers only)--allow-all-tools --allow-all-paths
oh-my-pi (omp)--auto-approve

These come from internal/harness/harness.go and internal/harness/supervisor.go. The model and effort flags are left out above.

Codex asks whether to trust a folder, and the flag above does not skip that. Hand presses it when the screen names the attempt's own worktree and, if the screen names a repository root, the project's repository: any other folder or repository is left alone and the attempt is noted blocked. Accepting it makes Codex save the trust in $CODEX_HOME/config.toml (~/.codex/config.toml by default), for good, and for all of your Codex use and not only Hand's workers. For a worker it saves the project's repository, because a worktree is trusted through its main repository. For a supervisor Codex asks only when the fleet home is in a repository, which Hand does not accept, or is itself a repository root (a fleet home Hand did not create), and then it saves the fleet home. Codex then loads that repository's project config, hooks and exec policies whenever you run it there. hand attempt start prints what was accepted as trusted:, and the attempt records an attempt.trusted event with the folder and the repository (supervisor.trusted for a supervisor). Hand never edits config.toml. To withdraw the trust, delete the [projects."PATH"] entry for the path in the event from that file yourself. To decide it yourself instead, run codex once in the repository root and choose Trust: Hand then has nothing to press.

One prompt still appears. Claude Code asks before a recognized rm or rmdir of a critical path, such as /, your home folder or the working directory, even with prompts skipped. Its docs say no allow rule or hook approves one, and that the check can miss some commands, so it is not a safety control (Claude Code permission modes, Critical paths). In auto and bypassPermissions modes the prompt shows a two-minute countdown. If it runs out, Claude Code denies the command. After three unanswered prompts in a session it denies the next ones at once.

What you see in Hand for that prompt:

What Luvus reads as blocked for Codex, opencode and agy permission screens, and whether the Board parses them: not verified here. The Board's parser is written for Claude's screens. For Copilot, Luvus 0.14.3 read its trust and permission screens as blocked in one check on Linux; the footer of a worker started with the flags above still says "Manual Approval", although tools and paths run without asking.

Copilot asks whether to trust a folder, and the flags above do not skip that. Hand presses 1. Yes when the screen names the attempt's own worktree and never 2. Yes, and remember this folder, which would save the folder in your Copilot config for all your Copilot use. After the trust, Copilot loads the repository's own configuration: hooks (.github/hooks, and the hooks in .claude/settings.json), MCP servers and skills. It has no flag to turn hooks off, only the disableAllHooks setting, so a repository's hooks run in a Copilot worker, unlike --disable hooks for Codex. Copilot also does not refuse a model its plan lacks: it prints Model "X" from --model flag is not available. Using "auto" instead. and runs on Auto, so a worker meant for another model runs on Auto, which bills differently. For a few seconds after the launch Hand reads the screen above the first prompt line, and when it finds that line it notes the attempt blocked with the model's name; it does not stop the attempt. A line that scrolled off the pane before Hand read it is missed, and a long brief scrolls it off a fraction of a second after Copilot prints it. A signed-out Copilot (Please use /login to sign in to use Copilot) is noted the same way.

What that means

A worker can:

A git worktree is not a fence. It is a folder.

Hand gives each worker a scratch folder under the fleet home's scratch/ and points its temporary-directory variables at it, so its temporary files, downloads and build output have a correct default. That folder is not a fence either: it sits in the fleet home, and a worker that ignores the variables or writes an absolute path can still reach anything its account can.

What to do about it

In order of value for cost. The first two matter most.

1. Run Hand as its own OS user, or in a VM

Then a worker cannot read your account's private files: your own ~/.ssh, gh login and other folders stay out of reach. A dedicated user is not a VM. World-readable files, shared paths such as /tmp, network services and anything else that user may use stay reachable. Use a VM you can throw away if you want stronger isolation: share no folder with the host.

On Linux, as an administrator:

ls -ld ~
chmod go-rwx ~
sudo useradd --create-home --shell /bin/bash hand-agent
sudo -iu hand-agent

ls -ld ~ shows whether other users can enter your home folder. Skip the chmod if it already reads drwx------. Otherwise chmod go-rwx ~ closes it, so the new user cannot read your files. It also locks out anything else that reads your home folder as another user or group, such as a web server or a shared group, so skip it if you rely on that and close the folders that matter instead. As hand-agent, install Hand and a harness, and log in to the harness there. The account uses its own harness login, so a worker spends that login's quota.

s=$(curl -fsSL https://raw.githubusercontent.com/atqamz/hand/main/install.sh) && sh -c "$s"

To reach the Board from your own account, print its login link as that user and open it in your browser. Name the fleet folder with --home (replace fleets/demo with yours): otherwise Hand looks in the current directory. .local/bin is the installer's default folder (see Install); adjust the path if you installed elsewhere. The link carries the fleet's token, so keep it out of chats:

sudo -iu hand-agent .local/bin/hand --home /home/hand-agent/fleets/demo open --print

To keep the watcher and the Board running without a login, run sudo loginctl enable-linger hand-agent, then see Autostart.

The commands above are Linux-specific and were not run here. Do not run them on macOS or Windows. On those systems a standard (non-administrator) user can be created in the system settings, but this page gives no verified steps for the account, fleet or autostart setup: not verified here.

2. Keep secrets out of reach

Give the account only what a hijacked worker could use without harm to you.

3. Do not register repositories that hold production credentials

A worktree holds a full checkout. A .env file, a deploy key or a credentials file in the repository is in the worker's hands. Hand has no allowlist of paths. If a repository has them, keep it out of the fleet.

4. Review what workers push

Read a worker's PR before you merge it, including its workflow files, scripts and dependency changes. Rotate any token after a run that looked wrong.

5. Isolate fleets from each other

Workers of one fleet can read the other fleets of the same OS user, with their board.token and databases. Use one OS user, or one VM, for each group of fleets that must not see each other. ~/.secondhand is per user, so a second user gets its own fleets.

6. Board and phone

What Hand checks before running git

Hand runs git in your projects: git fetch and git worktree add to start an attempt, and git status and git worktree remove to clean one. Some settings in a project's git configuration name programs that git runs, for example core.sshCommand, a filter's smudge, a credential helper, a config-defined hook or an included file. Hand records the values of those settings when a project is registered (a project registered by an earlier version records them the first time Hand runs git in it) and refuses to run git in the project if they have changed since: it runs nothing, exits 2 and names the settings that changed. Read them with git -C REPO config --list --show-origin, then accept them with hand project trust PROJECT. hand doctor has a row for each project.

The check covers the repository's own configuration (with the files it includes and a worktree's own config.worktree) and .git/info/attributes. It does not cover the global or system git configuration, the environment, a submodule's own configuration or the programs on your PATH. One command runs git without the check: hand report add from a worktree without --attempt runs git rev-parse --show-toplevel. It is not a sandbox, so the account separation above stays the boundary. Reading the settings and running the command are two steps, so a change made between them is caught by the next command. A project registered by an earlier version accepts the settings it has when Hand first runs git in it: read git -C REPO config --list --show-origin once after you upgrade.

What Hand runs to list models

Hand lists the models of each harness with one command each, with no prompt and no model call, so it spends no tokens. The watcher runs every installed harness's listing command unattended whenever a list is stale or missing, not only for the harnesses a worker uses:

HarnessCommandWhat Hand keeps
Claude Codeclaude -p --input-format stream-json --output-format stream-json --verbose --no-session-persistence, with one initialize request on stdinthe models array of the answer: name, display name, supported efforts. The answer also holds account data and the command list; Hand's decoder declares neither and drops them
Codexcodex debug models --bundled, else a read of $CODEX_HOME/models_cache.jsonslug, display name, visibility, effort levels. Not identity, etag or any instruction text
opencodeopencode models --standalone, then opencode api --standalone GET /api/modelmodel names, one per line, and from the answer only each model's provider, id and variant ids; Hand's decoder declares nothing else, so the provider settings the answer carries, which can hold a key, are dropped, and a failed or unreadable call only leaves the variants out. Never without --standalone, which leaves a server running; never another api call, auth export or debug config, which can print a key
Antigravity (agy)agy modelsid and label
GitHub Copilot (copilot)copilot help configthe quoted names under model, and auto. The page also describes every setting; Hand reads nothing else from it
oh-my-pi (omp)omp models --jsoneach model's selector, name and thinking levels. Never models.db, a SQLite catalog

Each is also run as <harness> --version, whose first line is kept. Every run has a timeout (5 to 30 seconds), a 4 MiB output limit, no terminal, and its process group is killed when it ends. It gets only PATH, the home folders and the variables its own harness reads (ANTHROPIC_*, CLAUDE_*, CLOUD_ML_* and the AWS region and profile for Claude Code, CODEX_*, OPENCODE_*, COPILOT_*, PI_*), not the rest of your environment. Claude Code runs in the fleet home, or the current folder, so its project settings apply. Its stderr is read only for agy's sign-in line and is never printed or stored. Hand reads no settings.json, config.toml, opencode.json, config.yml or auth.json: it only compares the size and modification time of a few of them to know when a list is stale. Nothing is cached but names, labels, efforts and sources, in ~/.secondhand/models/ (folder mode 0700, files 0600): a token, an API key, an account email or an organisation reaches neither that folder, an error, nor a log. If a harness prints one in the part Hand decodes, the decoder keeps it out; tests feed each reader an invented secret and check that it appears nowhere.

Claude Code may reach the network on its own while it answers (an authenticated GET /v1/models when you turned on gateway model discovery, and its fast-mode check): that is what every Claude Code start does, with the variables it gets here, and not verified here for a subscription login.

What no setup of today prevents

MCP servers in workers

A Claude worker starts with --strict-mcp-config and no MCP server. Before, it inherited every server in your own Claude config, and one such server can drive a logged-in browser: a worker that follows text from an untrusted issue then had a path to it. hand attempt start --mcp mcp.json adds one MCP config, a path inside the fleet home to a file you maintain, for the tasks that need a credentialed or logged-in browser; see MCP. Hand never reads your Claude config. Only claude supports the flag; Codex, opencode, agy and Copilot keep loading the servers of their own configuration, and Hand claims no isolation for them. Put in mcp.json only servers you would let any instruction in that task's text reach.

Planned, not built

None of this exists yet. The operator's safe-mode study describes them. It is not in this repository. Which of them get built is decided after this page has been read.

Until then, the setup above is the only protection.

What each harness offers instead

Hand passes the flags above and none of these. Each is the harness's own feature, from its documentation as fetched on 2026-10-22 (UTC). Not verified here: that any of them works under Hand.

HarnessWhat Hand passesWhat the harness offers instead
Claude Code--dangerously-skip-permissionsModes default, acceptEdits, plan, auto (a classifier reviews each action), dontAsk and bypassPermissions. Allow, ask and deny rules. A Bash sandbox on Linux and macOS, none on native Windows. Modes, rules, sandbox
Codex--dangerously-bypass-approvals-and-sandbox --disable hooks --cd DIR--sandbox with read-only, workspace-write or danger-full-access, and approval policies. The Auto preset uses workspace-write and approval on request, with network off by default. Codex recommends it for version-controlled folders and read-only for others. Agent approvals and security
opencode--standalone --autoPer-tool allow, ask and deny with patterns, per agent. Without --auto the defaults already allow most tools. --auto approves whatever is not denied. Permissions
Antigravity (agy)--dangerously-skip-permissionspermissions.allow, ask and deny in its settings file, where deny wins over ask and ask over allow. Commands ask by default. agy --help also lists --mode and --sandbox: the docs page does not describe them, not verified here. Permissions
oh-my-pi (omp)--auto-approveApproval modes always-ask, always-ask-cached, write and yolo (--approval-mode), and --tools=NAME,... to enable only named tools. It has no OS sandbox, so approval is permission design, not containment. Approvals