|
| 1 | +# Agent Sandbox Workspace Direct Preview |
| 2 | + |
| 3 | +## 1. Goal |
| 4 | + |
| 5 | +Replace sandbox file S3 export with a short-lived, read-only URL: |
| 6 | + |
| 7 | +```text |
| 8 | +https://agent-proxy.fastgpt.cn/preview/{token}/test/preview.html |
| 9 | +``` |
| 10 | + |
| 11 | +The token authenticates one sandbox workspace. The remaining URL is resolved as a workspace-relative |
| 12 | +path. File bytes flow from the sandbox IDE agent through `agent-sandbox-proxy`; FastGPT only authenticates |
| 13 | +the business session, signs the token, and resolves the provider endpoint. |
| 14 | + |
| 15 | +## 2. Scope |
| 16 | + |
| 17 | +- Replace HTML S3 preview with direct workspace preview. |
| 18 | +- Replace `sandbox_get_file_url` S3 uploads with direct workspace URLs. |
| 19 | +- Support `GET`, `HEAD`, content type, content length, ETag, and single byte ranges. |
| 20 | +- Preserve relative HTML references such as `./assets/app.js` and `../images/a.png`. |
| 21 | +- Keep existing `fs` and `terminal` WebSocket behavior unchanged. |
| 22 | +- Support provider endpoint resolution through `ISandbox.getEndpoint`. |
| 23 | + |
| 24 | +The existing S3 workspace archive lifecycle is unrelated and remains unchanged. |
| 25 | + |
| 26 | +## 3. Architecture |
| 27 | + |
| 28 | +```text |
| 29 | +Browser / model |
| 30 | + -> GET /preview/{token}/{workspacePath} |
| 31 | + -> agent-sandbox-proxy |
| 32 | + 1. verifies HMAC token locally |
| 33 | + 2. resolves the sandbox endpoint through FastGPT verifyTicket using an internal header |
| 34 | + 3. proxies an authenticated HTTP request to port 1319 |
| 35 | + -> fastgpt-ide-agent preview listener |
| 36 | + 1. validates the internal agent password |
| 37 | + 2. confines the path to FASTGPT_WORKDIR |
| 38 | + 3. streams the file response |
| 39 | +``` |
| 40 | + |
| 41 | +## 4. Token Contract |
| 42 | + |
| 43 | +Preview tokens reuse `AGENT_SANDBOX_PROXY_SECRET` and the existing business identity claims: |
| 44 | + |
| 45 | +```ts |
| 46 | +{ |
| 47 | + sourceType, |
| 48 | + sourceId, |
| 49 | + userId, |
| 50 | + chatId, |
| 51 | + channel: 'preview', |
| 52 | + permission: 'read', |
| 53 | + exp |
| 54 | +} |
| 55 | +``` |
| 56 | + |
| 57 | +The token never contains provider endpoints or the IDE agent password. The default lifetime remains two |
| 58 | +hours to preserve the existing `sandbox_get_file_url` contract. |
| 59 | + |
| 60 | +## 5. HTTP Contracts |
| 61 | + |
| 62 | +### 5.1 Public proxy |
| 63 | + |
| 64 | +```text |
| 65 | +GET|HEAD /preview/{token}/{*path} |
| 66 | +``` |
| 67 | + |
| 68 | +- Preview may use an independent `PREVIEW_PORT`; when omitted it shares `PORT` with WebSocket routes. |
| 69 | +- `token` is one base64url JWT path segment. |
| 70 | +- `path` is workspace-relative. |
| 71 | +- Other methods return `405`. |
| 72 | +- The upstream host and port always come from FastGPT ticket resolution. |
| 73 | + |
| 74 | +### 5.2 Sandbox preview listener |
| 75 | + |
| 76 | +```text |
| 77 | +GET|HEAD /preview/{*path} |
| 78 | +X-FastGPT-Agent-Token: {agentPassword} |
| 79 | +``` |
| 80 | + |
| 81 | +- Binds to the fixed internal address `0.0.0.0:1319`. |
| 82 | +- Uses `FASTGPT_WORKDIR` as the root. |
| 83 | +- Does not expose directory listings. |
| 84 | +- Rejects absolute paths, traversal, invalid encodings, and symlinks escaping the workspace. |
| 85 | + |
| 86 | +## 6. FastGPT Changes |
| 87 | + |
| 88 | +- Add reusable preview token signing and URL construction in the sandbox application layer. |
| 89 | +- Require `AGENT_SANDBOX_PREVIEW_PROXY_URL` as the public HTTP preview origin. It remains separate |
| 90 | + from the required WebSocket `AGENT_SANDBOX_PROXY_URL` even when both URLs use the same port. |
| 91 | +- Change `getHtmlPreviewLink` to validate the session and return a preview URL without reading/uploading |
| 92 | + the HTML file. |
| 93 | +- Change `sandbox_get_file_url` to return preview URLs without reading/uploading the files. |
| 94 | +- Extend internal ticket verification with `channel=preview` and endpoint port `1319`. |
| 95 | +- Update OpenAPI descriptions and tool descriptions to remove S3 semantics. |
| 96 | + |
| 97 | +## 7. Resource Reference Behavior |
| 98 | + |
| 99 | +Given: |
| 100 | + |
| 101 | +```text |
| 102 | +/preview/{token}/test/preview.html |
| 103 | +``` |
| 104 | + |
| 105 | +- `./assets/app.js` resolves to `/preview/{token}/test/assets/app.js` and works. |
| 106 | +- `../images/a.png` resolves to `/preview/{token}/images/a.png` and works. |
| 107 | +- `/assets/app.js` loses the token prefix and is intentionally unsupported in this path-based version. |
| 108 | + |
| 109 | +The sandbox system prompt must require relative asset paths for generated previews. |
| 110 | + |
| 111 | +## 8. Security |
| 112 | + |
| 113 | +- A preview URL is a bearer capability for read-only access to the token's whole workspace. |
| 114 | +- Preview content uses the existing proxy origin, which must remain separate from the FastGPT app origin. |
| 115 | +- Responses set `Referrer-Policy: no-referrer`, `X-Content-Type-Options: nosniff`, and |
| 116 | + `Cache-Control: private, no-store`. |
| 117 | +- Proxy and ingress logs must redact the token path segment. |
| 118 | +- The proxy sends tickets to FastGPT in `X-Sandbox-Ticket`, not the query string, so the main app's |
| 119 | + request logs do not contain bearer tokens. FastGPT temporarily accepts the old query transport for rollout. |
| 120 | +- The proxy does not accept an upstream URL, host, scheme, or port from the client. |
| 121 | +- Cached endpoint resolution uses SHA-256 token keys and never bypasses local token expiration checks. |
| 122 | + |
| 123 | +## 9. Compatibility And Rollout |
| 124 | + |
| 125 | +- Sandbox runtime images must include the preview listener before FastGPT starts issuing preview URLs. |
| 126 | +- Default deployments expose one proxy port; split WebSocket and Preview ports only when explicitly configured. |
| 127 | +- Old sandboxes without port 1319 return a clear proxy error; local integration uses a freshly built image. |
| 128 | +- Upgrade FastGPT before agent-sandbox-proxy: the app accepts both old query tickets and new header tickets, |
| 129 | + while the new proxy only uses the non-logging header transport. |
| 130 | +- `fs`, `terminal`, editor upload/download, and workspace S3 archive paths remain unchanged. |
| 131 | +- Local verification builds worktree images and runs them against the existing Docker dependencies. |
| 132 | + |
| 133 | +## 10. TODO |
| 134 | + |
| 135 | +- [x] Implement and test the IDE agent preview HTTP listener. |
| 136 | +- [x] Implement and test proxy preview routing and streaming relay. |
| 137 | +- [x] Add preview token signing, ticket verification, and URL helpers. |
| 138 | +- [x] Replace HTML preview S3 upload. |
| 139 | +- [x] Replace `sandbox_get_file_url` S3 upload. |
| 140 | +- [x] Update OpenAPI, environment/runtime contracts, prompt, and rollout notes. |
| 141 | +- [x] Build local sandbox and proxy images. |
| 142 | +- [x] Run TypeScript and Rust unit tests. |
| 143 | +- [x] Run an OpenSandbox end-to-end test with HTML, CSS, image, HEAD, Range, expiry, auth, and missing files. |
| 144 | +- [x] Cover traversal and escaping symlinks in unit tests. |
| 145 | +- [x] Run repository-wide final validation. |
0 commit comments