From 4178f28e4141d1c9f4dc693b7447df7ebdc1805d Mon Sep 17 00:00:00 2001 From: jiaoliao Date: Thu, 11 Jun 2026 04:08:22 +0000 Subject: [PATCH 1/4] docs(sandbox): add sandbox proxy user guide Document the HTTP/WebSocket proxy endpoints exposed by ROCK Admin, including target-port selection (path/header/query), port restrictions, and error handling. Provided in both English and Chinese (zh-Hans). --- .../User Guides/sandbox_proxy.md | 151 ++++++++++++++++++ .../User Guides/sandbox_proxy.md | 151 ++++++++++++++++++ 2 files changed, 302 insertions(+) create mode 100644 docs/i18n/zh-Hans/docusaurus-plugin-content-docs/version-1.8.x/User Guides/sandbox_proxy.md create mode 100644 docs/versioned_docs/version-1.8.x/User Guides/sandbox_proxy.md diff --git a/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/version-1.8.x/User Guides/sandbox_proxy.md b/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/version-1.8.x/User Guides/sandbox_proxy.md new file mode 100644 index 0000000000..6185ac85ec --- /dev/null +++ b/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/version-1.8.x/User Guides/sandbox_proxy.md @@ -0,0 +1,151 @@ +--- +sidebar_position: 6 +--- + +# 沙箱代理 (Sandbox Proxy) + +ROCK Admin 提供了一层代理能力,可以让你**从集群外部**访问运行在沙箱**内部**的服务,而无需为每个沙箱单独分配公网地址。代理支持两种传输模式: + +| 模式 | 接入路径 | 适用场景 | +|------|---------|---------| +| HTTP 代理 | `/sandboxes/{sandbox_id}/proxy/...` | REST API、Web UI、文件下载等任何 HTTP/1.1 流量 | +| WebSocket 代理 | `ws(s)://.../sandboxes/{sandbox_id}/proxy/...` | 实时通道、流式输出、浏览器端 WS 客户端 | + +两种模式都通过 `sandbox_id` 路由。沙箱本身**不需要**公网 IP — Admin 负责接收客户端请求,并将其转发到集群内对应的运行时实例。 + +--- + +## 1. HTTP 代理 + +将任意 HTTP 请求转发到沙箱内部的服务。 + +### 接入路径 + +``` +{GET|POST|PUT|DELETE|PATCH|HEAD|OPTIONS} {ROCK_BASE_URL}/sandboxes/{sandbox_id}/proxy[/{path}] +``` + +- 请求方法、请求头、查询字符串和请求体会原样转发到目标服务。 +- 目标服务的响应(状态码、响应头、响应体)会被流式回传给客户端。 + +### 指定目标端口 + +代理需要知道要访问沙箱内的哪个端口。共有三种方式可以指定 — **只能选其一**,同时使用多种方式会返回 `400 Bad Request`。 + +| 优先级 | 指定方式 | 示例 | +|--------|---------|------| +| 1 | **路径前缀** | `/sandboxes/abc/proxy/port/8080/api/users` | +| 2 | **请求头** | `X-ROCK-Target-Port: 8080` | +| 3 | **查询参数** | `?rock_target_port=8080` | + +如果不指定目标端口,请求会被路由到沙箱的默认服务端口。 + +### 使用示例 + +```bash +# 通过路径方式指定端口 +curl -X POST \ + "$ROCK_BASE_URL/sandboxes/sb-123/proxy/port/8080/v1/predict" \ + -H "Content-Type: application/json" \ + -d '{"prompt": "hello"}' + +# 通过请求头方式指定端口 +curl -X POST \ + "$ROCK_BASE_URL/sandboxes/sb-123/proxy/v1/predict" \ + -H "X-ROCK-Target-Port: 8080" \ + -H "Content-Type: application/json" \ + -d '{"prompt": "hello"}' + +# 通过查询参数指定端口,并附带其他业务查询参数 +curl "$ROCK_BASE_URL/sandboxes/sb-123/proxy/items?rock_target_port=8080&limit=10" +``` + +--- + +## 2. WebSocket 代理 + +适用于需要 `ws://`(或 `wss://`)协议的服务 — 常见于流式输出、聊天、终端会话或任何全双工通道。 + +### 接入路径 + +``` +ws(s)://{ROCK_BASE_URL}/sandboxes/{sandbox_id}/proxy/{path} +``` + +- 原始的 WebSocket 握手信息(subprotocol、自定义 header)会被透传。 +- 文本帧和二进制帧在双向之间透明转发。 +- 任意一端关闭连接,都会同步关闭对端的上游连接。 + +### 指定目标端口 + +与 HTTP 代理完全相同 — 支持路径、请求头、查询参数三种方式,同样**只能选其一**: + +| 优先级 | 指定方式 | 示例 | +|--------|---------|------| +| 1 | 路径前缀 | `/sandboxes/abc/proxy/port/9000/socket` | +| 2 | 请求头 | `X-ROCK-Target-Port: 9000` | +| 3 | 查询参数 | `?rock_target_port=9000` | + +如果端口不合法(参见[端口限制](#3-端口限制)),WebSocket 会立刻以状态码 `1008`(策略违反)关闭。 + +### 使用示例 + +```bash +# 使用 wscat +wscat -c "$ROCK_WS_BASE/sandboxes/sb-123/proxy/port/9000/events" +``` + +```javascript +// 浏览器端 +const ws = new WebSocket( + "wss://rock.example.com/sandboxes/sb-123/proxy/events?rock_target_port=9000" +); +ws.onmessage = (evt) => console.log(evt.data); +ws.send("ping"); +``` + +--- + +## 3. 端口限制 + +WebSocket 代理对**沙箱内的目标端口**有以下限制: + +| 规则 | 允许范围 / 值 | +|------|--------------| +| 最小端口 | `1024` | +| 最大端口 | `65535` | +| 禁用端口 | `22`(SSH) | + +违反上述规则的请求会被拒绝: + +- **HTTP 代理** → 返回 `400 Bad Request`,并在 `detail` 字段中说明原因。 +- **WebSocket 代理** → 连接以状态码 `1008` 关闭,并在 `reason` 中给出原因。 + +> `1024` 以下的端口为系统保留特权端口,因此被禁止;`22` 端口被禁止则是为了避免无意中暴露 SSH 服务。 + +--- + +## 4. 常见错误对照 + +| 现象 | 可能原因 | +|------|---------| +| `400 Bad Request: Cannot specify target port via multiple sources` | 同时使用了路径、请求头、查询参数中的两种以上方式指定端口 — 只保留其中一种。 | +| `400 Bad Request` / WS `1008` 且提示端口范围错误 | 目标端口小于 `1024`、大于 `65535` 或等于 `22`。 | +| WS 关闭码 `1011`(`Proxy error: ...`) | 沙箱内的上游服务返回了错误,或无法被连通。请确认目标端口上确实有服务在监听。 | +| 上游返回 `404 Not Found` | 沙箱内服务并不存在该 HTTP 路径 — 应检查目标服务的路由,而不是代理 URL。 | +| 握手阶段连接卡住 | 沙箱可能仍在初始化。请先调用 `is_alive` 接口确认沙箱已就绪。 | + +--- + +## 5. 快速选型 + +``` +要调用沙箱内的 REST API? → HTTP 代理 +要连接沙箱内的 WebSocket 服务? → WebSocket 代理 +``` + +## 相关文档 + +- [API 参考](../References/api.md) +- [配置指南](configuration.md) +- [快速开始](../Getting%20Started/quickstart.md) diff --git a/docs/versioned_docs/version-1.8.x/User Guides/sandbox_proxy.md b/docs/versioned_docs/version-1.8.x/User Guides/sandbox_proxy.md new file mode 100644 index 0000000000..d598c49ae5 --- /dev/null +++ b/docs/versioned_docs/version-1.8.x/User Guides/sandbox_proxy.md @@ -0,0 +1,151 @@ +--- +sidebar_position: 6 +--- + +# Sandbox Proxy + +ROCK Admin exposes a proxy layer that lets you reach services running **inside** a sandbox from **outside** the cluster, without giving each sandbox its own public address. The proxy supports two transport modes: + +| Mode | Endpoint | Use it for | +|------|----------|------------| +| HTTP Proxy | `/sandboxes/{sandbox_id}/proxy/...` | REST APIs, web UIs, file downloads, any HTTP/1.1 traffic | +| WebSocket Proxy | `ws(s)://.../sandboxes/{sandbox_id}/proxy/...` | Real-time channels, streaming, browser-based WS clients | + +Both modes route by `sandbox_id`. The sandbox does **not** need a public IP — Admin terminates the client connection and forwards it to the right runtime inside the cluster. + +--- + +## 1. HTTP Proxy + +Forward any HTTP request to a service inside the sandbox. + +### Endpoint + +``` +{GET|POST|PUT|DELETE|PATCH|HEAD|OPTIONS} {ROCK_BASE_URL}/sandboxes/{sandbox_id}/proxy[/{path}] +``` + +- Method, headers, query string, and body are forwarded as-is to the target service. +- The response (status code, headers, body) is streamed back to the client. + +### Choosing the target port + +The proxy needs to know which port inside the sandbox to hit. You can specify it in one of three ways — **pick exactly one**; mixing them returns `400 Bad Request`. + +| Priority | Mechanism | Example | +|----------|-----------|---------| +| 1 | **Path prefix** | `/sandboxes/abc/proxy/port/8080/api/users` | +| 2 | **Request header** | `X-ROCK-Target-Port: 8080` | +| 3 | **Query parameter** | `?rock_target_port=8080` | + +If no port is specified, the request is delivered to the sandbox's default service port. + +### Examples + +```bash +# REST call via path-style port +curl -X POST \ + "$ROCK_BASE_URL/sandboxes/sb-123/proxy/port/8080/v1/predict" \ + -H "Content-Type: application/json" \ + -d '{"prompt": "hello"}' + +# Same call via header-style port +curl -X POST \ + "$ROCK_BASE_URL/sandboxes/sb-123/proxy/v1/predict" \ + -H "X-ROCK-Target-Port: 8080" \ + -H "Content-Type: application/json" \ + -d '{"prompt": "hello"}' + +# Query-string port + extra query params +curl "$ROCK_BASE_URL/sandboxes/sb-123/proxy/items?rock_target_port=8080&limit=10" +``` + +--- + +## 2. WebSocket Proxy + +For services that expect a `ws://` (or `wss://`) connection — typical for streaming, chat, terminal sessions, or any full-duplex channel. + +### Endpoint + +``` +ws(s)://{ROCK_BASE_URL}/sandboxes/{sandbox_id}/proxy/{path} +``` + +- The original WebSocket handshake (subprotocols, custom headers) is forwarded. +- Both text and binary frames pass through transparently in both directions. +- Closing either side cleanly tears down the upstream connection. + +### Choosing the target port + +Identical to HTTP Proxy — path, header, or query parameter — and again, only one at a time: + +| Priority | Mechanism | Example | +|----------|-----------|---------| +| 1 | Path prefix | `/sandboxes/abc/proxy/port/9000/socket` | +| 2 | Header | `X-ROCK-Target-Port: 9000` | +| 3 | Query | `?rock_target_port=9000` | + +Invalid port values (see [Port restrictions](#3-port-restrictions)) cause the WebSocket to close immediately with code `1008` (Policy Violation). + +### Example + +```bash +# Using wscat +wscat -c "$ROCK_WS_BASE/sandboxes/sb-123/proxy/port/9000/events" +``` + +```javascript +// Browser client +const ws = new WebSocket( + "wss://rock.example.com/sandboxes/sb-123/proxy/events?rock_target_port=9000" +); +ws.onmessage = (evt) => console.log(evt.data); +ws.send("ping"); +``` + +--- + +## 3. Port restrictions + +The WebSocket proxy enforces the following rules on the **target port** inside the sandbox: + +| Rule | Allowed range / value | +|------|----------------------| +| Minimum port | `1024` | +| Maximum port | `65535` | +| Forbidden | `22` (SSH) | + +Requests violating these rules are rejected: + +- **HTTP Proxy** → `400 Bad Request` with a `detail` message. +- **WebSocket Proxy** → connection closed with code `1008` and a reason string. + +> Ports below `1024` are blocked because they are reserved for privileged services; port `22` is blocked to prevent inadvertently exposing SSH. + +--- + +## 4. Error handling reference + +| Symptom | Likely cause | +|---------|--------------| +| `400 Bad Request: Cannot specify target port via multiple sources` | You set the port in two of {path, header, query} — pick one. | +| `400 Bad Request` / WS close `1008` with port-range message | Target port is `< 1024`, `> 65535`, or equal to `22`. | +| WS close `1011` (`Proxy error: ...`) | Upstream service inside the sandbox returned an error or could not be reached. Check that the service is actually listening on the target port. | +| `404 Not Found` from the upstream | The HTTP path inside the sandbox does not exist — verify the service's route, not the proxy URL. | +| Connection hangs on handshake | The sandbox may still be initializing. Confirm `is_alive` returns `True` before proxying. | + +--- + +## 5. Quick decision guide + +``` +Need to call a REST API inside the sandbox? → HTTP Proxy +Need a bidirectional WebSocket to a WS server? → WebSocket Proxy +``` + +## Related documents + +- [API Reference](../References/api.md) +- [Configuration](configuration.md) +- [Quick Start](../Getting%20Started/quickstart.md) From 2be8b6091f7c08a94a0cbcfac7e01c2d41b7a116 Mon Sep 17 00:00:00 2001 From: jiaoliao Date: Thu, 11 Jun 2026 08:25:10 +0000 Subject: [PATCH 2/4] fix express --- .../version-1.8.x/User Guides/sandbox_proxy.md | 6 +++--- .../version-1.8.x/User Guides/sandbox_proxy.md | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/version-1.8.x/User Guides/sandbox_proxy.md b/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/version-1.8.x/User Guides/sandbox_proxy.md index 6185ac85ec..f3667bb388 100644 --- a/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/version-1.8.x/User Guides/sandbox_proxy.md +++ b/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/version-1.8.x/User Guides/sandbox_proxy.md @@ -21,7 +21,7 @@ ROCK Admin 提供了一层代理能力,可以让你**从集群外部**访问 ### 接入路径 -``` +```http {GET|POST|PUT|DELETE|PATCH|HEAD|OPTIONS} {ROCK_BASE_URL}/sandboxes/{sandbox_id}/proxy[/{path}] ``` @@ -68,7 +68,7 @@ curl "$ROCK_BASE_URL/sandboxes/sb-123/proxy/items?rock_target_port=8080&limit=10 ### 接入路径 -``` +```http ws(s)://{ROCK_BASE_URL}/sandboxes/{sandbox_id}/proxy/{path} ``` @@ -139,7 +139,7 @@ WebSocket 代理对**沙箱内的目标端口**有以下限制: ## 5. 快速选型 -``` +```text 要调用沙箱内的 REST API? → HTTP 代理 要连接沙箱内的 WebSocket 服务? → WebSocket 代理 ``` diff --git a/docs/versioned_docs/version-1.8.x/User Guides/sandbox_proxy.md b/docs/versioned_docs/version-1.8.x/User Guides/sandbox_proxy.md index d598c49ae5..f78aa23880 100644 --- a/docs/versioned_docs/version-1.8.x/User Guides/sandbox_proxy.md +++ b/docs/versioned_docs/version-1.8.x/User Guides/sandbox_proxy.md @@ -21,7 +21,7 @@ Forward any HTTP request to a service inside the sandbox. ### Endpoint -``` +```http {GET|POST|PUT|DELETE|PATCH|HEAD|OPTIONS} {ROCK_BASE_URL}/sandboxes/{sandbox_id}/proxy[/{path}] ``` @@ -68,7 +68,7 @@ For services that expect a `ws://` (or `wss://`) connection — typical for stre ### Endpoint -``` +```http ws(s)://{ROCK_BASE_URL}/sandboxes/{sandbox_id}/proxy/{path} ``` @@ -139,7 +139,7 @@ Requests violating these rules are rejected: ## 5. Quick decision guide -``` +```text Need to call a REST API inside the sandbox? → HTTP Proxy Need a bidirectional WebSocket to a WS server? → WebSocket Proxy ``` From 7f72e9b33a777bece7c8a0ad7f41ca5ef4050fb0 Mon Sep 17 00:00:00 2001 From: jiaoliao Date: Thu, 11 Jun 2026 08:55:54 +0000 Subject: [PATCH 3/4] opt format --- .../version-1.8.x/User Guides/sandbox_proxy.md | 13 +++++++------ .../version-1.8.x/User Guides/sandbox_proxy.md | 13 +++++++------ uv.lock | 2 +- 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/version-1.8.x/User Guides/sandbox_proxy.md b/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/version-1.8.x/User Guides/sandbox_proxy.md index f3667bb388..1ce6b2aa2d 100644 --- a/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/version-1.8.x/User Guides/sandbox_proxy.md +++ b/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/version-1.8.x/User Guides/sandbox_proxy.md @@ -8,8 +8,8 @@ ROCK Admin 提供了一层代理能力,可以让你**从集群外部**访问 | 模式 | 接入路径 | 适用场景 | |------|---------|---------| -| HTTP 代理 | `/sandboxes/{sandbox_id}/proxy/...` | REST API、Web UI、文件下载等任何 HTTP/1.1 流量 | -| WebSocket 代理 | `ws(s)://.../sandboxes/{sandbox_id}/proxy/...` | 实时通道、流式输出、浏览器端 WS 客户端 | +| HTTP 代理 | `/sandboxes/:sandbox_id/proxy/...` | REST API、Web UI、文件下载等任何 HTTP/1.1 流量 | +| WebSocket 代理 | `ws(s)://.../sandboxes/:sandbox_id/proxy/...` | 实时通道、流式输出、浏览器端 WS 客户端 | 两种模式都通过 `sandbox_id` 路由。沙箱本身**不需要**公网 IP — Admin 负责接收客户端请求,并将其转发到集群内对应的运行时实例。 @@ -21,8 +21,9 @@ ROCK Admin 提供了一层代理能力,可以让你**从集群外部**访问 ### 接入路径 -```http -{GET|POST|PUT|DELETE|PATCH|HEAD|OPTIONS} {ROCK_BASE_URL}/sandboxes/{sandbox_id}/proxy[/{path}] +```text +方法 (Methods) : GET | POST | PUT | DELETE | PATCH | HEAD | OPTIONS +URL : $ROCK_BASE_URL/sandboxes/:sandbox_id/proxy[/:path] ``` - 请求方法、请求头、查询字符串和请求体会原样转发到目标服务。 @@ -68,8 +69,8 @@ curl "$ROCK_BASE_URL/sandboxes/sb-123/proxy/items?rock_target_port=8080&limit=10 ### 接入路径 -```http -ws(s)://{ROCK_BASE_URL}/sandboxes/{sandbox_id}/proxy/{path} +```text +ws(s)://$ROCK_BASE_URL/sandboxes/:sandbox_id/proxy/:path ``` - 原始的 WebSocket 握手信息(subprotocol、自定义 header)会被透传。 diff --git a/docs/versioned_docs/version-1.8.x/User Guides/sandbox_proxy.md b/docs/versioned_docs/version-1.8.x/User Guides/sandbox_proxy.md index f78aa23880..e10d47db43 100644 --- a/docs/versioned_docs/version-1.8.x/User Guides/sandbox_proxy.md +++ b/docs/versioned_docs/version-1.8.x/User Guides/sandbox_proxy.md @@ -8,8 +8,8 @@ ROCK Admin exposes a proxy layer that lets you reach services running **inside** | Mode | Endpoint | Use it for | |------|----------|------------| -| HTTP Proxy | `/sandboxes/{sandbox_id}/proxy/...` | REST APIs, web UIs, file downloads, any HTTP/1.1 traffic | -| WebSocket Proxy | `ws(s)://.../sandboxes/{sandbox_id}/proxy/...` | Real-time channels, streaming, browser-based WS clients | +| HTTP Proxy | `/sandboxes/:sandbox_id/proxy/...` | REST APIs, web UIs, file downloads, any HTTP/1.1 traffic | +| WebSocket Proxy | `ws(s)://.../sandboxes/:sandbox_id/proxy/...` | Real-time channels, streaming, browser-based WS clients | Both modes route by `sandbox_id`. The sandbox does **not** need a public IP — Admin terminates the client connection and forwards it to the right runtime inside the cluster. @@ -21,8 +21,9 @@ Forward any HTTP request to a service inside the sandbox. ### Endpoint -```http -{GET|POST|PUT|DELETE|PATCH|HEAD|OPTIONS} {ROCK_BASE_URL}/sandboxes/{sandbox_id}/proxy[/{path}] +```text +Methods : GET | POST | PUT | DELETE | PATCH | HEAD | OPTIONS +URL : $ROCK_BASE_URL/sandboxes/:sandbox_id/proxy[/:path] ``` - Method, headers, query string, and body are forwarded as-is to the target service. @@ -68,8 +69,8 @@ For services that expect a `ws://` (or `wss://`) connection — typical for stre ### Endpoint -```http -ws(s)://{ROCK_BASE_URL}/sandboxes/{sandbox_id}/proxy/{path} +```text +ws(s)://$ROCK_BASE_URL/sandboxes/:sandbox_id/proxy/:path ``` - The original WebSocket handshake (subprotocols, custom headers) is forwarded. diff --git a/uv.lock b/uv.lock index eb6e373dfc..505ea671ae 100644 --- a/uv.lock +++ b/uv.lock @@ -4280,7 +4280,7 @@ wheels = [ [[package]] name = "rl-rock" -version = "1.8.0" +version = "1.9.0" source = { editable = "." } dependencies = [ { name = "anyio" }, From 563eb634c5e137ab6ed423481e6406e62d12b02c Mon Sep 17 00:00:00 2001 From: jiaoliao Date: Thu, 11 Jun 2026 11:35:40 +0000 Subject: [PATCH 4/4] opt format --- docs/versioned_docs/version-1.8.x/User Guides/sandbox_proxy.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/versioned_docs/version-1.8.x/User Guides/sandbox_proxy.md b/docs/versioned_docs/version-1.8.x/User Guides/sandbox_proxy.md index e10d47db43..c6c972056b 100644 --- a/docs/versioned_docs/version-1.8.x/User Guides/sandbox_proxy.md +++ b/docs/versioned_docs/version-1.8.x/User Guides/sandbox_proxy.md @@ -130,7 +130,7 @@ Requests violating these rules are rejected: | Symptom | Likely cause | |---------|--------------| -| `400 Bad Request: Cannot specify target port via multiple sources` | You set the port in two of {path, header, query} — pick one. | +| `400 Bad Request: Cannot specify target port via multiple sources` | You set the port in two of path, header or query — pick one. | | `400 Bad Request` / WS close `1008` with port-range message | Target port is `< 1024`, `> 65535`, or equal to `22`. | | WS close `1011` (`Proxy error: ...`) | Upstream service inside the sandbox returned an error or could not be reached. Check that the service is actually listening on the target port. | | `404 Not Found` from the upstream | The HTTP path inside the sandbox does not exist — verify the service's route, not the proxy URL. |