# Hermes Gateway

`hermes_gateway` connects Paperclip to a **remote Hermes API server** over HTTP or HTTPS. Use it when Hermes Agent is already running somewhere — another host, a cloud VM, or a separate process — and exposes its API server. Paperclip calls the Hermes REST/SSE API directly to create runs, stream events, and stop runs on timeout.

This is a first-class built-in adapter as of v2026.626.0. You configure it through the standard Paperclip agent config form, the same way you would configure `claude_local` or `codex_local`.

> For the older external Hermes agent invitation flow (pre-v2026.626.0), see [Bring Your Own Agent](../../how-to/bring-your-own-agent.md).

---

## When To Use

- Hermes Agent runs on a different host or inside a separate process and exposes its API server.
- You want to connect Paperclip to a cloud-hosted or team-shared Hermes instance.
- You need to call multiple Hermes servers from one Paperclip deployment.
- You are building a multi-machine setup where Hermes and Paperclip run independently.

## When Not To Use

- You want Paperclip to launch Hermes as a local child process. Use [`hermes_local`](./hermes.md) instead.
- The Hermes API server is not enabled on the target host.

---

## Config Fields

| Field | Required | Default | Notes |
|---|---:|---|---|
| `apiBaseUrl` | yes | — | Hermes API server base URL. Accepts the dashboard root or chat URL (such as `http://127.0.0.1:9119/chat`) and maps it to `/api`. Prefer HTTPS for non-loopback hosts. |
| `apiKey` | yes | — | The Hermes `API_SERVER_KEY`, **not** your `PAPERCLIP_API_KEY`. Stored as a Paperclip secret reference so it never appears in plain text. |
| `dangerouslyAllowInsecureRemoteHttp` | no | `false` | Unsafe dev-only escape hatch. Loopback HTTP (`127.0.0.1`, `localhost`) is always allowed; this toggle is only needed for non-loopback HTTP hosts. |
| `sessionKeyStrategy` | no | `"issue"` | Controls the `X-Hermes-Session-Key` header. Options: `issue` (Issue scoped), `agent` (Agent scoped), `run` (Run scoped), `none` (disabled). See [Session Key Strategy](#session-key-strategy) below. |
| `timeoutSec` | no | `600` | Run timeout in seconds. |
| `eventReconnectMs` | no | `2000` | Delay in milliseconds before reconnecting the Hermes SSE events stream after a non-terminal disconnect. |
| `paperclipApiUrl` | no | — | Optional Paperclip API URL reachable from the remote Hermes host. This is not a credential; it tells Hermes how to call back into Paperclip if needed. |
| `headers` | no | — | Optional JSON object of extra non-secret headers. Security-critical headers (such as the API key and session key) are generated by the adapter and cannot be overridden here. |
| `instructions` | no | — | Optional stable instructions sent to Hermes separately from the wake input, so they stay consistent across runs without bloating each task's prompt. |

---

## How It Works

When Paperclip triggers a run for a `hermes_gateway` agent, the adapter does the following:

1. **Creates a run** via `POST /v1/runs`. The request includes an `Idempotency-Key` equal to the Paperclip run id, which lets you correlate Hermes runs back to Paperclip records.
2. **Streams events** from `GET /v1/runs/{run_id}/events` (SSE). If the stream disconnects unexpectedly, the adapter waits `eventReconnectMs` and reconnects. It also polls `GET /v1/runs/{run_id}` as a fallback when the SSE stream is unavailable.
3. **Stops the run** with `POST /v1/runs/{run_id}/stop` if the timeout is reached before the run completes.

---

## Security

**API key storage.** The `apiKey` field maps to Hermes' `API_SERVER_KEY`. Paperclip stores it as a secret reference and never writes it to plain-text config. If the field shows that a secret is stored, do not repaste the key unless you are intentionally rotating it.

**HTTP vs HTTPS.** Loopback addresses (`127.0.0.1`, `localhost`) can use plain HTTP. Any other host should use HTTPS or a private overlay network (such as Tailscale or a VPN). If you need to connect to a non-loopback host over plain HTTP during development, enable `dangerouslyAllowInsecureRemoteHttp` — but disable it before production.

**Extra headers.** The `headers` field is for non-secret auxiliary headers only. The adapter generates the authentication and session headers itself; any attempt to include security-critical headers via `headers` will be ignored.

---

## Session Key Strategy

The `sessionKeyStrategy` field controls the `X-Hermes-Session-Key` header, which Hermes uses to group runs into a session and provide memory continuity between them.

| Strategy | `X-Hermes-Session-Key` scope | Best for |
|---|---|---|
| `issue` (default) | One session per Paperclip issue | Prevents cross-task memory bleed. Recommended for most teams. |
| `agent` | One session across all runs for this agent | Long-running agents where continuous memory is desirable. |
| `run` | A new session per individual run | Fully isolated runs with no shared memory. |
| `none` | Header not sent | Let Hermes decide session scoping on its own. |

The default `issue` strategy is the safest choice. It keeps each Paperclip issue's work in its own Hermes session so that one task's context does not leak into another.

---

## Example

```json
{
  "adapterType": "hermes_gateway",
  "adapterConfig": {
    "apiBaseUrl": "https://hermes.internal.example.com",
    "apiKey": {
      "type": "secret_ref",
      "secretId": "hermes-api-key",
      "version": "latest"
    },
    "sessionKeyStrategy": "issue",
    "timeoutSec": 600,
    "eventReconnectMs": 2000,
    "paperclipApiUrl": "https://paperclip.internal.example.com",
    "instructions": "You are an engineering assistant. Work carefully and post status updates as you go."
  }
}
```

For a local development setup where Hermes and Paperclip both run on the same machine:

```json
{
  "adapterType": "hermes_gateway",
  "adapterConfig": {
    "apiBaseUrl": "http://127.0.0.1:9119",
    "apiKey": {
      "type": "secret_ref",
      "secretId": "hermes-api-key",
      "version": "latest"
    },
    "sessionKeyStrategy": "issue",
    "timeoutSec": 300
  }
}
```

---

## Troubleshooting

**Runs do not start and the adapter reports a connection error**

Check that the Hermes API server is running and that `apiBaseUrl` is reachable from the Paperclip backend — not just from your browser. If Paperclip is inside Docker and Hermes is on the host, `127.0.0.1` will point to the container, not the host. Use `host.docker.internal` or a LAN address instead.

**The API key field says a secret is stored**

That is expected. Paperclip masks stored adapter secrets. Do not repaste the key unless you are rotating it.

**HTTP connection refused for a non-loopback host**

Non-loopback plain HTTP is blocked by default. Either switch the `apiBaseUrl` to an HTTPS URL, or enable `dangerouslyAllowInsecureRemoteHttp` for development use only.

**Events stream keeps reconnecting**

A high reconnect rate usually means the SSE stream drops frequently — often a proxy timeout, a load balancer with short keepalive limits, or an unstable network. Try increasing `eventReconnectMs` to reduce churn, and check whether a proxy between Paperclip and Hermes is terminating long-lived connections early.

**Runs complete in Hermes but Paperclip shows no results**

The adapter polls `GET /v1/runs/{run_id}` as a fallback when the SSE stream is lost. If both the stream and the poll fail, verify that `apiBaseUrl` resolves correctly and that the Hermes API server is healthy. Check the Paperclip run log for the last error from the adapter.

**Cross-task memory bleed between issues**

Switch `sessionKeyStrategy` to `issue` (the default). If it is already set to `issue`, confirm that the Hermes instance is correctly scoping sessions by key. Setting the strategy to `run` gives fully isolated sessions if you need a harder guarantee.

---

## Next Steps

- [Hermes](./hermes.md) — run Hermes as a local child process on the same machine instead.
- [Bring Your Own Agent](../../how-to/bring-your-own-agent.md) — for the external agent invitation flow and the broader model of connecting third-party agents to Paperclip.
- [Agent Adapters](../../guides/org/agent-adapters.md) — how Paperclip adapters work in general.
