Hermes yesterday, October 27, 2026: the docs recipe signed dashboard logins with a public key
What landed on Hermes Agent main on October 27, 2026: a dashboard signing secret from the docs that was a public string, Desktop messages kept across a close, in-app updates that stop failing and replies shown once.

Table of contents
- 1. The dashboard secret from the docs recipe was a public string
- 2. The Desktop stops losing what you typed
- 3. Desktop updates stop failing on a long list of setups
- 4. Replies stop showing up twice
- 5. A round of security fixes: URL checks, A2A, Feishu and the API server
- 6. The backend stops freezing under load
- 7. Linux Desktop: no blank windows, no false software rendering, sudo survives restarts
- 8. Desktop and Webapp settings now belong to the machine
- 9. The Webapp works on plain HTTP and loads your plugins
- 10. 3 new plugins in the catalog: backups, video editing and signed tool calls
- Meanwhile, in the engine room
- Watch the recap
If you set up a remote Hermes dashboard by copying the docs recipe, your login sessions may have been signed with a key anyone can read in those same docs. Main now ignores that key, but one line in ~/.hermes/.env is yours to fix. Also: the Desktop stops losing a message sent right before you close it, in-app updates stop failing for a long list of reasons, and replies stop showing up twice. 408 commits landed; here are the 10 that matter.
1. The dashboard secret from the docs recipe was a public string
Before: the remote-backend recipes in the Desktop and web dashboard docs appended HERMES_DASHBOARD_BASIC_AUTH_SECRET=$(openssl rand -base64 32) inside a quoted heredoc, so the shell never ran the command and .env got the literal text. The basic sign-in provider used that 26-character string as the key that signs session tokens, so every install that followed the recipe shared one key, and anyone who knows it can sign a session the dashboard accepts. After: the recipes generate a real secret, and Hermes ignores a secret holding unexpanded shell text ($(, a backtick or openssl rand) with an error and a one-time line on the console, then uses a random key per process, as with no secret set. Heads-up: you have to act, and you do not need to wait for the release. Run grep BASIC_AUTH_SECRET ~/.hermes/.env, replace a literal line with a freshly generated secret, and expect existing sessions to be signed out. Until then, on the new code, logins still work but end at every restart.
Pull request: Dashboard signing secret.
2. The Desktop stops losing what you typed
Before: pressing Enter and closing or reloading the Desktop within about 50 ms lost the message: it never reached the backend, and the emptied composer was what got saved. A draft started after the system clock jumped back could be evicted on its own save, and going back to an older Desktop and upgrading again reverted what you typed there, even bringing back a message you had already sent. A message queued behind a turn that a gateway crash lost stayed hidden after a relaunch that opened another chat, never ran, and the chat’s row said Finished, unread. After: each send is written to a local journal at Enter. On the next load, a send the backend never got comes back as the draft for you to send (never automatically), and one that landed is dropped. Drafts use one clock across windows, and edits made in an older build are adopted. After a restart the app re-reads paused queues, so the queued message shows up with its Discard card and never runs on its own. Limit: a restored message comes back as text only, without its attachments (a notice says so).
Pull requests: Send right before closing, Drafts across clock jumps and downgrades, Queued message after a crash.
3. Desktop updates stop failing on a long list of setups
Before: a large state.db timed out the 30 s pre-flight backup (a 431 MiB database failed and froze the app for 30 s in the PR’s test), a comma-decimal locale such as Italian or GNU date first on the macOS PATH stopped the hand-off before the update ran, a proxy set only in the macOS, Windows or GNOME settings was ignored by the update’s git and Python, a GitHub token refused with 403 broke the update check, and a stable update still named the previous release. On Windows, a tools folder held by a running Hermes, a gateway PID file left behind by --replace, or another program sitting inside the Desktop’s folder failed every update. And every update rebuilt the Desktop app (6 to 38 min on reporters’ Windows hosts), even after a Python-only change. After: the pre-flight scales its deadline with the database and keeps the window responsive (1.1 s in that test), the hand-off works under any locale and date, update children use the system proxy when no proxy variable is set (one you set in .env still wins), a refused token falls back to anonymous, and the release is named right. Windows keeps an identical held folder live, drops the stale PID file, and names the program holding the folder, closing it only with your consent. The rebuild is skipped when none of the Desktop’s build inputs changed, the update window names the stage it is in, and About shows this device’s version beside the connected backend’s. Limit: a SOCKS system proxy is not passed on.
Pull requests: state.db, locales, proxy, versions, Windows update failures, Rebuild skipped, stage names.
4. Replies stop showing up twice
Before: the Desktop could draw a reply twice after a reconnect, across a reload, in chats longer than 120 rows, or when you stopped a turn. On the backend side, every plain reply was also re-sent as reasoning, so it showed again in the Thought block (cut at 500 characters), and a message restored from storage or carried through compaction could be stored a second time. A chat whose first resume after a reload failed stayed on Resume failed with no reply. After: streamed segments carry their turn identity and the app updates the row instead of painting it again, stopped turns keep their stopped mark, /btw and /bg notices stay in their own turn, the backend stops writing and sending those second copies, and a failed resume retries and shows the running turn’s reply. Limits: a stream the model retries after partial output still paints as new text by design, and a socket drop mid-answer can still, rarely, settle a reply as its first chunk (1 of 31 test runs, against 1 of 10 before).
Pull requests: Desktop draws each reply once, Backend stops second copies, Failed resume retries, Reply re-read after a reconnect, Re-attach keeps one bubble.
5. A round of security fixes: URL checks, A2A, Feishu and the API server
Before: the URL safety check read the host with Python’s parser, while the browser tool and Node-based fetchers (such as a self-hosted Firecrawl) can read a different host from the same string, so a string with a local address before a backslash and a public one after it passed as public and a backslash form could reach the cloud metadata address. With no A2A token set, a web page open in your browser could reach the local A2A listener and hand it work. On Feishu with no Feishu-level admins or allowlist (what pairing-mode setup writes), any chat member could press Allow on a command approval meant for someone else. And an API key caller of POST /v1/runs could send its own room execution policy and replace the operator’s toolsets, approval mode and turn limit for that run. After: URLs that parsers could read differently are refused, the no-token A2A listener refuses browser-originated requests and foreign Host headers, Feishu clicks go through the gateway’s own allowlist, and a room policy without a room grant is refused with 403. Not covered yet: the URL scan in browser_exec and the local browser_cdp tool still lack this check.
Pull requests: URL parser mismatch, A2A no-token mode, Feishu approval clicks, API server room policy.
6. The backend stops freezing under load
Before: the gateway behind the Desktop did blocking work on its event loop: it opened every profile’s state.db at boot (about 1 s each), started a new thread for every task, made session reads wait up to 5 s on the token writer, and took file locks at the end of each turn, so with many sessions open everything stalled together. The sidebar’s session list also built every stored session before keeping one page; a user with about 35,000 cron runs reported a 121 s refresh that the Desktop gave up on. After: that work runs off the loop or skips locks it did not need (worst loop lag 1.26 s to 0.68 s in a loaded test with 11 profiles and 33 sessions, still above the 250 ms target), the dashboard’s status, file and stats routes read off the loop too, and the list ranks the page first and builds only its rows (a warm sidebar refresh went from 1,090 ms to 320 ms on a 36,000-session test store).
Pull requests: Event loop blockers, Sidebar page first.
7. Linux Desktop: no blank windows, no false software rendering, sudo survives restarts
Before: on Linux, the Desktop could open a blank window and loop crashing renderers with a /dev/shm error that chmod does not fix, whenever --no-sandbox was requested at runtime (the boot fallback or desktop.electron_flags). On Ubuntu with AppArmor’s user-namespace restriction, hermes desktop could abort with No usable sandbox. A stray second launch, or a GPU crash after a healthy boot, pinned the app to software rendering (4 to 9 CPU cores) or to --no-sandbox for good. And after the Desktop restarted itself, sudo stopped working in the agent’s terminal and an AppImage did not come back. After: the app relaunches with --no-sandbox on its command line, asks the Hermes binary whether Chromium’s sandbox can start, ties its fallback markers to the launch that wrote them and lets them expire on the next rebuild, update or driver change, and restarts keep sudo and the AppImage. Limit: a single hard abort during an NVIDIA boot still turns on software rendering for that build and driver.
Pull requests: /dev/shm crash, AppArmor sandbox probe, False GPU and sandbox fallbacks, Restarts keep sudo.
8. Desktop and Webapp settings now belong to the machine
Before: display preferences lived in each browser’s local storage, so every device, private window and Webapp address (127.0.0.1, the LAN IP, the public URL) and the native app had its own set. After: they are stored once, in ui-preferences.json in the root Hermes home of the machine that runs the app, and shared by the native Desktop and every browser using its Webapp: theme, density, keybindings, tips and onboarding included. Changes reach other native windows in about 2 s and Webapp tabs on focus or within 10 s, and the first run after the upgrade imports each app’s own values without overwriting anything. A Desktop connected to a remote gateway keeps its own machine’s store.
Pull request: Settings per machine.
9. The Webapp works on plain HTTP and loads your plugins
Before: in the Webapp (off by default) on a plain-HTTP network address, Send cleared the draft and sent nothing, copy buttons failed and the bundled Radio plugin did not start; right-click Cut, Copy and Paste did nothing in text fields; a rebuild broke tabs already open; desktop plugins you installed yourself never loaded in a browser tab, and nothing said so; and the Docker image and the Nix package shipped no Webapp at all. After: those work on plain HTTP, text fields get the browser’s own edit menu, open tabs keep working across a rebuild, your desktop plugins load in the browser, a blocked pop-out shows the allow-popups notice within 3 s, and Docker and Nix ship the Webapp ready to serve. Not yet: hot-reloading an edited plugin in a browser tab is still in review.
Pull requests: Plain HTTP, edit menus, rebuilds, Desktop plugins in a browser tab, Blocked pop-out notice, Webapp in Docker, Webapp in Nix, Plugin hot-reload, in review.
10. 3 new plugins in the catalog: backups, video editing and signed tool calls
3 new community entries: BackupDrill backs up a Supabase project to your own S3, R2 or B2 bucket and test-restores it into a throwaway Postgres in Docker; Kinocut edits video locally with FFmpeg (trim, caption, reformat, quality checks) through its MCP server and two skills; and Tracekit decides every tool call before it runs and signs each call and its outcome into a run you can verify offline. Heads-up: Tracekit sends every tool call’s name and arguments to its signer (a local one by default) and fails closed, so when the signer is unreachable every tool call is blocked. Catalog entries do not wait for a release: they reach installs through the published catalog.
- Backups: backupdrill
- Video: kinocut
- Agent safety: tracekit
Meanwhile, in the engine room
Dozens of fixes landed yesterday that most users never see, and the Desktop took most of them: sessions, drafts, clarify cards and restarts.
- In the Docker image, every dashboard action that starts a Hermes child process (Skills, update, doctor) failed with a no dependency environment error; Hermes now finds the image’s own payload manifest (pull request)
- A rotated API key, a refreshed token or a same-size
config.yamledit written in the same filesystem timestamp tick as the last read could be served stale from a cache until a restart; those caches now check the content (pull request) - ClawHub skills installed from the Desktop Skills catalog kept showing + as if not installed, and the agent’s
manage_catalogtool reported the finished install as a failure; they now read as installed (pull request) - After Hermes restarts mid-turn, the CLI’s paused-chat notice now says your message was not sent, quotes the turn that never finished and prints the exact
/discardcommand to continue (pull request)
If you noticed none of this, it worked.
All on main, ships in the next release; the three catalog entries already reach installs through the published catalog (Desktop app changes reach the Microsoft Store app with the release and the .exe and .dmg app with its next bundled release). Latest stable: v0.22.5.
Watch the recap
The 56-second motion recap, made with Remotion, is on X.
Was this configuration guide helpful?
If you have questions, encountered issues during setup, or want to discuss networking and security, connect or reach out on LinkedIn.
Connect on LinkedIn