Skills DirectorySkills Directory
SkillsLearnSecurityCategoriesDocsCommunityBlog
Sign InSubmit Skill
Skills Directory

Security-tested agent skills for Claude, coding agents, and AI workflows.

Directory

  • Browse Skills
  • All Skills A–Z
  • Claude Skills
  • Claude Code Skills
  • Agent Skills
  • Categories
  • Submit a Skill

Learn

  • Learn Hub
  • Install Claude Skills
  • Write SKILL.md
  • Skills vs MCP
  • Directories Compared

Security

  • Security
  • Methodology
  • Secure Claude Skills
  • Security Badges

Company

  • About
  • Community
  • Blog
  • API Docs
  • Advertise

2026 Skills Directory. All rights reserved.

Back to skills

Hermes Desktop Macos

FSecurity

> Skill by [ara.so](https://ara.so) — Daily 2026 Skills collection. Hermes Desktop is a native macOS app (Swift/SwiftUI, macOS 14+) that gives Hermes Agent users a first-class Mac workspace over direct SSH. It reads sessions from `~/.hermes/state.db`, edits canonical memory files (`USER.md`, `MEMORY.md`, `SOUL.md`), browses skills, shows token usage dashboards, and embeds a real SSH terminal — all without a gateway API, local file mirrors, or a remote helper service. ---

81 stars
0 votes
0 copies
0 views
Added 9/19/2026
developmentpythongoswiftshellbashsqltestinggitapisecurity

Works with

terminalcliapi

Security Analysis

F13/100
criticalAccesses sensitive system or user directories
criticalReads or references SSH private keys
criticalModifies startup scripts or system services for persistence
criticalModifies startup scripts or system services for persistence

Scanned 9/19/2026

Install to Claude Code

$npx -y skills add reason-machines/trending-skills --skill hermes-desktop-macos --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Hermes Desktop Macos?

Add the live security badge to your README — it updates automatically with every re-scan.

Security grade badge for Hermes Desktop Macos
[![Security: F — Skills Directory](https://www.skillsdirectory.com/api/skills/reason-machines-hermes-desktop-macos/badge)](https://www.skillsdirectory.com/skills/reason-machines-hermes-desktop-macos)

More formats (shields.io, HTML) on the badges page.

Download Zip
Files
SKILL.md
```markdown
---
name: hermes-desktop-macos
description: Native macOS SSH workspace for Hermes Agent — sessions, terminal, files, usage, and skills over direct SSH
triggers:
  - set up Hermes Desktop on my Mac
  - connect Hermes Desktop to my SSH host
  - configure Hermes Desktop SSH connection
  - browse Hermes sessions in the desktop app
  - edit Hermes memory files from Mac
  - build Hermes Desktop from source
  - troubleshoot Hermes Desktop SSH connection
  - add a new connection profile in Hermes Desktop
---

# Hermes Desktop macOS

> Skill by [ara.so](https://ara.so) — Daily 2026 Skills collection.

Hermes Desktop is a native macOS app (Swift/SwiftUI, macOS 14+) that gives Hermes Agent users a first-class Mac workspace over direct SSH. It reads sessions from `~/.hermes/state.db`, edits canonical memory files (`USER.md`, `MEMORY.md`, `SOUL.md`), browses skills, shows token usage dashboards, and embeds a real SSH terminal — all without a gateway API, local file mirrors, or a remote helper service.

---

## Installation

### Option A: Download the release binary

```bash
# 1. Download from GitHub Releases
# 2. Unzip
unzip HermesDesktop.app.zip

# 3. Move to Applications
mv HermesDesktop.app /Applications/

# 4. Verify the bundle signature
codesign --verify --deep --strict /Applications/HermesDesktop.app

# 5. Check SHA-256 matches the release asset
shasum -a 256 HermesDesktop.app.zip
```

> **First-launch gatekeeper bypass** (app is not yet notarized):
> Right-click → Open, or go to **System Settings → Privacy & Security → Open Anyway**.

### Option B: Build from source

```bash
git clone https://github.com/dodo-reach/hermes-desktop
cd hermes-desktop

# Requires Xcode 15+ and macOS 14 SDK
./scripts/build-macos-app.sh

# The built app lands in the repo root or DerivedData — check script output
open HermesDesktop.app
```

---

## Requirements

| Requirement | Detail |
|---|---|
| macOS | 14 Sonoma or newer |
| SSH auth | Key-based, no interactive prompts from this Mac |
| Host key | Already accepted once in Terminal |
| Remote | `python3` available in the SSH environment |
| Hermes data | `~/.hermes/` present on the remote host |

Quick sanity check — if this works without prompts, the app will work:

```bash
ssh your-host echo ok
# → ok
```

---

## Connecting to a Hermes Host

### Using an SSH alias (recommended)

Add an entry to `~/.ssh/config` on your Mac:

```sshconfig
Host hermes-pi
  HostName 192.168.1.42
  User pi
  IdentityFile ~/.ssh/id_ed25519

Host hermes-vps
  HostName vps.example.com
  User deploy
  Port 2222
  IdentityFile ~/.ssh/id_ed25519

Host hermes-local
  HostName localhost
  User alex
```

In the app: **Connections → New Profile → SSH alias → `hermes-pi`**. Leave Host/User/Port blank.

### Using explicit host details

In the app:
- **Host or IP**: `vps.example.com`
- **User**: `deploy`
- **Port**: `2222`

### Testing the connection

Click **Test** before **Use Host**. The preflight check verifies:
1. SSH target is reachable
2. Authentication completes without prompts
3. `python3` exists in the remote SSH environment

---

## App Sections

### Overview

Displays on first connect:
- Remote `$HOME` path
- Hermes root (`~/.hermes`)
- Which canonical files are present
- Session source (`state.db` or `.jsonl` fallback)

### Files — editing canonical Hermes files

The app provides conflict-aware editing for three files:

| File | Purpose |
|---|---|
| `~/.hermes/memories/USER.md` | User context fed to the agent |
| `~/.hermes/memories/MEMORY.md` | Agent working memory |
| `~/.hermes/SOUL.md` | Agent persona/soul definition |

**Conflict protection flow:**
1. App records the remote file hash at load time
2. Before saving, it re-fetches the remote hash
3. If the file changed on the host since load → save is blocked, edits preserved
4. Prompt: **Reload from Remote** → review diff → re-apply edits → save

### Sessions

- Source: `~/.hermes/state.db` (SQLite, canonical)
- Fallback: `~/.hermes/sessions/*.jsonl` if SQLite unavailable
- Features: search, metadata display, remote deletion, refresh-on-entry

### Usage

Aggregate token dashboard sourced from `~/.hermes/state.db`:
- Total input / output tokens
- Top sessions by token count
- Per-model breakdowns
- Recent session trend

### Skills

Recursive browser for `~/.hermes/skills/**/SKILL.md`:
- Discovers all nested skill files
- Quick filter/search in the list
- Full markdown preview

### Terminal

Embedded SSH terminal with tabs. Uses `/usr/bin/ssh` — the same binary as Terminal.app. Connects to the same host as the active profile.

---

## SSH Configuration Patterns

### Key-based auth setup (if not already done)

```bash
# Generate a key on your Mac if needed
ssh-keygen -t ed25519 -C "hermes-desktop" -f ~/.ssh/id_hermes

# Copy public key to the remote host
ssh-copy-id -i ~/.ssh/id_hermes.pub user@host

# Or manually:
cat ~/.ssh/id_hermes.pub | ssh user@host "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys"

# Test — must return without any prompt
ssh -i ~/.ssh/id_hermes user@host echo ok
```

### SSH config for common topologies

```sshconfig
# Tailscale (MagicDNS hostname)
Host hermes-tailscale
  HostName my-machine.tail1234.ts.net
  User alex
  IdentityFile ~/.ssh/id_ed25519

# Jump host / bastion
Host hermes-behind-bastion
  HostName 10.0.0.5
  User ubuntu
  ProxyJump bastion.example.com
  IdentityFile ~/.ssh/id_ed25519

# Same Mac via localhost
Host hermes-local
  HostName localhost
  User alex
  IdentityFile ~/.ssh/id_ed25519
  StrictHostKeyChecking no
```

> **Note:** Hermes Desktop uses standard `/usr/bin/ssh`. If your setup requires the separate `tailscale ssh` command, use a ProxyCommand instead:

```sshconfig
Host hermes-tailscale-cmd
  HostName my-machine
  User alex
  ProxyCommand tailscale ssh --nc %h %p
```

---

## Building from Source — Key Files

```
hermes-desktop/
├── scripts/
│   └── build-macos-app.sh          # Universal (arm64 + x86_64) build script
├── HermesDesktop/
│   ├── App/
│   │   └── HermesDesktopApp.swift  # SwiftUI @main entry point
│   ├── Views/
│   │   ├── SessionsView.swift
│   │   ├── FilesView.swift
│   │   ├── UsageView.swift
│   │   ├── SkillsView.swift
│   │   └── TerminalView.swift
│   ├── Models/
│   │   └── ConnectionProfile.swift
│   └── SSH/
│       └── SSHClient.swift         # Wraps /usr/bin/ssh process calls
└── HermesDesktop.xcodeproj
```

### Build script usage

```bash
# Full universal build
./scripts/build-macos-app.sh

# Verify the result is a universal binary
lipo -info HermesDesktop.app/Contents/MacOS/HermesDesktop
# → Architectures in the fat file: arm64 x86_64
```

### Xcode build (development)

```bash
# Debug build for current arch
xcodebuild -scheme HermesDesktop -configuration Debug build

# Run tests
xcodebuild -scheme HermesDesktop test
```

---

## Swift Patterns Used in the Codebase

### Running SSH commands (non-interactive)

```swift
import Foundation

func runSSHCommand(
    alias: String,
    command: String,
    completion: @escaping (Result<String, Error>) -> Void
) {
    let process = Process()
    process.executableURL = URL(fileURLWithPath: "/usr/bin/ssh")
    process.arguments = [
        "-o", "BatchMode=yes",          // never prompt interactively
        "-o", "ConnectTimeout=10",
        alias,
        command
    ]

    let pipe = Pipe()
    process.standardOutput = pipe
    process.standardError = pipe

    process.terminationHandler = { proc in
        let data = pipe.fileHandleForReading.readDataToEndOfFile()
        let output = String(data: data, encoding: .utf8) ?? ""

        if proc.terminationStatus == 0 {
            completion(.success(output))
        } else {
            completion(.failure(SSHError.nonZeroExit(output)))
        }
    }

    do {
        try process.run()
    } catch {
        completion(.failure(error))
    }
}

// Usage
runSSHCommand(alias: "hermes-pi", command: "python3 --version") { result in
    switch result {
    case .success(let version): print("python3 ok:", version)
    case .failure(let err): print("preflight failed:", err)
    }
}
```

### Reading a remote file with conflict detection

```swift
struct RemoteFile {
    let content: String
    let sha256: String       // hash at load time
}

func fetchRemoteFile(
    alias: String,
    path: String,
    completion: @escaping (Result<RemoteFile, Error>) -> Void
) {
    // Fetch content and hash in one SSH call
    let cmd = "python3 -c \""
        + "import hashlib, pathlib; "
        + "p = pathlib.Path('\(path)').expanduser(); "
        + "b = p.read_bytes(); "
        + "print(hashlib.sha256(b).hexdigest()); "
        + "print(b.decode())\""

    runSSHCommand(alias: alias, command: cmd) { result in
        switch result {
        case .success(let output):
            let lines = output.components(separatedBy: "\n")
            guard let hash = lines.first else {
                completion(.failure(RemoteFileError.parseError)); return
            }
            let content = lines.dropFirst().joined(separator: "\n")
            completion(.success(RemoteFile(content: content, sha256: hash)))
        case .failure(let err):
            completion(.failure(err))
        }
    }
}

func saveRemoteFile(
    alias: String,
    path: String,
    newContent: String,
    originalHash: String,
    completion: @escaping (Result<Void, Error>) -> Void
) {
    // Check current remote hash before writing
    let checkCmd = "python3 -c \""
        + "import hashlib, pathlib; "
        + "p = pathlib.Path('\(path)').expanduser(); "
        + "print(hashlib.sha256(p.read_bytes()).hexdigest())\""

    runSSHCommand(alias: alias, command: checkCmd) { result in
        switch result {
        case .success(let currentHash):
            if currentHash.trimmingCharacters(in: .whitespacesAndNewlines) != originalHash {
                completion(.failure(RemoteFileError.conflict))
                return
            }
            // Safe to write — pipe content via stdin
            let writeCmd = "cat > \(path)"
            // ... write newContent via stdin to the process
            completion(.success(()))
        case .failure(let err):
            completion(.failure(err))
        }
    }
}

enum RemoteFileError: Error {
    case parseError
    case conflict   // remote changed since load — prompt user to reload
}
```

### SwiftUI connection profile model

```swift
import Foundation

struct ConnectionProfile: Identifiable, Codable {
    var id: UUID = UUID()
    var name: String
    var sshAlias: String?       // preferred: uses ~/.ssh/config
    var host: String?
    var user: String?
    var port: Int = 22

    /// The argument passed to /usr/bin/ssh
    var sshTarget: String {
        if let alias = sshAlias, !alias.isEmpty {
            return alias
        }
        let u = user.map { "\($0)@" } ?? ""
        let p = port != 22 ? " -p \(port)" : ""
        return "\(u)\(host ?? "localhost")\(p)"
    }
}
```

---

## Troubleshooting

### `Test` fails: "Authentication failed"

```bash
# Verify key auth works without prompts
ssh -o BatchMode=yes -o ConnectTimeout=10 your-host echo ok

# If it asks for a password, set up key-based auth:
ssh-copy-id -i ~/.ssh/id_ed25519.pub user@host

# Check the SSH agent has the key loaded
ssh-add -l
ssh-add ~/.ssh/id_ed25519
```

### `Test` fails: "python3 not found"

```bash
# On the remote host, check python3 path
which python3
# If it's in a non-standard location like /usr/local/bin, ensure PATH is set
# in ~/.bashrc or ~/.zshrc (non-interactive shells may have a minimal PATH)

# Quick fix: symlink to a standard location
sudo ln -s /usr/local/bin/python3 /usr/bin/python3
```

### `Test` fails: "Host key verification failed"

```bash
# Accept the host key interactively first
ssh your-host
# Type 'yes' when prompted

# Or clear a stale entry
ssh-keygen -R your-host
ssh your-host   # accept fresh key
```

### Sessions view shows no sessions

The app tries `~/.hermes/state.db` first. Check on the remote host:

```bash
ssh your-host "ls -lh ~/.hermes/state.db"
# If missing, check for jsonl fallback:
ssh your-host "ls ~/.hermes/sessions/*.jsonl | head -5"
```

### Conflict error when saving a memory file

The remote file changed after you opened it. In the app:
1. Click **Reload from Remote**
2. The editor will show the current remote content
3. Re-apply your edits manually
4. Save again

### Tailscale: SSH works with `tailscale ssh` but not `ssh`

Hermes Desktop uses `/usr/bin/ssh`. Configure a ProxyCommand in `~/.ssh/config`:

```sshconfig
Host hermes-tailscale
  HostName my-machine
  User alex
  ProxyCommand /usr/local/bin/tailscale nc %h %p
```

Then use `hermes-tailscale` as the SSH alias in the app.

### Monitor live network activity

```bash
# Watch what the app connects to
sudo nettop -p HermesDesktop

# Or use lsof to see open connections
lsof -i -n -P | grep HermesDesktop
```

---

## Key Design Constraints

| Constraint | Reason |
|---|---|
| No gateway API | Remote host is the only source of truth |
| No local file mirror | Avoids stale state and sync conflicts |
| No remote helper service | Zero install footprint on the host |
| Uses `/usr/bin/ssh` | Same path as Terminal — no novel auth surface |
| `BatchMode=yes` on all non-terminal SSH calls | Prevents silent interactive prompt hangs |
| Conflict check before every save | Protects against agent overwriting newer remote state |
```

Attribution

reason-machinesreason-machines
View sourceMore from reason-machines →
SSkills DirectorySkills Directory

Ship a skill? Prove it's safe.

Free 120-pattern security scan, letter grade, and an embeddable README badge.

Submit a skill

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments (0)

No comments yet. Be the first to comment!

SSkills DirectorySkills Directory

Ship a skill? Prove it's safe.

Free 120-pattern security scan, letter grade, and an embeddable README badge.

Submit a skill

Related Skills

Browser Extension Developer

Use this skill when developing or maintaining browser extension code in the `browser/` directory, including Chrome/Firefox/Edge compatibility, content scripts, background scripts, or i18n updates.

281612 votes

Seo Optimizer

SEO optimization with keyword analysis, readability assessment, technical validation, content quality. Use for search rankings, blog posts, content audits, or encountering keyword density, readability scores, meta tags, schema markup errors.

2132 votes

Google Official Seo Guide

Official Google SEO guide covering search optimization, best practices, Search Console, crawling, indexing, and improving website search visibility based on official Google documentation

1862 votes

Tanstack Start

Build a full-stack TanStack Start app on Cloudflare Workers from scratch — SSR, file-based routing, server functions, D1+Drizzle, better-auth, Tailwind v4+shadcn/ui. Use whenever the user mentions TanStack Start, asks to scaffold a full-stack Cloudflare app with SSR, wants an SSR dashboard, or asks for a React 19 + Cloudflare Workers app with file-based routing and server functions — even if they don't name TanStack Start specifically. No template repo — Claude generates every file fresh per ...

9881 votes

Pentest

PTES-aligned adversarial security audit for backend, frontend, and mobile applications. Produces a CVSS-scored Hacker Report with verified PoCs and phased remediation.

5491 votes
View all in development →