---
name: hermes-vps-desktop-setup
description: Set up, verify, or repair Christian's Hermes Desktop connection to the Hostinger Hermes Agent VPS through Tailscale. Use when the user asks about Hermes Desktop, Hermes Agent dashboard login, VPS gateway connectivity, Tailscale setup, Codex/OpenAI OAuth inside Hermes, or replacing the temporary SSH tunnel with the default tailnet gateway.
---

# Hermes VPS Desktop Setup

## Core Facts

- Hermes Desktop app: `/Users/christianreyes/.hermes/hermes-agent/apps/desktop/release/mac-arm64/Hermes.app`
- Tailscale Mac hostname: `christians-macbook-pro`
- Hermes VPS tailnet hostname: `hermes-hostinger-1807396`
- Hermes VPS tailnet IP: `100.67.8.114`
- Preferred Hermes Desktop remote gateway URL: `http://hermes-hostinger-1807396:9119`
- Temporary SSH tunnel URL, only for fallback: `http://127.0.0.1:9119`
- VPS private backend behind tunnel: `127.0.0.1:9120`
- SSH key for fallback tunnel: `/Users/christianreyes/.ssh/hostinger-hermes-agent-1807396`
- Dashboard username: `hermes`
- Dashboard password location: macOS Keychain service `hostinger-hermes-agent-dashboard`, account `hermes`

Never paste the dashboard password into chat. Retrieve it only when needed, preferably after explicit user approval, with:

```bash
security find-generic-password -s hostinger-hermes-agent-dashboard -a hermes -w
```

## Adaptive Production Operator

For any production install, MCP repair, or packaged-command proof, read
[`references/adaptive-production-operator.md`](references/adaptive-production-operator.md)
in full before the first mutation. The AI owns observation, diagnosis, action
selection, and adaptation. Use scripts only for deterministic safety primitives
and independent verification. Never repeat an unchanged failed strategy.

## What Working Means

Hermes Desktop is correctly set up when:

- Tailscale shows the Mac and VPS online.
- `curl http://hermes-hostinger-1807396:9119/api/status` returns Hermes JSON.
- Hermes Desktop Settings -> Gateway uses `http://hermes-hostinger-1807396:9119`.
- Hermes Desktop shows `Gateway ready` and `backend v0.18.0`.
- The right sidebar file tree shows `/opt/hermes`, confirming the app is on the VPS backend.
- Sellable Admin and Sellable slash commands appear in Desktop autocomplete for
  `/sellable` and `/sellable-admin`, not only in Hermes Agent OS chat.
- No local listener is required on `127.0.0.1:9119`.

Use these checks:

```bash
tailscale status
tailscale status --json | jq -r '.Self.Online as $self | .Peer[] | select(.HostName=="hermes-hostinger-1807396") | "self_online=\($self) vps_online=\(.Online) vps_host=\(.HostName) vps_tailscale_ip=\(.TailscaleIPs[0])"'
curl -sS -m 5 http://hermes-hostinger-1807396:9119/api/status
lsof -nP -iTCP:9119 -sTCP:LISTEN || true
```

Expected healthy signals:

- `self_online=true`
- `vps_online=true`
- `hermes-hostinger-1807396` active in `tailscale status`
- Hermes API reports version `0.18.0` or newer.
- `lsof` for local port `9119` is empty unless intentionally using fallback.

## Setup Workflow

1. Install Tailscale on the Mac if absent.

```bash
brew install --cask tailscale-app
open -a Tailscale
```

The installer or app may require the user to approve macOS admin, VPN, or network-extension prompts. Do not enter the user's Mac admin password.

2. Sign in to the existing Tailscale tailnet.

```bash
tailscale login
```

Open the generated login URL if needed. The expected tailnet/admin context is `dittto.ai`. Use the user's chosen identity provider; do not guess private credentials.

3. Verify the Mac and VPS are connected.

```bash
tailscale status
curl -sS -m 5 http://hermes-hostinger-1807396:9119/api/status
```

4. Open Hermes Desktop and set the gateway.

Use Computer Use when available:

- Open Hermes Desktop.
- Open Settings -> Gateway.
- Select Remote gateway.
- Set Remote URL to `http://hermes-hostinger-1807396:9119`.
- If the previous URL was `http://127.0.0.1:9119`, sign out and sign in again so the session is scoped to the tailnet host.
- Sign in to the Hermes Agent dashboard with username `hermes` and the Keychain-stored password.
- Click Test remote and require: `Connected to http://hermes-hostinger-1807396:9119 · Hermes ...`.
- Click Save for next restart.

5. Verify the provider.

In Hermes Desktop, Providers/Accounts should show OpenAI OAuth/ChatGPT connected, and the model picker should offer/select `openai-codex: gpt-5.6-sol` with high reasoning or the current Codex model.

If OpenAI OAuth fails with `deviceauth/usercode returned 429`, stop retrying from multiple windows. Wait 10-15 minutes, then retry once. This is an OpenAI device-code rate limit, not a Tailscale or VPS failure.

## SSH Tunnel Fallback

Use this only when Tailscale is not installed, not signed in, or temporarily unavailable:

```bash
ssh -f -N \
  -i /Users/christianreyes/.ssh/hostinger-hermes-agent-1807396 \
  -o BatchMode=yes \
  -o ExitOnForwardFailure=yes \
  -L 9119:127.0.0.1:9120 \
  root@srv1807396.hstgr.cloud
```

Then set Hermes Desktop Remote URL to:

```text
http://127.0.0.1:9119
```

When Tailscale is working again, switch Hermes Desktop back to `http://hermes-hostinger-1807396:9119`, sign in again, save for restart, and stop the fallback listener:

```bash
lsof -tiTCP:9119 -sTCP:LISTEN | xargs -r kill
```

## Troubleshooting

- `tailscale status` says logged out: run `tailscale login`.
- Tailnet host does not resolve: ensure Tailscale is online and MagicDNS is enabled for the tailnet.
- `curl http://hermes-hostinger-1807396:9119/api/status` fails but `tailscale status` is healthy: inspect the VPS Hermes container/service.
- Hermes says remote gateway session expired: Settings -> Gateway -> Sign in, then use `hermes` and the Keychain-stored password.
- Hermes test says it cannot mint a WebSocket ticket after switching from localhost to tailnet: sign out of Gateway, set the tailnet URL again, sign in again, test remote, and save for restart.
- Hermes shows local files instead of `/opt/hermes`: it is on the local gateway; switch to Remote gateway.
- Sellable Admin MCP works but `/sellable` shows `No matches`: rerun
  `@sellable/admin-install@latest` Hermes bootstrap and confirm
  `/opt/data/config.yaml` contains `skills.external_dirs` pointing at
  `/opt/data/profiles/sellable-admin/skills/sellable-admin`. Desktop command
  discovery scans the gateway/root skill config, not just the selected profile.
- Hermes Agent OS chat shows `/sellable-*` commands but Hermes Desktop still
  says `No matches`: inspect the dashboard process. The broken signature is
  `-p default dashboard --open-profile sellable-admin`; it preselects the
  profile in the UI but can leave Desktop completion on the default-scoped
  dashboard path. Persist the dashboard launch as
  `hermes -p sellable-admin dashboard --isolated --host 0.0.0.0 --port 4860 --no-open --skip-build`.
- On the Hostinger VPS, the durable fix is a compose entrypoint override:
  `/docker/hermes-agent-mc30/docker-compose.yml` points to
  `/opt/data/bin/entrypoint-sellable-admin.sh`. The Admin installer writes this
  wrapper during Hermes bootstrap; it starts
  `hermes -p sellable-admin gateway run`, then execs the isolated Sellable Admin
  dashboard. The `-p sellable-admin` part is required for profile-local Slack
  env such as `SLACK_BOT_TOKEN`, `SLACK_APP_TOKEN`, and
  `SLACK_ALLOWED_CHANNELS`.
- For Hermes skill content, use registered MCP aliases shaped like
  `mcp_sellable_admin_<tool>`. Hyphenated server names are normalized to
  underscores by Hermes tool registration; verify the live registry instead of
  guessing the generated alias.

Command-discovery verification:

```bash
docker exec -i -u hermes hermes-agent-mc30-hermes-agent-1 /opt/hermes/.venv/bin/python - <<'PY'
from prompt_toolkit.document import Document
from hermes_cli.commands import SlashCommandCompleter
from agent.skill_commands import get_skill_commands
from agent.skill_bundles import get_skill_bundles
completer = SlashCommandCompleter(
    skill_commands_provider=lambda: get_skill_commands(),
    skill_bundles_provider=lambda: get_skill_bundles(),
)
for prefix in ["/sell", "/sellable", "/sellable-admin"]:
    items = list(completer.get_completions(Document(prefix, len(prefix)), None))
    print(prefix, [item.text for item in items if "sellable-admin" in item.text][:12])
PY
```

Desktop/OS split verification:

```bash
ssh -i /Users/christianreyes/.ssh/hostinger-hermes-agent-1807396 root@srv1807396.hstgr.cloud \
  'cd /docker/hermes-agent-mc30 && docker exec -u root hermes-agent-mc30-hermes-agent-1 ps -efww | grep -E "dashboard|gateway run|sellable" | grep -v grep'
```

Expected dashboard process:

```text
hermes -p sellable-admin dashboard --isolated --host 0.0.0.0 --port 4860 --no-open --skip-build
```

## Security Rules

- Do not store the dashboard password in plaintext files or skill content.
- Do not paste secrets into chat.
- Do not send or create OpenAI/Tailscale credentials unless the user explicitly approves the login or OAuth action.
- Prefer Tailscale over exposing the dashboard publicly or keeping SSH tunnels running long term.
