deepseek-harness
deepseek-ai
DeepSeek Harness: Everything is a Plugin.
PROJECT TOPICS
PROJECT README
用户管理 + 登录门禁:自带一个 HTTPS 网关(默认 https://<本机IP>:19843)挡在 dsh web 前面——未登录访问任何页面自动跳到登录/注册页,API 与 WebSocket 一律 401;内置管理员/普通用户两级角色,管理员管所有用户(新增、删除、禁用、重置密码、角色调整、IP 封禁、重置两步验证)并查看登录记录、访问记录、操作日志,普通用户只能查看和修改自己的信息。支持 TOTP 两步验证(Google Authenticator / 1Password 等验证器 App 扫码绑定,密码之后再加一道动态码)与注册审批制(自助注册默认待管理员启用)。

两步验证演示(登录动态码 + 扫码绑定,亮色 / 暗色):
| 亮色主题 | 暗色主题 |
|---|---|
![]() |
![]() |
node:https 监听器(默认端口 19843)反代 loopback 的 dsh web——所有请求先过会话校验,未登录的页面访问 302 跳 /login,API 请求、WebSocket 升级一律 401。登录页为插件自带的独立页面(登录 / 注册双 Tab),不依赖宿主前端,深浅色自适应autoActivate 关闭时,自助注册的账号(首个管理员除外)落为禁用状态——注册页提示「账号需管理员审核启用后方可登录」,管理员在用户表点「启用」即完成审批;开关打开则注册即激活直接登录。审批动作零新界面,复用用户表的禁用/启用按钮与状态徽章loginFailLimit / lockoutSeconds 可调;内存态,重启清零);关闭需输登录密码确认,管理员可对任意用户重置。零第三方依赖,兼容 Google Authenticator / 1Password / 微软验证器 / Aegis 等sslip.io / nip.io 通配 DNS 别名 + localhost);设置页「HTTPS 证书」页一键下载 PEM/DER、核对指纹、复制 macOS / Windows / Linux 导入命令——导入信任链后浏览器不再弹证书警告certs/custom/、0600 权限、界面不回显);一键重新生成自签证书(改了 hosts 后重签,替代手动删文件重启)与重启网关sessionDays 可调),dsh 重启不掉线;HttpOnly Cookie,密码 scrypt 加盐哈希;认证与数据层零第三方依赖——TOTP 为 node:crypto 手写实现(过 RFC 6238 官方测试向量)、二维码为内置 MIT 编码器,仅证书签发用 selfsigneddsh plugin --profile web add @weibaohui/user-management -w
装完重启 dsh web 即生效。
https://<本机IP>:19843(浏览器首次会提示自签证书警告,按下方「HTTPS 证书」引导导入信任链后消失)——首个注册的账号就是管理员~/.dsh/user-management/(users.json / sessions.json / activity.jsonl / audit.jsonl / bans.json,0600 权限,原子写;审计账本滚动保留最近 5000 条)remoteAddress)判定:本插件自带的网关不做代理改写,看到的即是客户端真实地址;若你在网关前面另加反代层,看到的将是反代的 IPuser-management 向宿主提供 cordis 服务 user-management,兄弟插件据此知道自己收到的请求是"谁"(归属定时任务/分享/编辑记录等场景)。
// ⚠️ 不要把 'user-management' 写进静态 inject 数组 —— user-management 未安装时
// 你的插件会卡死激活。用运行时 inject,装了才有、没装就跳过:
module.exports = {
name: 'my-plugin',
inject: ['webServer'],
apply(ctx) {
ctx.inject(['user-management'], (scope) => {
const um = scope['user-management']
ctx.effect(() => ctx.webServer.register({
kind: 'prefix', path: '/my-plugin/api',
handler: async (req, res) => {
const user = await um.resolveRequest(req) // 解析请求 cookie
if (!user) return sendJson(res, 401, { error: '未登录' })
if (user.role !== 'admin') return sendJson(res, 403, { error: '需要管理员' })
// user.username / user.id 可用于操作归属
},
}), 'my-plugin: api')
})
},
}
| 方法 | 说明 |
|---|---|
resolveRequest(req) |
从请求的 um_session cookie 解析当前用户(推荐入口) |
resolveToken(token) |
已自行取出 token 时的底层变体 |
返回 UmUser:{ id, username, role: 'admin'|'user', disabled, totpEnabled, createdAt, lastLoginAt };以下情况一律返回 null:未登录、会话过期/伪造、用户被禁用或删除(禁用/删除即刻失效)、user-management 自身存储故障(不向消费方抛错)。服务一 provide 即可安全调用(内部等待存储就绪)。
页面里直接 fetch('/user-management/api/session') 即可,网关本地应答 { user: {...} | null }(HttpOnly cookie 自动携带)。
这只回答"这个请求是谁发的",不构成数据隔离——dsh 宿主的会话与工作区仍是全实例共享。另外身份可信的前提是 dsh web 只监听 loopback(本插件网关架构的默认要求):一旦有人绕过网关直连宿主端口,cookie 校验仍由 store 把关,但请保持宿主不对外暴露。
v0.4 起,本插件自带 HTTPS 远程访问网关:dsh web 留在 loopback(127.0.0.1:3080),网关(独立 node:https 监听器)反代到它——网关是唯一对外入口,认证不可绕过。配置走 ~/.dsh/settings.yaml 的 user-management: 段(也支持设置页热生效)。
| 字段 | 默认 | 说明 |
|---|---|---|
enabled |
true |
关掉则不启动网关监听器 |
autoActivate |
false |
注册审批开关。false(默认,审批制):自助注册的账号(首个管理员除外)直接落为禁用状态,注册页提示等待管理员审核,管理员在用户表点「启用」即完成审批;true:注册即激活、直接登录(历史行为) |
listenHost |
0.0.0.0 |
网关监听地址;127.0.0.1 仅本机可达 |
port |
19843 |
网关 HTTPS 端口 |
sites[] |
[](=自动) |
站点白名单 + 证书;空 = 自动枚举本机所有 IP(含 sslip.io / nip.io 别名)。配了 sites 与自动列表合并而非替换:见下方「场景 3」 |
sites[].hosts |
— | Host 白名单(域名/IP,支持 *.example.com 通配)。不带 cert/key 的 site 的 hosts 合并进自签 site(去重) |
sites[].cert / sites[].key |
'' |
PEM 文件路径(fullchain + privkey);配了则保留为独立 SNI site(按域名选证书),不配则按 hosts 自签 |
title |
DSH 控制台 |
登录页标题 |
sessionDays |
7 |
会话有效期(天),滑动过期——Cookie Max-Age 与服务端会话 TTL 同时生效,保存即对新签发的会话生效 |
loginFailLimit / lockoutSeconds |
5 / 60 |
两步验证防爆破:连续错 N 次动态码后锁定 M 秒(内存态,重启清零;改设置即重置计数) |
maxBodyBytes |
16384 |
API JSON 体积上限(字节);界面粘贴的证书对也走此上限,fullchain 较大时适当调高 |
不想碰 YAML 时,管理员在 设置页 → 用户管理 → 网关 页签完成证书管理:
sites 文件路径等设置后可用)~/.dsh/user-management/certs/custom/(0600,私钥不回显)并自动重启网关sites[] 文件路径配置共存:自动站点 > 配置文件站点 > 上传站点(同主机名 SNI 先到先得;上传条目之间禁止重复主机名);界面能力与「场景 2」等价,选哪种全凭习惯;主机名重叠时按 自动生成 → 文件路径 → 本页添加 顺序取用不配 sites → 网关自动枚举本机所有非 loopback IP(IPv4 + IPv6,含 Tailscale)填进 hosts,并签发 100 年自签证书(SAN 覆盖这些 IP + localhost);listenHost 默认 0.0.0.0。打开 https://<本机任意 IP>:19843 → 信任自签证书 → 注册/登录(首个访问者即管理员)。
user-management:
listenHost: '0.0.0.0'
port: 19843
sites:
- hosts: ['dsh.example.com']
cert: '/etc/letsencrypt/live/dsh.example.com/fullchain.pem'
key: '/etc/letsencrypt/live/dsh.example.com/privkey.pem'
cert / key 是 PEM 文件路径(fullchain + privkey,如 certbot certonly -d <域名> 申请);配了就加载你的证书,不配则按 hosts 自签。sites(每项自己的 hosts + cert + key),网关按 SNI 选证书。服务器有个 NAT 进来的公网 IP(不在本机网卡上,不会被自动枚举到)。配 sites(不配 cert/key)即可把它合并进自签 site——本机 IP / LAN / Tailscale 访问不丢,公网 IP 也进自签证书 SAN:
user-management:
listenHost: '0.0.0.0'
port: 19843
sites:
- hosts: ['111.228.30.150'] # NAT 公网 IP,不带 cert/key -> 合并进自签 site
sites 会替换自动列表,本机 IP 全丢 → 本地/LAN/Tailscale 访问被 421。v0.5.4 起改为加法合并。<ip>.sslip.io 等用 Let's Encrypt 签真证书(见「场景 2」写法)。sites 白名单 + 用域名证书 + 先在 loopback(https://127.0.0.1:19843)注册首个 admin 再对外。~/.dsh/user-management/certs/;改了 hosts/SAN 后要重签:设置页「网关」页签点「重新生成自签证书」,或手动删 localhost.crt / localhost.key 重启(否则复用旧证书保指纹稳定)。合并新增的公网 IP 同理——要让旧自签证书 SAN 补上该 IP 就重签一次。node:https,见上文「Remote Gateway 配置」)——所有请求先过网关的会话校验(未登录 document 跳 /login,API/WS 返 401),通过后才反代到 loopback dsh web;不再依赖宿主 webServer.server 监听器重排(旧版 0.3 的 attachGate + 降级路由级网关已移除)autoActivate: false):注册成功但账号处于禁用状态,无法登录,需管理员在「用户」表点「启用」放行(登录页会提示等待审核);首个注册的管理员不受此开关影响。开关打开时注册即激活直接登录。请勿将 dsh web 暴露给不受信任的网络GET /user-management/api/cert(PEM,?format=der 得 Windows 用的 .cer)与 GET /user-management/api/cert-info(SHA-256 指纹 / 有效期 / 覆盖名称)。设置页「HTTPS 证书」卡片一键下载 + 复制各系统导入命令。自签证书不会因 SAN 完整而不弹警告——消除警告靠导入信任链(导入后 IP / sslip.io / nip.io 三种访问方式全部干净);公网 IP 可用 Let's Encrypt 给 <ip>.sslip.io 签真证书实现真正零警告;Tailscale 用户优先用 tailscale cert + 机器名.尾网名.ts.net(真 CA 证书、零警告),尾网 IP 的 SAN 别名仅作兜底GET /user-management/api/gateway/status(状态 + 每站点证书详情)与 POST /user-management/api/gateway/certs(dryRun: true 只检测不保存;正式提交要求证书可解析、私钥配对、主机名合法且不与已上传条目冲突)、DELETE .../gateway/certs/:id、POST .../gateway/certs/regenerate(删自签重签)、POST .../gateway/restart 全部匿名 401 / 普通用户 403,动作均记入活动台账;接口任何响应都不回显私钥,私钥只落 certs/custom/(0600)users.json(0600 + 原子写)——动态码验证需要原文,无法像密码那样单向哈希,这是自托管实现的通行做法(Gitea 同款);验证码只在密码正确后才被要求,接口不会向未持正确密码者泄露某账号是否开启了两步验证;动态码一次一用 + 连错 5 次锁 60 秒;丢失验证器:由管理员在用户表「重置两步验证」恢复,最后的管理员丢手机且无第二个管理员时,需手动编辑 users.json 删除该用户的 totpSecret 字段并重启;开启两步验证不踢已有会话(已登录设备不受影响,管的是"下次进门")忘记管理员密码/用户名、IP 封错解封、证书警告、网关没起来、审计文件清理等常见问题的处理办法见 docs/FAQ.md。

本插件与 DeepSeek Harness(@deepseek-ai/dsh)的版本对应关系:
| 插件版本 | 适配 dsh 版本 | 备注 |
|---|---|---|
| 0.9.7 | 0.1.7-rc.2 | 当前版本,已在 @deepseek-ai/dsh@0.1.7-rc.2 下验证运行 |
发版约定:每次发布新版本时,请在上表追加一行,记录该插件版本实际验证所用的
@deepseek-ai/dsh版本。package.json的engines.dsh声明最低支持版本;本表记录实际验证版本,二者配合使用。
CLASSIFICATION EVIDENCE
系统优先读取 GitHub Topics,再与站内分类词典和词根规则比对。当前命中: 无有效分类标签。