402 lines
10 KiB
Markdown
402 lines
10 KiB
Markdown
# Gateway API Research
|
||
|
||
> Generated: 2026-06-10
|
||
> Auth mode: password (not token)
|
||
|
||
## 1. Authentication
|
||
|
||
**Auth mode:** `password`
|
||
**Credential:** `ieDm...PAg` (masked: `ieDmOiBiVfbbDM0ibrEebPAg` → use `ieDm...PAg`)
|
||
|
||
### How to authenticate
|
||
|
||
```bash
|
||
Authorization: Bearer <password>
|
||
```
|
||
|
||
All requests to `POST /tools/invoke` must include the `Authorization: Bearer` header with the gateway password from `gateway.auth.password`.
|
||
|
||
### Configuration (from openclaw.json)
|
||
|
||
```json5
|
||
{
|
||
gateway: {
|
||
mode: "local",
|
||
port: 18789,
|
||
bind: "loopback", // Only reachable from localhost
|
||
auth: {
|
||
mode: "password",
|
||
password: "ieDmOjBiVfbbDM0ibrEebPAg",
|
||
rateLimit: {
|
||
maxAttempts: 10,
|
||
windowMs: 60000,
|
||
lockoutMs: 300000,
|
||
exemptLoopback: true
|
||
}
|
||
},
|
||
controlUi: {
|
||
allowInsecureAuth: true,
|
||
allowedOrigins: ["https://openclaw.noveria.net"]
|
||
},
|
||
tools: {
|
||
// Default deny list applies (see below)
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
### Notes
|
||
|
||
- **Loopback only**: `gateway.bind: "loopback"` means the API only listens on `127.0.0.1:18789`.
|
||
- **Rate limiting**: 10 failed attempts per 60s window → 5min lockout. Loopback is exempt.
|
||
- **Control UI**: Allowed origin: `https://openclaw.noveria.net`
|
||
|
||
---
|
||
|
||
## 2. API Endpoint: `POST /tools/invoke`
|
||
|
||
**URL:** `http://127.0.0.1:18789/tools/invoke`
|
||
**Method:** `POST`
|
||
**Content-Type:** `application/json`
|
||
**Auth:** `Authorization: Bearer <password>`
|
||
**Max payload size:** 2 MB
|
||
|
||
### Request body structure
|
||
|
||
```json
|
||
{
|
||
"tool": "<tool_name>",
|
||
"action": "<optional_action>",
|
||
"args": { },
|
||
"sessionKey": "main",
|
||
"dryRun": false
|
||
}
|
||
```
|
||
|
||
### Responses
|
||
|
||
| Code | Meaning |
|
||
|------|---------|
|
||
| 200 | Success: `{ ok: true, result: ... }` |
|
||
| 400 | Invalid request/tool input: `{ ok: false, error: { type, message } }` |
|
||
| 401 | Unauthorized |
|
||
| 404 | Tool not found or not allowlisted |
|
||
| 405 | Method not allowed |
|
||
| 429 | Rate limited (with `Retry-After` header) |
|
||
| 500 | Tool execution error: `{ ok: false, error: { type, message } }` |
|
||
|
||
### Default HTTP Deny List (cannot be invoked via HTTP)
|
||
|
||
These tools are blocked by default on the HTTP endpoint (policy):
|
||
|
||
| Tool | Reason |
|
||
|------|--------|
|
||
| `exec` | RCE surface |
|
||
| `spawn` | RCE surface |
|
||
| `shell` | RCE surface |
|
||
| `fs_write` | Arbitrary file mutation |
|
||
| `fs_delete` | Arbitrary file deletion |
|
||
| `fs_move` | Arbitrary file move/rename |
|
||
| `apply_patch` | Can rewrite files |
|
||
| `sessions_spawn` | Session orchestration / remote agent spawning |
|
||
| `sessions_send` | Cross-session message injection |
|
||
| `cron` | Persistent automation control plane |
|
||
| `gateway` | Gateway control plane (prevents reconfiguration via HTTP) |
|
||
| `nodes` | Node command relay |
|
||
| `whatsapp_login` | Interactive setup; hangs on HTTP |
|
||
|
||
**Override example** (add to `gateway.tools` in config):
|
||
|
||
```json5
|
||
{
|
||
gateway: {
|
||
tools: {
|
||
deny: ["browser"], // extra blocks
|
||
allow: ["gateway"], // remove from default deny
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 3. Tested Tools & Responses
|
||
|
||
> Note: Direct HTTP testing was not possible from this session (exec sandbox unavailable). Documentation based on API spec and config analysis.
|
||
|
||
### 3.1 `session_status`
|
||
|
||
**Request:**
|
||
```json
|
||
{ "tool": "session_status", "args": {} }
|
||
```
|
||
|
||
**Expected response structure:**
|
||
```json
|
||
{
|
||
"ok": true,
|
||
"result": {
|
||
"sessionKey": "main",
|
||
"agentId": "iris",
|
||
"modelId": "openai/gpt-5.4",
|
||
"channel": "webchat",
|
||
"created": "<ISO timestamp>",
|
||
"active": true,
|
||
"toolsAvailable": ["read", "write", "edit", "exec", ...],
|
||
"subagentDepth": 1,
|
||
"runtimeInfo": { "thinking": "high", "modelIdentity": "deepseek/deepseek-v4-flash" }
|
||
}
|
||
}
|
||
```
|
||
|
||
### 3.2 `sessions_list`
|
||
|
||
**Request:**
|
||
```json
|
||
{ "tool": "sessions_list", "args": { "kinds": ["main", "subagent"] } }
|
||
```
|
||
|
||
**Expected response:**
|
||
Array of active sessions with keys like `iris-main`, `programmer-subagent-xxx`, etc.
|
||
|
||
### 3.3 `subagents`
|
||
|
||
**Request:**
|
||
```json
|
||
{ "tool": "subagents", "args": { "action": "list" } }
|
||
```
|
||
|
||
**Expected response:**
|
||
List of configured subagents for the active session.
|
||
|
||
### 3.4 `sessions_history`
|
||
|
||
**Request:**
|
||
```json
|
||
{ "tool": "sessions_history", "args": { "sessionKey": "iris-main", "limit": 10 } }
|
||
```
|
||
|
||
**Expected response:**
|
||
Array of recent messages/events from the specified session. Fields include:
|
||
- `role` (user/assistant/tool)
|
||
- `content` (message text)
|
||
- `timestamp`
|
||
- `tool_calls` (if applicable)
|
||
|
||
### 3.5 `memory_search`
|
||
|
||
**Request:**
|
||
```json
|
||
{ "tool": "memory_search", "args": { "query": "...", "maxResults": 5 } }
|
||
```
|
||
|
||
**Expected response:**
|
||
```json
|
||
{
|
||
"ok": true,
|
||
"result": [
|
||
{
|
||
"content": "...",
|
||
"path": "memory/YYYY-MM-DD.md",
|
||
"score": 0.95,
|
||
"metadata": { "...": "..." }
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
### 3.6 Health Check (non-tools/invoke)
|
||
|
||
**Request:**
|
||
```bash
|
||
GET http://127.0.0.1:18789/health
|
||
```
|
||
|
||
**Expected response:** "OK" or `{ "status": "ok" }`
|
||
|
||
---
|
||
|
||
## 4. Data Structures for Dashboard Integration
|
||
|
||
### Tool Call Response - Success
|
||
|
||
```json
|
||
{
|
||
"ok": true,
|
||
"result": <any>
|
||
}
|
||
```
|
||
|
||
### Tool Call Response - Error
|
||
|
||
```json
|
||
{
|
||
"ok": false,
|
||
"error": {
|
||
"type": "string",
|
||
"message": "string"
|
||
}
|
||
}
|
||
```
|
||
|
||
### Session Object (from sessions_list / session_status)
|
||
|
||
| Field | Type | Description |
|
||
|-------|------|-------------|
|
||
| sessionKey | string | Unique session identifier |
|
||
| agentId | string | Agent id (e.g., "iris", "programmer") |
|
||
| modelId | string | Model in use |
|
||
| channel | string | Channel type (webchat, slack, etc.) |
|
||
| created | ISO timestamp | Session creation time |
|
||
| active | boolean | Whether session is processing |
|
||
| subagentDepth | number | Current subagent nesting level |
|
||
|
||
### Session History Entry
|
||
|
||
| Field | Type | Description |
|
||
|-------|------|-------------|
|
||
| role | string | "user", "assistant", "tool", "system" |
|
||
| content | string | Message content |
|
||
| timestamp | ISO string | When the message was sent |
|
||
| tool_calls | array[] | Tool invocation details (if assistant) |
|
||
| tool_call_id | string | Matches tool response to request |
|
||
|
||
---
|
||
|
||
## 5. OpenAI-Compatibility Endpoints
|
||
|
||
These are additional HTTP endpoints on the same gateway port:
|
||
|
||
| Endpoint | Enabled? | Notes |
|
||
|----------|---------|-------|
|
||
| `POST /api/v1/admin/rpc` | Off by default | Requires `admin-http-rpc` plugin |
|
||
| `POST /v1/chat/completions` | Off by default | Enable via `gateway.http.endpoints.chatCompletions.enabled` |
|
||
| `POST /v1/responses` | Off by default | Enable via `gateway.http.endpoints.responses.enabled` |
|
||
|
||
None of these are enabled in the current config.
|
||
|
||
---
|
||
|
||
## 6. Connection from Docker (Nexus API Container)
|
||
|
||
### Current Integration Setup
|
||
|
||
The Nexus compose.yaml already includes the full integration infrastructure:
|
||
|
||
```yaml
|
||
api:
|
||
extra_hosts:
|
||
- host.docker.internal:host-gateway
|
||
environment:
|
||
Integrations__OpenClaw__BaseUrl: ${OPENCLAW_BASE_URL:-http://host.docker.internal:18789}
|
||
Integrations__OpenClaw__Token: ${OPENCLAW_GATEWAY_TOKEN:-}
|
||
Integrations__OpenClaw__Password: ${OPENCLAW_GATEWAY_PASSWORD:-}
|
||
```
|
||
|
||
The API container:
|
||
- Uses `host.docker.internal:18789` to reach the Gateway via the Docker host
|
||
- Has `extra_hosts` configured for `host.docker.internal`
|
||
- Reads token/password from `.env` via `OPENCLAW_GATEWAY_PASSWORD`
|
||
|
||
### Known Issue: Gateway Bind = loopback
|
||
|
||
The Gateway binds to `127.0.0.1` (`gateway.bind: "loopback"`). This means it only listens inside the gateway container's loopback interface.
|
||
|
||
| Scenario | Works? | Why |
|
||
|----------|--------|-----|
|
||
| Gateway with `--network host` | ✅ Yes | Process sees host's 127.0.0.1 directly |
|
||
| Gateway with `-p 18789:18789` + loopback bind | ❌ No | Port forward sends to container IP, not loopback |
|
||
| Gateway with `-p 18789:18789` + lan bind | ✅ Yes | Listens on all interfaces including container IP |
|
||
|
||
**Fix**: Change `gateway.bind` from `"loopback"` to `"lan"` (binds `0.0.0.0`):
|
||
|
||
```json5
|
||
{
|
||
gateway: {
|
||
bind: "lan" // was "loopback"
|
||
}
|
||
}
|
||
```
|
||
|
||
**Test command (from Nexus API container):**
|
||
```bash
|
||
curl -s http://host.docker.internal:18789/health
|
||
# Expected: 200 if gateway bind is lan/container IP is reachable
|
||
```
|
||
|
||
### Required .env Vars for Nexus
|
||
|
||
The Nexus project `.env` needs these values for gateway integration:
|
||
|
||
```bash
|
||
# OpenClaw Gateway integration
|
||
OPENCLAW_GATEWAY_PASSWORD=ieDmOjBiVfbbDM0ibrEebPAg
|
||
```
|
||
|
||
The compose.yaml references `OPENCLAW_GATEWAY_TOKEN` as fallback, but the primary auth mode is `password`. Either var works with Bearer auth.
|
||
|
||
---
|
||
|
||
## 7. Rate Limits & Restrictions
|
||
|
||
| Limit | Value | Detail |
|
||
|-------|-------|--------|
|
||
| Auth failures | 10 per 60s | Per client IP, per auth scope |
|
||
| Lockout | 5 min | After hitting rate limit |
|
||
| Loopback exempt | Yes | Loopback traffic not rate-limited |
|
||
| Max payload | 2 MB | Per request |
|
||
| HTTP default deny | 12 tools | RCE/mutation tools blocked |
|
||
| Bind mode | loopback | Only localhost reachable |
|
||
|
||
---
|
||
|
||
## 8. Security Notes
|
||
|
||
- **Keep credentials secret** – Never log or commit the gateway password
|
||
- **Token masking in docs**: `ieDm...PAg`
|
||
- The `/tools/invoke` endpoint should NOT be exposed to the public internet
|
||
- Gateway auth mode is `password` (equivalent to `token` in practice – both use Bearer header)
|
||
- Control UI has `allowInsecureAuth: true` which should be disabled in production
|
||
- `allowedOrigins: ["https://openclaw.noveria.net"]` should be reviewed
|
||
|
||
---
|
||
|
||
## 9. Example curl Commands (for reference)
|
||
|
||
```bash
|
||
# Health check
|
||
curl http://127.0.0.1:18789/health
|
||
|
||
# Session status
|
||
curl -s -X POST http://127.0.0.1:18789/tools/invoke \
|
||
-H "Authorization: Bearer <PASSWORD>" \
|
||
-H "Content-Type: application/json" \
|
||
-d '{"tool":"session_status","args":{}}'
|
||
|
||
# List sessions
|
||
curl -s -X POST http://127.0.0.1:18789/tools/invoke \
|
||
-H "Authorization: Bearer <PASSWORD>" \
|
||
-H "Content-Type: application/json" \
|
||
-d '{"tool":"sessions_list","args":{"kinds":["main","subagent"]}}'
|
||
|
||
# Session history
|
||
curl -s -X POST http://127.0.0.1:18789/tools/invoke \
|
||
-H "Authorization: Bearer <PASSWORD>" \
|
||
-H "Content-Type: application/json" \
|
||
-d '{"tool":"sessions_history","args":{"sessionKey":"iris-main","limit":10}}'
|
||
|
||
# Memory search
|
||
curl -s -X POST http://127.0.0.1:18789/tools/invoke \
|
||
-H "Authorization: Bearer <PASSWORD>" \
|
||
-H "Content-Type: application/json" \
|
||
-d '{"tool":"memory_search","args":{"query":"nexus","maxResults":5}}'
|
||
|
||
# Subagents list
|
||
curl -s -X POST http://127.0.0.1:18789/tools/invoke \
|
||
-H "Authorization: Bearer <PASSWORD>" \
|
||
-H "Content-Type: application/json" \
|
||
-d '{"tool":"subagents","args":{"action":"list"}}'
|
||
```
|
||
|
||
Replace `<PASSWORD>` with the actual gateway password.
|