Files
dsh_shenxian/poc
admin c2b7c5ef71 平台共享模型改为「管理员逐用户授权」+ 品牌中文名改「能力网络」+ 修掉跨机模型落地/语言偏好静默失效
三条线合并入库(同一次部署批次,源码与生产此前已一致):

一、档案 138 · 平台共享模型:管理员逐用户授权(默认关闭)
  用户口径原文:「admin 设置的共享模型,需要 admin 在用户列表中开启(新增选项,默认关闭),
  用户才能在会话中使用(以及在设置的模型设置页面展示)」。
  · DB 迁移 v11:新增 users.shared_model_granted(DEFAULT 0 = 默认关闭)。
    ⚠️ 刻意**不**复用 v6 的 shared_model_enabled —— 那是用户侧偏好(用户能自己关,默认 1),
    而本需求要的是**管理员门禁**;共用一列则用户点一下就给自己授权,门禁形同不存在。
    生效 = granted ∧ enabled(server.ts#sharedLandingRows)。
  · 新路由 POST /api/admin/users/:id/models/shared(requireAdmin)+ 审计 shared_model_grant
    + **尽力而为**重启该用户实例(租约被占/实例未运行都不算失败)。
  · admin 用户列表新增「共享模型」列;用户侧 /api/me/keys 增 shared.granted / sharedModelGranted;
    插件 0.3.24:未授权时「平台共享模型」整块不渲染。

二、顺带修掉一个既有真缺陷:平台侧写用户 home 必须走 UserFs(用户卷在 worker 上)
  landModels 原先 join(owner.home_dir, …) + 本机 fs ⇒ 对「实例不在控制面本机」的用户
  读到空串(**不报错**)⇒ 模型落地一直是**静默空操作**(托管清单还被清空)
  ——即档案 87 的模型设置页对 guest 这类用户**从未生效**。
  · UserFs 新增 readHomeFile/writeHomeFile + 文件名白名单(settings.yaml / .credentials.yaml)
  · worker agent 新增 /fs/home-read /fs/home-write(只认白名单裸文件名)
  · landModels 改走 userFs(与"文件面"同一份按归属路由 ⇒ 落地与实例必然同机)
  · 同轮把 /api/me/locale(档案 102 语言偏好)也改成同一套(原先同样失效)
  · home-files.ts 抽出 backupHomeFile(备份留平台侧,命名规则逐字不变)

三、档案 139 · 品牌中文名:能力枢纽 → 能力网络(其他语言仍 CapabilityNet)
  落点四处:i18n 中文词条 / admin.html 顶栏 / favicon.svg 的 title+aria-label / design.css 注释;
  test/i18n-brand.test.mjs 期望值同步。档案 137 顶部加"后续"指针,不改历史。

验证(全部真机实测):
  · 红腿:未授权 → 106 上 guest 的 .credentials.yaml refs 变空(共享 key 被撤)
  · 绿腿:授权 → key 回来 + 托管清单恢复 ["DEEPSEEK_API_KEY"]
  · admin 列表带出 sharedModelGranted;用户侧 granted 随授权翻面(true/shared ↔ false/none)
  · 开关两次均 200(不再假失败);插件实装 0.3.24 且含 gating 字符串
  · 语言偏好:106 上 settings.yaml 出现 locale.preference=en(属主=实例属主,既有段逐字保留)
  · 本机 npm test 226 tests / 225 pass / 0 fail / 1 skipped;四个 verify 脚本全绿

部署:47 推 51 个 lib 产物、106 推 12 个(lib/ 是 gitignore ⇒ 回滚点物化在
/opt/dsh/backups/seq138b-20260919-125345/,逐文件对账 0 不一致;先 106 后 47);
插件 business-plugins 0.3.24(两机 artifacts 与本机 pack md5 一致)。
2026-09-19 13:51:01 +08:00
..

Phase 3 核心路径 PoC(不写业务代码)

🧭 ← 返回 README · 结论落地为:K8s 部署教程

对应内部设计稿 §7 的四项风险验证。不引入任何业务代码——全部用一次性 镜像(busybox / node / socat / CloudNativePG)验证基础设施假设。四项全部通过, 才允许进入 Phase 0 镜像化 / Phase 2/3 落地,避免 Phase 3 返工。

前置条件

  • 一个 k3s 集群(k3s --cluster-init 3 server + N worker 皆可,单机 k3s 也能跑 PoC)。
  • 已装且健康:
    • Longhorn(item 1、4 依赖;item 1 是硬前提,必须 longhorn.io StorageClass 可用)
    • Cilium(item 3 依赖;NetworkPolicy 必须被强制)
    • CloudNativePG operator(item 4 依赖)
    • cert-manager / MetalLB / kube-vip 与本 PoC 无关,暂不需要
  • kubectl 指向该集群(kubectl get nodes 有输出)。
  • 本地有 bash + curl(item 2/3 用);item 2 的 WS 客户端用 node(本机或容器内均可)。
kubectl get nodes
kubectl get storageclass longhorn        # item 1/4 前提
kubectl get pods -n kube-system -o wide | grep -E 'cilium|longhorn|csi'
kubectl get crd clusters.postgresql.cnpg.io  # item 4 前提

Item 1 — Longhorn RWX + subPath + runAsUser/fsGroup(§4.9 / §6.0 的 PoC)

验证的未知点:fsGroup 是否会 chown subPath 叶子?非 root 目标 uid 能否在 PVC 根建目录、0700 属主是否正确、子目录挂载后能否读写。

⚠️ 这是 docs/k8s.md §4.9 标明的「PoC 第一项必须验证」。若失败,按 §4.9 的 回退:bootstrap 阶段用特权 Job 完成 chmod 1777(在启 PSA restricted 之前), 时序固定为「初始化 PVC → 启 PSA → 允许用户 Pod」。

cd 01-longhorn-subpath
./run.sh

通过标准(脚本自动断言):

  1. bootstrap Job(特权)chmod 1777 /mnt 成功。
  2. init Job(非 root,runAsUser/fsGroup = 100001)能 mkdir -p /mnt/u1/{ws,home} 且 chmod 0700 /mnt/u1 成功——即非 root uid 能写 PVC 根(chmod 1777 生效)。
  3. 测试 Pod(runAsUser=100001,subPath: u1)能写/读文件,且 stat 显示目录属主 uid=100001、权限 0700。
  4. (可选)另一个 uid(100002)无法读该目录——subPath 叶子 + DAC 的越权边界。

Item 2 — socat 桥 WebSocket 透明转发(§3.2 / §4.3)

验证的未知点:DSH 只监听 loopback,socat TCP-LISTEN:8081 → 127.0.0.1:8080 的纯 TCP 转发对 WebSocket Upgrade 是否透明。

cd 02-socat-ws
./run.sh

通过标准:

  1. 通过 sidecar 的 8081 能拿到 fake-dsh 的 HTTP 200(TCP 基础通)。
  2. WebSocket Upgrade 握手经 8081 返回 101 Switching Protocols,且 Sec-WebSocket-Accept 校验正确(证明 Upgrade 请求与响应都原样穿透 socat)。
  3. 一条文本帧 echo 往返成功(双向透明)。

若 2/3 不透明,回退:换 nginx/netcat sidecar(同端口转发),见 §9。


Item 3 — NetworkPolicy 默认拒绝 + 控制面→DSH 单向(§3.5 / §4.4)

验证的未知点:Cilium 是否强制 NetworkPolicy;default-deny 下「仅控制面可达 DSH 8081」的单向放行是否正确,跨来源访问被拒。

cd 03-networkpolicy
./run.sh

通过标准:

  1. 同 namespace 内打了 app=dsh-orchestrator 的「控制面」Pod 能连通 DSH 8081。
  2. 同 namespace 内其它 Pod(app=attacker)访问 DSH 8081 被拒(超时/拒绝)。
  3. 这也覆盖 §3.5 的「每次 NetworkPolicy 变更后跑自动化验证」——本脚本即该验证的雏形。

若 flannel(k3s 默认)下 2 仍通,说明 CNI 未强制策略,必须切 Cilium(§11.2)。


Item 4 — CloudNativePG on Longhorn 的 pgbench 基线(§4.5)

验证的未知点:Postgres 对延迟/IOPS 敏感,Longhorn RWO 复制是否拖慢同步复制, 延迟是否可接受。

cd 04-cnpg-pgbench
./run.sh

通过标准(人工判读,无固定阈值,取决于硬件):

  1. CloudNativePG 3 实例主备建立、Ready。
  2. pgbench -S(select-only)tps 与延迟与本机 NVMe 基线对比,落在可接受区间。 §4.5 结论:不够则回退「节点本地 NVMe + PG 主备」。

通过后

四项全过 → 记录结果(tps/latency 数字、Longhorn 权限行为结论)到本 README 尾部, 再进入 Phase 0(镜像化)。任一项失败 → 按对应 §9 回退方案调整后重跑。