# Determining whether a Hermes session is local or remote — worked example

Session: 2026-08-06, user asked "check and see if Desktop is running local or remote."

## The probe sequence that worked

Three parallel terminal calls from the agent's own shell:

```bash
# Call 1: identity + processes + listeners
hostname; uname -a
ps -eo pid,etime,cmd | grep -E '[h]ermes' | head -20
systemctl is-active hermes-gateway
ss -tlnp | grep -E ':(9119|7777|7778)'
```

```bash
# Call 2: live client connections
ss -tn state established '( sport = :9119 or sport = :7777 or sport = :7778 )'
ss -tn state established | grep -v '127.0.0.1' | head -20   # non-loopback peers
```

```bash
# Call 3: client history from the WS log
tail -50 /root/.hermes/logs/gui.log | grep -iE 'ws closed|connect' | tail -20
```

## What the evidence looked like (remote session)

- `hostname` → `robshermes` (the VPS, not the user's laptop `Connie`) — decisive on its own.
- Dashboard process: `hermes dashboard --port 9119 --host 127.0.0.1 --skip-build` (PID 137273), bound loopback only.
- Loopback-only bind means external clients arrive via nginx 443 → the real client shows up as a non-loopback peer on **:443**, not :9119:
  `2.25.172.164:443 ← 138.74.197.93:57517` (established).
- The one `127.0.0.1:9119 ↔ 127.0.0.1:40958` established pair is the nginx→dashboard hop, NOT the client. Don't mistake it for a local user.
- Re-running the `ss` check 3s apart showed the same :443 entry persisting → real long-lived websocket, not a request burst.
- `gui.log` `ws closed peer=127.0.0.1:42680 ... 2026-08-06 00:52:51` — note the peer is logged as loopback for the same proxy reason; timestamps are still useful for "previous session ended N minutes ago".

## Conclusion template

"Running on the VPS (host `<hostname>`). Desktop on `<laptop>` holds a live websocket in through nginx :443 → dashboard :9119. Everything in this conversation executes on the VPS. Closing the laptop doesn't kill the session; losing internet does."

## Pitfalls specific to this diagnostic

- **Loopback-bound dashboard hides the client IP on :9119.** Always check :443 (or whichever proxy port) for the real peer when `--host 127.0.0.1` + reverse proxy is in play.
- **Don't trust `gateway_state.json`** for this question — it self-reports `running` even with no process alive (see SKILL.md Pitfalls).
- **Multiple `Hermes.exe` processes on the Windows side are normal Electron** — their existence proves nothing about where the session executes. The answer always comes from the server side's socket table.
- A non-loopback established socket to an unrelated port (e.g. `:22000` Syncthing) is not a client connection — filter to the dashboard/proxy ports.
