Add per-agent `publicMode` configuration that suppresses local runtime details (hostname, OS, architecture, workspace paths, skills snapshot) from the system prompt when serving public-facing agents via the HTTP API. - New `publicMode?: boolean` field in agent config schema - `resolveAgentPublicMode()` in agent-scope for config lookup - System prompt emits `"Runtime: public-facing agent"` instead of host/OS/arch details when publicMode is active - Skills snapshot suppressed for publicMode agents - New `X-OpenClaw-Sender-Is-Owner` HTTP header for both chat completions and OpenResponses endpoints, replacing the hardcoded `senderIsOwner: true` - For publicMode agents, missing header defaults to non-owner semantics; for normal agents, missing header defaults to owner semantics - `resolveIngressSenderIsOwner()` utility in http-utils - Documentation updates for security boundary, header usage, and config - Tests for system prompt suppression, param redaction, and header parsing Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
135 lines
4.0 KiB
Markdown
135 lines
4.0 KiB
Markdown
---
|
||
summary: "Expose an OpenAI-compatible /v1/chat/completions HTTP endpoint from the Gateway"
|
||
read_when:
|
||
- Integrating tools that expect OpenAI Chat Completions
|
||
title: "OpenAI Chat Completions"
|
||
---
|
||
|
||
# OpenAI Chat Completions (HTTP)
|
||
|
||
OpenClaw’s Gateway can serve a small OpenAI-compatible Chat Completions endpoint.
|
||
|
||
This endpoint is **disabled by default**. Enable it in config first.
|
||
|
||
- `POST /v1/chat/completions`
|
||
- Same port as the Gateway (WS + HTTP multiplex): `http://<gateway-host>:<port>/v1/chat/completions`
|
||
|
||
Under the hood, requests are executed as a normal Gateway agent run (same codepath as `openclaw agent`), so routing/permissions/config match your Gateway.
|
||
|
||
## Authentication
|
||
|
||
Uses the Gateway auth configuration. Send a bearer token:
|
||
|
||
- `Authorization: Bearer <token>`
|
||
|
||
Notes:
|
||
|
||
- When `gateway.auth.mode="token"`, use `gateway.auth.token` (or `OPENCLAW_GATEWAY_TOKEN`).
|
||
- When `gateway.auth.mode="password"`, use `gateway.auth.password` (or `OPENCLAW_GATEWAY_PASSWORD`).
|
||
- If `gateway.auth.rateLimit` is configured and too many auth failures occur, the endpoint returns `429` with `Retry-After`.
|
||
|
||
## Security boundary (important)
|
||
|
||
Treat this endpoint as an **operator-access** surface for the gateway instance unless you
|
||
intentionally target a `publicMode` agent through a trusted upstream.
|
||
|
||
- HTTP bearer auth here is not a narrow per-user scope model.
|
||
- A valid Gateway token/password for this endpoint should still be treated like an owner/operator credential.
|
||
- Requests run through the same control-plane agent path as trusted operator actions.
|
||
- For normal agents, missing `x-openclaw-sender-is-owner` defaults to owner semantics.
|
||
- For agents with `publicMode: true`, missing `x-openclaw-sender-is-owner` defaults to non-owner semantics; only an explicit `x-openclaw-sender-is-owner: true` restores owner access.
|
||
- `publicMode` is not a full per-user sandbox. It redacts local runtime details in prompts and strips owner-only tools, but any non-owner tools still allowed by the target agent remain callable.
|
||
- Keep this endpoint on loopback/tailnet/private ingress only; do not expose it directly to the public internet.
|
||
|
||
See [Security](/gateway/security) and [Remote access](/gateway/remote).
|
||
|
||
## Choosing an agent
|
||
|
||
No custom headers required: encode the agent id in the OpenAI `model` field:
|
||
|
||
- `model: "openclaw:<agentId>"` (example: `"openclaw:main"`, `"openclaw:beta"`)
|
||
- `model: "agent:<agentId>"` (alias)
|
||
|
||
Or target a specific OpenClaw agent by header:
|
||
|
||
- `x-openclaw-agent-id: <agentId>` (default: `main`)
|
||
|
||
Advanced:
|
||
|
||
- `x-openclaw-session-key: <sessionKey>` to fully control session routing.
|
||
|
||
## Enabling the endpoint
|
||
|
||
Set `gateway.http.endpoints.chatCompletions.enabled` to `true`:
|
||
|
||
```json5
|
||
{
|
||
gateway: {
|
||
http: {
|
||
endpoints: {
|
||
chatCompletions: { enabled: true },
|
||
},
|
||
},
|
||
},
|
||
}
|
||
```
|
||
|
||
## Disabling the endpoint
|
||
|
||
Set `gateway.http.endpoints.chatCompletions.enabled` to `false`:
|
||
|
||
```json5
|
||
{
|
||
gateway: {
|
||
http: {
|
||
endpoints: {
|
||
chatCompletions: { enabled: false },
|
||
},
|
||
},
|
||
},
|
||
}
|
||
```
|
||
|
||
## Session behavior
|
||
|
||
By default the endpoint is **stateless per request** (a new session key is generated each call).
|
||
|
||
If the request includes an OpenAI `user` string, the Gateway derives a stable session key from it, so repeated calls can share an agent session.
|
||
|
||
## Streaming (SSE)
|
||
|
||
Set `stream: true` to receive Server-Sent Events (SSE):
|
||
|
||
- `Content-Type: text/event-stream`
|
||
- Each event line is `data: <json>`
|
||
- Stream ends with `data: [DONE]`
|
||
|
||
## Examples
|
||
|
||
Non-streaming:
|
||
|
||
```bash
|
||
curl -sS http://127.0.0.1:18789/v1/chat/completions \
|
||
-H 'Authorization: Bearer YOUR_TOKEN' \
|
||
-H 'Content-Type: application/json' \
|
||
-H 'x-openclaw-agent-id: main' \
|
||
-d '{
|
||
"model": "openclaw",
|
||
"messages": [{"role":"user","content":"hi"}]
|
||
}'
|
||
```
|
||
|
||
Streaming:
|
||
|
||
```bash
|
||
curl -N http://127.0.0.1:18789/v1/chat/completions \
|
||
-H 'Authorization: Bearer YOUR_TOKEN' \
|
||
-H 'Content-Type: application/json' \
|
||
-H 'x-openclaw-agent-id: main' \
|
||
-d '{
|
||
"model": "openclaw",
|
||
"stream": true,
|
||
"messages": [{"role":"user","content":"hi"}]
|
||
}'
|
||
```
|