Admin API 完整参考
July 7, 2026 · View on GitHub
HotPlex Admin API 提供网关运维管理能力:会话管理、健康检查、监控指标、配置审计、日志查询和定时任务控制。默认监听 localhost:9999,独立于网关主端口(8888)。
交互式 API 控制台:可直接在浏览器中浏览和测试所有端点 → 打开 API 控制台 (基于 OpenAPI 规范自动生成,与代码保持同步)
认证
所有 Admin 端点(/admin/health 和 /admin/health/ready 除外)均需 Bearer Token 认证。Token 通过以下两种方式传递:
# 方式一:Authorization header(推荐)
curl -H "Authorization: Bearer <token>" http://localhost:9999/admin/stats
# 方式二:Query string(适用于浏览器场景)
curl http://localhost:9999/admin/stats?access_token=<token>
Token 使用 crypto/subtle.ConstantTimeCompare 进行常量时间比较,防止时序攻击。
Cookie 认证兜底(WebChat 多租户部署):当请求未携带 Bearer Token 时,若已启用 WebChat 多租户(
CookieAuth与本地账号身份提供者就绪),中间件会转而解析 chat session cookie,校验为 active 且角色为admin的用户后放行,并授予完整 scope 集合——嵌入式 WebChat 中已登录的管理员可直接访问 Dashboard/Bots/Cron,无需另发 admin token。Bearer 仍优先;standalone/CLI 部署(未启用多租户)保持仅 Bearer 行为。该 cookie 通道的写方法同样适用下文的「CSRF 同源校验」;跨源 cookie 调用还需在 CORSallowedOrigins显式列出 WebChat origin(*通配会抑制Allow-Credentials,浏览器将拒绝发送 session cookie)。
Token 配置
admin:
enabled: true
addr: "localhost:9999"
tokens: # 简单 token,使用 default_scopes
- "my-admin-token"
token_scopes: # 细粒度 scope token
"ops-token": ["session:read", "stats:read", "health:read"]
"admin-token": ["session:read", "session:write", "session:delete", "stats:read", "health:read", "admin:write"]
default_scopes: ["session:read", "session:write", "session:delete", "stats:read", "health:read", "admin:write"]
Scope 权限矩阵
不同的 Scope 控制着对 Admin API 不同模块的访问级别。
| Scope Token | Health | Sessions | Stats | Config | Debug | Cron | 覆盖的核心端点 (Endpoints) |
|---|---|---|---|---|---|---|---|
health:read | 🟢 Read | - | - | - | - | - | /admin/health/workers |
session:read | - | 🟢 Read | - | - | - | - | GET /admin/sessionsGET /admin/sessions/{id}/stats |
session:write | - | 🟠 Write | - | - | - | - | POST /admin/sessions/{id}/terminate |
session:delete | - | 🔴 Delete | - | - | - | - | DELETE /admin/sessions/{id} |
stats:read | - | - | 🟢 Read | - | - | - | GET /admin/statsGET /admin/metrics |
config:read | - | - | - | 🟢 Read | - | - | POST /admin/config/validate |
config:write | - | - | - | 🟠 Write | - | - | POST /admin/config/rollback |
admin:read | - | - | - | - | 🟢 Read | 🟢 Read | GET /admin/logsGET /admin/debug/...GET /admin/botsGET /admin/cron/jobs |
admin:write | - | - | - | - | - | 🟠 Write | POST/PATCH/DELETE /admin/cron/jobsPOST /admin/cron/jobs/{id}/run |
💡 图例:🟢 Read (只读查询) | 🟠 Write (状态变更/操作) | 🔴 Delete (物理删除)
安全中间件
按以下顺序执行:
- CORS —
Access-Control-Allow-Origin: *,允许GET/POST/DELETE/OPTIONS - Panic Recovery —
defer recover()捕获 handler panic,返回500 Internal Server Error - Rate Limiting — 令牌桶算法(默认 10 req/s,burst 20),超限返回
429 Too Many Requests - IP Whitelist — CIDR 匹配(默认
127.0.0.0/8,10.0.0.0/8),使用r.RemoteAddr防止 X-Forwarded-For 伪造 - Token Auth — Bearer Token 提取 + scope 校验
Rate Limit 和 IP Whitelist 支持配置热重载,无需重启生效。
操作审计
所有 admin 写操作(POST/PUT/PATCH/DELETE)均进入 user_activity 防篡改审计表(issue #833),并在兼容期继续输出结构化 slog 事件(admin_audit,issue #788 A5)。user_activity 是权威审计载体;admin_audit 仅保留给既有日志看板迁移使用。
覆盖范围:
/admin/*(端口 9999,Bearer Token):在AdminAPI.Middleware的审计 defer 中统一记录——无论请求最终成功或失败,写方法都会产生一条审计记录;(method, path)映射为稳定 action 枚举,未映射路由回退"<method> <path>",确保无写操作静默丢失。/api/admin/*与/api/workspaces/*(端口 8888,Cookie):在各 handler 的成功/失败路径显式记录;CSRF 同源校验拦截的跨站写请求同样记审计(actionauth.denied,详见下文「CSRF 同源校验」)。
审计字段:
| 字段 | 说明 |
|---|---|
actor_user_id | 操作者标识:Cookie 通道为用户 uid;Bearer 通道为 admin-token;认证未解析或失败时为 anonymous |
action | 稳定动作枚举(见下表),日志看板按此过滤,不可重命名 |
target | 请求路径(含资源 id) |
result | ok(成功)/ failed(操作失败)/ denied(认证或授权拒绝) |
action 枚举:
| 资源域 | action |
|---|---|
| 网关 | gateway.restart |
| Bot | bot.create / bot.update / bot.delete |
| API Key | apikey.create / apikey.update / apikey.delete |
| Cron | cron.create / cron.update / cron.delete / cron.trigger |
| Session | session.delete / session.terminate / session.patch / session.put |
| Audit | audit.identity_link.create / audit.identity_link.delete |
| Config | config.rollback / config.validate |
| 多租户成员/邀请 | member.status.update / invitation.create / invitation.delete |
| 认证拒绝 | auth.denied |
端点总览
健康检查
| 方法 | 路径 | Scope | 说明 |
|---|---|---|---|
| GET | /admin/health | 无需认证 | 综合健康状态(gateway + DB + workers) |
| GET | /admin/health/workers | health:read | Worker 粒度健康状态 |
| GET | /admin/health/ready | 无需认证 | 就绪探针(k8s readiness) |
GET /admin/health — 无需认证,适合负载均衡器探活。返回 status(healthy/degraded)、checks(gateway + database + workers)和 version。数据库不可用时降级为 degraded,database.error 附带错误信息。
GET /admin/health/workers — Worker 粒度健康状态,含 workers[](healthy/type/pid)和 checked_at。任一 Worker 不健康时返回 503。
会话管理
| 方法 | 路径 | Scope | 说明 |
|---|---|---|---|
| GET | /admin/sessions | session:read | 列出会话(分页) |
| GET | /admin/sessions/{id} | session:read | 获取单个会话 |
| DELETE | /admin/sessions/{id} | session:delete | 物理删除会话 |
| POST | /admin/sessions/{id}/terminate | session:write | 终止会话(状态迁移) |
| GET | /admin/sessions/{id}/stats | session:read | 会话 Turn 统计 |
GET /admin/sessions — 支持 query 参数过滤:
curl -H "Authorization: Bearer $TOKEN" \
"http://localhost:9999/admin/sessions?limit=50&offset=0&platform=slack&user_id=U12345"
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
limit | int | 100 | 每页数量(上限 1000) |
offset | int | 0 | 偏移量 |
platform | string | "" | 按平台过滤 |
user_id | string | "" | 按用户过滤 |
POST /admin/sessions/{id}/terminate — 将会话状态迁移至 terminated(软终止,保留记录)。DELETE 则为物理删除。
注意:会话创建不通过 Admin API,而是通过 Gateway API(
POST /api/sessions)或 WebSocketinit握手完成。Admin API 仅提供只读查询和终止/删除操作。
监控指标
| 方法 | 路径 | Scope | 说明 |
|---|---|---|---|
| GET | /admin/stats | stats:read | 网关聚合统计 |
| GET | /admin/metrics | stats:read | Prometheus 格式指标 |
GET /admin/stats — 返回 gateway(uptime/websocket_connections/sessions_active/sessions_total)、workers(按 worker_type 分组统计)和 database(sessions_count)。
配置管理
| 方法 | 路径 | Scope | 说明 |
|---|---|---|---|
| POST | /admin/config/validate | config:read | 校验配置片段 |
| POST | /admin/config/rollback | config:write | 回滚到历史版本 |
POST /admin/config/validate — 校验配置合法性(不应用),请求体最大 1MB:
curl -X POST -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"gateway":{"addr":":8888"},"pool":{"max_size":50}}' \
http://localhost:9999/admin/config/validate
支持校验:gateway(buffer sizes >= 0)、db(path 长度 <= 4096)、worker(timeouts)、pool(max_size 1-10000)。返回 { "valid", "errors[]", "warnings[]" }。
POST /admin/config/rollback — 回滚到指定版本,请求体 {"version": 3},返回 { "ok", "rolled_back", "history_index" }。无 configWatcher 时返回 503。
日志与调试
| 方法 | 路径 | Scope | 说明 |
|---|---|---|---|
| GET | /admin/logs | admin:read | 最近日志(环形缓冲区) |
| GET | /admin/debug/sessions/{id} | admin:read | 会话调试快照 |
GET /admin/logs — 从 100 条环形缓冲区读取,?limit=N(最大 1000)。返回 { "logs[]", "total", "limit" }。
GET /admin/debug/sessions/{id} — 会话详情 + 调试快照(debug.available、has_worker、turn_count、last_seq_sent、worker_health)。
Cron 定时任务
| 方法 | 路径 | Scope | 说明 |
|---|---|---|---|
| GET | /admin/cron/jobs | admin:read | 列出所有任务 |
| GET | /admin/cron/jobs/{id} | admin:read | 获取单个任务 |
| POST | /admin/cron/jobs | admin:write | 创建任务 |
| PATCH | /admin/cron/jobs/{id} | admin:write | 更新任务 |
| DELETE | /admin/cron/jobs/{id} | admin:write | 删除任务 |
| POST | /admin/cron/jobs/{id}/run | admin:write | 手动触发执行 |
| GET | /admin/cron/jobs/{id}/runs | admin:read | 执行历史 |
请求体约定:
- POST /admin/cron/jobs — JSON body 含
name、schedule(cron:/every:/at: 前缀)、message、bot_id、owner_id、enabled。返回201 Created。 - PATCH /admin/cron/jobs/{id} — 部分更新,JSON body。返回
204 No Content。 - POST /admin/cron/jobs/{id}/run — 手动触发(异步),返回
202 Accepted。 - GET /admin/cron/jobs/{id}/runs — 查询执行历史。
Cron 未启用时返回 503 Service Unavailable。
Bot 管理
Bot 状态查询、配置管理和 Agent 配置文件操作端点。
| 方法 | 路径 | Scope | 说明 |
|---|---|---|---|
| GET | /admin/bots | admin:read | 列出所有活跃 bot |
| GET | /admin/bots/{name} | admin:read | 单个 bot 详情 |
| POST | /admin/bots | admin:write | 注册新 bot |
| PATCH | /admin/bots/{name} | admin:write | 更新 bot 配置(部分更新) |
| DELETE | /admin/bots/{name} | admin:write | 删除 bot 注册 |
| GET | /admin/bots/config | admin:read | 列出所有 bot 配置 |
| GET | /admin/bots/{name}/config | admin:read | 单个 bot 完整配置 |
| GET | /admin/bots/{name}/config/{file} | admin:read | 读取 Agent 配置文件(如 SOUL.md、AGENTS.md) |
| PUT | /admin/bots/{name}/config/{file} | admin:write | 写入 Agent 配置文件 |
| GET | /admin/bots/platform/{platform}/config/{file} | admin:read | 读取平台级 channel 默认配置文件(webchat 等,绕过 registry 寻址 dir/{platform}/ 层) |
| PUT | /admin/bots/platform/{platform}/config/{file} | admin:write | 写入平台级 channel 默认配置文件 |
| GET | /admin/bots/{name}/preview | admin:read | 预览组装后的系统提示(B+C 通道完整输出) |
GET /admin/bots — 返回所有活跃 bot 列表,含 name、platform、worker_type、状态等信息。
POST /admin/bots — 注册新 bot,JSON body 含平台凭证和配置。返回 201 Created。
PATCH /admin/bots/{name} — 部分更新 bot 配置(凭证、worker_type 等)。返回 204 No Content。
GET /admin/bots/{name}/preview — 返回该 bot 组装后的完整系统提示,包含 B 通道(directives)和 C 通道(context)内容,便于调试 Agent 人格配置。
**PUT /admin/bots/{name}/config/{file}** — 写入指定 Agent 配置文件(如 SOUL.md、AGENTS.md、USER.md)。请求体为文件内容,Content-Type 为 text/plain`。
GET/PUT /admin/bots/platform/{platform}/config/{file} — 读写平台级 channel 默认 Agent 配置文件。{platform} 为平台标识(如 webchat),绕过 messaging registry 直接寻址 dir/{platform}/ 层,让无 bot 实例的平台(webchat team-default)获得与消息平台 bot 对等的配置编辑能力。文件白名单与 bot 级端点一致(SOUL.md/AGENTS.md/SKILLS.md/USER.md/MEMORY.md),复用 /admin/* 中间件鉴权(admin:read/admin:write)与审计。
网关重启
| 方法 | 路径 | Scope | 说明 |
|---|---|---|---|
| POST | /admin/restart | admin:write | 触发网关重启 |
POST /admin/restart — 异步触发网关重启。Gateway 在 500ms 延迟后执行重启,立即返回 { "status": "restarting" }。使用 restart helper(独立 PGID)确保安全隔离。未配置 restart handler 时返回 503。
用户行为审计
/admin/activity 系列端点查询 issue #833 的 user_activity 表。所有读端点需要 admin:read;导出默认脱敏,include_pii=true 需要 admin:write。每次导出都会写入 system.audit_export meta-audit。
| 方法 | 路径 | Scope | 说明 |
|---|---|---|---|
| GET | /admin/activity | admin:read | 查询全局活动时间线 |
| GET | /admin/activity/export | admin:read | 导出全局活动时间线(`format=json |
| GET | /admin/users/{id}/activity | admin:read | 查询单个原生 user_id 的活动 |
| GET | /admin/users/{id}/activity/export | admin:read | 导出单个原生 user_id 的活动 |
| GET | /admin/audit/identity-links | admin:read | 列出跨通道身份链接 |
| POST | /admin/audit/identity-links | admin:write | 创建或更新身份链接 |
| DELETE | /admin/audit/identity-links/{id} | admin:write | 删除身份链接 |
查询参数:
| 参数 | 说明 |
|---|---|
user_id | 按审计行原始 user_id 查询 |
principal_user_id | 按本地 canonical 用户查询;会展开 audit_identity_links 中的所有平台原生 subject |
action | 精确匹配 action,如 tool.call / admin.bot.create |
outcome | success / failure / denied |
from / to | RFC3339 时间范围 |
limit / offset | 分页 |
format | 导出格式:json 或 csv |
POST /admin/audit/identity-links — JSON body:
{
"principal_user_id": "local-user-uuid",
"provider": "feishu",
"subject": "ou_xxx",
"subject_type": "platform",
"display_name": "Alice",
"email": "alice@example.com"
}
principal_user_id、provider、subject 必填;subject_type 默认为 platform,允许 registered / platform / system / anonymous。UNIQUE(provider, subject) 保证一个平台原生 ID 只归属一个 principal。写操作通过 middleware 审计为 admin.audit.identity_link.create / admin.audit.identity_link.delete。
API Key 用户管理
管理 API Key 到用户身份的映射,用于企业级多用户 Session 隔离。每个 user_id 与 API Key 为 1:1 映射(一个 user_id 仅能关联一个 API Key),创建或更新时若 user_id 已被其他映射占用则返回 409 Conflict。需要数据库支持(SQLite 或 PostgreSQL),未配置 DB resolver 时返回 501 Not Implemented。
| 方法 | 路径 | Scope | 说明 |
|---|---|---|---|
| GET | /admin/api-keys | admin:read | 列出所有 API Key 映射 |
| POST | /admin/api-keys | admin:write | 创建 API Key 映射 |
| GET | /admin/api-keys/{id} | admin:read | 获取单个映射详情 |
| PATCH | /admin/api-keys/{id} | admin:write | 更新映射 |
| DELETE | /admin/api-keys/{id} | admin:write | 删除映射 |
POST /admin/api-keys — 创建 API Key → UserID 映射。JSON body 含 user_id(必填,最长 128 字符)和 description(可选,最长 512 字符)。API Key 由系统自动生成(24 字节随机 hex)。返回 201 Created;若 user_id 已存在则返回 409 Conflict。
GET /admin/api-keys — 返回所有映射列表,api_key 字段自动脱敏(仅显示前 8 + 后 4 位)。
PATCH /admin/api-keys/{id} — 更新指定映射的 user_id 和 description。若新 user_id 与其他映射冲突则返回 409 Conflict。
DELETE /admin/api-keys/{id} — 物理删除映射,同时清除缓存的 resolver 条目。
Workspace 管理(admin 控制台)
🆕 issue #807 — admin 控制台的 workspace 全局视图。区别于用户自助的
/api/workspaces/*(见下文 Gateway API 段):这两个端点走 admin 双通道鉴权(Bearer + Cookie fallback,强制role==admin && status==active),列出全平台 workspace 并支持 inline 修改permission_mode。
| 方法 | 路径 | Scope | 说明 |
|---|---|---|---|
| GET | /admin/workspaces | admin:read | 列出所有 workspace(带 owner 可读标识) |
| PATCH | /admin/workspaces/{id} | admin:write | 修改单个 workspace 的 permission_mode |
GET /admin/workspaces — 返回所有 active workspace,LEFT JOIN users 带上 owner 可读标识(owner_display_name + owner_username),admin 无需面对裸 UUID。owner 行缺失时(并发删除窗口)owner 字段回落为空串、workspace 仍返回。响应:{"workspaces":[{id, owner_user_id, owner_display_name, owner_username, name, work_dir, permission_mode, status, created_at, updated_at, agent_config_overrides, worker_preference}]}。读操作不审计。
PATCH /admin/workspaces/{id} — 仅接受 permission_mode 字段(admin 控制台范围最小化:不暴露 work_dir/name 修改,避免 admin 误改用户工作目录;这些仍走用户自助 /api/workspaces/{id})。JSON body:{"permission_mode":"<tier>"},permission_mode 必须显式提供(缺失返回 400 BAD_REQUEST);值为 4 档之一(bypass/auto-edit/workspace/read-only)或空串(清除覆盖,回落 config.worker.default_permission_mode,缺省 workspace)。返回更新后的 workspace 裸行。
⏱️ 生效语义:写库后运行中 session 不受影响(worker 进程已注入权限参数);新 session(新对话 /
/reset)初始化时resolveWorkspacePermissionMode读到新值。UI 标注「对新会话生效」。
🛡️ 审计:PATCH 写操作由 middleware 级
admin_audit统一记录,action =workspace.permission_mode.update,actor = uid(Cookie 通道)或admin-token(Bearer),target =/admin/workspaces/{id}。
PATCH 错误码:400 INVALID_PERMISSION_MODE(档位非法)/ 400 BAD_REQUEST(body 缺 permission_mode)/ 404 WORKSPACE_NOT_FOUND(id 不存在)/ 409 WORKSPACE_VERSION_MISMATCH(updated_at CAS 冲突;或并发更新下 workspace 恰有活跃会话时的 CAS heuristic 误判——本端点只改 permission_mode、不动 work_dir,活跃会话不真正阻塞,故两种成因都应重新 GET 最新 updated_at 后重试,新值仅对新会话生效)。
Gateway API 端点
Gateway API(/api/sessions)监听在网关主端口(8888),面向客户端 SDK 和 WebSocket 连接,使用 API Key 认证(非 Bearer Token)。
| 方法 | 路径 | 认证 | 说明 |
|---|---|---|---|
| GET | /api/sessions | API Key | 列出当前用户的会话 |
| POST | /api/sessions | API Key | 创建会话 |
| GET | /api/sessions/{id} | API Key | 获取单个会话 |
| DELETE | /api/sessions/{id} | API Key | 删除会话 |
| POST | /api/sessions/{id}/cd | API Key | 切换工作目录 |
| GET | /api/sessions/{id}/history | API Key | 获取会话历史 |
| GET | /api/sessions/{id}/events | API Key | 获取会话事件流 |
| GET | /api/workers | API Key | 列出 Worker 类型及二进制安装状态 |
所有 Gateway API 端点启用 CORS(Access-Control-Allow-Origin: *),支持 GET、POST、DELETE、OPTIONS 方法。
GET /api/workers — 返回所有已注册 Worker 类型(claude_code / opencode_server / codex_cli / acp)的二进制安装状态,供客户端(如 WebChat「新建会话」弹窗)动态过滤可选引擎,避免误选未安装的 Worker。命令名取自配置(worker.<type>.command),缺失时回落到各类型的默认二进制(claude / opencode / codex / hermes),再经 exec.LookPath 探测。响应体为 JSON 数组:[{"type":"<worker_type>","installed":<bool>,"path":"<abs_path, omitempty>"}]。
WebChat 多租户端点(spec ① / ④ / ⑥)
WebChat 多租户登录与工作区管理端点,同样监听在网关主端口 8888,默认使用 Cookie 认证(登录后签发的 HMAC Cookie);其中 workspace CRUD 还接受 API Key(跨通道租户接入,详见下方「Workspace 管理」)。仅在启用 WebChat 多租户(CookieAuth + WorkspaceStore 就绪)时注册,普通 SDK/WebSocket 集成不涉及。
账号登录(spec ①):
| 方法 | 路径 | 认证 | 说明 |
|---|---|---|---|
| POST | /api/auth/login | 无 | 内建账号登录(username/password),成功签发 Cookie |
| POST | /api/auth/logout | Cookie | 登出并清除 Cookie |
| GET | /api/auth/me | Cookie | 当前登录用户信息 |
| POST | /api/auth/accept-invite | 无(邀请码) | 凭邀请码(body code)注册本地账号并自动登录(签发 cookie,返回 first_login:true),加入 workspace |
| GET | /api/auth/bootstrap-status | 无 | 首次部署引导状态(是否已存在管理员),登录页据此决定是否引导建号 |
企业 SSO(spec ④,OIDC + PKCE):
| 方法 | 路径 | 认证 | 说明 |
|---|---|---|---|
| GET | /api/auth/oauth/providers | 无 | 列出已配置的 SSO provider(登录页据此渲染按钮) |
| GET | /api/auth/oauth/{provider}/login | 无 | 发起 OIDC 登录,重定向到 IdP(name 须匹配 [a-z0-9-]+) |
| GET | /api/auth/oauth/{provider}/callback | state cookie | OIDC 回调,完成 token exchange + ID Token 验证后签发 Cookie |
GET /api/auth/oauth/providers 始终返回稳定 envelope:
{
"providers": [
{ "name": "keycloak", "display_name": "企业 SSO" }
]
}
Workspace 管理(spec ⑥ A1 / ⑦ 跨通道租户):
Workspace CRUD 是跨通道租户锚:同一个 users.id 无论经 API Key(header X-API-Key 或 query api_key,面向第三方集成)还是 Cookie(WebChat UI)进入,都能拥有并管理自己的 workspace。鉴权链为 API Key 优先、Cookie 兜底(AuthenticateRequest),与 session REST、WS upgrade 对齐。work_dir workspace 级可改:PATCH 携带 work_dir 时校验 owner 沙箱(403 WORK_DIR_OUTSIDE_SANDBOX)且 workspace 必须无活跃会话(409 WORKSPACE_NOT_EMPTY,因 work_dir 进 session key 派生)。session 级 switch-workdir 对 workspace-bound session 返回 400 WORK_DIR_IMMUTABLE(worker 须跟随 workspace)。详见 WebChat-Workspace-Create-WorkDir-Prefix-Spec §5.1.4。
| 方法 | 路径 | 认证 | 说明 |
|---|---|---|---|
| POST | /api/workspaces | API Key / Cookie | 创建 workspace |
| GET | /api/workspaces | API Key / Cookie | 列出当前用户可访问的 workspace |
| GET | /api/workspaces/{id} | API Key / Cookie | workspace 详情(含归属校验) |
| PATCH | /api/workspaces/{id} | API Key / Cookie | 更新 workspace(乐观并发,见下) |
| DELETE | /api/workspaces/{id} | API Key / Cookie | 删除 workspace |
🔒
permission_mode为 admin-only 字段(r3 #804):POST/api/workspaces与 PATCH/api/workspaces/{id}仅 admin 可设置/修改permission_mode。非 admin 请求体携带该字段(含空串清空)一律返回403 PERMISSION_DENIED(message:permission_mode can only be configured by admins);非 admin 仍可改name/work_dir/agent_config_overrides/worker_preference。未显式配置的 workspace 由 bridge 注入config.worker.default_permission_mode(缺省workspace)。
PATCH 乐观并发(CAS):更新基于
updated_at做乐观锁——服务端UPDATE ... WHERE id = ? AND updated_at = ?,调用方缓存的updated_at不再匹配(被并发修改)时影响 0 行,返回409 WORKSPACE_VERSION_MISMATCH("workspace concurrently modified, please re-fetch and retry")。客户端无需在 body 显式传版本字段(先 GET 取最新值再 PATCH),收到 409 后应重新 GET 再重试,避免静默 lost update。
PATCH body 字段语义:
name、agent_config_overrides传空字符串表示不更新(无法通过 PATCH 清空这两项);worker_preference为指针类型以区分三态——省略(字段未传)不更新、空字符串""显式清除回默认 worker、非空设为指定类型(校验失败返回400 INVALID_WORKER_TYPE);work_dir为 workspace 级可变字段——省略/空字符串不更新,非空时校验 owner 沙箱(403 WORK_DIR_OUTSIDE_SANDBOX)、值变更须 workspace 无活跃会话(409 WORKSPACE_NOT_EMPTY,因 work_dir 进 session key 派生)、且未被该 owner 其他 workspace 占用(409 WORK_DIR_TAKEN)后更新(见上)。
Workspace 用户与邀请管理(spec ⑥,admin 维度):
| 方法 | 路径 | 认证 | 说明 |
|---|---|---|---|
| GET | /api/admin/users | Cookie | 列出 workspace 用户 |
| PATCH | /api/admin/users/{id} | Cookie | 启用/禁用用户(disable 后 per-request 即时拦截) |
| POST | /api/admin/invitations | Cookie | 创建邀请码 |
| GET | /api/admin/invitations | Cookie | 列出邀请 |
| DELETE | /api/admin/invitations/{id} | Cookie | 删除邀请 |
GET /api/admin/invitations — 每条邀请额外返回服务器计算的
is_expired(true表示邀请码未被使用且已过期),客户端无需自行比对expires_at与本地时钟,规避时钟漂移。
⚠️ 注意区分:此处的
/api/admin/*(端口8888,Cookie 认证,WebChat workspace 维度)与本页上方「认证」章节描述的 Admin API(端口9999,Bearer Token,网关运维维度)是两套独立端点,认证模型和作用域完全不同,不要混淆。
🔒 CSRF 同源校验(写方法):
/api/admin/*与/api/workspaces/*的状态变更方法(POST/PATCH/DELETE)挂载同源验证中间件,防御针对 SameSite=None session cookie 的跨站写请求;GET一律放行。写方法需提供「同源证明」之一——浏览器Sec-Fetch-Site取值为same-origin/same-site,或Origin精确命中 CORSallowedOrigins(*通配不满足:SameSite=None cookie 恰在 allowlist 宽松时跨站送达)。未通过返回403 FORBIDDEN(message:cross-origin write blocked)并记入 admin 审计日志。嵌入式 WebChat SPA 与网关同源,天然通过;跨域前端需将自身 origin 列入allowedOrigins。
错误响应格式
所有错误统一返回 JSON envelope(Content-Type: application/json),形状为:
{"error":{"code":"NOT_FOUND","message":"resource not found"}}
code 为机器可读的大写下划线标识符,message 为人类可读的英文描述。客户端应依据 code 做分支,而非解析 message。此信封由 web.WriteAppError 统一生成,Admin API(Bearer)与 /api/admin/*(Cookie)共用同一形状。
错误码表
| HTTP | code | 典型场景 |
|---|---|---|
| 400 | BAD_REQUEST | JSON 解析失败、参数校验错误、非法枚举值 |
| 400 | INVALID_CONFIG_JSON | workspace agent_config_overrides 不是合法 JSON 对象 |
| 400 | UNKNOWN_CONFIG_FILE | workspace overrides 含未知配置文件键 |
| 400 | CONFIG_TOO_LARGE | workspace overrides 超过单文件大小上限 |
| 400 | INVALID_CONFIG_VALUE | workspace overrides 值类型/内容非法 |
| 400 | INVALID_WORK_DIR | workspace work_dir 路径无法展开或不存在 |
| 400 | INVALID_WORKER_TYPE | workspace worker_preference 非已注册 worker 类型 |
| 401 | UNAUTHORIZED | Token 缺失或无效 |
| 401 | INVALID_CREDENTIALS | Cookie 会话失效或未登录 |
| 403 | INSUFFICIENT_SCOPE | Bearer Token scope 不满足(Admin API) |
| 403 | FORBIDDEN | 非管理员访问管理端点,或请求 IP 不在白名单(见「安全中间件」IP Whitelist,两类 403 共用同一 code) |
| 403 | USER_DISABLED | 用户已被禁用 |
| 403 | WORKSPACE_FORBIDDEN | 非所有者访问他人 workspace(且非 admin) |
| 403 | WORK_DIR_FORBIDDEN | workspace work_dir 命中安全黑名单(系统目录等) |
| 403 | WORK_DIR_OUTSIDE_SANDBOX | workspace work_dir 不在 owner 沙箱前缀下 |
| 403 | PERMISSION_DENIED | 非 admin 试图在 workspace Create/Update 设置或修改 permission_mode(admin-only 字段,r3 #804) |
| 404 | NOT_FOUND | Session/Cron Job/Invitation 未找到 |
| 404 | WORKSPACE_NOT_FOUND | workspace id 不存在 |
| 409 | CONFLICT | 资源状态冲突 |
| 409 | WORKSPACE_VERSION_MISMATCH | PATCH workspace 乐观并发冲突(updated_at CAS 失败,re-fetch 后重试) |
| 409 | WORKSPACE_NOT_EMPTY | workspace 存在活跃会话,拒绝改 work_dir / 删除 |
| 409 | WORK_DIR_TAKEN | workspace work_dir 已被该 owner 的其他 workspace 占用 |
| 429 | RATE_LIMITED | 触发 Rate Limit |
| 500 | INTERNAL | Handler panic、未分类内部错误 |
| 501 | NOT_IMPLEMENTED | 该接口尚未实现 |
| 503 | SERVICE_UNAVAILABLE | 数据库故障、Cron 未启用 |
| 503 | NO_IDP | 未配置身份提供者 |
常用操作示例
# 快速健康检查(无需 Token)
curl http://localhost:9999/admin/health
# 查看活跃会话
curl -H "Authorization: Bearer $TOKEN" \
http://localhost:9999/admin/sessions?limit=10
# 查看 Prometheus 指标
curl -H "Authorization: Bearer $TOKEN" \
http://localhost:9999/admin/metrics
# 终止异常会话
curl -X POST -H "Authorization: Bearer $TOKEN" \
http://localhost:9999/admin/sessions/abc-123/terminate
# 查看最近日志
curl -H "Authorization: Bearer $TOKEN" \
"http://localhost:9999/admin/logs?limit=20"
# 调试特定会话
curl -H "Authorization: Bearer $TOKEN" \
http://localhost:9999/admin/debug/sessions/abc-123
# 触发 Cron 任务
curl -X POST -H "Authorization: Bearer $TOKEN" \
http://localhost:9999/admin/cron/jobs/daily-health/run