# Hermes Desktop Connection Configuration

## File Location

```
~/.config/Hermes/connection.json    (Linux/macOS)
%APPDATA%/Hermes/connection.json    (Windows)
```

The desktop app creates this file on first launch. It can also be written manually.

## Config Shape

```json
{
  "mode": "local",
  "remote": {
    "url": "http://192.168.1.50:9119",
    "authMode": "oauth",
    "token": {
      "encoding": "plain",
      "value": ""
    }
  },
  "profiles": {}
}
```

## Fields

| Field | Values | Meaning |
|-------|--------|---------|
| `mode` | `"local"` / `"remote"` | `local` = spawn Hermes backend on this machine; `remote` = connect to a running gateway |
| `remote.url` | URL string | Dashboard (:9119) or Gateway (:18789) endpoint. **Dashboard** has the web UI + auth; **Gateway** is the API endpoint the desktop talks to |
| `remote.authMode` | `"oauth"` / `"token"` | Auth protocol. `"none"` is NOT a valid value — falls back to `"token"` default. `"oauth"` shows a sign-in button; `"token"` shows a token input field |
| `remote.token.value` | string | Bearer token for `authMode: "token"` |
| `remote.token.encoding` | `"plain"` | Only plain is supported |

## Auth Probe Flow — How the Desktop Decides Sign-in vs Token

When you enter a remote URL in Settings → Gateway, the desktop probes it immediately:

1. **Probe endpoint:** `GET <remoteUrl>/api/status`
2. **Response shape:** `{ auth_required: bool, auth_providers: string[], version: string, ... }`
3. **Decision logic:**

| Probe Result | Desktop Shows |
|---|---|
| `auth_required: true` + `auth_providers: ["basic"]` | **Sign In** button (opens webview to dashboard login page) |
| `auth_required: true` + `auth_providers: ["nous"]` | **Sign In** button (OAuth redirect) |
| `auth_required: false` (any providers) | **Paste session token** field |
| Probe fails / unreachable | **Paste session token** field (falls back to saved `remoteAuthMode`) |
| Probe returns `authMode: "unknown"` | Falls back to saved `remoteAuthMode` |

**Key insight:** The `/api/status` endpoint is always public (no auth required). But when the dashboard runs with `--insecure` on a non-loopback bind, it reports `auth_required: false` even when `basic_auth` provider IS registered. This causes the desktop to show the token field instead of the sign-in button.

## Fix: "Paste Session Token" When You Want Login

**Symptom:** Desktop shows "Paste session token" instead of a "Sign in" button.

**Root cause:** The dashboard runs with `--insecure` flag → reports `auth_required: false` → desktop falls to token mode.

**Fix — remove `--insecure` from the dashboard:**

1. Edit the systemd service:
   ```bash
   # ~/.config/systemd/user/hermes-dashboard.service
   # Remove --insecure from ExecStart:
   ExecStart=... python -m hermes_cli.main dashboard --port 9119 --host 0.0.0.0
   ```

2. Restart dashboard:
   ```bash
   systemctl --user daemon-reload
   systemctl --user restart hermes-dashboard.service
   ```
   (If blocked by "cannot restart gateway from inside gateway process", use `systemctl --user restart` via Python subprocess or a separate terminal.)

3. Verify:
   ```bash
   curl -s http://<host>:9119/api/status | grep auth_required
   # Should now show: "auth_required": true,
   ```

4. Restart Hermes Desktop → Settings → Gateway → URL should show **Sign in** button

## Dashboard Auth Provider (Basic Auth)

The dashboard supports basic auth (username/password) via a plugin at `plugins/dashboard_auth/basic/`.

**Configuration** (via environment variables, loaded from `~/.hermes/.env`):

```
HERMES_DASHBOARD_BASIC_AUTH_USERNAME=<username>
HERMES_DASHBOARD_BASIC_AUTH_PASSWORD=<password>
```

The systemd service reads these via `EnvironmentFile=%h/.hermes/.env`.

**Config.yaml equivalent** (lower priority than env vars):

```yaml
dashboard:
  basic_auth:
    username: <username>
    password: <password>
    # OR use precomputed scrypt hash:
    password_hash: "scrypt$..."
    session_ttl_seconds: 43200  # default 12h
```

**Login page:** Visit `http://<host>:9119/login` — renders username/password form.

**Password login endpoint:** `POST /auth/password-login` with `{ provider: "basic", username, password }`.

## Gateway vs Dashboard — Which Port to Use

| Port | Process | Binds To | Purpose |
|------|---------|----------|---------|
| 9119 | Dashboard | 0.0.0.0 by default | Web UI + auth + session management |
| 18789 | Gateway | **127.0.0.1 only** | API endpoint for agent communication |

The desktop should connect to the **Dashboard (port 9119)** because that's the endpoint with auth support. The Gateway (port 18789) has no `--host` flag and is hardcoded to 127.0.0.1. To make it externally accessible:

```bash
# Install socat and forward from 0.0.0.0 to localhost:
sudo apt install socat
socat TCP-LISTEN:18788,bind=0.0.0.0,reuseaddr,fork TCP:127.0.0.1:18789

# Or install as systemd service:
cat > /etc/systemd/system/hermes-gateway-proxy.service << 'EOF'
[Unit]
Description=Hermes Gateway Proxy (socat -> localhost)
After=network-online.target

[Service]
Type=simple
ExecStart=/usr/bin/socat TCP-LISTEN:18788,bind=0.0.0.0,reuseaddr,fork TCP:127.0.0.1:18789
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target
EOF
```

## How the Logged-In Web Session Works

1. User visits `http://<host>:9119/login` in browser
2. Enters username/password → POST to `/auth/password-login`
3. Server sets session cookies (HMAC-signed, stateless)
4. Dashboard redirects to `/` — SPA loads, reads session token from HTML
5. All API requests include `X-Hermes-Session-Token` header (in `--insecure` mode) or session cookie (in gated mode)

## Connection File Path on CachyOS / Arch Linux

```
~/.config/Hermes/connection.json
```

The desktop binary is at `~/.local/bin/hermes-desktop`, which is a shell script that execs `$HOME/.hermes/hermes-agent/apps/desktop/release/linux-unpacked/Hermes`.
