Skip to content

About

Hermes Agent for Obsidian — fork with mobile support (iPad/iPhone/Android), remote multi-profile gateways and an in-plugin approval dialog. Desktop unchanged. MIT.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

 
 

Repository files navigation

Hermes Agent — Obsidian plugin

Chat with your locally installed Hermes Agent from inside Obsidian. The plugin talks to the local Hermes gateway (started by Hermes Desktop) over its HTTP API and streams replies into a multi-tab sidebar panel. It sends the current note or selected text as context.

It is a separate plugin from Claudian and does not modify or replace it.

About this fork

This fork makes the Hermes Agent plugin run on Obsidian Mobile (iPad, iPhone, Android) and adds a few things you need once Hermes runs on another machine than the one you are typing on. Desktop behaviour is unchanged: with default settings the plugin works exactly like upstream.

The changes live on the branch mobile-support as small, separate commits and are offered upstream as pull request #22. Until they are merged, install this fork with BRAT (see below).

What's new

  • Runs on mobile. A new network layer uses Node http/https on desktop (as before) and fetch with a streamed response on mobile, so replies still arrive token by token. If the gateway sends no CORS headers, it falls back to Obsidian's requestUrl (works, but without streaming). Node modules are loaded lazily, so the bundle starts on iOS.
  • Remote and multi-profile gateways. A Profile setting sends requests to /p/<profile>/ on a gateway that serves several Hermes profiles, each with its own API key. Several people (or devices) can share one Hermes server without seeing each other's sessions.
  • Approval dialog. With Auto-approve tool requests off, every command the gateway flags as dangerous (e.g. rm -rf, sudo) opens a dialog: Allow once, Allow for this chat, Always allow or Deny. Closing the dialog counts as Deny. Previously the plugin cancelled the run instead. Works on desktop and mobile.
  • Session-authority gateways. Approvals and stops are answered in the format current Hermes gateways require (request_id / prompt_id + execution_generation, admission_id); older gateways keep the previous format.
  • Stop really stops. Stopping a chat now also stops the run on the gateway, so a run that waits for an approval no longer blocks the next message in that chat.
  • Visible failures. If the gateway refuses an approval answer, the chat says so instead of waiting silently.
  • Tests for the new protocol helpers (69 in total, all passing).

Install with BRAT

  1. Install and enable the community plugin BRAT (Obsidian42).
  2. BRAT → Add beta plugin → mgl-git/hermes-agent-obsidian-plugin → version Latest.
  3. Enable Hermes Agent. Existing settings and chat history are kept; new settings start empty, which means "behave as before".

Using a remote gateway from a phone or tablet

  • Serve the gateway over HTTPS (iOS blocks plain http:// to other hosts), e.g. tailscale serve --https=8443 http://127.0.0.1:8642, and use that URL as Gateway base URL.
  • Allow the Obsidian app origins via CORS in the gateway's .env: API_SERVER_CORS_ORIGINS=app://obsidian.md,capacitor://localhost,http://localhost
  • Multi-profile gateway: set Profile and that profile's own API_SERVER_KEY (in the profile's .env). A profile key only opens its own profile.
  • Working folder: leave empty on mobile; on desktop with a remote gateway, set it to the path where the gateway sees the vault (e.g. /workspace), otherwise the agent is told a local path it cannot reach.

Security notes

  • No addresses, keys or other credentials are in the code; everything is entered in the plugin settings.
  • The settings (including the API key) are stored in .obsidian/plugins/hermes-agent/data.json inside the vault and travel with your vault sync. Use a per-profile key, not the owner's key, on shared or less trusted devices.
  • Restrict network access to the gateway port (for example with a Tailscale ACL that lets a tagged device reach only that port), and keep Auto-approve off where people should confirm dangerous commands.

Tested with

Hermes Agent v0.22.3 (multiplex gateway with session authority, Docker terminal backend), Obsidian desktop on Windows and Obsidian on iPadOS, gateway reached over Tailscale HTTPS.

License

MIT, unchanged from upstream. Original work © Jason (jsun2020); mobile and gateway changes contributed under the same license.

How it works

  • Hermes Desktop runs a local gateway (default http://127.0.0.1:8642).
  • The plugin capability-detects via GET /v1/capabilities and then uses:
    • the Runs transport (POST /v1/runs + GET /v1/runs/{id}/events) when available — this gives tool/reasoning/usage events; or
    • Chat Completions (POST /v1/chat/completions, OpenAI-compatible, streaming) as a fallback.
  • Auth uses Authorization: Bearer <API_SERVER_KEY>. You paste that key into the plugin settings.

The transport logic is a direct port of Hermes Desktop's own client (src/main/hermes.ts / src/main/run-stream.ts), so it mirrors the reference behavior.

Prerequisites

The plugin only needs the Hermes gateway HTTP API reachable (default http://127.0.0.1:8642). It does not talk to Hermes Desktop directly, so it works whether the gateway is started by the Desktop app or by the command-line / TUI install. Pick the path that matches your setup.

Option A — Hermes Desktop (easiest)

  1. Install + run Hermes Desktop at least once. This creates ~/.hermes (on Windows: %USERPROFILE%\.hermes), generates API_SERVER_KEY, and starts the gateway. The portable build is at hermes-desktop/dist/hermes-desktop-0.5.8-portable.exe.
  2. Find your key in %USERPROFILE%\.hermes\.env (line API_SERVER_KEY=...).
  3. The gateway must be running (Hermes Desktop open) when you use the plugin.

Option B — CLI / TUI install (no Desktop)

The gateway is a subcommand of the Python Hermes Agent CLI, so you don't need Desktop — but note that the TUI is not the gateway. The TUI is an interactive chat REPL; it does not expose the HTTP API the plugin needs. Run the gateway as its own process, in addition to (or instead of) the TUI:

  1. Enable the API server in ~/.hermes/config.yaml (Desktop writes this automatically; CLI users add it once):
    platforms:
      api_server:
        enabled: true
        extra:
          port: 8642
          host: "127.0.0.1"
    (Or pass it at launch via env: API_SERVER_ENABLED=true, API_SERVER_PORT=8642.)
  2. Set API_SERVER_KEY so the gateway requires auth (anonymous requests get 401). Put it in ~/.hermes/.env (API_SERVER_KEY=...) or export it in the env, then paste the same value into the plugin. If it isn't set, the gateway runs unauthenticated and history-load is disabled.
  3. Start the gateway:
    hermes gateway
    Leave it running while you use the plugin. (The TUI can run separately; it shares the same ~/.hermes config and underlying model, but it is not what the plugin connects to.)
  4. Sanity-check the gateway is up before opening Obsidian — list models over the same HTTP API the plugin uses:
    # bash
    curl -H "Authorization: Bearer $API_SERVER_KEY" http://127.0.0.1:8642/v1/models
    # PowerShell
    curl.exe -H "Authorization: Bearer $env:API_SERVER_KEY" http://127.0.0.1:8642/v1/models
    A JSON { "data": [ ... ] } means the gateway is reachable and the key works. 401 means the key is wrong/missing; a connection refused means the gateway isn't running.

In both options the plugin is identical — set the base URL + key in settings and click Test connection.

Build

cd C:\Users\sr9rfx\.claude-project\Obsidian\hermes-agent
npm install
npm run build      # emits main.js
npm test           # runs protocol unit tests (Node built-in test runner)

npm run build produces main.js next to manifest.json and styles.css — the three files Obsidian needs. npm run lint runs the same checks as Obsidian's community review (eslint-plugin-obsidianmd + typescript-eslint); npm test runs the unit tests.

Releasing (maintainers)

Releases are automated by .github/workflows/release.yml. Bump the version in manifest.json, package.json, and versions.json, then push a matching tag (Obsidian convention: a bare version, no v prefix):

git tag 0.8.3 && git push origin 0.8.3

The workflow runs npm ci + npm run build, verifies the tag equals the manifest.json version, generates GitHub build-provenance attestations for main.js/manifest.json/styles.css, and creates the GitHub release with those files attached.

Install

Via BRAT (recommended for now)

This plugin is distributed from GitHub releases, so the easiest install is the BRAT community plugin:

  1. Install BRAT from Obsidian's Community plugins browser and enable it.
  2. Run the command BRAT: Add a beta plugin for testing.
  3. Paste the repo URL: https://github.com/jsun2020/hermes-agent-obsidian-plugin
  4. BRAT downloads the latest release and installs Hermes Agent; enable it under Settings -> Community plugins. BRAT will also keep it auto-updated.

Manual

Download main.js, manifest.json, and styles.css from the latest release and copy them into a new plugin folder (do NOT touch the existing claudian folder):

C:\Users\sr9rfx\Obsidian-vault\.obsidian\plugins\hermes-agent\
  main.js
  manifest.json
  styles.css

Then in Obsidian: Settings -> Community plugins -> enable Hermes Agent (toggle it on; it will be added to community-plugins.json alongside claudian).

Configure

Open Settings -> Hermes Agent:

  • Gateway base URL — http://127.0.0.1:8642 (named profiles may use 8643-8742).
  • API key — paste API_SERVER_KEY.
  • Model — e.g. gpt-5.5, or leave empty for the gateway default. Use Test connection to list available models.
  • Transport — Auto recommended.
  • Optional: reasoning effort, include-full-note-content, request timeout, max tabs.

Remote gateway (e.g. over Tailscale)

The plugin talks straight to the gateway's OpenAI-compatible API server (default port 8642), so Obsidian and the agent don't have to run on the same machine. A common topology: Obsidian (and Hermes Desktop) on a Mac, the agent backend on a Linux host reachable over Tailscale.

Obsidian plugin on Mac
        |
        |  HTTP(S) over Tailscale, Authorization: Bearer <API_SERVER_KEY>
        v
Hermes API server on Linux :8642
        |
        v
Linux Hermes agent, tools, sessions and model

Things that trip people up:

  • The plugin does not reuse Hermes Desktop's remote connection. Desktop's remote mode connects to the hermes serve dashboard (commonly port 9119) with dashboard auth. The plugin needs the API server on 8642 and its own API_SERVER_KEY. Port 9119 is not a substitute.
  • API_SERVER_KEY is a secret you generate, on the machine that runs the API server. It is not a model-provider key and not the Desktop connection token.
  • The API server binds to 127.0.0.1 by default, which is unreachable from other machines no matter what your firewall or Tailscale ACLs allow. Bind it to the host's Tailscale address.

On the gateway host, set (in ~/.hermes/.env, or the equivalent platforms.api_server.extra keys in config.yaml) and restart the gateway:

API_SERVER_ENABLED=true
API_SERVER_HOST=<linux-tailscale-ip>   # not 127.0.0.1; prefer this over 0.0.0.0
API_SERVER_PORT=8642
API_SERVER_KEY=<output of: openssl rand -hex 32>
hermes gateway restart
ss -ltnp 'sport = :8642'                          # must show the Tailscale IP, not 127.0.0.1
curl http://<linux-tailscale-ip>:8642/health

Then, in the plugin settings on the Mac, set Gateway base URL to http://<linux-tailscale-ip>:8642, API key to the generated key, Transport to Auto, and click Test connection. To check auth outside Obsidian:

curl -H "Authorization: Bearer <API_SERVER_KEY>" http://<linux-tailscale-ip>:8642/v1/models

Security: the API server exposes the agent's full toolset (terminal, filesystem, ...) to anyone holding the key. Use a long random key, bind to the specific tailnet address rather than 0.0.0.0, don't expose the port to the public internet, and prefer HTTPS (a reverse proxy, Tailscale Serve, or a tunnel) if the traffic leaves a network you trust. Note that the agent's file tools act on the server's filesystem, so the vault must be reachable there (synced copy, mount, ...) for file access to work; the "current note" / "selection" toggles work regardless because they embed the text in the prompt.

Some Hermes CLI versions print 'gateway.api_server.host' is not a recognized config key from hermes config set even though the runtime honors the setting. That warning comes from the CLI's validator, not from this plugin; if the listener shows the right address, the config is working.

Agent workspace (file access)

This is what makes Hermes a real collaborator instead of a chatbot: the vault becomes the agent's working directory, so it can read, write, search, and run multi-step workflows over your notes.

  • Working folder — the folder the agent operates in, relative to the vault root. Leave empty to use the whole vault. An absolute path is also accepted. The plugin sends this to the gateway as the run's instructions (Hermes has no cwd field), telling the agent to use file tools with absolute paths under this folder. The quickest way to set it is the folder chip in the chat footer — click it to open a native folder picker (folders inside the vault are stored relative, outside the vault as an absolute path).
  • Auto-approve tool requests (default on) — lets the agent use file read/write, search, and terminal tools without prompting. When Hermes asks for permission mid-run, the plugin answers always via POST /v1/runs/{id}/approval. This grants the agent real read/write access to the working folder. Turn it off to get plain, tool-less replies (every tool request is then cancelled and the run falls back to a normal chat completion).

Filesystem access & the Codex sandbox (important). Whether the agent can actually read/write your vault depends on the gateway, not this plugin. With the gpt-5.5 / OpenAI-Codex provider, the agent runs inside a Codex sandbox whose workspace root is the gateway process's launch directory (os.getcwd()), and which defaults to read-only. The gateway's /v1/runs endpoint exposes no way for an API client to set a per-run working directory (no body field, header, env, or config key — agent.session_cwd is never set on the API path). So if your vault is not the gateway's launch directory, file reads of vault paths fall outside the sandbox and are escalated — and those escalations are auto-denied because the run is non-interactive. That is the "two read-only permission requests were rejected" message.

This is not fixable from the plugin (a pure API client) — but you can fix it on the Codex side, since the gateway drives Codex via ~/.codex/config.toml (CODEX_HOME). Codex defaults to sandbox_mode = read-only, which blocks the shell/command escalations the agent uses.

The fix (verified): add these keys to ~/.codex/config.toml, then restart Hermes Desktop (the gateway loads the config at startup). This is the config equivalent of SuperAI Agent's codex exec -a never -s workspace-write:

approval_policy = "never"
sandbox_mode = "workspace-write"

[sandbox_workspace_write]
# writes allowed only under these roots (reads are allowed anywhere); add your vault:
writable_roots = ['C:\Users\you\Obsidian-vault']
network_access = true

Windows caveat (CreateProcessWithLogonW failed: 1385). On locked-down / corporate Windows the Codex sandbox itself can't start — 1385 = ERROR_LOGON_TYPE_NOT_GRANTED, i.e. your account lacks the "Log on as a batch job" right that [windows] sandbox = "elevated" needs. Both read-only and workspace-write depend on that sandbox, so they fail before startup. The only mode that works there is sandbox_mode = "danger-full-access" (no sandbox → no CreateProcessWithLogonW). Either grant the logon right via an elevated secedit/ntrights (may be blocked/reverted by Group Policy), or use full-access.

With this, the agent can read anywhere, run commands, and write into the vault — no per-command prompts. For unrestricted access (read/write anywhere + network, like --dangerously-bypass-approvals-and-sandbox) use sandbox_mode = "danger-full-access" instead and drop the [sandbox_workspace_write] table. To revert, remove the added keys.

The agent's process cwd is still the gateway's directory, not the vault, so it must use absolute paths under your vault — the plugin's working-folder instructions already tell it to, and to treat that folder as "the current directory".

Upstream nicety: if the gateway's /v1/runs ever accepts a per-run cwd, the agent's sandbox could be rooted at the vault automatically — this plugin already sends cwd on every run, so it would work the moment the gateway honors it.

No-config alternative: use the current note / selection toggles (with Include full note content on) or paste the notes into the message — the agent then works from the attached content.

Click Test connection to verify reachability, the chosen transport, and the model list.

Use

  • Ribbon bot icon or command Hermes Agent: Open chat view opens the sidebar.
  • Type a message and press Enter (Shift+Enter for newline).
  • Context toggles above the input attach the current note and/or selection to the message.
  • Commands:
    • Open chat view
    • New chat tab
    • Send current note to Hermes
    • Send selection to Hermes
  • The + button adds a tab; each tab is an independent conversation. The stop (square) button cancels the in-flight reply.

Chat history

The plugin remembers your past conversations across reloads and restarts. Click the history (clock) icon in the panel header to open a list of saved chats — each shows a title (from the first message), how long ago it was, and its message count. Click a row to reopen it in a tab; the conversation is fully restored, including the gateway session id, so you can keep going with the agent's server-side context intact. The trash icon on a row deletes that conversation.

History is saved automatically after each reply (and after errors) to a separate history.json in the plugin folder (.obsidian/plugins/hermes-agent/history.json) — not in data.json, so your API key and settings stay isolated. The newest 100 conversations are kept. This is the plugin's own record; it is independent of the gateway's state.db session store.

Greeting

An empty chat shows a greeting. Set Your name in settings to personalize it (e.g. "What's new, Jason?"); leave it empty for a neutral "How can I help you today?". The greeting disappears once you send the first message.

Chat footer (meta bar)

Below the input, a Claudian-style meta bar shows, left to right:

  • Model — the real underlying model id (e.g. gpt-5.5). The gateway API only ever advertises the hermes-agent meta-label (or the profile name), so the plugin reads the real model from your local Hermes config.yaml (model.default). Resolution order: an explicit Model setting, then config.yaml, then the gateway's advertised label. Point Hermes home at the folder containing config.yaml if auto-detect ($HERMES_HOME, then ~/.hermes) can't find it — for the portable build that's the hermes-data/hermes folder next to the exe. Click to open settings.
  • Thinking: — the reasoning effort. Click to pick one (default/minimal/low/medium/ high/xhigh) from a menu, applied immediately.
  • Context gauge — a donut + percentage showing how full the model's context window is. The percentage is the latest turn's prompt tokens divided by the model's context window (e.g. 272k for gpt-5.5, read from context_length_cache.yaml in the Hermes home when available). It appears once a reply reports usage, and grows as the conversation lengthens.
  • Folder chip — the agent's working folder. Click to open a native folder picker; the chip dims when auto-approve is off (read-only).

Smart graph

An agent-built relationship graph of your vault. Unlike Obsidian's native Graph View — which only knows explicit [[wikilinks]] — this asks Hermes to read a summary of your notes and surface semantic connections: shared topics, one note elaborating another, prerequisites/dependencies, and contradictions.

  • Open it from the ribbon git-fork icon or the command Open smart graph (it opens in a main editor tab, not the sidebar — a graph wants room).
  • Click Analyze vault (or the command Analyze vault for smart graph). The plugin gathers your notes (title + a short excerpt + existing wikilinks), sends them to Hermes in a single call, and renders the returned relationships as an interactive force-directed graph.
  • Interact: drag a node to reposition it, drag the background to pan, scroll to zoom, hover a node to see its relationship labels, and click a node to open that note in a new tab. The fit button reframes the whole graph.
  • Semantic edges (inferred by Hermes) are drawn in the accent color and weighted by strength; muted edges are explicit wikilinks (toggle them off in settings). Nodes are tinted by topic group when Hermes returns one.
  • Settings (under Smart graph): Max notes to analyze (caps prompt size; large vaults keep the most-linked notes), Minimum edge strength (hide weak inferred links), and Show wikilink edges.
  • The last analysis is cached to graph-cache.json in the plugin folder (not data.json), so reopening the view shows your previous graph instead of a blank canvas. Re-run Analyze vault to refresh it. Like the chat, this needs the gateway reachable and a model configured.

Mobile (iPad, iPhone, Android)

The plugin also runs in Obsidian Mobile. A phone or tablet cannot run Hermes itself, so it talks to a gateway on another machine you can reach — typically your desktop or a home server over a private network such as Tailscale. Desktop behaviour is unchanged.

What differs on mobile:

  • Network: requests use fetch and replies still stream token by token. The gateway must allow the Obsidian app origin via CORS, e.g. in the gateway's .env: API_SERVER_CORS_ORIGINS=app://obsidian.md,capacitor://localhost,http://localhost (capacitor://localhost is iOS/iPadOS, http://localhost is Android). Without CORS the plugin falls back to Obsidian's requestUrl, which works but delivers the reply in one piece.
  • HTTPS: iOS blocks plain http:// to other hosts, so serve the gateway over HTTPS (e.g. tailscale serve --https=8443 http://127.0.0.1:8642) and use that URL as Gateway base URL.
  • Profile: for a gateway that serves several Hermes profiles, set Profile (requests go to /p/<profile>/) together with that profile's own API key.
  • Working folder: the vault path on the device means nothing to a remote gateway. Set Working folder to the absolute path where the gateway sees the (synced) vault, or leave it empty and let the profile's own working directory apply.
  • Approvals: with Auto-approve tool requests off, each tool call opens a dialog (allow once / for this chat / always / deny). This works on desktop too.
  • Reading the local Hermes config.yaml for the model footer is desktop-only; on mobile the footer uses the gateway's /v1/models.

Manual test checklist

  1. With Hermes Desktop running, open settings and click Test connection -> expect "Connected. Transport: runs|chat. N model(s) available."
  2. Open the panel, send "hello" -> a streamed reply appears under "Hermes".
  3. Select text in a note, run Send selection to Hermes -> the reply references the selection.
  4. Stop a long reply mid-stream with the square button -> streaming halts.
  5. With Hermes Desktop closed, send a message -> a clear error explains the gateway is unreachable.

Troubleshooting

  • "Cannot reach the gateway" — start the gateway: launch Hermes Desktop (it auto-starts it), or on a CLI/TUI install run hermes gateway. Running only the TUI is not enough — the TUI is a chat REPL, not the HTTP gateway (see Prerequisites - Option B).
  • Auth failed (401/403) — re-paste API_SERVER_KEY; it must match the active profile's key.
  • Empty/!200 from /v1/models — older gateways may not expose it; the chat still works. Set the model id manually.
  • Wrong port — named profiles bind 8643-8742; check platforms.api_server.extra.port in ~/.hermes/config.yaml and set the base URL accordingly.
  • Connection refused / timeout to a remote gateway — a listener, bind-address, firewall or Tailscale-policy problem, not an auth problem. Check the listener first (ss -ltnp 'sport = :8642'): a gateway bound to 127.0.0.1 is not remotely reachable. See Remote gateway above.
  • Model menu in the chat footer shows an error notice — the gateway's /v1/models request failed or timed out; the notice and the developer console ([hermes-agent] listModels failed) carry the reason. "Auto-detect" stays selectable.

About

Hermes Agent for Obsidian — fork with mobile support (iPad/iPhone/Android), remote multi-profile gateways and an in-plugin approval dialog. Desktop unchanged. MIT.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages