Standalone Go binary that pairs a remote laptop with a Hermes Agent brain over WSS and exposes the laptop's shell + filesystem to the agent over an authenticated, encrypted WebSocket connection. The node is the arm in a brain-and-arm architecture — it connects outbound, so no inbound ports required on the laptop.
- Quick Start
- Installation
- Subcommands
- Configuration
- Architecture
- Security
- Troubleshooting
- Contributing
- FAQ
# Install the binary
curl -sSL https://raw.githubusercontent.com/blaspat/hermes-node/main/install/install.sh | sh
# Pair with your Hermes brain
hermes-node pair --server wss://vps.yourdomain.com:7000 --token <TOKEN> --name work-laptop
# Edit config to set allowed paths
# ~/.hermes-node/config.toml
# Start the daemon
hermes-node run# macOS / Linux
curl -sSL https://raw.githubusercontent.com/blaspat/hermes-node/main/install/install.sh | sh
# Windows (PowerShell)
irm https://raw.githubusercontent.com/blaspat/hermes-node/main/install/install.ps1 | iexThe installer tries to build from source first (Go 1.22+ required). If Go is not available, it falls back to downloading the pre-built binary.
Installer flags:
| Flag | Description |
|---|---|
--no-service |
Install binary only, skip background service registration |
--yes |
Skip confirmation prompts (non-interactive mode) |
--dry-run |
Preview what would be installed without making changes |
--version <tag> |
Install a specific version instead of the latest |
Example — install without service:
curl -sSL ... | sh -s -- --no-serviceBuilds from source using the latest release tag.
git clone https://github.com/blaspat/hermes-node.git
cd hermes-node
go build -o hermes-node ./cmd/hermes-nodeRequires Go 1.22+.
The scripts/build.sh script cross-compiles for these targets:
| OS | Arch | Binary name |
|---|---|---|
| Linux | amd64 | hermes-node-linux-amd64 |
| Linux | arm64 | hermes-node-linux-arm64 |
| macOS | amd64 | hermes-node-darwin-amd64 |
| macOS | arm64 | hermes-node-darwin-arm64 |
| Windows | amd64 | hermes-node-windows-amd64.exe |
| Windows | arm64 | hermes-node-windows-arm64.exe |
Confirmed platforms: Linux (amd64) and macOS (amd64 + arm64). Windows builds but is not yet end-to-end tested (requires WSL or Git Bash for the shell executor).
Writes a fresh config.toml with the server URL, node name, and pairing token. The file is created with mode 0600. The operator edits it after pairing to add allowed_paths.
hermes-node pair --server wss://vps.example.com:7000 --token <TOKEN> --name <name> [--config <path>]Long-lived background service. Loads the config, opens the audit log, connects to the server, and stays connected across network drops via exponential backoff.
hermes-node run [--config <path>]SIGHUP reload: Send SIGHUP to the daemon process to reload log_level at runtime without restarting:
kill -HUP <pid>The daemon re-reads config.toml and applies the new log_level. Other changes (allowed_paths, log_path, server_url) still require a full restart.
Exit codes: The daemon exits 0 on clean shutdown, 1 on fatal error.
Reads the daemon's status file and displays connection state, session ID, uptime, and last error. No server connection required. The state is cross-checked against the actual process table — if the daemon was killed, it shows stopped even if the status file hasn't been updated.
hermes-node statusOutput when running:
hermes-node dev go1.26.3 a1b2c3d4 2026-06-22
PID: 12345
State: connected
Name: work-laptop
Server: wss://vps.example.com:7000
Session: abc-def-123
Started: 2026-06-22T21:00:00Z
Connected: 2026-06-22T21:05:00Z
If the daemon is not running:
hermes-node: daemon not running (status file not found)
Stops the running daemon. Sends SIGTERM and waits up to 5 seconds for a clean shutdown. If the daemon doesn't respond, escalates to SIGKILL.
hermes-node stopSelf-updates the binary from GitHub Releases. Downloads the latest release for your OS/arch, verifies it, and replaces the running binary (Unix only — Windows prints manual instructions).
hermes-node update # latest release with confirmation
hermes-node update --yes # skip confirmation prompt
hermes-node update --version v0.1.0 # pin to a specific version
hermes-node update --restart-service # also restart systemd/launchdThe downloaded binary is verified by running --version before replacement. On Linux, os.Rename replaces the binary atomically. If the temp and target directories are on different filesystems, you'll get instructions to copy manually.
Removes the binary, stops and deregisters the background service, and optionally removes the config directory.
hermes-node uninstall # remove binary + service, keep config
hermes-node uninstall --purge # also remove ~/.hermes-node/
hermes-node uninstall --dry-run # preview without making changesPer-platform service removal:
- Linux:
systemctl --user disable --now hermes-node.service, then deletes~/.config/systemd/user/hermes-node.service - macOS:
launchctl unload, then deletes~/Library/LaunchAgents/com.blaspat.hermes-node.plist - Windows: Use
install.ps1 --Uninstall(Task Scheduler removal)
The config directory (~/.hermes-node/) is left in place by default. Pass --purge to also remove all config, tokens, and audit logs.
Validates config.toml without connecting to the server.
hermes-node validate [--config <path>]Checks performed:
- TOML syntax and required fields (
server_url,name,token) allowed_pathsexist and are directories- Log path directory is writable
- TLS
ca_certfile exists (if set) andpinned_cert_sha256is valid hex
Exit codes:
0— all checks passed1— one or more validation failures2— flag/argument error or config file not found
Displays version and build metadata:
$ hermes-node --version
hermes-node dev go1.26.3 a1b2c3d4 2026-06-22
Format: hermes-node <version> <go-version> <commit-sha>[-dirty] <date>. When built from a git repo (the normal case), the commit SHA and date are embedded automatically by debug.ReadBuildInfo(). A bare hermes-node dev indicates the binary was built without VCS info (e.g., go run).
The config file is written by hermes-node pair and edited manually by the operator. Default location: ~/.hermes-node/config.toml.
[node]
# Required — set by `hermes-node pair`
server_url = "wss://vps.example.com:7000"
name = "work-laptop"
token = "abc123..."
# Filesystem roots the agent can touch (deny-by-default)
# Empty list rejects all paths. Each entry must be an
# existing directory.
allowed_paths = ["/home/user", "/tmp"]
# Audit log location (default: ~/.hermes-node/audit.log)
log_path = "/home/user/.hermes-node/audit.log"
# Log level (default: "info")
# One of: debug, info, warn, error
log_level = "debug"
# Reconnect backoff (defaults shown)
backoff_initial = "1s" # default; Go duration, e.g. "500ms", "5s"
backoff_max = "60s" # default; maximum delay between retries
backoff_factor = 2.0 # default; multiplier per retry
# Proxy: hermes-node respects HTTPS_PROXY / HTTP_PROXY / NO_PROXY
# env vars automatically. No config field needed — just set them
# in the shell before running `hermes-node run`.[server]
# Custom CA bundle for self-signed certs (optional)
ca_cert = "/home/user/.hermes-node/my-ca.pem"
# Leaf certificate SHA-256 pin (optional, hex-encoded 64 chars)
pinned_cert_sha256 = "a1b2c3d4e5f6..."┌───────────────────────┐ outbound WSS ┌───────────────────────┐
│ Laptop │ ──────────────►│ VPS (Hermes brain) │
│ hermes-node (Go) │ ◄──────────────│ hermes-node-plugin │
│ • shell exec │ commands │ • Python server │
│ • file read/write │ + results │ • token auth │
│ • audit log │ │ • registers as env │
│ • auto-reconnect │ │ │
└───────────────────────┘ └───────────────────────┘
Same protocol on both sides — see PROTOCOL.md.
- Remote shell execution — persistent
bashsession with preserved cwd and env across calls. Stderr is captured alongside stdout. - Remote file read/write — read and write files through a WSS tunnel, with per-path allowlisting enforced on the laptop.
- Real-time streaming — stdout and stderr stream back to the brain in real time with a 10 MB per-stream cap.
- Auto-reconnect — exponential backoff (configurable via
config.toml). Survives reboots as a background service. - Full audit log — every call is recorded in append-only JSONL with automatic rotation at 50 MB (keeps 5 files).
- TLS 1.3 required — public CAs work out of the box; custom CA and cert pinning supported for self-signed deployments.
- Deny-by-default security — empty
allowed_pathsrejects all paths. The allowlist is enforced on the laptop — the server cannot bypass it.
- No camera, screen, browser, mic, push notifications, or location
- No live file watcher / auto-sync
- No interactive REPLs (
vim,python, etc.) - No multi-server pairing (one node → one brain)
- No GUI pairing flow (text token only)
- No cross-platform state sync (cwd/env is per-laptop)
- Token is stored in plaintext in
config.toml(mode 0600). Revoke it on the server withhermes node revoke --name <name>— revocation is immediate. - Path allowlist is enforced on the laptop. Each path is symlink-resolved before the check, so symlinks escaping the allowlist are rejected.
- TLS 1.3 required for all connections. Custom CA and cert pinning are supported.
- Audit log every call with action, target, duration, exit code, and status.
TLS / cert errors — Ensure the server uses a public CA (Let's Encrypt) or configure ca_cert / pinned_cert_sha256 in config.toml.
Permission denied on allowed_paths — Verify every path in allowed_paths exists and is readable/writable by the user running the node. An empty list (allowed_paths = []) leaves the node read-only.
Node fails to start after reboot — Confirm the service was installed (systemctl --user status hermes-node or launchd entry). Check the audit log (cat ~/.hermes-node/audit.log).
Stderr not captured — Since v0.1.0, stderr is captured from the shell executor. If you see unexpected output, check if the command is producing stderr (file not found, permission errors, etc.).
Config file not found — Ensure ~/.hermes-node/config.toml exists. Run hermes-node pair to create it, or pass --config <path> to every subcommand.
Daemon not responding to status — Run hermes-node status to check if the daemon is running. If the status file is stale (PID not running), the (not running) indicator will appear.
See CONTRIBUTING.md for the full guidelines.
Quick summary:
- Go 1.22+ required for development. End users do not need Go.
- Run
go test ./...andgo test -race ./...before opening a PR. - Run
gofmt -l .— must produce no output. - Commit format:
<type>(<scope>): <imperative summary>— e.g.feat(cli): add hermes-node status subcommand. - Reference the issue / discussion in the PR body.
- Wire-format changes must update
PROTOCOL.mdin the same commit.
-
Q: Does the node require Hermes Agent on the laptop? A: No. The binary is independent — it will continue to function even if all AI agent software is removed from the machine.
-
Q: Can the server bypass the path allowlist? A: No. The allowlist is enforced on the laptop. The server never sees its contents.
-
Q: What happens if the laptop is stolen? A: The token is in
~/.hermes-node/config.toml(mode 0600). Revoke the token on the server withhermes node revoke --name <name>— revocation is immediate. -
Q: Will v2 add keychain support? A: Yes. OS keychain token storage is a roadmap item for v2.
-
Q: How do I uninstall? A: Run
hermes-node uninstallto remove the binary and service. Add--purgeto also remove~/.hermes-node/. On Windows, useinstall.ps1 --Uninstall. -
Q: How do I update? A: Run
hermes-node updateto self-update from GitHub Releases. Pass--version <tag>to pin a specific release. -
Q: How do I check if the daemon is running? A: Run
hermes-node status. It reads the status file written by the daemon and shows connection state, PID, session ID, and uptime. -
Q: Can I reload config without restarting? A: Send
SIGHUPto the daemon process to reloadlog_level. Other changes require a restart. -
Q: Does the node support HTTP proxies? A: Yes. The WebSocket client respects the standard
HTTPS_PROXY,https_proxy,HTTP_PROXY,http_proxy, andNO_PROXYenvironment variables automatically. For Basic auth, include credentials in the URL:http://user:password@proxy:port. For NTLM/Kerberos proxies, use a local authenticating proxy bridge (e.g.cntlm). -
Q: What does
--versionshow? A: The version, Go version, commit SHA, and build date. Example:hermes-node v0.1.0 go1.26.3 abc12345 2026-06-22.
- Hermes Node Plugin — the Hermes Agent plugin (the "brain" server side)
- Hermes Agent — the agent framework this plugs into
- PROTOCOL.md — the wire protocol contract between node and brain
License: MIT | Author: Blasius Patrick