Skip to main content

Hermes Local Hands

CI CodeQL PyPI

Hermes Local Hands is an alpha local companion for a remote Hermes agent. It gives that agent a deliberately narrow view of one registered repository, while the computer that owns the repository retains the final say over every change and check.

It solves a concrete split-machine problem: Hermes can reason on a Mac Studio, server, or VM, while the developer's source code remains on a different local machine. The remote side can inspect approved files and create requests. It cannot run a shell, approve or deny a request, write the active checkout, merge, or push.

Security boundary in v0.1. workspace_status and read_file are remote inspection tools. propose_patch and request_check only create a pending, expiring request. Approval and rejection are local-operator actions. Local Hands applies patches and launches checks from an isolated snapshot rather than writing the registered checkout itself. Approved check code still has the local user's normal host and network access and can deliberately modify the registered checkout; this is not a sandbox. Check stdout/stderr stays local: remote request_status returns bounded execution metadata, never the captured output.

Why this now

The architecture follows real upstream demand for safer local execution and split-runtime workflows, rather than adding a second Hermes runtime. Useful primary context is the upstream discussions #18715, #42807, and #16462, plus the related implementation work in #63966 and #43045.

What it does — and does not do

Capability Remote Hermes can do it Local operator must do it
See sanitised Git status Yes, for a granted workspace Register the workspace and grant the client
Read a text file Yes, only inside the explicit read allowlist Define the allowlist
Propose a textual patch Create a pending request only Review and approve it
Request a fixed check profile Create a pending request only Review and approve it
Apply or test No Local Hands launches it in a snapshot; approved code remains host-capable
Reject, merge, push, use a shell No Reject is local; merge/push/shell are outside the protocol

Every request is tied to an authenticated client, an allowed workspace, the current Git HEAD, the workspace-policy hash, an idempotency key scoped to the client, and a short TTL. A changed policy or commit invalidates a request. Re-registering an existing workspace ID replaces its client grants with exactly the new --client list; old grants do not follow a changed root or policy. Interrupted execution is recorded as uncertain, not silently reported as a success. The local state store also writes a signed, append-only receipt chain for requests and operator decisions.

Install and local-only quickstart

Requirements: Python 3.11+ and Git. Install the isolated command with uv or pipx:

uv tool install hermes-local-hands
# Alternative: pipx install hermes-local-hands

For development from a source checkout instead:

# Use a Python 3.11+ executable; macOS /usr/bin/python3 may still be too old.
python3.11 -m venv .venv
source .venv/bin/activate
python -m pip install -e '.[dev]'

The example below uses demo and only exposes src and tests; replace the absolute path and allowlist with your own deliberate choices.

# Creates private local state and a bearer credential file (mode 0600).
hermes-local-hands init --client-id hermes-mac-studio

# Register one Git repository and fixed, named check profiles.
hermes-local-hands workspace add \
  --id demo \
  --root /absolute/path/to/repository \
  --read src \
  --read tests \
  --write src \
  --write tests \
  --check syntax=/usr/bin/python3,-m,compileall,-q,src \
  --client hermes-mac-studio

# Start the listener. It refuses non-loopback bind addresses.
hermes-local-hands serve --host 127.0.0.1 --port 8741

init stores the token under the private state directory rather than printing it. On macOS/Linux the default is ~/.local/state/hermes-local-hands/clients/<client-id>.token; set XDG_STATE_HOME first to choose another private state location. Read the file locally and export it only in the Hermes runtime's private environment:

export HERMES_LOCAL_HANDS_TOKEN="$(< "$HOME/.local/state/hermes-local-hands/clients/hermes-mac-studio.token")"

Do not put a real token in a repository, issue, screenshot, shell history, or public configuration file.

End-to-end workflow

  1. The local operator registers a workspace, its explicit read/write allowlist, fixed check profiles, and the Hermes client grant. workspace grants <workspace-id> lists current grants; workspace revoke <workspace-id> <client-id> removes one and records the decision.

  2. The local operator starts serve on loopback only.

  3. A trusted reverse proxy/tunnel terminates HTTPS and forwards to that local listener. Local Hands still accepts only the exact proxy host(s) named at startup; it does not infer trust from arbitrary forwarded headers.

  4. Hermes calls workspace_status or read_file, or creates a pending patch or check request. Patch and check requests require a clean registered checkout both when they are created and when they are approved; uncommitted changes are never copied into the managed snapshot.

  5. The local operator inspects the request and chooses one of these local-only commands:

    hermes-local-hands request list --state pending
    # Default output is a multiline, control-escaped local review.
    hermes-local-hands request show <request-id>
    # Copy the approval_code shown above; it is intentionally request-specific.
    hermes-local-hands request approve <request-id> --confirm <approval-code>
    # or:
    hermes-local-hands request deny <request-id> --reason "not approved"
    

    Use request show <request-id> --json only when escaped machine-readable output is needed. The default review prefixes every untrusted patch line and escapes terminal/bidirectional controls so a patch cannot visually imitate the approval fields.

  6. An approved operation is revalidated against the recorded HEAD and policy, then Local Hands applies or launches it from a managed snapshot. Approved check code is still host-capable. Inspect its full output locally with hermes-local-hands request status <request-id>. Remote request_status exposes only structured execution metadata such as exit state, output size, and output digest. Verify the receipt chain locally with hermes-local-hands receipt-verify.

  7. After reviewing a succeeded or failed snapshot, free its retention slot only through the exact-ID local deletion gate:

    hermes-local-hands snapshot list
    hermes-local-hands snapshot delete <request-id> --confirm <request-id>
    

    The command records deletion-requested and deletion-completed receipts. It refuses pending, executing, missing, symlinked, or mismatched targets and never deletes snapshots automatically. An uncertain snapshot can be removed only after explicit local review and the same exact-ID confirmation. A successful deletion empties the already-open request directory in place and retains only a tiny completion marker instead of performing a final path-based directory removal. Completed marker directories are omitted from snapshot list; incomplete deletions remain visible there for local investigation.

For a check request linked to an approved patch, create it locally with the patch request ID. The check then uses that patch's approved snapshot rather than the mutable active checkout:

hermes-local-hands check add demo syntax check-after-patch-001 \
  --client hermes-mac-studio \
  --patch-request <approved-patch-request-id>

That approval creates a fresh snapshot at the recorded base commit and replays the exact stored patch bytes whose digest was reviewed; it does not reuse a mutable previous snapshot. A check result is evidence about that snapshot only; it is not a merge, deployment, or production safety claim.

Hermes MCP configuration

Generate the configuration fragment rather than hand-copying names:

# Local same-machine use:
hermes-local-hands hermes-config --port 8741

# Remote use: pass the exact, already configured HTTPS tunnel endpoint.
hermes-local-hands hermes-config \
  --endpoint https://mac-studio.example.ts.net/mcp \
  --token-env HERMES_LOCAL_HANDS_TOKEN

The generated YAML includes only these five tools:

  • workspace_status
  • read_file
  • propose_patch
  • request_check
  • request_status

The two inspection tools advertise the MCP readOnlyHint. Hermes versions and clients may still apply their own approval or policy gate to those tools; the hint is useful metadata, not a compatibility guarantee or a bypass.

Hermes releases affected by upstream issue #88858 may still prompt for every read-only call while trust: untrusted is configured. That is a fail-closed Hermes client behaviour, not additional Local Hands authority. The upstream fix is tracked in #88372. Keep the generated untrusted setting unless you have reviewed the implications of changing the client-side trust policy.

For a reverse proxy, bind Local Hands only to loopback and name every permitted external Host exactly:

hermes-local-hands serve --host 127.0.0.1 --port 8741 \
  --proxy-host mac-studio.example.ts.net

The endpoint generator accepts loopback http://.../mcp or an explicit non-loopback https://.../mcp URL. It rejects embedded credentials, query strings, and unsafe schemes. Configure the tunnel's own identity, authentication, and HTTPS separately; do not expose port 8741 directly to the public Internet.

Safety properties and limits

  • Explicit scope: a client must have a workspace grant; path reads and patches must stay inside the workspace allowlist. Secret-looking and binary material is not returned as normal text.
  • No direct mutation: no remote endpoint approves, denies, executes a shell, writes the registered checkout, merges, or pushes.
  • Snapshot working directory: accepted patches and checks use a snapshot rooted at the recorded commit and do not include uncommitted checkout changes. This constrains Local Hands' own file operations, not what approved check code can access on the host.
  • Checks are not sandboxed: fixed profiles limit what the protocol launches, but the selected program still runs as the local user. It can access that user's files, network, credentials, services, and can cause host-side effects. Only approve profiles and repositories you trust.
  • Check output is local-only: stdout/stderr is retained for local review but omitted from the remote request view. The remote client receives bounded metadata and a digest, closing the direct output-content channel through request_status. Approved code is still not sandboxed and can influence metadata or communicate through its normal host and network access.
  • Checkout observation is limited: after a check, Local Hands compares only Git-visible checkout status before and after. same does not prove that no non-Git file, service, network, credential, or other host-side effect occurred.
  • Audit evidence: retained request/decision events form a signed receipt chain. Verification detects edits and reordering within that retained chain; without an externally anchored head it cannot detect deletion of a valid tail or rollback to an earlier valid database. Receipts also do not prove a host was not compromised.
  • Bounded alpha retention: v0.1 permits at most 32 open requests per client, 256 open requests globally, 512 retained requests per client, and 2,048 retained requests globally. Managed storage also permits at most 128 snapshot deletion-marker directories. It never silently deletes requests, snapshots, or markers. Reviewed terminal snapshots can be removed one at a time through the local exact-ID command above, but there is no request-record or marker-prune command yet. Reaching a retained-request or deletion-marker cap is a deliberate fail-closed stop that needs a later reviewed retention/migration release, not a database or filesystem deletion workaround.
  • Failed creation markers: snapshot construction uses a private, high-entropy .creating-* staging directory and atomic no-replace publication. If construction fails, Local Hands clears only the directory bound to its open descriptor and deliberately does not perform a race-prone path deletion. An empty or partially cleared staging marker can therefore remain and counts conservatively toward the 32-snapshot limit. v0.1 has no CLI prune operation for these internal markers; repeated build failures require a reviewed recovery/migration rather than manual deletion while evidence matters.

Read THREAT_MODEL.md and SECURITY.md before using it with sensitive repositories.

Development validation

ruff check .
ruff format --check .
pip-audit --local --skip-editable --progress-spinner off
pytest --cov=hermes_local_hands --cov-report=term-missing
python -m build
python -m twine check dist/*

CI additionally installs the built wheel into a clean environment. These are release checks for the package, not proof of a safe production rollout.

On 2026-09-07 one real two-machine alpha flow was completed using Hermes on one Mac, this service on another Mac, and HTTPS over a private Tailscale network. The run covered unauthenticated rejection, status and file reads, a pending patch, local approval, snapshot application, a linked check, and receipt-chain verification. This is evidence for that exact environment only; it is not a general compatibility, availability, or production-security claim.

For a durable local service setup, see docs/SERVICE.md.

Project direction

The security core is intended to remain free and open source. There is no paid plan, hosted service, customer, or revenue today. Adoption and safety come before any optional convenience layer. See ROADMAP.md for the public, evidence-gated direction.

Contributing and security

See CONTRIBUTING.md for development expectations and SECURITY.md for private vulnerability reporting. Never put credentials, private source, or real action receipts in a public issue or pull request.

License

Released under the MIT License.

Hermes Local Hands is an independent community project and is not affiliated with or endorsed by Nous Research.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

hermes_local_hands-0.1.1.tar.gz (179.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

hermes_local_hands-0.1.1-py3-none-any.whl (58.8 kB view details)

Uploaded Python 3

File details

Details for the file hermes_local_hands-0.1.1.tar.gz.

File metadata

  • Download URL: hermes_local_hands-0.1.1.tar.gz
  • Upload date:
  • Size: 179.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for hermes_local_hands-0.1.1.tar.gz
Algorithm Hash digest
SHA256 332d7aab4ae106de4cbc3ae33f21f58dfd6faf51ec9d6ffe28379d92a4933905
MD5 6ff7bbfea4584f95eca30e88a61c6d16
BLAKE2b-256 d99549c13b6c3e5e360d3962a3e3eb25f5166c2286b19bb79d2934f2d7a7e413

See more details on using hashes here.

Provenance

The following attestation bundles were made for hermes_local_hands-0.1.1.tar.gz:

Publisher: publish.yml on mauricemohr88-debug/hermes-local-hands

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file hermes_local_hands-0.1.1-py3-none-any.whl.

File metadata

File hashes

Hashes for hermes_local_hands-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 9a40136984b2f9a60b7c7b1339471e0b86ef230af9eb3830835aa695c861c93c
MD5 00addd76f371097ff57987bdb102c576
BLAKE2b-256 7f10dd907dbf24c1d11ad6aaa0bd87e1861007497ab2ee1b7eba257b8019bb3b

See more details on using hashes here.

Provenance

The following attestation bundles were made for hermes_local_hands-0.1.1-py3-none-any.whl:

Publisher: publish.yml on mauricemohr88-debug/hermes-local-hands

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

This release

0.1.1 This release

2 files

0.1.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page