Your tailnet is already running. It just wasn't in the sidebar.
Hermes Tailscale puts the machines on this device's tailnet on a page in Hermes Desktop. See who is online, copy an address, SSH in, send a file, or publish this Hermes onto the mesh. It talks to the Tailscale CLI you already installed. No admin API token. No cloud round trip.
POWERED BY HERMES AGENT · COMMUNITY PLUGIN · VERSION 0.0.3
See the roster · Install it · How it talks to Tailscale
Hermes Tailscale is a community-built roster for Hermes Desktop. It uses the Hermes plugin SDK, shell.exec, and the in-app PTY when Desktop exposes hermesDesktop.terminal. Same profile-aware environment you already use.
The page is whatever Tailscale the Hermes gateway can exec. On a normal desktop that's this laptop. A remote hermes serve shows that machine's tailnet instead.
Most Tailscale UIs live in a tray icon or a browser tab you forget to open. This one sits next to Sessions.
| See Name, OS, IPv4, and one status line. Online count in the status bar. Empty states if Tailscale is missing, stopped, or waiting for login. |
Copy IP, MagicDNS, or an ssh line. Palette commands for the page and your own address. |
| Reach Ping a peer. Open SSH in an xterm overlay. Type the username each time. It is not saved. |
Move Send a file with Taildrop and watch the bar fill. Pick an exit node. Switch accounts when more than one is logged in. Publish this Hermes with tailscale serve. You pick the local port; the plugin checks something is listening before it runs. |
Mutating actions ask first. Funnel stays off.
- Owner is hidden until you turn it on in Settings.
- Shared machines stay out of the list unless you ask for them.
- The page polls every 8 seconds while you are looking at it, and every 60 seconds otherwise.
- SSH uses the Desktop PTY and xterm. If this build has no terminal, the ssh line is copied instead.
- Taildrop send runs in a PTY so a large file is not killed by the 30 second
shell.execbudget. Progress, speed, and ETA show while it copies. - Windows OpenSSH often cannot resolve a MagicDNS short name, so plain ssh falls back to the Tailscale IPv4.
Hermes Tailscale is one uncompiled plugin.js with its own Tailscale row in the sidebar. It works on stock Hermes Desktop. There is no fork, upstream patch, separate backend, build step, or package manager.
It does not call the Tailscale cloud API. It does not dial LocalAPI from the renderer (named pipe / unix socket). The CLI is the door, the same way Resetwatch shells out.
Copy plugin.js to Hermes' desktop plugin directory:
~/.hermes/desktop-plugins/hermes-tailscale/plugin.js
On Windows:
%USERPROFILE%\.hermes\desktop-plugins\hermes-tailscale\plugin.js
If you use a named profile, the root is $HERMES_HOME/profiles/<name>/desktop-plugins/. The folder name must be hermes-tailscale.
Open Hermes and choose Tailscale in the sidebar. If it is missing, use Cmd+K (Ctrl+K on Windows) → Reload desktop plugins. Restart Hermes after replacing the file if an already-open page keeps the old plugin loaded.
You need the Tailscale client installed and logged in on the same machine that runs the Hermes gateway.
- Windows:
C:\Program Files\Tailscale\tailscale.exe, ortailscaleon PATH. - macOS:
tailscaleon PATH,/usr/local/bin/tailscale,/opt/homebrew/bin/tailscale, or the App Store binary at/Applications/Tailscale.app/Contents/MacOS/Tailscale(the plugin setsTAILSCALE_BE_CLI=1so that launch does not open the GUI). - Linux:
tailscaleon PATH,/usr/bin/tailscale,/usr/local/bin/tailscale, or/snap/bin/tailscale.
The same plugin.js file is both the source and the installable artifact.
The SSH overlay needs xterm. By default the plugin downloads @xterm/xterm@5.5.0/lib/xterm.js from jsDelivr (or unpkg) and only runs it if the SHA-384 matches the pin in plugin.js. To skip the network, put that same file next to the plugin:
~/.hermes/desktop-plugins/hermes-tailscale/xterm.js
Get it from the npm tarball or either CDN. The local copy is checked against the same hash, so a wrong or edited file is refused and the plugin moves on to the CDN.
The helpers that build shell commands, quote paths, and parse CLI output are covered by node:test. No dependencies, no install step:
node --test
CI runs the same command on Ubuntu and Windows for every push and pull request.
Your Hermes Desktop → Tailscale CLI on this machine → your tailnet
No Tailscale API key. No account token stored by the plugin. SSH usernames are asked per connection and not remembered.
The status cache. tailscale status --json is longer than the 4k shell.exec stdout cap, so the plugin writes it to status-cache.json next to plugin.js and reads it back. That file holds what tailscale status shows: device names, tailnet IPs, owners, tags, OS, and last-seen times. It is rewritten on every poll (8s on the page, 60s otherwise), created 0600 on macOS and Linux, relies on the user-only profile ACL on Windows, and is deleted when the plugin unloads or Hermes quits cleanly. If Hermes crashes it stays until the next run overwrites it. Delete it by hand any time; the plugin recreates it.
- Local CLI. Roster, ping, serve, exit node, account switch, and Taildrop all exec the installed client.
- Confirm before write. Serve, exit node, account switch, and file send ask first.
- Funnel off. Publish is
tailscale serve --bg --yes <port>, tailnet only. The port defaults to 9119, is editable in the confirm bar, and is remembered per profile. Before running serve the plugin checks that something answers on127.0.0.1:<port>(a renderer fetch, thencurl). If nothing does, serve is not run. If it cannot tell, it says so and asks again. - SSH overlay. The in-app terminal is xterm 5.5.0. The plugin fetches it once, hashes the bytes, and refuses to run anything that does not match the SHA-384 pinned in
plugin.js. It looks forxterm.jsnext toplugin.jsfirst, then jsDelivr, then unpkg. After that, keystrokes go to the local PTY.
Removing the plugin file does not log you out of Tailscale or delete Hermes sessions.
Hermes Tailscale uses the desktop plugin SDK and host.request('shell.exec'). SSH and Taildrop progress use window.hermesDesktop.terminal when this Desktop build has it.
| Platform | Tailscale CLI | PTY shell | Status |
|---|---|---|---|
| Windows 11 | Current stable (C:\Program Files\Tailscale\tailscale.exe or on PATH) |
PowerShell | Tested with each release |
| macOS | Current stable (Homebrew, standalone, or the App Store binary) | zsh | Same CLI door, POSIX quoting. Not yet tested by the maintainer |
| Linux | Current stable (/usr/bin, /usr/local/bin, or snap) |
bash or sh | Same CLI door, POSIX quoting. Not yet tested by the maintainer |
The CLI needs tailscale serve --bg (Tailscale 1.48 or newer). Hermes Desktop: any build that ships the desktop plugin SDK and shell.exec. SSH and Taildrop progress additionally need hermesDesktop.terminal; without it the ssh line is copied and file send runs under shell.exec with no progress bar.
Each tagged release lists the Hermes Desktop and Tailscale versions it was tested against. If you run it on a row marked "not yet tested" and it works, open an issue and say so.
- The serve port check only proves a listener exists. It does not prove that listener is Hermes.
- A remote gateway shows that machine's tailnet, not the laptop in front of you.
- Ping, serve, and other
shell.execcalls still have a 30 second cap. SSH and file send do not, because they use a PTY. - One Taildrop send at a time.
- The SSH overlay is xterm, not a full Desktop terminal app. Fine for a shell,
apt, and passwords. A poor place to live in tmux all day. - xterm is fetched at runtime unless you drop a copy next to
plugin.js. Offline first launch with no local copy falls back to a plain log.
The machines you already trust, in the app you already have open.
MIT.
Community project
Hermes Tailscale is an independent community plugin. It is not affiliated with, endorsed by, sponsored by, or officially associated with Nous Research, the Hermes Agent project, or Tailscale. Hermes, Hermes Agent, Nous Research, and Tailscale are names and marks belonging to their respective owners.