one serve
本地 Web UI 管理 Workspace、Project 与机器级 Profile。
one serve 启动一个仅监听 127.0.0.1 的本地 Dashboard,并自动打开浏览器。Dashboard 会列出这台机器上已识别的 Workspace,让你切换 Workspace、以草稿方式修改并审阅 Workspace 环境变量 Backend 与 Project 配置、管理 Infisical 密钥,以及管理 one configure 使用的机器级 Profile。
为什么仍不让 AI 直接编辑 Profile 文件:里面是 API key、kubeconfig path、registry token,泄漏代价高于 AI 能省下的几次输入。one serve 是把这些字段从命令行 / agent 上下文里物理隔离出来的入口。
用法
one serve [options]
启动后阻塞在前台,按 Ctrl-C 退出。Workspace 环境变量 Backend 与 Project 配置修改先保留为浏览器草稿;右上角保存按钮展示精确差异,用户确认后,Project 修改通过 revision 校验的原子 Manifest patch 发布,Backend 修改通过 revision 校验的 env switch 流程发布。选择 Infisical 会初始化并持久化 Workspace 的 Infisical 项目绑定,但不会在 provider 之间迁移已有密钥值。源码仍保持只读。Profile 编辑与 one configure 共用 ~/.config/one/{config,credentials}.json;Workspace/Project 选择只把 Profile 名写入 ~/.config/one/profile-bindings.json。
参数
| 参数 | 说明 |
|---|---|
--host <host> | 绑定主机;只接受 loopback(默认 127.0.0.1,也允许 localhost、::1)。非 loopback 直接报 SERVE_BIND_FORBIDDEN,无逃生 |
--port <n> | 监听端口;默认 0 = 由内核分配空闲端口,避免冲突 |
--open | 完成后自动用浏览器打开(默认 true);CI / headless / WSL / 远程 SSH 场景传 --open=false 关闭 |
-o, --output <fmt> | json / yaml / text(默认按 TTY 检测) |
交互模式
one serve 没有终端交互式向导。浏览器可以把 Workspace 环境变量 Backend,以及白名单内的 Project 运行、环境、容器和部署配置加入草稿,再统一确认写入 Manifest。Profile 绑定仍是独立的机器本地保存。Workspace 使用 env/infisical 时,还可以列出 key,并逐条新增、显示、修改或删除远端值。
本地人工配置直接运行 one serve;脚本、CI、agent 可以用 --open=false 获取普通的 loopback URL 并直接调用 API。由于 API 能读取和修改敏感配置,不要在存在不可信本地进程的共享机器上运行它。
Workspace 识别与持久化
One CLI 在两种情况下把 Workspace 登记到本机列表:
one create完整创建成功后;- 在 Workspace 根目录或任意子目录执行
one serve时。
记录保存在 XDG-aware 的 ~/.config/one/workspaces.json。这里只存本机条目 ID、Manifest Workspace ID、名称、规范化绝对路径和最近访问时间,不复制 Project 配置、Backend、Profile 或凭据。Workspace 目录暂时不可用时会保留并显示为 missing;Forget 只删除本机列表记录,不会删除目录、Manifest、Profile 或凭据。
在 Workspace 外运行 one serve 也可以打开历史列表。Dashboard 默认选中本次启动所在的 Workspace;没有当前 Workspace 时,选中最近访问且可用的记录。
环境选择与本机存储
Dashboard UI 环境选择器只提供开发(?env=dev)、预览(?env=preview)和生产(?env=prod)三种;UI 中的未知 query 值会回退到开发环境。切换它不会向 Manifest 添加环境,也不会升级 Manifest schema。核心/API 存储仍可表示 staging 等由其他 CLI/API 工作流传入的安全自定义 ID。
全局 Settings 页会隐藏环境选择器,因为 Profile 定义和 CRUD 是机器全局的,不按环境分区。链接仍保留 query,返回 Workspace/Project 时会恢复之前的绑定上下文。
~/.config/one/
├── config.json # Profile 名、非敏感字段、default、旧绑定
├── credentials.json # Profile 凭据
├── profile-bindings.json # v1:规范化 root + environment -> Profile 名
└── workspaces.json # 已识别 Workspace 注册表
profile-bindings.json 是 mode 0600 原子替换的机器本地 v1 存储。它用规范化 Workspace root 作为 key,所以即使两份代码拷贝带着相同 Manifest Workspace ID,选择也互不影响。文件不含凭据值,也不会写入任何代码库。
对一个 (domain, backend),Profile 解析顺序为:
- 本次命令的
--profile; - Project + environment 绑定;
- Workspace + environment 绑定;
config.json中的旧 Project 绑定;config.json中的旧 Workspace 绑定;- 机器 default。
输出
绑定成功后立即向 stdout 发出一次启动信封,然后阻塞:
{
"schema": "one-cli/serve/v2",
"status": "listening",
"url": "http://127.0.0.1:54321/",
"host": "127.0.0.1",
"port": 54321
}
启动 URL 不包含登录信息,API 也不使用 session token。进程退出后服务随即停止;如果以后有另一个 one serve 复用了相同端口,原 URL 会指向新的本地进程。
安全模型
one serve 持有 profile 文件,profile 文件持有凭据,因此这个本地服务属于敏感接口。它只信任本机边界,不做 session 级身份认证;任何能访问该 loopback 端口的本地进程都可以调用 API。以下防御仍然生效:
| 防御层 | 挡住的威胁 | 行为 |
|---|---|---|
| Host header 校验 | DNS rebinding(攻击者域名 resolve 到 127.0.0.1) | Host 必须是绑定的 127.0.0.1:<port> 或 localhost:<port>,否则返 421 Misdirected Request |
| Origin 校验(仅 mutating) | 跨源表单 / 脚本 POST | POST/PUT/DELETE 的 Origin 必须等于服务 self-origin,否则 403 Forbidden |
| 类型化代码库发布器 | 过期草稿或越权字段覆盖配置 | Project patch 与 env Backend 切换使用各自的白名单接口;revision 不匹配返回 SERVE_MANIFEST_CONFLICT |
| 旧路由边界 | 旧客户端调用历史 settings PUT | 旧 mutation 路径返回 409 SERVE_REPOSITORY_READ_ONLY |
凭据默认掩码:GET /api/configure* 返回 clientSecret: "********" / accessKeySecret: "********" / password: "********"。UI 的 "显示原文" 按钮调 ?reveal=1 取真值。Infisical 列表只返回 key;原文按单条请求并带 Cache-Control: no-store,也不会进入 SWR 缓存。Workspace/Project 投影只返回解析到的 Profile 名和 source,不返回 Profile 字段或凭据。
不在范围:
- 多用户访问(仅 127.0.0.1 单人)
- 0.0.0.0 / 局域网暴露(
SERVE_BIND_FORBIDDEN直接拒绝) - 文件外部变更实时推送(外部
one configure ... add改了文件,需要刷浏览器才能看到)
示例
默认(推荐):随机端口 + 自动开浏览器
one serve
# ✓ profile UI 已启动: http://127.0.0.1:54321/
# 系统默认浏览器自动打开,Ctrl-C 退出
CI / headless / WSL:只要 URL,不开浏览器
one serve --open=false
# 印出 URL,由你或别的工具自己用
固定端口(测试 / 文档截屏)
one serve --port 17900
容器 / 远程 SSH
one serve 默认只绑 127.0.0.1。要在远端机器跑、本地浏览器访问,靠 SSH 端口转发:
# 远端
one serve --open=false --port 17900
# 本地
ssh -L 17900:127.0.0.1:17900 remote-host
# 复制远端 stdout 打印的 URL(替换主机为 127.0.0.1)打开
不要试图改 --host 0.0.0.0——会被 SERVE_BIND_FORBIDDEN 直接拒掉。
REST API
UI 用什么,你就能用什么。所有路由都需要 Host 头匹配;mutating 路由还需要 Origin 头匹配,不需要 token。
| 方法 | 路径 | 说明 | 响应 schema |
|---|---|---|---|
GET | /api/configure | 全部 profile section | one-cli/serve-configure-config/v1 |
GET | /api/configure/{domain}/{backend} | 单个 section(?reveal=1 取真值) | one-cli/serve-configure-section/v1 |
POST | /api/configure/{domain}/{backend} | upsert:body {name, profile, use?} | one-cli/serve-configure-upsert/v1 |
DELETE | /api/configure/{domain}/{backend}/{name} | 删除 | one-cli/serve-configure-remove/v1 |
PUT | /api/configure/{domain}/{backend}/default | 切 default:body {name} | one-cli/serve-configure-use/v1 |
GET | /api/workspaces | 本机 Workspace 列表与状态 | one-cli/workspaces/v1 |
DELETE | /api/workspaces/{entryId} | Forget 本机记录,不删除 Workspace | 无响应体 |
GET | /api/workspaces/{entryId}/overview | 所选 Workspace 与 Project 概览 | one-cli/workspace-overview/v1 |
GET | /api/workspaces/{entryId}/profile-bindings/env?env={environment} | Workspace env Profile 的有效名/source | one-cli/workspace-profile/v1 |
PUT | /api/workspaces/{entryId}/profile-bindings/env?env={environment} | 选择/取消 Workspace env Profile;body {profile} | one-cli/workspace-profile/v1 |
PUT | /api/workspaces/{entryId}/environment/backend?env={environment} | 带 revision 校验的 env Backend 切换;body {revision, backend} | one-cli/workspace-profile/v1 |
POST | /api/workspaces/{entryId}/environment/backend/initialize?env={environment}&project={name?} | 修复缺失的 Infisical 项目绑定 | one-cli/workspace-profile/v1 |
GET | /api/workspaces/{entryId}/projects/{name}?env={environment} | Project/配置投影、Manifest revision 与有效 Profile 名 | one-cli/workspace-project/v1 |
PUT | /api/workspaces/{entryId}/projects/{name}/profile-bindings/{domain}?env={environment} | 选择/取消 Project Profile;body {profile} | one-cli/workspace-project/v1 |
PUT | /api/workspaces/{entryId}/manifest | 应用已审阅的类型化 Project patch;body {revision, changes} | one-cli/workspace-manifest-apply/v1 |
GET/POST | /api/workspaces/{entryId}/secrets?env={environment}&project={name?} | 列出直接定义的 key / 新增一条 Infisical 值 | one-cli/env-list/v1 / one-cli/env-set/v1 |
GET/PUT/DELETE | /api/workspaces/{entryId}/secrets/{key}?env={environment}&project={name?} | 显示、修改或删除单条 Infisical 值 | one-cli/env-get/v1、one-cli/env-set/v1 或 one-cli/env-delete/v1 |
GET/PUT | /api/workspace/profile-bindings/env?env={environment} | 启动 Workspace 的 Workspace 绑定别名 | 与复数路由相同 |
PUT | /api/workspace/environment/backend?env={environment} | 启动 Workspace 的 Backend 切换别名 | one-cli/workspace-profile/v1 |
POST | /api/workspace/environment/backend/initialize?env={environment}&project={name?} | 启动 Workspace 的绑定修复别名 | one-cli/workspace-profile/v1 |
GET | /api/workspace/projects/{name}?env={environment} | 启动 Workspace 的 Project 投影别名 | one-cli/workspace-project/v1 |
PUT | /api/workspace/projects/{name}/profile-bindings/{domain}?env={environment} | 启动 Workspace 的 Project 绑定别名;body {profile} | one-cli/workspace-project/v1 |
复数 Workspace API 只接受不透明的 entryId。服务端从注册表解析路径,并在每次读取或 mutation 前重新校验 Manifest;客户端提交的任意 root 不会参与路径选择。Manifest 发布接收类型化 patch,而不是整份替换文档。密钥 folder 由服务端根据 Workspace/Project 推导,浏览器不能提交任意 path。空 Profile 字符串会删除该层直接绑定,恢复 fallback 解析。
旧的 Project/Environment/Deploy/Container settings PUT 路径(包括 /api/workspace/... 和 /api/workspaces/{entryId}/...)仍为旧客户端保留,但始终返回 409 SERVE_REPOSITORY_READ_ONLY;代码库写入使用带 revision 校验的 /manifest 与 /environment/backend 路由。复制 Workspace 导致两个有效路径共用一个 Manifest ID 时,两条记录都会保留并标记冲突:允许只读检查,所有 mutation 在冲突解决前返回 409 Conflict。
合法 (domain, backend) 包括:env/infisical、env/dotenv、deploy/aws-s3、deploy/aliyun-oss、deploy/tencent-cos、deploy/minio、deploy/rustfs、deploy/r2、deploy/kustomize、deploy/vercel、deploy/cloudflare、deploy/edgeone、container/docker。其它组合返回 404。
curl 探活示例(替换 <port> 为 stdout 信封里的端口):
curl -s "http://127.0.0.1:<port>/api/configure" | jq '.config | keys'
错误恢复
| 错误码 | 处理 |
|---|---|
SERVE_PORT_BUSY | 换端口,或 --port 0 让内核挑空闲端口 |
SERVE_BIND_FORBIDDEN | 仅允许绑定 loopback;改回 127.0.0.1(远程访问走 SSH 隧道) |
SERVE_PAYLOAD_INVALID | POST/PUT 请求体不是合法 JSON 或缺必填字段(如 name 或 profile) |
SERVE_MANIFEST_CONFLICT | 重新加载 Workspace,检查当前 Manifest 后再创建草稿 |
SERVE_REPOSITORY_READ_ONLY | 使用类型化 /manifest 草稿流程;当前调用的旧路由不可写 |
PROFILE_FILE_INVALID | 修复错误指向的本机 Profile 文件(config.json、credentials.json 或 profile-bindings.json) |
PROFILE_IN_USE | 先把所有引用该 Profile 的 Workspace/Project 环境绑定改为 Automatic,再删除 |
PROFILE_BACKEND_INVALID | URL 里的 (domain, backend) 不是合法 pair |
完整码表:错误码大全。