部署教程(新手版)— DSH 服务端登录插件

August 26, 2026 · View on GitHub

🧭 ← 返回 README · 进阶加固:硬隔离 · 出问题:排查手册 · 域名细节:域名配置示例

这份教程假设你第一次部署这类服务。每一步都写清楚「做什么、为什么、怎么做、应该看到什么」。照着顺序做,大约 15–30 分钟能把整套跑通。

全文用 example.com 当例子,请把所有 example.com 换成你自己的域名。比如你的域名是 dsh.baidu.com,那么 dsh.example.com 就是 dsh.baidu.com*.dsh.example.com 就是 *.dsh.baidu.com


0. 先搞懂几个词(看不懂也没关系,后面会一直用到)

大白话解释
服务器一台一直开机的电脑(这里指 Linux 云服务器),你的服务跑在上面。
域名人类好记的名字,比如 example.com。它最终会被翻译成服务器的 IP。
DNS负责「域名 → IP」翻译的系统。你在域名商那里改 DNS 记录,就是告诉全世界「这个名字指向那台服务器」。
子域名在域名前面再加一段。比如 dsh.example.comcarol.dsh.example.com 都是 example.com 的子域名。
端口一台服务器上的不同「门牌号」。一个服务占一个端口。
nginx一个「反向代理」软件:站在门口,把外面来的请求按域名转发给里面跑的服务。
HTTPS / 证书让浏览器显示「🔒 安全」的加密层。证书要针对具体域名签发。
环境变量给程序传配置的方式,形如 名字=值
进程正在运行的一个程序。

0.1 整体长什么样(先有个全局印象)

你的用户(浏览器)

   │ 访问 dsh.example.com(登录、管理、桌面)
   │ 访问 carol.dsh.example.com(carol 这个用户的 DSH 聊天界面)

nginx(门口,按域名分发 + 加 HTTPS 锁)

编排服务 dsh-server-login(本插件,跑在 127.0.0.1:3080)
   ├─ 管登录、审核、桌面
   └─ 按域名把 carol 的请求转发给「carol 的 DSH 进程」
            └─ DSH 进程(DeepSeek Harness,跑在随机的本机端口上)

两个东西别搞混

  • dsh-server-login(编排服务):我们这个插件,管「登录、审核、桌面、按域名转发」。
  • dsh(DSH):DeepSeek Harness,真正的 AI 聊天界面。它由编排服务自动为每个用户启动,你不需要手动跑它。

1. 部署前准备(清单)

  1. 一台 Linux 服务器,能 root 登录(推荐 Ubuntu 22.04+)。
  2. 一个自己的域名,能登录域名商后台改 DNS 记录。
  3. 大概半小时。

2. 第 1 步:安装 Node.js

本插件需要 Node 22 以上。用 nvm 装最省心:

# 1) 安装 nvm(Node 版本管理器)
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
# 2) 让当前终端生效(或关掉重开一个终端)
source ~/.bashrc
# 3) 安装 Node 22
nvm install 22
# 4) 验证
node -v    # 应该显示 v22.x.x

3. 第 2 步:安装 DSH(DeepSeek Harness)

这是聊天界面的本体。用 npm 装它的 CLI:

npm install -g @deepseek-ai/dsh

如果这个包名不对(DSH 还在预发布阶段,安装方式可能变),以 DeepSeek Harness 官方文档为准。装完验证一句话

dsh --version    # 能打印版本号就是装好了

4. 第 3 步:安装本插件(dsh-server-login)

# 1) 下载源码
git clone https://github.com/pointer-a/dsh-server-login.git
cd dsh-server-login

# 2) 装依赖 + 编译
npm install
npm run build

装完这个目录里会有个 lib/(编译产物)和 node_modules/(依赖)。

让子 DSH 能加载本插件的运行时插件:本插件会通过 --patch 给每个用户的 DSH 挂一个运行时插件(负责注入「守护 DSH」上下文、绑定端口等)。前提是 dsh-server-login 要装进 DSH 的 profile:

dsh plugin --profile web add /dsh_login/dsh-server-login

不做这步,运行时插件加载不了,守护 DSH 的上下文注入就不会生效。


5. 第 4 步:创建管理员账号

管理员是第一个账号,由他审核其他注册用户。

# 先加载环境变量(让数据库路径一致,见下面警告)
source /etc/dsh-server-login.env
node lib/cli.js bootstrap-admin --username admin --password '你的强密码'
  • --username admin:管理员用户名(可换)。
  • --password '你的强密码':管理员密码(换成你自己的,别用弱密码)。

看到 admin "admin" created (...) 就成功了。

⚠️ 数据库路径必须全程一致:管理员、编排服务、systemd 用的必须是同一个数据库。 本教程统一用 <DATA_ROOT>/server-login.db(= /var/lib/dsh-server-login/server-login.db),所以建管理员和启动服务都不加 --db、都先 source /etc/dsh-server-login.env。 如果你在某处加了 --db 别的路径,那建管理员和 systemd 必须加同一个路径,否则登录不进。


6. 第 5 步:配置环境变量 + 启动编排服务

先写一个环境变量文件,把配置集中放一起,方便以后改:

cat > /etc/dsh-server-login.env <<'EOF'
DSH_SERVER_LOGIN_PORT=3080
DSH_SERVER_LOGIN_DATA_ROOT=/var/lib/dsh-server-login
DSH_SERVER_LOGIN_BASE_DOMAIN=dsh.example.com
DSH_SERVER_LOGIN_COOKIE_DOMAIN=.dsh.example.com
DSH_SERVER_LOGIN_SECURE_COOKIES=true
DSH_SERVER_LOGIN_DSH_BIN=/root/.nvm/versions/node/v22.23.2/bin/dsh
EOF

逐个解释:

变量为什么
DSH_SERVER_LOGIN_PORT3080编排服务自己监听的端口(nginx 会转发到这个端口)。
DSH_SERVER_LOGIN_DATA_ROOT/var/lib/dsh-server-login每个用户的文件/配置存哪里。
DSH_SERVER_LOGIN_BASE_DOMAINdsh.example.com关键:告诉编排服务「子域名长什么样」——<用户名>.dsh.example.com
DSH_SERVER_LOGIN_COOKIE_DOMAIN.dsh.example.com关键:登录 cookie 加这个 Domain,才能被子域名共享。注意前面的
DSH_SERVER_LOGIN_SECURE_COOKIEStrue走 HTTPS,cookie 必须标 Secure
DSH_SERVER_LOGIN_DSH_BIN/root/.nvm/versions/node/v22.23.2/bin/dsh编排服务用它启动每个用户的 DSH。用绝对路径,别写 dsh——systemd 的 PATH 找不到(否则报 spawn dsh ENOENT)。版本号按你实际 nvm 版本改:ls ~/.nvm/versions/node/

🔐 可选加固(生产建议):在 env 文件里再加一行 DSH_SERVER_LOGIN_PORT_GUARD=true。它用 iptables 的 OUTPUT owner-match 规则,禁止同机其他账号直连各用户 DSH 的回环端口(编排服务自身不受影响)。仅 Linux + root 有效;在不支持的环境下开启会直接拒绝启动,避免「以为有防护其实没有」。

🔑 API 密钥是每用户自己的密钥库:不再在环境变量里配平台 key。每个用户登录后,在桌面 → DSH 窗口点「管理密钥」(在「管理插件」旁)添加多个命名的密钥(AES-256-GCM 加密存在该用户自己的数据里),并可选择启用哪一个;spawn 时只注入该用户当前启用的 key。未设置 key 的用户,其 DSH 能启动但无法调用模型。

6.1 先手动跑一次(验证能起来)

source /etc/dsh-server-login.env
node lib/cli.js

看到 dsh-server-login listening on http://127.0.0.1:3080 就是起来了。Ctrl+C 停掉,下面配成开机自启。

6.2 配成开机自启(systemd)

cat > /etc/systemd/system/dsh-server-login.service <<'EOF'
[Unit]
Description=DSH server login orchestrator
After=network.target

[Service]
WorkingDirectory=/root/dsh-server-login
EnvironmentFile=/etc/dsh-server-login.env
Environment=PATH=/root/.nvm/versions/node/v22.23.2/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
ExecStart=/root/.nvm/versions/node/v22.23.2/bin/node lib/cli.js
Restart=on-failure

[Install]
WantedBy=multi-user.target
EOF

systemctl daemon-reload
systemctl enable --now dsh-server-login
systemctl status dsh-server-login   # 看到 active (running) 就对了

WorkingDirectory 换成你实际 git clone 的目录(上面假设是 /root/dsh-server-login)。

⚠️ 两个 nvm + systemd 必踩的坑(不这样写就会起不来):

  1. ExecStart 必须用 nvm node 的绝对路径,不能写 /usr/bin/env node——systemd 的 PATH 没有 nvm,node 会解析到别的版本,导致 better-sqlite3 原生模块报 ERR_DLOPEN_FAILED(ABI 版本不匹配,编排服务起不来)。
  2. 必须把 nvm 的 bin 加进 PATH(上面那行 Environment=PATH=...)——否则编排服务 spawn 子 DSH 时 dsh 找不到(spawn dsh ENOENT),而且 dsh 脚本内部也是 #!/usr/bin/env node,同样需要 node 在 PATH。 上面所有 nvm 路径按你实际版本改:ls ~/.nvm/versions/node/

7. 第 6 步:配置 DNS

登录你的域名商后台,加两条 A 记录,都指向服务器的 IP:

类型主机记录
Adsh你的服务器 IP
A*.dsh你的服务器 IP
  • dsh → 让 dsh.example.com 指向服务器。
  • *.dsh(通配)→ 让 carol.dsh.example.combob.dsh.example.com 等任意子域名都指向服务器。

改完等几分钟 DNS 生效。验证(在本机跑,把 IP 换成你的服务器 IP):

ping dsh.example.com      # 应该解析到你的服务器 IP

8. 第 7 步:签发 HTTPS 证书(通配证书)

子域名多、又不能挨个签,所以签一张通配证书*.dsh.example.com)。通配证书必须用 DNS 验证(证明你真的拥有这个域名)。

以 Cloudflare 为例:

# 1) 装 Cloudflare 的 certbot 插件
apt install -y certbot python3-certbot-dns-cloudflare

# 2) 准备一个存 API 令牌的文件(去 Cloudflare 后台建一个 DNS 编辑权限的 token)
cat > /etc/cloudflare.ini <<'EOF'
dns_cloudflare_api_token = 你的token
EOF
chmod 600 /etc/cloudflare.ini

# 3) 签发(注意两条 -d:通配 + 主域)
certbot certonly --dns-cloudflare \
  --dns-cloudflare-credentials /etc/cloudflare.ini \
  -d '*.dsh.example.com' -d 'dsh.example.com'

如果你不用 Cloudflare,改用对应插件(阿里云 --dns-aliyun、腾讯云 --dns-tencentcloud、DNSPod --dns-dnspod 等),原理一样。

⚠️ Cloudflare 免费套餐只代理二级及以下的域名;若你的通配域名到了第三级(如 *.dsh.example.com),需要把该记录的 Cloudflare 代理关掉(灰云,仅 DNS)。这会暴露服务器真实 IP,请自行权衡风险。

签好后证书在这里:

ls /etc/letsencrypt/live/dsh.example.com/
# 应看到 fullchain.pem 和 privkey.pem

9. 第 8 步:配置 nginx

先装 nginx:

apt install -y nginx

新建配置文件:

cat > /etc/nginx/conf.d/dsh-server-login.conf <<'EOF'
map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

# 主域:登录 / 管理台 / 桌面
server {
    listen 443 ssl http2;
    server_name dsh.example.com;

    ssl_certificate     /etc/letsencrypt/live/dsh.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/dsh.example.com/privkey.pem;

    location / {
        proxy_pass http://127.0.0.1:3080;
        proxy_set_header Host              $host;   # 关键:保留真实域名
        proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Upgrade    $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
        proxy_read_timeout 3600s;
    }
}

# 通配子域:每个用户的 DSH
server {
    listen 443 ssl http2;
    server_name *.dsh.example.com;

    ssl_certificate     /etc/letsencrypt/live/dsh.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/dsh.example.com/privkey.pem;

    location / {
        proxy_pass http://127.0.0.1:3080;
        proxy_set_header Host              $host;   # 关键:保留原始子域名
        proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Upgrade    $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
        proxy_read_timeout 3600s;
    }
}
EOF

最重要的两行(做错就会各种 404/401):

  • server_name dsh.example.comserver_name *.dsh.example.com:区分主域和子域。
  • proxy_set_header Host $host别删,它把原始域名透传给编排服务,子域路由靠它。如果你写成 Host dsh.example.com(固定值)或干脆不写,所有子域都会被当成主域,路由就乱了。

改完重载:

nginx -t && systemctl reload nginx

10. 第 9 步:(可选)账号级硬隔离

新手可跳过这一步,默认的「软隔离」已经能跑、能用。硬隔离是给生产环境防「一个用户偷看另一个用户文件」用的进阶项,需要 root 权限。想做就按 hard-isolation.md 一步步操作。


11. 第 10 步:从头验证一遍

按这个顺序走一遍,每步看到对应结果就说明那一步对了:

  1. 打开主域:浏览器访问 https://dsh.example.com → 看到登录页(不是 502/404)。
  2. 注册一个用户:点「注册」,填用户名(比如 carol)+ 密码 → 提示「等待审核」。
  3. 审核:用管理员账号登录 → 管理台 → 点「通过」carol。
  4. 用户登录:carol 登录 → 进入桌面(文件浏览器)。
  5. 启动 DSH:桌面点「在此文件夹启动 DSH」→ 显示「运行中」。
  6. 打开 DSH:点「打开 DSH」→ 跳到 https://carol.dsh.example.com/ → 看到 DSH 聊天界面(不是 502/404/401)。

冷启动:真实 DSH 启动要 几秒到十几秒(源码启动约 4s)。点「启动」后 status 会先显示 running,但端口要等插件树 boot 完才绑上——别立刻点「打开 DSH」,等 10 秒再开,否则会 502。

如果某一步卡住了

对照 troubleshooting.md 找现象 → 根因 → 修法。最常见的几个:

  • 502:DSH 刚启动还在「冷启动」(等 10 秒),或 DSH 没起来。
  • 404:BASE_DOMAIN 没设、或 nginx 的 Host 头被改掉了。
  • 401:cookie 没到子域(COOKIE_DOMAIN 没设或没带前导点),或浏览器里是旧 cookie(删掉重新登录)。
  • 403:DSH 的信任栅栏不认请求(Origin 头问题,已在内置代理里处理)。

12. 以后怎么更新插件

cd /root/dsh-server-login
git pull
npm run build            # 重新编译(改过代码就必须跑)
systemctl restart dsh-server-login

重要:git pull 不够,一定记得 npm run build——因为跑的是编译产物 lib/,不编译的话改动不会生效。