build / build-and-scan (push) Waiting to run
文档库:目录改为编号制(01-规范/02-架构设计/03-数据库/04-调整方案/
05-交接单/06-ops/07-scripts/08-skills/09-archive),顶层散文件归入 01-规范/;
INDEX.md 与 docs-manifest.json 重刷(档案 146 篇);旧目录名引用全量对齐。
IM 线:src/im/**(SDK / hub / store / presence / ws / gateway-token)、
src/web/routes/im.ts、src/db/plugin-data/**、src/supervisor/plugin-assembly.ts
及对应 test/**。
插件线:poc/{im-agent-bridge,im-connection-gateway,im-conversation-tabs,
business-plugins-im,carbon-mcp-probe}、src/web/routes/{sessions,overlay-device}.ts、
src/net/relay/{device-grant,instance-credential}.ts。
仓库卫生:清出 40 个历史误入库 / 已改名文件(34 个交接单归档 + 6 个旧结构,
本地均有副本);dsh-server-docs/.gitignore 补 tmp/;交接单不入库(政策)。
8.1 KiB
8.1 KiB
DEPLOY — 本部署说明(ai1net.com / 47.77.182.89)
用途:把"这套平台到底部署在哪、装了什么、改了什么、怎么重建/回滚"一次讲清,供后来人(或未来的我)在不翻聊天记录的前提下独立重建。 对应档案 19 §C9。最后更新:2026-09-19(域名迁至 ai1net.com)
1. 拓扑与入口
| 项 | 值 |
|---|---|
| 服务器 | 47.77.182.89(Alibaba Cloud Linux 8,阿里云)|SSH 别名 bt-server(端口 32022,root) |
| 门户 / 用户实例 | https://ai1net.com / https://<用户名>.ai1net.com(Cloudflare 代理,回源走 443) |
| 旧域名 | alotbuy.com(含 *.alotbuy.com、*.dsh.alotbuy.com)→ 301 到新域(2026-09-19 降为过渡装置;relay-direct.alotbuy.com 与 /dshs-relay、/dshs-overlay/bootstrap 仍透传) |
| 面板 | 宝塔(站点 vhost 由面板管理,注意:面板保存会重写 vhost,可能抹掉自定义指令) |
| 编排器 | 自研 dshs(Node/TS),systemd 单元 dshs,监听 127.0.0.1:3080 |
| 被托管运行时 | 官方 @deepseek-ai/[email protected](全局安装于 /usr/local/lib/node_modules/@deepseek-ai/dsh,平台不改其任何文件) |
2. 目录布局
| 路径 | 说明 |
|---|---|
/opt/dshs |
编排器源码仓库(也是 git 仓库;lib/ 被 .gitignore 忽略 → 需构建) |
/var/lib/dshs |
dataRoot:dshs.db(better-sqlite3)、users/<uuid>/ |
<userRoot> = dataRoot/users/<uuid> |
每用户根:ws/(工作区,用户可见)、home/(DSH_HOME:profiles/sessions/storages/skills)、tmp/(1777) |
<profile> = <userRoot>/home/profiles/web |
该用户的 dsh profile:node_modules/、cordis.patch.yml、package.json、pnpm-lock.yaml |
/opt/dsh/ |
运维脚本与备份:switch-domain-alotbuy.sh、backups/、artifacts/(插件 tgz) |
/www/server/panel/vhost/nginx/ |
ai1net.com.conf(现役门户)、relay-direct.conf(443/TCP 兜底)、alotbuy.com.conf(2026-09-19 降级为 301)、dsh.alotbuy.com.conf(301)+ 多份 .bak-* |
/etc/letsencrypt/live/{ai1net.com,alotbuy.com,dsh.alotbuy.com}/ |
证书(新域 DNS-01 via /etc/cloudflare-ai1net.ini;旧域 via /etc/cloudflare.ini) |
3. 环境变量(/etc/dshs.env)
| 键 | 作用 |
|---|---|
DSHS_PORT |
编排器监听端口(127.0.0.1:3080) |
DSHS_DATA_ROOT |
dataRoot |
DSHS_BASE_DOMAIN |
门户/用户子域基域(现 ai1net.com) |
DSHS_COOKIE_DOMAIN |
会话 Cookie 域(现 .ai1net.com;见档案 22 §五 取舍说明) |
DSHS_SECURE_COOKIES |
Cookie Secure 标记(HTTPS 下应开) |
DSHS_DSH_BIN |
子实例可执行路径 |
DSHS_ISOLATION_MODE |
隔离模式(本部署 account = bwrap + setpriv + systemd-run scope) |
DSHS_BASE_UID |
每用户 uid 起始值 |
其余可调项走代码默认值(崩溃退避/熔断、idle-reap、bundled skill dir 等),见
src/config.ts的DEFAULT_*与档案 20。
4. 外部依赖(版本为实测)
| 依赖 | 版本 | 用途 |
|---|---|---|
| Node | 22.23.2 | 编排器与子实例运行时 |
| pnpm | 9.15.9 | profile 侧插件安装账本(pnpm-lock.yaml) |
| bubblewrap | 0.4.0 | 每用户沙箱(合成根 + 只读系统 + 私有 tmp + --unshare-pid) |
| systemd-run | — | per-instance scope(MemoryHigh=448M(基础/软限) + MemoryMax=1024M(上界/硬限),与插件开关无关(档案 96)/ CPUQuota=150% / TasksMax=128) |
| nginx | 1.28.3(宝塔) | 站点与反代(关键自定义指令见档案 21/22) |
| nftables | — | 出网护栏(/etc/nftables-dsh-egress.nft + dsh-egress.service) |
| sqlite3 CLI | 3.26 | 仅运维排查(平台内置用 better-sqlite3) |
| certbot + dns-cloudflare | — | 证书签发/续期(必须 --dns-cloudflare-propagation-seconds 60,否则失败) |
5. 平台侧脚本族
| 脚本 | 作用 |
|---|---|
ensure-role-profile-patch.cjs |
角色化 profile patch(非 admin 隐藏模型分区/收归插件开关) |
ensure-workspace-picker.cjs |
目录选择器收敛全量/新用户铺开(写平台段 + 逐用户 pnpm add;幂等,--dry-run/--restart) |
07-scripts/install-workspace-picker.sh <tgz> |
逐用户安装插件包(自动判断是否加 pnpm -w) |
/opt/dsh/switch-domain-alotbuy.sh |
域名切换(改 env → drain → 重启) |
/usr/local/bin/provision-new-users.sh |
新用户供应(建 OS 账号 + chown + 调用 picker 铺开);由 dsh-provision.path 触发 |
07-scripts/ci.sh |
类型检查 + 构建 + 单测(不含需要实例/凭据的 smoke) |
6. 构建 / 部署 / 回滚(三步)
# 构建(改代码后必做;lib/ 不入库)
cd /opt/dshs && npm run build && bash 07-scripts/ci.sh
# 部署(drain: 停服务 → 清残留实例 → 起服务)
systemctl stop dshs; for p in $(ps -eo pid,user,args | awk '$2 ~ /^dsh-/ {print $1}'); do kill $p; done; systemctl start dshs
# 回滚(示例:回滚到某个提交)
git -C /opt/dshs revert <sha> && npm run build && systemctl restart dshs
- 本机 ↔ 服务器同步:服务器无 Gitea 凭据 → 服务器
git bundle create→ 本机git fetch <bundle>(路径必须用D:/…)→merge --ff-only→push。 - 文档库:
bash 07-scripts/docs-sync-check.sh(双端 md5 对账,0 = 全绿);服务器侧/opt/dsh/docs(root 600)。
6.4 数据库初始化(PostgreSQL 13 / 47 本机进程)
DB 不在容器里,是 47 上本机进程(127.0.0.1:15432)。重建时按序跑:
-- ① 建角色 + 库(初始形态)
CREATE ROLE dshs LOGIN PASSWORD '<见 config/platform.env>';
CREATE DATABASE dshs OWNER dshs ENCODING 'UTF8' TEMPLATE template0;
-- ② 🔴 插件建库权限(2026-09-23 · B 档拍板)
-- 用途 = 平台侧为每个业务插件建独立库 `dshs_pl_<pluginId>`(一插件一库)
-- ⛔ 只给这一位;⛔ 不得顺手给 CREATEROLE / SUPERUSER
ALTER ROLE dshs CREATEDB;
- 连接串只从 systemd drop-in
dshs.service.d/cluster.conf取(DSHS_DB_URL);⚠️source /etc/dshs.env会拿到错的5432(见CODEBUDDY.md §8)。 - 回滚②:
ALTER ROLE dshs NOCREATEDB;⇒ 已建的dshs_pl_*库不受影响,仅新插件建不了库(平台建库口返回PG_CREATEDB_MISSING/ 503)。 - ⚠️ 权限校验判据(重建后必跑):
rolsuper=false·rolcreatedb=true·rolcreaterole=false—— 多点一位即为权限失控。 - ⚠️ PG 没有"只允许
dshs_pl_*前缀"的原生机制(实测建任意名库成功)⇒ 库名白名单只靠平台代码兜(点生成 + 正则复校 + 建库落台账)。详见03-数据库/DB-03-插件数据面规范.md §六。 - 迁移表由平台启动时
runPgMigrations自动落(v15 起含台账表plugin_datastores/plugin_data_audit)⇒ 部署新版本代码后随systemctl restart dshs自动补齐,⛔ 不需手工建表。
6.5 定时维护(cron,2026-09-11 建立)
| 任务 | 文件 | 时间 |
|---|---|---|
| 存储用量快照 | /etc/cron.d/dsh-maintenance |
每小时 :05 |
| 工作区清理 / 会话回收 / 回收站清空 | 同上 | 04:10 / 04:20 / 04:40 |
| 平台全量备份 + 旧备份清理 | /etc/cron.d/dsh-backup |
每周日 05:00 / 05:30 |
备份脚本
/opt/dsh/backup.sh(入库副本07-scripts/backup-platform.sh)含 SQLite 一致性快照;恢复步骤见档案 02 附录 C.8。
7. 默认红线(改任何东西前先读)
- 不自动升级 dsh;升级必须走"测试 → 评估 → 修复",并按档案 26 回归清单逐项验。
- 不改官方 dsh 主程序与缓存(
/usr/local/lib/node_modules/@deepseek-ai/dsh/**保持原封)。 - client bundle 禁
exports.default(R3)。 - 未明确要求不擅自 commit/push(服务器 git 与文档 git 都一样)。
- 服务器
/opt/dsh/docs保持 root 600。
相关档案:01(架构)|02(运维,含附录 C 速查)|21(nginx 性能/流式)|22(域名迁移)|26(升级耦合点与回归清单)