Files
dsh-web/ACCEPTANCE.md

42 KiB
Raw Permalink Blame History

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。

当前实现与验收状态(2026-10-16 文档核对)

本节优先于后面的历史记录。已完整阅读两份文档并对照当前源码;本地验证由本次维护任务的独立验证执行者完成并回报,不将代码存在、模拟测试或旧部署证据冒充当前真实环境通过。

本次实际验证

  • bash scripts/check-trial.sh:退出0;Go普通测试/race各5包通过,vet/build通过;前端25文件198测试通过,TypeScript/Vite生产构建通过;node --check scripts/test-panel.mjs通过。
  • node --test scripts/check-trial.test.mjs:10/10通过。
  • 两项Docker live测试因未启用DSH_DOCKER_LIVE_TEST跳过,不计真实Docker通过。本次未执行真实浏览器、公网入口或完整Git发布部署验收,未调用模型或搜索。
  • 历史测试数量、镜像ID、截图及/tmp路径仅证明当时记录;本轮未逐一重新获取这些临时证据,不代表当前线上状态。

当前能力矩阵

范围 当前代码与本地验证 当前版本仍缺的真实证据
前端与架构 React19/TS/Vite、Tailwind4/Radix、Monaco;Go标准库Docker HTTP client、SQLite;本轮自动化/构建通过 当前UI浏览器全流程与真机;Flutter/Android仅历史工作区残留,无当前原生发布验收
Git发布与准入 管理员检查发布/精确commit、本机构建验证/历史;模板7与当前平台身份检查;本地测试通过 真实发布→构建→验证→准入→创建完整链路、故障及重启恢复;不能沿用旧npm/实验构建结论
实例与存储 两实例事务名额、每实例bridge/home,/workspace为DSH_HOME;删除清理实例数据及备份;无预约或共享入口 当前删除语义、并发名额与不确定创建恢复的真实Docker复验;旧“删除保留数据”证据不适用
代理与会话 归属鉴权、服务端token/cookie桥接、WS/SSE与续期;本地测试通过 当前部署iframe、独立页、长时/后台/多页续期与真实WS/SSE;历史615秒结果不是当前整体重验
文件与配额 完整home、上传下载/ZIP/文本编辑/图片预览、账号软配额;本地测试通过 当前浏览器编辑冲突/图片显示/敏感确认、跨用户访问、真实配额耗尽及容器直接写盘边界
配置与升级 Profile校验/预览、密码加密配置备份/导入/恢复、快照/回滚;本地测试通过 当前跨版本/插件重建/离线失败/旧包兼容及回滚数据保留;同版本替换不证明跨版本迁移
部署与安全 登录/refresh/CSRF/审计/改密、非root/只读/资源限制;本地测试通过 实际域名HTTPS/代理、socket权限、真实浏览器;不宣称不可信公网多租户隔离或生产SLA
可选增强 XFS/ZFS硬配额、配额sidecar、socket-proxy未作为当前默认交付 未实现/未验收,不标通过;不列为默认软配额流程失败项

关键纠偏:除usage外文件API不带instance返回410,新容器不挂/shared;准入布局仅支持模板7,旧模板3–6不能靠说明正向准入。实例删除会清理自身目录和备份,孤儿回收是另一流程。创建不确定时不凭超时404清除依据。见internal/api/files.go、runtime.go、instance_data.go、admission.go、resource_recovery.go。

结论:本次本地自动化与构建通过;当前完整真实部署/浏览器验收未完成,不作M1–M7整体结案。

历史阶段记录(以下均非当前版本整体验收)

各节“当前”“本轮”“最终”“已部署”均指该节当时版本;旧命令可能已移除。旧结案不覆盖文首矩阵;各历史阶段之间的覆盖关系仅适用于对应迭代。

恢复宽限期与轮询缓存修复(历史,已被后续修复替代)

范围:本轮为对上述工作树的独立代码审计与全量重测后的修复。审计确认八项修复中六项正确,发现三处残留:创建结果不确定的挂起标志永不过期导致实例永久无法删除(高)、构建校验不确定行无限重试并持续新增审计(中)、意图轮询每秒对每个成功镜像发起 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加入[email protected]验证依赖安装/恢复/迁移;不可达依赖触发失败,原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、组件库或目录示意。

后续UI改版记录(历史独立阶段,不由2026-10-10结案覆盖)

UI 改版与功能回归(2026-10-15)

本节为本轮证据,后文保留历史记录;不代表所有历史待验收项均已关闭。

  • 前端改为 Tailwind v4、Radix 原语的 shadcn 风格组件及 lucide 图标;桌面侧栏、移动导航、响应式卡片、确认弹窗和备份操作统一。
  • 最终前端 20 个测试文件、131 个用例通过,TypeScript/Vite 生产构建通过;Go 全量测试、race 测试、vet、build 均通过;check-trial 脚本的 10 个测试通过,git diff --check 通过。
  • 浏览器验证桌面及 320/375/667px 移动视口:无页面横向溢出、主要控件触控高度至少 44px、弹窗关闭焦点恢复、准入编辑器只挂载一份且跨断点保留草稿。配额读回、重设密码后登录、自助修改密码后退出、删除取消/确认、重命名冲突在弹窗内提示并可修正重试、错误备份密码在当前弹窗提示均通过。截图位于本地 .playwright-cli/。
  • 隔离真实 Docker 面板执行 scripts/test-panel.mjs,TEST_UPGRADE=1,最终退出码 0:登录/刷新、镜像选择、创建/代理、文件夹/上传/下载/ZIP/重命名/删除、Profile 保存、加密备份/下载/导入/恢复、启停、同镜像替换升级、实例删除通过。补充验证未认证 401、缺少 CSRF/外部 Origin 403、普通用户不能访问用户管理、配额更新与负值拒绝。
  • 真实回归发现并修复两个后端缺陷:空 Profile 规范化为合法空数组 YAML;回滚停止容器接受 Docker 304,但必须继续核验确实停止。新增回归用例,修复后完整集成流程重新通过。
  • 本次升级仅覆盖 0.2.0-rc.2 到相同版本的容器替换;跨版本升级、插件重建、离线失败回滚、长时会话续期,以及真实配额耗尽/跨用户实例访问未在本轮完整实测,不计为通过。未调用付费模型。
  • 一次性测试实例及其容器/网络均已清理;原有 DSH 实例未重启。测试日志及隔离备份保留于 /tmp/dsh-isolated-integration-BFUCkK/(本地临时证据,不随提交分发)。

早期实现与待验收表(已归档)

原表描述模板2、npm构建及预约阶段,已由文首当前矩阵替代。早期未闭项须按各阶段证据解释,不能推导当前Git发布通过。配置备份为白名单包,不含完整工作区、依赖或会话;旧密钥包仍须保管原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;现为Tailwind v4及Radix原语的shadcn风格组件,保留Monaco 历史确认不替代当前UI浏览器验收

代码主要按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

本节的/shared挂载、共享入口与迁移结果仅描述当时方案;当前共享入口和新容器共享挂载均已移除,实例删除会清理自身数据及备份,详见文首矩阵。

管理交互优化验收

  • 配额静态展示与模态编辑、常用预设、未改变禁提交;密码明确按钮、原生模态、显示隐藏、即时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 验证不替代浏览器验收。