> ## Documentation Index
> Fetch the complete documentation index at: https://docs.claw.boo/llms.txt
> Use this file to discover all available pages before exploring further.

# @clawboo/gateway-proxy

> Same-origin WebSocket proxy for the OpenClaw Gateway with server-side token + Ed25519 device-auth injection.

|                    |                                                                                                                                                                                                       |
| ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Version**        | `0.1.0`                                                                                                                                                                                               |
| **Purity**         | server-only                                                                                                                                                                                           |
| **Purpose**        | Same-origin WS proxy (`/api/gateway/ws`) that injects the upstream auth token and a persistent Ed25519 device signature into connect frames server-side, plus a token-cookie access gate for HTTP/WS. |
| **Workspace deps** | `@clawboo/config`                                                                                                                                                                                     |
| **External deps**  | `@noble/ed25519`, `ws`                                                                                                                                                                                |

The proxy is what keeps the browser from ever seeing the Gateway token: the SPA opens a WebSocket to the same origin, and the proxy fills in `auth.token` and a device signature on the `connect` frame before forwarding upstream. The device identity is a persistent keypair on disk (`~/.clawboo/proxy-device-identity.json`), so preview / incognito / fresh-machine browser contexts all connect without managing their own keys. Its device-auth primitives are also re-used by the server-side `AgentSource` connection (a non-browser `GatewayClient`) via the gateway-client `signConnect` hook.

## Public API

### Functions

| Export                            | Signature                                                                                                          | Contract                                                                                                                                                                                                                                                                                                                                            |
| --------------------------------- | ------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `createGatewayProxy`              | `(options: ProxyOptions) => GatewayProxyHandle`                                                                    | Builds the WS proxy. Loads the proxy device identity eagerly, opens the upstream connection per browser WS (before the first browser message, to catch the spontaneous `connect.challenge`), injects token + device signature into `connect` frames, and forwards bidirectionally with a bounded connect-buffer and an application-level keepalive. |
| `createAccessGate`                | `(options?: AccessGateOptions) => AccessGate`                                                                      | Builds a token-cookie access gate. Disabled when no token is given (`enabled: false`, all requests pass). When enabled: a `?access_token=…` query sets an HttpOnly cookie and 302-redirects; `/api/*` requests then require that cookie. Token comparison is constant-time (SHA-256 + `timingSafeEqual`).                                           |
| `getProxyDeviceIdentityPath`      | `() => string`                                                                                                     | Absolute path of the proxy's Ed25519 identity file, `resolveClawbooDir()/proxy-device-identity.json` (holds the private key).                                                                                                                                                                                                                       |
| `loadOrCreateProxyDeviceIdentity` | `() => Promise<DeviceIdentity>`                                                                                    | Loads the persisted identity (re-hardening perms to `0600` on every load), or generates + persists a fresh one (`0700` dir, `0600` file). Persistence is best-effort; an in-memory identity still works for the session if the write fails.                                                                                                         |
| `signConnectParams`               | `(identity: DeviceIdentity, params: Record<string, unknown>, nonce: string \| null) => Promise<ProxyDeviceFields>` | Extracts `client.id` / `client.mode` / `role` / `scopes` / `auth.token` from connect params, builds the device-auth payload (v2 when a `nonce` is present, else v1), and Ed25519-signs it. Returns the `{ device: { id, publicKey, signature, signedAt, nonce? } }` field to merge into the connect frame.                                          |

### Types & interfaces

| Export               | Kind      | Contract                                                                                                                                                                                                                                                    |
| -------------------- | --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `ProxyOptions`       | interface | `createGatewayProxy` input: `{ loadUpstreamSettings: () => Promise<UpstreamSettings>; allowWs?; log?; logError?; keepaliveIntervalMs?; maxPendingFrames? }`. Defaults: `allowWs` matches `/api/gateway/ws`, keepalive `30_000` ms, buffer cap `512` frames. |
| `UpstreamSettings`   | interface | `{ url: string; token: string }`, the upstream Gateway WS URL + bearer token, resolved per connection by `loadUpstreamSettings`.                                                                                                                            |
| `GatewayProxyHandle` | interface | `createGatewayProxy` return: `{ handleUpgrade: (req, socket, head) => void; wss: WebSocketServer }`. Pass `handleUpgrade` to `server.on('upgrade', …)`.                                                                                                     |
| `AccessGateOptions`  | interface | `{ token?: string; cookieName?: string; queryParam?: string }`. Defaults: cookie `clawboo_access`, query param `access_token`. Empty/undefined token disables the gate.                                                                                     |
| `AccessGate`         | interface | `{ enabled: boolean; handleHttp: (req, res) => boolean; allowUpgrade: (req) => boolean }`. `handleHttp` returns `true` when it has written the response (caller must not write again).                                                                      |
| `DeviceIdentity`     | type      | `{ deviceId: string; publicKey: string; privateKey: string }`, public/private keys base64url-encoded; `deviceId` is the SHA-256 fingerprint of the public key.                                                                                              |
| `ProxyDeviceFields`  | type      | `{ device: { id: string; publicKey: string; signature: string; signedAt: number; nonce?: string } }`, the signed device field merged onto connect params.                                                                                                   |

<Note>
  The barrel exposes no classes or runtime constants. `WebSocketServer` surfaces only as a field type on `GatewayProxyHandle.wss` (re-exported from `ws`, not from this package). The keepalive interval and frame-cap are config options on `ProxyOptions`, not exported constants.
</Note>

## Used by

* `apps/web/server/index.ts`, boots the proxy (`createGatewayProxy`) for the `/api/gateway/ws` upgrade and the `createAccessGate` for HTTP/WS authorization.
* `apps/web/server/lib/agentSource/registry.ts`, re-uses `loadOrCreateProxyDeviceIdentity` + `signConnectParams` (+ the `DeviceIdentity` type) so the server-side `OpenClawAgentSource`'s non-browser `GatewayClient` signs its own connect frames with the already-paired proxy identity.
* `apps/web/server/lib/bootProbe.ts`, calls `getProxyDeviceIdentityPath` to check the vault/identity file location during the boot health probe.

## Source

Barrel: [`packages/gateway-proxy/src/index.ts`](https://github.com/clawboo/clawboo/tree/main/packages/gateway-proxy/src/index.ts). Implementation: `proxy.ts` (the WS proxy + token/device injection + keepalive), `access-gate.ts` (the token-cookie gate), `proxy-device-auth.ts` (Ed25519 identity load/generate + connect-frame signing).

## See also

* [Gateway & events pipeline](/concepts/gateway-and-events), where the proxy sits in the Gateway flow.
* [Security](/operating/security), access gate, device auth, and safe exposure.
* [@clawboo/gateway-client](/reference/packages/gateway-client), the WS client whose `signConnect` hook consumes `signConnectParams`.
* [@clawboo/config](/reference/packages/config), source of `resolveClawbooDir`, which locates the identity file.
