Files
dsh-web/ACCEPTANCE.md
T

254 lines
38 KiB
Markdown
Raw 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.
# DESIGN.md 验收记录
> 发布架构变更说明:下文为各阶段的历史验收证据,保留当时命令、测试数量及结论,不作为当前操作指南。main 已移除内置 runtime 构建资产;`release.json`、完整 `image/` 和初始化测试仅在 `dockerfile` 发布分支维护,新构建仅允许管理员通过 Git 发布检查并确认精确 commit。旧 npm/实验构建及构建后创建预约流程已被取代,这是 breaking change:移除 `/api/latest`、`/api/compatibility`、`POST /api/builds` 及 `/api/intents` 的全部查询、创建和取消接口,不提供 410 兼容入口。仅保留 `GET /api/builds` 和 `GET /api/builds/{id}` 构建历史读取。不迁移或恢复旧构建及旧创建意图,不在后台续跑或完成旧预约;这不意味着删除已有实例或数据。旧 `scripts/build-image.sh` 与 `scripts/test-intent-live.mjs` 已删除;`scripts/test-panel.mjs` 现在复用预先验证且准入的本地镜像。历史通过记录不等于新 Git 发布架构已通过真实部署验收,当前使用方式见 README.md、API.md、OPERATIONS.md。
## 恢复宽限期与轮询缓存修复(独立复审后)
范围:本轮为对上述工作树的独立代码审计与全量重测后的修复。审计确认八项修复中六项正确,发现三处残留:创建结果不确定的挂起标志永不过期导致实例永久无法删除(高)、构建校验不确定行无限重试并持续新增审计(中)、意图轮询每秒对每个成功镜像发起 Docker inspect(中)。修复未改变凭据文件管理边界或准入策略;未部署、未操作真实 Docker 实例。
- `runtime_creates` 增加 `container_pending_at`/`network_pending_at` 置位时间(列名白名单,禁用未校验列名拼接)。确定性名称 404 在宽限窗口(默认1分钟)内仍视为不确定;窗口过后判定资源从未创建,标志落账清除,实例可正常删除,删除成功后总是清理 `runtime_creates`/`runtime_networks` 行。无时间戳的遗留行视为已过期立即解析。
- `image_build_checks` 增加 `created_at`。无已记录容器 ID 且名称 404 的不确定行在宽限窗口过后删除行停止重试;窗口内保持降频重试与 `build.cleanup_error` 审计(≤1/分钟不变)。
- 意图轮询改用 `verifiedImagesPoll`:成功构建行每秒重读,逐镜像准入评估按 `imageDeleteGeneration`+5秒TTL缓存;镜像删除或准入写入使缓存立即失效。创建/升级入口仍走未缓存的权威校验。
- 前端:无 Web Locks 浏览器的会话恢复重试窗口从约0.85秒延长至约3.85秒;`useResource` 对60秒不返回的请求自动放弃并重新武装轮询(序号守卫防止迟到结果覆盖新状态);`runtimeSession` 测试改为 TypeScript 并纳入 tsc 检查;`test-panel.mjs` 构建详情轮询对瞬时 5xx 容错续轮。
回归覆盖:宽限窗口内删除被拒、窗口过后删除成功且无残留行(TestUncertainPendingResolvesAfterGrace);校验行窗口内保持不确定、窗口过后与遗留行均解析(TestBuildCheckUncertainRowResolvesAfterGrace);轮询三次仅一次 Docker inspect、代际提升后缓存失效(TestIntentPollerCachesImageEvaluations);悬挂请求逃生阀(ui.test.ts)。Go共有192个顶层测试(另有子测试)。前端15个测试文件、95项测试通过;`@types/node` 新增为 devDependency,官方源 npm audit 0 漏洞。
## 完整home与镜像准入策略实施
本节落实用户确认的完整home所有者访问、兼容清单及管理员实验构建策略,取代下方上一轮“尚未改变这两项策略”的历史边界。未部署,未操作真实Docker实例或为实际镜像写入准入。
- 所有者可读取/打包凭据,普通文件管理不强制使用专用Profile流程;匿名、跨用户和路径穿越仍拒绝。下载及敏感修改增加确认,取消不触发操作;上传同名拒绝不覆盖。专用Profile页面仍要求停机,文案与通用文件管理明确区分。
- 默认普通构建支持linux/amd64、模板7的rc.1/rc.2;未知版本只能管理员实验构建。实验完整执行原检查且success不自动可选。准入绑定精确imageID/version/recipe/platform与成功验证记录,管理员填写依据,策略和审计同事务,审计失败回滚。
- 历史模板3–6默认仅存量,经明确验收可准入并使用旧双挂载;未知布局拒绝正向准入,模板7向旧共享布局降级拒绝。已准入未知版本只通过本地imageID使用,不自动扩大版本构建白名单。
- blocked拒绝新建、升级目标、start/restart及受控配置非GET操作,避免恢复/备份自动启动绕过;不自动停止既有容器,普通文件管理保持可用。队列恢复/执行重查版本、平台及实验请求者管理员权限。
- 独立复核修正了准入与生命周期锁顺序、受控配置启动遗漏及两处提示矛盾;最终静态复核未发现剩余阻塞项。
回归覆盖精确版本/模板/平台匹配、旧模板实际创建挂载、实验success仍不可选、未知请求零预留、普通用户和CSRF拒绝、审计回滚、并发降级等待创建完成、blocked启停/配置拒绝,以及前端准入确认/取消和选择器过滤。Go共有189个顶层测试(另有子测试);本轮Go普通全量和全量race禁缓存通过,API包分别约17秒与97秒。前端15个测试文件、94项测试通过。浏览器使用模拟API验证实验警告、准入表单、创建筛选及unknown latest禁止预留,临时服务和浏览器已关闭;非真实Docker或新DSH版本兼容验收。
完整 `scripts/check-trial.sh` 最终退出码0;`go vet`、Go构建、TypeScript与Vite生产构建、profile初始化3项Node测试、验收脚本自身11项Node测试和脚本语法检查均通过。仍有既有Vite大chunk提示,非构建失败;`git diff --check`通过。
## 2026-10-14 真实环境补充验收与构建拆包
- 全量 race:`go test -race -count=1 -timeout=180s ./...` 退出 0,全部包通过、无 race 报告;API 包 166.645 秒,总耗时约 170 秒。
- 真实 Docker:新增默认跳过、需 `DSH_DOCKER_LIVE_TEST=1` 启用的 `TestDockerLiveRecovery`。经故障代理向真实 `/var/run/docker.sock` 转发,真实 create/delete 成功后丢失响应,或断开客户端并延迟创建,先取得真实 404 再放行。六个子场景连续两轮通过,另一次 race 模式通过。覆盖 runtime/network/build-check 恢复、面板未连接/完整 ID/名称连接及删除响应丢失。仅使用专用唯一标签资源、本地 node:24-alpine;按精确 ID 清理后标签查询零残留。测试调用恢复方法,不模拟整机断电,也不重启共享 daemon。
- 真实浏览器续期:当前 Go 源码、隔离临时数据库、真实 DSH rc.2 模板7容器、Chromium 独立 Cookie 上下文,原始 5 分钟 access / 4 分钟续期间隔。2026-10-14T03:49:07.767Z 至 03:59:22.828Z 共 615.061 秒;初次、第 240.023 秒及第 480.020 秒 refresh 均 200;每 15 秒原生 fetch 检查 `/api/me` 和真实 runtime,共 41 轮全部 200(含第 301 秒和第 601 秒)。没有加速时钟、手动续期或共享 Cookie 的其他页面。证据 `output/playwright/renewal-acceptance.json`;不代表休眠/后台节流或多小时压力验收。
- Monaco 改为 React.lazy 按需加载,依模块分层拆包;主入口从约 2589 kB 降至 284.92 kB,最大 chunk 411.21 kB,worker 232.35 kB,保留默认 500 kB 阈值且最终构建无大 chunk 或循环 chunk 告警。新增构建测试约束体积、入口不静态依赖 Monaco 及 chunk 无环;16 文件/114 项前端测试通过。真实浏览器验证编辑输入、运行中保存禁用、只读预览及 worker 初始化成功,截图 `output/playwright/editor-chunks-verified.png`。
- 最终 `go vet ./...`、`go build ./...`、TypeScript/Vite 构建和 `git diff --check` 通过。专用面板、两个浏览器已关闭,验收容器/网络及临时数据和凭据已清理;原四个运行容器仍在,未部署到现有面板、未更改共享镜像。
复现真实故障测试:`DSH_DOCKER_LIVE_TEST=1 go test ./internal/api -run TestDockerLiveRecovery -v -count=2 -timeout=3m`;需可访问 Docker socket 和本地 node:24-alpine,不会自动拉取镜像。
## 再次审查修复(本地回归)
- runtime 注入脚本每 4 分钟主动续期,不再因 `/api/me` 仍有效而跳过;保留 Web Locks 串行刷新、同页请求合并及冲突复验。模拟时钟覆盖连续续期,补测锁等待期间 Cookie 更新。
- 资源加载超时会结束调用方等待链、清除 loading 并提示重试;覆盖无轮询、旧请求和后续请求均悬挂、迟到成功/失败、超时配置变化,不再只清空 in-flight 引用。
- Docker 创建结果不确定时,不凭一分钟后的名称 404 清除 pending/验证容器记录;覆盖旧/缺失/非法时间戳及迟到资源回收。网络清理只断开确实连接的面板端点,保留身份校验和真实错误处理。持续未确认的创建需按 OPERATIONS.md 人工确认。
- 准入依据前端按实际提交内容计算 UTF-8 字节数,超出 4096 字节时提示并禁止提交;后端保持字节上限及请求体限制,长度错误明确返回。覆盖中文、ASCII、多字节、表情及边界。
本轮通过:`go test -count=1 -timeout=180s ./...`、准入与资源恢复相关 API 测试的禁缓存 race 检查、`go vet ./...`、`go build ./...`;前端 15 文件/113 测试及 TypeScript/Vite 生产构建;profile 初始化与验收脚本自身共 14 项 Node 测试、集成脚本语法检查、`git diff --check`。本轮未执行全量 race、真实浏览器长时续期或真实 Docker 故障注入;未部署、未操作现有实例。Vite 仍有既有大 chunk 提示。
## 八项审查问题闭环(本地回归)
范围:普通创建响应丢失、验证容器回收、构建终态持久化、多页面续期、慢请求轮询、当前密码重认证、配额更新竞态、构建历史分页与按需日志。未改变凭据文件管理边界或历史镜像兼容策略;未部署、未操作真实 Docker 实例。
- 资源恢复:创建前保存恢复依据;校验资源身份后按持久 ID 清理。覆盖迟到资源(首次404随后出现)、网络创建/删除响应丢失、验证容器重命名、外部同名网络拒删、清理失败重试、重开数据库后无排队任务自动回收。个别预约待恢复不会阻止面板启动。
- 构建终态:任务与镜像索引事务提交,故障注入验证无半提交;独立重试不阻挡后续构建,Store关闭后退出。持续不确定创建保留恢复记录,不凭单次404释放资源依据;人工确认流程见 OPERATIONS.md。
- 认证:备份、升级、改密共用失败计数与锁定;旧备份密码不参与登录失败计数。并发refresh冲突返回409且不签发cookie,前端及runtime复验后保留可重试错误,真正撤销仍退出;覆盖慢升级不阻塞其他账号登录。
- 文件与前端:上传提交锁内读取最新额度;管理员修改额度共用用户锁。慢轮询不重叠、不废弃有效慢响应,显式刷新保证额外读取,key切换隔离旧结果。构建列表默认20条、最多100条且不读取日志正文,详情按需加载;参数、路由鉴权、整页边界和当前页镜像对账均有回归。
最终验证:
- 完整 `scripts/check-trial.sh` 通过,最终退出码0;最终Go测试通过 `GOFLAGS=-count=1` 禁用测试结果缓存。Go共有182个顶层测试(另含子测试),API普通测试约18秒,全量race中API包约110秒;无race报告。
- `go vet ./...`、`go build ./...`、前端类型检查与生产构建通过;Vite仍提示原有大chunk,不影响构建成功。
- 前端10个文件、57项测试通过;profile初始化3项Node测试、验收脚本自身11项Node测试通过;真实runtime注入脚本的401/403/409/500行为已自动测试。
- 浏览器配合模拟API验证每页20条、末页和禁用下一页、日志打开/关闭及终态停止轮询。临时浏览器和开发服务器已关闭。
- `git diff --check`通过。Docker故障场景使用httptest模拟,并非真实daemon断电/断网验收;未运行指向真实部署的test-panel脚本,仅更新其单任务查询并检查语法。
升级注意:已打开的旧页面需刷新;外部 `/api/builds` 消费者需迁移分页和单任务日志接口。持续数据库故障或身份冲突不能承诺自动成功,保留记录并按运维流程处理。
## 2026-10-12 可信团队试运行加固(本地自动化验收)
范围:本机或仅可信账号的团队,可由管理员通过公网 HTTPS 反代访问。未修改 Nginx/Caddy,未部署面板或操作现有实例。本节仅说明当前代码的本地自动化验收,不将历史部署证据当作本次公网实测。
已修复:
- 登录来源30次/分钟、有界1024来源桶、8个并发槽;Origin/严格JSON校验先于额度消耗;保留账号失败锁定。默认不信任XFF,仅配置的可信代理CIDR可提供来源链,非法链回退peer。非法公网URL/CIDR在创建数据目录前拒绝;URL规范化大小写与默认端口。
- 实例文件传输改为短锁登记lease,不再持全局生命周期锁传输;同实例启停/删除/修改/升级/恢复冲突返回409。配置JSON/multipart锁外有界接收(并发4、30秒读deadline),JSON响应在解锁后发送。
- runtime token按容器ID/StartedAt加密持久化,Host分别缓存;日志流式扫描64MiB上限、完整帧校验、逐行选最新启动token。上游401/403失效cookie,下次请求重新交换,不重放写请求。
- 恢复快照复制失败清理本次半成品,保留有效快照;清理失败明确500及审计。目录列表10000项上限,用量和快照分批遍历。
- 构建容量测试注入可控探测,生产2GiB阈值不变;更新模板7文档、实例范围集成脚本和一键验收入口。
实际通过:
- `bash scripts/check-trial.sh` 退出0:Go全量测试、全量race、vet、build;前端32测试及生产构建;初始化3测试;集成脚本语法检查。全量race在此脚本中使用刚刚分包成功运行的Go测试缓存;API包单独完整race约54秒通过。此前一次全量并行race被工具60秒上限终止,不计通过。
- `node --test scripts/check-trial.test.mjs`:11项通过(包括每阶段失败即停)。
- `TestSecurityTLSCookieJarLifecycle`:真实本地TLS HTTP服务/cookiejar登录、me、refresh轮换、logout、旧会话拒绝;来源隔离、伪造头、非法配置、并发限流回归通过。不是公网证书/反代验收。
- 慢上传/下载/配置body/错误JSON不持锁、同实例busy、失败恢复三次不耗名额回归及race通过。
- runtime专项race覆盖9MiB raw/framed日志、逐字节分片、截断帧拒绝、面板对象重建、Host切换、启动时间变化、磁盘权限/加密/篡改、写盘失败不缓存、cookie拒绝后重交换。Docker及上游由测试HTTP服务模拟。
- Compose用占位环境变量只读解析,公网URL及可信代理CIDR正确展开。
剩余边界与部署前检查:
- 未重新构建/启动真实runtime镜像,未执行真实Docker生命周期、浏览器或实际公网域名验收;未调用模型、搜索或收费服务。必须在部署入口确认登录/刷新、iframe、WS/SSE和插件设置工作。
- 必须设置实际 `PANEL_PUBLIC_URL` 和准确的 `TRUSTED_PROXY_CIDRS`;代理及后端可达范围由管理员负责。公网并不意味着支持不可信租户。
- 旧实例首次成功连接前若启动token已轮转丢失,需维护窗口重启该实例再连接;任意上游token主动轮转不保证自动恢复。软配额、同源插件、已建立WS/SSE不即时撤销等既有边界保留。
- 前端仍有Monaco大chunk警告。本轮未补许可证、依赖漏洞全量扫描或开源发布材料,不作开源发布就绪声明。
结论:本地代码与自动化验收通过,可进入本机/可信团队受控试运行的部署验证;不宣称实际公网部署已验收或长期生产SLA已达标。
## Free Search 模板5(部署及真实iframe验收完成)
- 最终面板已部署:6067f9e8bd12aff649c3b8cc662c37a7a14eebec2d686b84a0d965ee599a4dc5,镜像sha256:6f397fba50a9c3469c1e5ae1ed685e6a4fe90f08ed91d947f38eca9c0b3a4932;原配置/挂载保留,旧面板停止留作回滚。正常API构建记录617457ffdd4c168c58f9cb21e7a591cc成功,模板5可用。
- 专用新实例5dfabbdde3339c8df7bb616e1c65dce9真实iframe:Installed2、Free Search0.8.5 Running、Configure打开完整设置;前缀describe/credentials-status/mutate均200。Bing地区及缓存时长修改后保存,再恢复zh-CN/5并确认;普通DSH页面重新加载200。截图output/playwright/free-search-settings.png。未执行搜索/引擎测试/在线更新/密钥配置。
- 实际HTTP匿名401、恶意Origin403、缺失Origin403,根路径请求拒绝;跨用户/public Host由代理自动化测试覆盖,本轮未另做实际公开域名或真实跨用户浏览器验证。全量race TEST_EXIT=0(/tmp/dsh-free-search-race.log)。
- 上游0.8.5已确认UI限制:单改Bing地区不会置dirty,保存按钮不启用;修改缓存时长后可一起保存。未擅改上游发布包,不把此缺陷视为已修复。
- 测试实例已经正常API删除,bind数据保留。旧63b实例仍为recipe4,容器ID/StartedAt13:01:01.657475588Z/RestartCount0保持,未安装或启用free-search。
- 固定内置dsh-free-search0.8.5及三项普通依赖原始tarball,SHA-512校验;新profile正式依赖及bundle默认启用,旧profile不注入,mobile旧路径保留。runtime镜像dsh-web:0.2.0-rc.2-5,ID sha256:aea9e1ed8614b30457c1d60274a68175e85c1716734e4bca396205bffa885eb6。
- 断网/非root/只读rootfs启动、两个插件inventory及listBundles、settings describe200、无重复宿主peers或绝对symlink、离线恢复和禁用状态保留、recipe4 mobile-only恢复通过。证据output/recipe5-build.log及recipe5-smoke.log。未调用搜索、引擎测试、收费服务或模型。
- 面板精确适配8个settings路径,先登录/实例归属/Origin检查再转换loopback Host及Origin,并重新交换Host绑定cookie;客户端fetch仅重写同源精确路径。真实HTTP upstream测试涵盖公开Host、body/query、普通资源、匿名/跨用户/恶意或缺失Origin拒绝,Node测试覆盖Request/URL/string及流语义。Go全量/vet、初始化3测试和聚焦代理race通过。
- 上游会接管等于deepseek-official的搜索及默认http抓取(包含显式同名值);其他自定义provider保留。默认Bing但自动回退可能使用已有收费API凭据,不承诺零费用。
- 早期部署曾被权限规则拒绝,后续用户授权继续,主会话接手完成上述部署和验收;实际公网域名未单独验收。
## 管理员重设与用户自助修改密码验收
- 已部署 `dsh-mgr:password-complete`(sha256:83189ad42e35b5a7db3ba74e27f62adcd35f4c45635be35d4f5e568b37c54c75)至127.0.0.1:40411。面板Config除Image外、HostConfig及Mounts一致,旧面板停止保留供回滚;DSH runtime ID/启动时间/重启计数未变。
- 管理员入口:设置→用户与审计→用户列表→重设密码。普通用户及管理员自助入口:设置→账号→修改密码,必须验证当前密码。
- 专用成员pwdqa_1791643434738真实Playwright CLI验收:管理员重设、确认不一致错误、新密码登录;成员错误当前密码保留登录、确认不一致错误、成功修改返回登录页及新密码登录均通过。没有修改既有用户密码。
- 两次改密后旧密码及旧access/session请求401,携带原CSRF的旧refresh请求403(session已删除),新session刷新200。审计显示user.reset_password和user.change_password,不含密码。
- Go全量race通过(/tmp/dsh-password-race-final.log,TEST_EXIT=0),vet、前端21测试及生产构建通过;既有Monaco大chunk警告仍在。
- 证据:output/playwright/password-member-empty-form.png、password-admin-reset-empty-form.png;/tmp/dsh-password-browser-evidence.txt。测试会话已退出;因无删除用户API,专用成员保留,随机测试密码未持久化。
- 限制:已建立的WS/SSE不会被主动即时断开,后续请求及重连拒绝旧会话。保留的旧面板是部署回滚副本,不是数据库时间点备份。
## 模板4:修复插件列表不可见(取代模板3管理可用性结论)
- 根因:仅host依赖和bundle可使插件运行,但DSH管理UI按profile dependencies判断installed,模板3未登记而被过滤。模板4固定tarball正式依赖,补缺失workspace的no-auto-peers配置,保留已有manifest/bundle禁用状态。
- runtime `dsh-web:0.2.0-rc.2-4`:sha256:d967800c73888cefba67b8ea14b923c14032f96058311630a37210219877fcdd。面板已部署模板4并经正常API登记镜像。
- 当前实例63b337c848bf23156270068ff9e5cf38已备份、升级并补mobile依赖,容器2e2d6c75946a65eb7b39a11d72e2e3d23394c3d92d6508cfe25a8fa827a450fb,配置/数据保留。备份73e27b58f421868de00b07958f88113d及此前备份保留。
- 实际面板iframe触屏浏览器:Plugins新增Installed1,dsh-web-mobile v3.0.5,详情组件Running;真实开关关闭显示Off、再次开启恢复运行,最终保持启用。截图output/playwright/plugin-touch/recipe4-installed-detail.png。UI显示Uninstall但未执行卸载;无独立参数配置表单。
- 初始化3测试、Go全量/race/vet通过。断网非root只读冒烟验证正式inventory、上游listBundles、无重复peers/绝对symlink,以及仅manifest/patch/lockfile恢复后离线安装并保留disabled状态。当前实例未另做disabled后容器重启测试。
- 迁移曾因旧package.json.lock及.plugin-manager/run.json阻塞并回滚;锁PID16与新容器安装进程PID16相同。确认实例完全停止后将两份记录改名保留,停机直接升级成功。未删除配置数据;该上游纯PID锁风险未通过自动删锁规避,后续遇到需停所有profile写入者再人工核验恢复。
## 默认内置 dsh-web-mobile 增量验收
- 模板修订3固定内置 `[email protected]`,校验发布包SHA-512与宿主peer版本,不执行插件生命周期脚本。当前限定DSH rc.1/rc.2;真实镜像测试使用rc.2。
- 新建空web profile默认加入mobile bundle;已有manifest或其他profile文件不覆盖、不强制启用。初始化拒绝符号链接目录,幂等/已有配置保留及符号链接测试均通过。
- 发布包原始权限导致非root加载失败,已通过镜像内 `chmod -R a+rX` 修复。修复后 `node scripts/test-mobile-image.mjs` 通过:断网、非root、只读rootfs启动,manifest与实际dump-config均含mobile插件。专用冒烟容器及其tmpfs测试数据已删除。
- 验收面板 `127.0.0.1:40411` 已更新至 `dsh-mgr:plugin3-active`(sha256:06bca1aae39accbac0721af723082e8a35a0c9cb2129e4cb942015b48fbffd85),HTTP 200。正常构建API成功登记rc.2模板3,镜像API状态available。旧面板停止保留供回滚,现有模板2实例未重启或自动加入插件。
- 本轮通过:初始化2测试、Go全量测试/race/vet、前端19测试及生产构建(仅既有Monaco大chunk警告)。
- 后续浏览器验收发现单纯放入node_modules仍会导入失败:DSH运行时只解析安装依赖图。已将内置包加入宿主package.json dependencies,且冒烟增加stdout/stderr激活失败检查(不输出token)。最终runtime ID:sha256:30ca773ce5d70a3a540a8f4aae149c83733102f8dfcd6a77ef9794bd992bde8e。此前只检查dump-config的成功结果不等于运行时激活成功,以下复验取代它。
- Playwright CLI独立loopback runtime触屏复验:coarse pointer=true、maxTouchPoints=1;含dsh-web-mobile/client.js的实际模块请求200;四个data-mobile-nav标记生效。首页及tap展开目录后viewport/client/scroll/body宽度均390。截图:output/playwright/plugin-touch/rc2-3-fixed-default-390.png、rc2-3-fixed-drawer-390.png。临时runtime已删除。
- 边界:本次插件复验未覆盖面板iframe、实体手机或模型交互;额外目录关闭tap超时,未计通过。前轮专用临时runtime token曾在工具输出回显,已删除该容器使服务不可访问,未涉及旧实例凭据。插件还包含响应压缩、会话删除入口和推理档位补全,不是纯CSS;未执行模型调用。
## 2026-10-10 移动端内嵌工作台增量验收
用户追加需求:登录默认 iframe 内嵌 DSH,设置切换容器,移动端底部导航。已实现并部署至原验收地址,镜像 dsh-mgr:mobile-renew(sha256:392c8bb64c822572005625df5d662774e1f780a1c83121caa249fafe6a16d01f)。
- 真实浏览器390×844及320×640:外层scrollWidth等于viewport宽度;iframe底边分别780/576,与底栏顶边一致,无底栏遮挡。桌面1440×900保留侧栏。
- 设置切换第二个真实容器、整页刷新后选择保留;停止所选实例显示启动提示而非自动切换;删除测试实例后回到可用实例。测试实例已删除,bind数据保留。
- 设置往返iframe DOM标记保留,未重载;退出登录iframe卸载;重新登录默认iframe已验证。
- 清除dsh_access模拟过期,真实iframe续期refresh200并回到原DSH地址,无加载错误。修复iframe导航续期及SPA X-Frame-Options冲突;使用SAMEORIGIN与frame-ancestors self保留跨源嵌入限制。
- Go全量测试、race及vet通过;前端19测试/生产构建通过。截图:output/playwright/design-final/mobile-settings.png及page-2026-10-10T09-29-51-590Z.png、page-2026-10-10T09-34-19-112Z.png。
- 验证为Chrome小屏视口,不代表iOS/Android真机软键盘兼容已全测。同源iframe不是安全沙箱;保留受信任团队部署边界。
以 DESIGN.md 为目标,不通过修改设计降低标准。以下最新结论覆盖后文早期进度记录。
## 2026-10-10 最终验收结案
**结论:按用户确认的默认路径代理、软配额及功能等价替代范围,验收通过并结案。** 用户在最终结构化确认中明确选择“接受功能等价替代”:标准库 Docker HTTP 客户端替代 SDK、每实例独立 bridge 替代共享 bridge、服务端保管 DSH cookie 替代下发浏览器、React+CSS 替代 shadcn(保留 Monaco)、部分模块集中于 internal/api。上述选型不再是待确认项;并未修改 DESIGN.md 来降低要求。
结案时再次确认 /healthz 返回 ok,dsh-design-check 正在运行 dsh-mgr:design-final,用户1000:1000、只读根文件系统、内存限制1073741824字节。此次结案仅更新验收文档,没有新增代码变更;沿用下列已完成测试证据。
默认路径代理、软配额方案的 M1–M7 功能已实现,真实主要流程通过;故障边界结合自动化注入测试验收,不声称所有故障均在宿主实际制造。可选 XFS/ZFS、socket-proxy、公网 TLS 未实测。
| 范围 | 最新证据 |
|---|---|
| M1 镜像 | rc.1/rc.2 recipe2实际构建,rc.2 smoke通过,精确版本/非root/只读/pnpm10.11.0 |
| M2 生命周期 | 最终面板完整API通过;test-intent-live.mjs验证预约自动创建、两个真实运行实例、第三创建与预约409、删除释放 |
| M3 代理 | 新浏览器DSH/空白会话/插件资源/SSE200;最终WS 1连接、5发送、5接收;面板替换自动重新接入网络;独立DSH页超过5分钟refresh200且PTY可操作 |
| M4 工作区 | 33项真实HTTP+43项多用户断言;ZIP/Range/穿越;PTY读取上传文件;直接调用镜像内DSH read/write实现在/workspace往返(无模型调用);扩展名白名单.txt成功/.exe415 |
| M5 配置备份 | Monaco键盘编辑、保存启动、bundle/脱敏dump-config;浏览器备份创建/下载/错误密码拒绝/正确密码导入恢复 |
| M6 升级回滚 | rc.1→rc.2;manifest加入is-number@7.0.0验证依赖安装/恢复/迁移;不可达依赖触发失败,原ID/版本/停止状态恢复,数据marker保留。fixture验证安装机制,不代表任意第三方插件兼容 |
| M7 管理安全 | JWT/refresh/隔离/配额/审计;最终面板1000:1000、只读、1GiB、socket组989;管理员显式回收停止孤儿容器通过且bind目录保留 |
故障注入回归覆盖:构建去重/限频、磁盘不足/未知磁盘、npm精确版本、镜像缺失、重启恢复、失败释放预约、镜像引用保护、备份资源与旧包、路径/符号链接/配额并发和回滚。见internal/api/*_test.go及internal/backup/*_test.go;没有填满宿主磁盘或删除共享镜像。
真实验收发现并修复:dump-config顶层YAML列表误拒绝;代理日志旧token污染启动token提取。新增测试并容器复验通过。
最终镜像:
- panel dsh-mgr:design-final:sha256:d2da2f35e00e15fc0da7afc5183c49e40be6215bd4c5d89145e48f1034617cc5
- rc.2 recipe2:sha256:7bd09f12b4b60fa9d13358e99bae14caafc79d3a0936e70cf590fdd1af64998e
- rc.1 recipe2:sha256:1bb553deb3cef25c4529f93526d8cd3282d218df22787be2dc5d5762defaf0c4
通过命令:go test ./... -timeout 45s;go test -race ./... -timeout 90s;go vet ./...;npm --prefix web test(12项);npm --prefix web run build。前端有Monaco大chunk警告。
证据:output/playwright/design-final/;/tmp/dsh-design-check.8ekWaC/{api-check-results.txt,isolation-results.json};/tmp/dsh-design-plugin-rollback-verified.log;/tmp/dsh-design-cross-version.log。保留专用面板及一个浏览器实例;临时实例删除但数据保留,未推送镜像、未调用付费模型。
用户确认的孤儿策略:管理员二次确认回收身份/挂载核验通过、无实例/升级引用的停止容器;不自动纳管,不触碰运行中或来源不明容器,不删数据/网络。其他架构替代见下表;不声称逐字遵循SDK、组件库或目录示意。
## 早期进度记录(以下由上方最新结论覆盖)
## 当前实现与待验收
| 设计章节 | 代码已接入 | 尚需真实证据 |
|---|---|---|
| §1–6 | sibling路径映射、每用户两实例、持久预约/取消/失败恢复、生命周期与身份对账 | 模板2非root全流程、刷新/重启预约恢复、并发名额 |
| §5 | 精确npm版本、模板/平台身份、构建去重/限频/磁盘预检、镜像usage/missing及管理员清理 | 真实构建与删除、外部删镜像、空间/网络故障及重启对账 |
| §7–8 | 路径反代、归属鉴权、服务端token/cookie桥接、WS/SSE | 新部署的新浏览器完整流程、重启重新认证、长时续期 |
| §9 | 文件管理、目录ZIP、写前配额检查/串行化、周期巡检、管理员额度更新 | 新UI文件流程、agent实际读写、面板各写入路径配额覆盖 |
| §10 | 密码加密tar.gz v2、配置/资源目录/manifest、外部包导入、旧key包兼容、停启/健康检查/回滚 | 真实资源往返、原密码/错误密码、无key新包、旧key包、插件恢复失败回滚 |
| §11 | 本地Monaco、YAML校验、bundle与脱敏dump-config预览、保存后启动验证 | 新浏览器编辑/预览/错误路径;当前仍需先停止再保存 |
| §12 | 预升级密码备份、home快照、成功删旧、失败回滚及恢复原运行状态 | 不同DSH版本、真实插件重建/离线失败、故障矩阵与数据保留 |
| §13–16 | 短JWT/refresh轮换/失败锁定、审计、资源限制、非root部署配置 | 模板2非root数据写入和socket访问、新浏览器及DSH独立页长JWT续期 |
| §17–19 | 默认路径反代与软配额;已有回归测试及旧版集成记录 | 必需流程未关闭,不标记M1–M7整体完成;硬quota为可选增强 |
备份包含六个固定配置文件及skins、pets、agent-presets、.agent-presets资源树和manifest,不包含node_modules、会话或工作区。新密码包不依赖backup.key;保留旧密钥包时仍须保管原key。备份/恢复/升级不再统一要求用户手动停机;操作中断或回滚失败不保证自动灾难恢复。
## 合理替代方案(不改变验收目标)
| DESIGN选型 | 当前实现 | 等价目标与边界 |
|---|---|---|
| 官方Docker SDK | stdlib net/http Docker Engine client,API v1.47 | 仍须验证编排、构建流、错误处理和daemon兼容性,不能以未用SDK认定功能缺失 |
| 共享bridge | 每实例独立bridge,面板接入 | 减少实例直接互通;不等于完整出站或宿主网络隔离 |
| DSH cookie改写后下发浏览器 | 服务端交换/缓存cookie,反代注入并移除上游Set-Cookie | 保留面板登录与实例归属鉴权;WS/SSE、重启失效与续期仍需实测 |
| shadcn/ui | 普通React组件及CSS,保留本地Monaco | 不要求替换组件库,但交互、错误反馈和可用性仍须浏览器验收 |
代码主要按internal/api、internal/docker、internal/backup、internal/store组织,与设计目录示意不同;不宣称逐字一致。默认软配额符合§9/§18路线,XFS/ZFS硬quota依赖运维选择,不列为默认必过项。npm 0.2.0-rc.2不支持--public-url,当前使用路径代理而不传该参数。
## 历史证据与本轮边界
本轮已取得真实证据:
- 非root面板 `dsh-design-check`(1000:1000、只读rootfs、socket附加组989)已启动;33项HTTP认证/刷新/配额/ZIP/Range检查通过,报告 `/tmp/dsh-design-check.8ekWaC/api-check-results.txt`。
- 两普通用户隔离与预约43项断言通过,包括越权拒绝、两名额及取消释放;报告同目录 `isolation-results.json`。未把预约名额测试算作真实runtime并发测试。
- 浏览器基础UI登录、配额、ZIP、镜像状态、版本确认与预约页面通过,证据 `output/playwright/design-final/`。实例相关Monaco/备份/长JWT待runtime模板2就绪。
- 模板2实际构建进行中,详细安装日志持续有进度;尚无runtime2启动通过结论。
- 既有记录包含旧版真实Docker文件/实例/备份恢复及同镜像替换流程;同镜像替换不能证明跨DSH版本迁移。
- 旧浏览器证据目录output/playwright/browsercheck18091/记录插件加载、空白会话及WS/SSE,不代表模型生成、PTY或真实插件安装通过,也不能替代模板2和新增UI。
- 上轮记录的go test -race ./... -timeout 90s、go vet ./...、前端12项测试及前后端构建通过,仅为当时检查结果;冻结后须重跑,不能据此宣称全完成。
- **仍需关闭:模板2非root真实部署、新浏览器新增功能、插件跨版本/离线失败、长时JWT续期**。主代理进行中,结论与证据待回填;本文件不将此前权限拒绝误写为当前持续阻塞。
- 真实测试仅限已授权的本项目测试资源;工具再次拒绝时不绕过。不得隐式使用实际模型密钥或付费调用,不自动删除未知孤儿容器。
# 默认工作区 /workspace(模板 6,已由下述模板 7 数据根方案取代)
- 当前用户实例已通过 DSH 界面添加并选择 `/workspace`,刷新后仍保留;原 `normal` 工作区和历史会话未移动。
- 空实例初始化通过 DSH workspaceRegistry 注册准确的 `/workspace`,重复启动复用默认 ID;有历史工作区或会话时不强制迁移。
- 真实断网非 root 容器首次初始化、插件加载、备份恢复及旧模板兼容验证通过,详见 `/tmp/dsh-recipe6-test.log`。
- 面板已部署 `dsh-mgr:workspace6`;正常构建 API 记录 `e9a368bbb66e51e6c8f2439a8e94698a` 成功,recipe_rev=6,镜像 `sha256:38e04308da602da22e8df49d54166da1835922d50a3fa485711b7c5f6479d287`。
- 面板部署保留配置和挂载,旧面板停止留作回滚;现有 DSH 实例未重启,构建前后实例记录一致。
## 模板 7:完整 DSH_HOME 改为 /workspace
### 管理交互优化验收
- 配额静态展示与模态编辑、常用预设、未改变禁提交;密码明确按钮、原生模态、显示隐藏、即时UTF-8校验、取消清空及焦点恢复。账号创建规则对齐,移动用户卡片和退出分区。
- 前端26测试、生产构建通过。Playwright真实测试账号 uxqa_1791711946466:配额10→20→10 GiB;管理员重设→登录→自助修改→退出→新密码登录均通过;取消清空、焦点恢复、确认不一致禁提交、320/390px对话框不越界通过。
- 测试账号最终重新随机化密码、撤销会话,账号保留;未修改真实账号配额或密码。
- 正式面板 dsh-mgr:ux-polish 已部署,挂载集合核验相同(Docker返回顺序不同),DSH容器953ed34d034f未重启。正式入口重新加载后配额/密码对话框检查通过。
- 截图:output/playwright/ux-before-settings.png、ux-before-admin.png、ux-deployed-settings.png、ux-deployed-quota.png、ux-mobile-quota.png、ux-mobile-password-320.png。浏览器密码管理器用户名提示尚存;不声称全面无bug或完整无障碍合规。
- 依用户澄清,实例 home 直接挂载 /workspace,四个 HOME 环境变量同步;/data/dsh-home 为兼容软链接。旧共享目录保留为 /shared,不合并、不覆盖历史文件。
- 面板文件接口以 instance 参数校验归属、跟随所选实例;旧共享文件保留显式入口。账号总配额不变,恢复期间禁止写入,实例文件请求与升级互斥。
- 当前实例 63b337c848bf23156270068ff9e5cf38 正常升级成功,容器 953ed34d034faed9e0f8c7373964dd0156450b1d65c2965c73a539a83a226934,运行镜像 sha256:bd541770aaaf5a846ecb898ec80863c8a356524eb7f4b8053b0eb646d6e10fad。
- 构建 6c3589998c431acc809b5307c30e8e1e 成功,真实离线 smoke 验证环境变量、旧路径链接及同文件读写。升级中修复宿主路径转换和 pnpm 非交互日志边界;早期失败自动回滚,快照和备份保留。
- 最终验证:DSH 代理 GET 200;实例文件 API 可见 profiles/storages,storages/normal 可见四项历史报告/笔记;旧共享接口可访问。未调用模型或搜索。
- 前端 23 测试/build 通过,后端全量及 vet 曾通过,race 通过。最终定向 Workspace/Upgrade/Orphan 测试通过;最后一次全量受 Docker 磁盘不足 2 GiB 策略影响,3 个构建测试失败,不记为全绿。浏览器交互验证被权限拒绝,未绕过;HTTP 验证不替代浏览器验收。