English · 简体中文
A remote client for Hermes Agent — one codebase for Android, Windows, macOS, and the browser.
Connect to the Hermes Agent running on your computer from your phone or desktop, and work with your agent the way you'd chat with a person: multiple profiles, multiple sessions, streaming replies, reasoning traces, tool calls, approval and clarification prompts, image/file/voice attachments, and scheduled-task management.
- One codebase, four targets — React Native (Android) + react-native-web (browser) + Electron (Windows / macOS). The UI and protocol layers are fully shared; only native capabilities (SSH, audio recording, file picking) fork per platform.
- Built-in SSH tunnel — a native SSH module detects or launches
hermes serveon the remote host, opens a local port forward, and reconnects with exponential backoff, resuming in-flight turns viasession.resume. No manual tunneling, and no need to expose the service to the internet. - Zero server-side changes — uses only Hermes' public protocols (WS JSON-RPC + REST). It never modifies the server's source, configuration, or data.
Prebuilt packages are published on the Releases page. Current release: v0.2.5
| Platform | Download | Notes |
|---|---|---|
| Android | HermesChat-0.2.5.apk | Signed with a public debug keystore — see SECURITY.md |
| Windows | HermesChat-Setup-0.2.5.exe | Unsigned — choose Run anyway at the SmartScreen prompt |
| macOS (Apple Silicon) | HermesChat-0.2.5-arm64.dmg | Unsigned — right-click → Open on first launch (Gatekeeper) |
Prefer building from source? See Getting Started. All versions are listed on the Releases page.
| Area | What you get |
|---|---|
| Connections | Multiple connection profiles (SSH tunnel / direct), card-based management, one-tap connect, optional auto-connect on launch |
| Profiles | Avatar + nickname + model badge + status dot; edit nickname and avatar in-app (profiles.configure / profiles.set_asset) |
| Sessions | Create / resume / rename / delete / restart; isolated per profile; search, category filter (Chats / Automation / All), two sort modes (recent activity / creation time) |
| Chat | Streaming text, collapsible reasoning traces, tool-call cards (arguments + result + diff highlighting), errors, Markdown rendering including horizontally scrollable tables |
| Interactions | Inline permission-approval buttons (once / session / always / deny), clarification cards (single choice / multi-select / batched), interrupt long-running turns |
| Models | Bottom-sheet model picker (model.options + config.set); the header shows model · context usage · reasoning effort in real time |
| Attachments | Multi-select photos (compressed by default, optional original-quality upload), full-screen image preview, arbitrary files (@file: references), voice recording → server-side STT transcription |
| Search | "Find in chat" with jump + highlight; session list search by title and preview |
| Scheduled tasks | Task list (status badge / human-readable schedule / next run / error details), pause & resume, run now, run history, read-only run replay, six-mode schedule builder (interval / daily / weekly / monthly / one-off / custom cron) |
| Desktop extras | Responsive three-column layout (narrow / medium / wide breakpoints), native file dialogs, paste & drag-and-drop attachments, completion notifications while unfocused |
Mobile UI, captured against a mock gateway with demo data (via node scripts/web-shot-readme.js — nothing real is ever photographed).
┌──────────── Client (this repo) ─────────┐ ┌──── Your machine ────┐
│ UI (RN / RNW / Electron) │ SSH (22) │ hermes serve │
│ ↓ │ ═══════════════> │ 127.0.0.1:9119 │
│ Store (zustand) ──> RpcClient (JSON-RPC)│ built-in tunnel │ /api/ws │
│ ↓ ↑ │ 127.0.0.1:L │ /api/** │
│ SshManager ──> native SSH / ssh2 │ auto-reconnect │ │
└─────────────────────────────────────────┘ + session resume └──────────────────────┘
src/ssh/— every platform difference is confined here: theHermesSshcontract plus per-platform implementations (Kotlin/JSch on Android, ssh2 in the Electron main process, direct HTTP/WS in the browser). TheSshManagerstate machine (disconnected → connecting → bootstrapping → tunneling → ready → reconnecting) is shared by all three.src/rpc/— JSON-RPC request/response pairing, per-session event dispatch, the message aggregator (event stream →TimelineItem[]), and REST wrappers (cron, run history).src/panels/— panels shared between the phone screens and the desktop columns (session list, timeline, composer, overlays, flow helpers). This is what keeps behaviour identical across platforms.desktop/— the Electron main process: window, ssh2 tunnel, and a loopback proxy. The renderer is served from127.0.0.1:<port>, so it is same-origin with the gateway and sidesteps both CORS and the gateway's WS Host/Origin checks.
Client
- Node.js ≥ 22.11
- Browser: any modern Chromium / Firefox
- Android: JDK 21 + Android SDK (compileSdk 37 / targetSdk 36, build-tools 37.0.0, NDK 27.1.12297006)
- iOS: not enabled yet (the project scaffolding is kept; the native SSH module still needs to be written)
- Desktop: no extra toolchain (Electron is installed via npm; building the Windows package is best done on Windows)
Server
- Hermes Agent installed and running on your computer, with
hermes serve/hermes dashboardlistening on127.0.0.1:9119(the default)
Start the Hermes service on your computer (it listens on 127.0.0.1:9119 by default):
hermes serve # or: hermes dashboardThe app talks to the FastAPI service behind serve / dashboard (WS /api/ws + REST /api/**) — not to hermes gateway, which is the messaging-platform gateway.
npm install
npm run web # vite dev server, http://localhost:5188 by defaultOpen the page and click "⚡ Browser direct (local 127.0.0.1:9119)" on the connection screen. Vite proxies /api/** to port 9119 and rewrites Host/Origin; the session token is extracted from the SPA HTML automatically.
Browser mode can only reach a gateway on the same machine (the proxy targets
127.0.0.1:9119), and native-dependent features (attachments, voice, SSH tunneling) are stubbed out. It exists for development and UI preview.
npm run android # requires the full RN toolchain (JDK 21 + Android SDK + NDK)Or just build and install an APK:
cd android && ./gradlew assembleRelease # output: android/app/build/outputs/apk/release/
adb install -r app/build/outputs/apk/release/app-release.apkThere are also convenience scripts under scripts/ (build-android.sh, install-android.sh, device-log.sh), plus build-android-remote.sh / build-desktop-remote.sh for the case where your machine has no Android toolchain and you build on a second machine over SSH. Those drive a remote git pull and build; the host and paths are configurable through environment variables (see the header comments in each script).
npm run desktop:dev # dev mode: build the web bundle + compile the main process + launch ElectronPackaging:
npm run dist:mac # macOS dmg (arm64)
npm run dist:win # Windows NSIS installer + win-unpacked portable buildArtifacts land in dist-desktop/. The packages are unsigned: on Windows choose "Run anyway" at the SmartScreen prompt, on macOS right-click → Open.
On the connection screen, tap "+ Add profile". There are two connection types:
| Field | Notes |
|---|---|
| Host / port | The machine running Hermes, e.g. 192.168.1.10 / 22 |
| Username | Your SSH login user |
| Auth | Password, or paste a private key in PEM format (passphrase supported) |
The app then handles everything: it checks whether hermes serve is already running remotely → starts it if not → opens a local port forward → connects the WebSocket. On disconnect it reconnects with exponential backoff (1s → 30s) and resumes your sessions.
This is the only way to reach a remote service safely from your phone — the service keeps listening on the remote loopback interface only and is never exposed to the internet.
Connects straight to an already-running gateway (host + port). If you leave the session token empty it is extracted from the gateway's index page (__HERMES_SESSION_TOKEN__); you can also fill it in manually.
- Desktop: the renderer is cross-origin to the gateway, so token extraction and request forwarding happen in the Electron main process (loopback proxy).
- Browser: only
127.0.0.1:9119on the same machine. - Phone: the gateway must listen on a non-loopback address (or run
adb reverse tcp:9119 tcp:9119first).
- Connect — tap a profile card on the home screen. To auto-connect on launch, tap the "auto-connect" dot on a card (it is global: pick one, tap again to clear).
- Pick a profile — the profile list shows every agent; tap one to see its sessions. Tap the avatar or nickname to edit it.
- Sessions — "New session" in the top right starts a conversation; pull to refresh; two dropdowns on the toolbar control filtering (Chats / Automation / All) and sorting (recent activity / creation time); the search pill filters by title and preview; long-press or right-click a row to rename, delete, or find in chat.
- Chat — type in the composer at the bottom. While a reply streams you can scroll up to read history (streaming content will not drag the viewport; scroll back to the bottom or send a message to resume following). The header shows "model · context usage · reasoning effort"; the ⋯ menu in the top right lets you switch models, rename or delete the session, find in chat, and hide tool and reasoning blocks.
- Responding to prompts — when the agent asks for permission or needs clarification, an inline card appears; just tap a button. There is no need to type commands.
- Attachments — the "+" button offers photos, files (inserted as
@file:references), and voice (recorded, then transcribed server-side via STT). Photos are compressed by default; check Original image beside the photo button to upload untouched bytes (up to 25 MB). Tap a chat image to view it full-screen. On desktop you can also paste images and drag files onto the window. - Copying Markdown — on desktop/web, select text in an assistant reply and press
Cmd/Ctrl+Cto get the Markdown source rather than rendered text. The bubble's context menu also offers "Copy Markdown" / "Copy plain text". - Scheduled tasks — switch to "Scheduled" from the top left of the home screen: browse tasks, pause/resume, run now, edit, and view run history. Tapping a run opens a read-only replay (metadata + transcript), with an option to open it in chat.
Desktop shortcuts: Enter to send / Shift+Enter for a newline, Ctrl+N for a new session, Esc to close overlays.
npx tsc --noEmit # type check
npm run lint # eslint
npm test # jest unit tests
npm run web # browser dev server (hot reload)
npm run web:build # build the web bundle into dist-web/
npm run desktop:build # compile the Electron main process
node scripts/harness.mjs # end-to-end against local 127.0.0.1:9119 (sends one real prompt)src/
screens/ Mobile navigation screens (connection / profiles / sessions / chat / cron)
panels/ Panels shared by phone and desktop (session list, timeline, composer, overlays, flows)
desktop/ Electron renderer shell (responsive three-column)
components/ UI components (bubbles, avatar, Markdown, tool card, reasoning block, approval/clarify cards…)
rpc/ JSON-RPC client, event aggregator, REST wrappers, types
ssh/ Native capability contract, per-platform implementations, tunnel state machine
store/ zustand stores (connection / chat / sessions / profiles / cron)
utils/ Pure helpers (scroll follow, search, sorting, validation, Markdown source mapping…)
web-stubs/ Browser stubs for native modules
desktop/ Electron main process + preload + packaging config
scripts/ Build, install, and smoke-test scripts
__tests__/ jest specs
docs/ Design, protocol, and native-module contracts
scripts/ contains Playwright-based end-to-end smoke tests (web-*-smoke.js / desktop-*-smoke.js). Together with scripts/mock-gateway.js — a local fake gateway covering streaming replies, sessions, the cron REST API, and more — they exercise UI behaviour without touching a real service:
node scripts/web-scroll-follow-smoke.js # starts the mock, builds, previews — fully automated- UI strings and code comments are written in Chinese.
- zustand selectors must return stable references (no inline objects such as
?? []). - Platform differences stay inside
src/ssh/; the UI and protocol layers remain platform-agnostic.
- Documentation: this README is available in English and 简体中文. The design documents under
docs/are currently Chinese-only. - App UI: English and Simplified Chinese are supported. The interface follows your system language automatically (unsupported languages fall back to English), and a small in-app toggle (System / 中文 / EN) lets you override it. Adding a new language only requires one dictionary file — see
docs/i18n.md(Chinese) for the framework contract.
| Document | Contents |
|---|---|
docs/plan.md |
Overall design (goals, scope, architecture, milestones) |
docs/protocol.md |
WS JSON-RPC + REST protocol details and measured findings |
docs/ssh-module.md |
Native SSH module contract |
docs/desktop.md |
Unified desktop and web approach |
DECISIONS.md |
Architecture and technology decisions |
PROGRESS.md |
Development progress and pitfalls |
CONTRIBUTING.md |
Contribution guide (branching, DCO, sensitive-data discipline) |
SECURITY.md |
Security policy and private vulnerability reporting |
(Design documents are written in Chinese.)
This project is licensed under the GNU Affero General Public License v3.0 or later (AGPL-3.0-or-later) — an OSI-approved free software license.
Copyright (c) 2026 CobyLee66
You are free to use, modify, and distribute this software, including commercially — no fees, and no need to ask the author for permission.
Provided that: if you distribute the software or a derivative work, or make it available to others as a network service (AGPL section 13), you must release the complete corresponding source code under the same AGPL license.
In one sentence: use it yourself, modify it freely, use it commercially — but you may not ship a closed-source derivative or run one as a closed service.
This project also runs as a browser and desktop app, so it could be turned into a network service offered to others. GPL only governs distribution and says nothing about software that merely runs on a server without being distributed; AGPL section 13 closes that gap, ensuring that modified versions offered as a service contribute their source back.
| Scenario | Allowed? |
|---|---|
| Install and use it yourself (including at work) | ✅ Yes |
| Modify it for internal use without distributing | ✅ Yes |
| Integrate it into a commercial product and distribute | ✅ Yes, but you must open-source your derivative under AGPL |
| Turn it into a website/service for others | ✅ Yes, but you must provide the complete source to users |
| Sell it as a closed-source product | ❌ No |
| Operate it as a closed-source cloud service | ❌ No |
The full terms are in LICENSE — the verbatim FSF text, unmodified.
All dependencies use permissive licenses (44 × MIT, 2 × Apache-2.0), which are compatible with AGPL. UI icons come from Tabler Icons (MIT).
Because this project is AGPL, contributions are released under the same license. Please add a Signed-off-by: line to your commits (DCO), certifying that you have the right to submit the code and agree to license it under AGPL. See CONTRIBUTING.md for details.
Please do not report security issues in public. Use the private vulnerability reporting channel — see SECURITY.md.
HermesChat is an unofficial third-party client. It is not affiliated with or endorsed by the Hermes Agent project or its authors. All "Hermes" names and marks belong to their respective owners. The software is provided "as is", without warranty of any kind; evaluate the risks yourself before use, particularly around SSH credentials and access to remote hosts.






