Files
nexus/docs/gateway-api-research.md
developer b7b44494f0
CI - Build & Test / Backend (.NET) (push) Successful in 26s
CI - Build & Test / Frontend (Vue/TS) (push) Successful in 16s
CI - Build & Test / Security Check (push) Successful in 2s
fix(shadcn): isolate Nexus CSS vars with --nx- prefix + admin password reset endpoint
2026-06-11 10:06:58 +02:00

402 lines
10 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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.