🎁 福利专区全网大模型免费应用 + 新用户福利 + 注册活动入口,低成本玩转 AI

轨道自托管集群 IkariKr/dsh-orbit

DeepSeek Harnessv0.0.1
IkariKr46 天前有提交已提供中文文档扫描:含敏感能力

安全远程接入多节点,锁定版本兼容升级

30.7/100
综合分
1
GitHub 星标
—
npm 周下载
—/5
用户评分

数据截至 2026/9/19 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。

自动兼容检查未通过:仓库 package.json 标记 private,未发布到 npm,需从源码安装。以下命令仍可尝试,但可能失败。
安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add IkariKr/dsh-orbit
该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)

状态速览

判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态
信任档位:已验证
是什么
dsh 原生插件 · other
装得上吗
本站已在 dsh 0.2.1-rc.5 下真实安装成功
非静态推断
L4 · 真实安装等级说明 →
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
更新放缓:最近一次提交在 46 天前
档位由哪些信号合成

本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。

验证判据与等级说明 →
社区实装还没有人打卡 · 装过的第一个就是你
安装兼容性检查需源码安装

仓库 package.json 标记 private,未发布到 npm,需从源码安装

展开查看检查明细 ↓

以下结论由程序自动检查 npm 包、engines 声明与入口文件得出,未做人工实机验证——能装不等于用着没问题。

✓npm 包dsh-orbit @ 0.0.1
✓Node 引擎要求 >=22 · 基线 Node 22.19 满足
✓dsh CLI 依赖未声明 dsh 版本约束
✓入口文件main/exports/bin 已声明

源码安装时若 pnpm 拦截 prepare 脚本,按其提示在 pnpm-workspace.yaml 的 allowBuilds 中放行后重跑。

验证方式:npm registry 存在性 + package.json 静态校验 · 最后检查 2026/10/12 15:09:27

安全扫描自动 · 静态分析源码
v2.7.0
🔵 扫描:含敏感能力尚无人工评估
展开查看扫描说明与证据 ↓

静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。

可执行外部命令读写/删除本地文件读取剪贴板或密钥环境变量发起外部网络请求
查看扫描证据(共 86 处,展示前 80 条)
  • 发起网络请求
    src/plugin/client.cjs:476
    const res = await fetch(endpoint, {
  • 发起网络请求
    src/plugin/client.cjs:493
    let res = await fetch(endpoint, {
  • 发起网络请求
    src/plugin/client.cjs:571
    const res = await fetch(`${this.hubBaseUrl}/hub/pairing/status`, {
  • 读取密钥环境变量
    bin/dsh-orbit-hub.mjs:38
    const gatewaySecret = process.env.DSH_ORBIT_HUB_GATEWAY_SECRET ?? null;
  • 读取本地文件
    bin/dsh-orbit-hub.mjs:54
    if (existsSync(caTarget)) {
  • 读取本地文件
    bin/dsh-orbit-hub.mjs:55
    caCertificates = [readFileSync(caTarget, "utf8")];
  • 读取本地文件
    bin/dsh-orbit-hub.mjs:73
    if (!existsSync(agingClockPath)) throw new Error("clock file does not exist");
  • 读取本地文件
    bin/dsh-orbit-hub.mjs:74
    const raw = readFileSync(agingClockPath, "utf8").trim();
  • 读取本地文件
    bin/dsh-orbit-node.mjs:72
    if (existsSync(caTarget)) {
  • 读取本地文件
    bin/dsh-orbit-node.mjs:73
    caCertificates = [readFileSync(caTarget, "utf8")];
  • 读取本地文件
    bin/dsh-orbit-node.mjs:157
    const report = JSON.parse(await readFile(reportFile, "utf8"));
  • 读取密钥环境变量
    bin/dsh-orbit-node.mjs:218
    const tlsKeyConfigured = Boolean(process.env.DSH_ORBIT_NODE_ROUTE_TLS_KEY);
  • 读取密钥环境变量
    bin/dsh-orbit-node.mjs:227
    const keyVal = process.env.DSH_ORBIT_NODE_ROUTE_TLS_KEY;
  • 读取本地文件
    bin/dsh-orbit-node.mjs:229
    const key = existsSync(keyVal) ? readFileSync(keyVal, "utf8") : keyVal;
  • 读取本地文件
    bin/dsh-orbit-node.mjs:229
    const key = existsSync(keyVal) ? readFileSync(keyVal, "utf8") : keyVal;
  • 读取本地文件
    bin/dsh-orbit-node.mjs:230
    const cert = existsSync(certVal) ? readFileSync(certVal, "utf8") : certVal;
  • 读取本地文件
    bin/dsh-orbit-patch.mjs:35
    await access(path);
  • 执行外部命令来自配置/变量
    bin/dsh-orbit-run-hooks.mjs:17
    const result = spawnSync(command, [hook.path], {
  • 发起网络请求
    src/node/client.mjs:88
    const req = client.request(url, reqOptions, (res) => {
  • 发起网络请求
    src/node/reverse-channels.mjs:428
    request = mod.request({
  • 发起网络请求
    src/node/reverse-client.mjs:48
    const request = mod.request(
  • 发起网络请求
    src/node/route-ingress.mjs:145
    const req = mod.request(
  • 发起网络请求
    src/node/route-ingress.mjs:327
    const upstreamReq = clientMod.request(reqOptions, (upstreamRes) => {
  • 发起网络请求
    src/node/route-ingress.mjs:474
    const upstreamReq = clientMod.request(reqOptions);
  • 读取本地文件
    src/node/store.mjs:332
    raw = readFileSync(path, "utf8");
  • 读取本地文件
    src/node/store.mjs:345
    raw = await readFile(path, "utf8");
  • 读取本地文件
    src/node/store.mjs:359
    const mode = statSync(path).mode & 0o777;
  • 写入本地文件
    src/node/store.mjs:382
    await mkdir(dirname(path), { recursive: true });
  • 写入本地文件
    src/node/store.mjs:385
    await writeFile(temporary, json, { encoding: "utf8", mode: 0o600 });
  • 写入本地文件
    src/node/store.mjs:388
    await rename(temporary, path);
  • 写入本地文件
    src/node/store.mjs:390
    await chmod(path, 0o600);
  • 读取本地文件
    src/plugin-patch-dsh-ssh.mjs:123
    const raw = await readFile(join(pluginRoot, "package.json"), "utf8");
  • 读取本地文件
    src/plugin-patch-dsh-ssh.mjs:145
    const source = await readFile(indexPath, "utf8");
  • 写入本地文件
    src/plugin-patch-dsh-ssh.mjs:183
    await writeFile(indexPath, patched, "utf8");
  • 读取本地文件
    src/plugin-patch-dsh-ssh.mjs:200
    const source = await readFile(join(root, "lib", "index.js"), "utf8");
  • 读取本地文件
    src/registry/backup.mjs:54
    await access(path, constants.F_OK);
  • 写入本地文件
    src/registry/backup.mjs:101
    await chmod(path, 0o600);
  • 读取本地文件
    src/registry/backup.mjs:124
    if (!existsSync(filePath)) {
  • 写入本地文件
    src/registry/backup.mjs:179
    await mkdir(dirname(destination), { recursive: true });
  • 写入本地文件
    src/registry/backup.mjs:197
    await rename(temporary, destination);
  • 删除本地文件
    src/registry/backup.mjs:216
    await rm(temporary, { force: true });
  • 写入本地文件
    src/registry/backup.mjs:237
    await mkdir(dirname(target), { recursive: true });
  • 写入本地文件
    src/registry/backup.mjs:245
    await copyFile(backup, stage);
  • 写入本地文件
    src/registry/backup.mjs:256
    if (targetExists || sidecarExists.some(([, present]) => present)) await mkdir(quarantine);
  • 删除本地文件
    src/registry/backup.mjs:287
    await rm(stage, { force: true });
  • 发起网络请求
    src/registry/route-probe.mjs:26
    const req = client.request(url, reqOptions, (res) => {
  • 发起网络请求
    src/registry/route-proxy.mjs:636
    const upstreamReq = clientMod.request(reqOptions, (upstreamRes) => {
  • 发起网络请求
    src/registry/route-proxy.mjs:910
    upstreamReq = clientMod.request(reqOptions);
  • 读取本地文件
    src/registry/server.mjs:146
    readFile(new URL("index.html", AUTH_UI_ROOT))
  • 读取本地文件
    src/registry/server.mjs:463
    readFile(new URL(fileName, SELECTOR_UI_ROOT))
  • 读取本地文件
    src/registry/server.mjs:549
    readFile(new URL(fileName, UI_ROOT))
  • 写入本地文件
    src/registry/sqlite.mjs:157
    if (existsSync(filePath)) chmodSync(filePath, 0o600);
  • 读取本地文件
    src/registry/sqlite.mjs:157
    if (existsSync(filePath)) chmodSync(filePath, 0o600);
  • 读取本地文件
    src/remote-settings-patch.mjs:115
    const raw = await readFile(packageJsonPath, "utf8");
  • 读取本地文件
    src/remote-settings-patch.mjs:135
    readFile(serverPath, "utf8"),
  • 读取本地文件
    src/remote-settings-patch.mjs:136
    readFile(clientPath, "utf8"),
  • 写入本地文件
    src/remote-settings-patch.mjs:142
    if (server.changed) await writeFile(serverPath, server.source, "utf8");
  • 写入本地文件
    src/remote-settings-patch.mjs:143
    if (client.changed) await writeFile(clientPath, client.source, "utf8");
  • 读取本地文件
    src/remote-settings-patch.mjs:155
    readFile(`${root}/index.js`, "utf8"),
  • 读取本地文件
    src/remote-settings-patch.mjs:156
    readFile(`${root}/client.js`, "utf8"),
  • 读取本地文件
    src/runtime-hooks.mjs:17
    entries = await readdir(hookDir, { withFileTypes: true });
  • 读取本地文件
    src/snapshot-contract.mjs:72
    raw = await readFile(manifestPath, "utf8");
  • 删除本地文件
    src/snapshot-contract.mjs:104
    await rm(manifestPath, { force: true });
  • 读取本地文件
    src/upgrade-runner.mjs:300
    await access(dir);
  • 发起网络请求
    src/upgrade-runner.mjs:346
    const request = transport.request(
  • 读取本地文件
    src/upgrade-runner.mjs:481
    const certificate = new X509Certificate(await readFile(identity.certPath));
  • 写入本地文件
    src/upgrade-runner.mjs:720
    await mkdir(config.workdir, { recursive: true });
  • 写入本地文件
    src/upgrade-runner.mjs:721
    await writeFile(`${config.workdir}/evidence.json`, JSON.stringify(evidence, null, 2) + "\n", "utf8");
  • 写入本地文件
    src/upgrade-runner.mjs:722
    await writeFile(`${config.workdir}/report.json`, renderReportJson(report) + "\n", "utf8");
  • 写入本地文件
    src/upgrade-runner.mjs:757
    await mkdir(config.workdir, { recursive: true });
  • 写入本地文件
    src/upgrade-runner.mjs:778
    await writeFile(
  • 写入本地文件
    src/upgrade-runner.mjs:790
    await chmod(gatewayIdentity.certPath, 0o640);
  • 读取本地文件
    src/upgrade-runner.mjs:814
    const identityCa = await readFile(gatewayIdentity.certPath, "utf8");
  • 读取本地文件
    src/upgrade-runner.mjs:851
    const overrideText = await readFile(`${config.workdir}/compose.override.yaml`, "utf8").catch(() => null);
  • 读取本地文件
    src/upgrade-runner.mjs:866
    const identityCa = await readFile(identityCertPath, "utf8");
  • 读取本地文件
    src/upgrade-runner.mjs:907
    return JSON.parse(await readFile(`${workdir}/evidence.json`, "utf8"));
  • 发起网络请求
    ui/selector/app.mjs:44
    let res = await fetch("/hub/session", {
  • 发起网络请求
    ui/selector/app.mjs:51
    res = await fetch("/hub/session", {
  • 发起网络请求
    ui/selector/app.mjs:69
    await fetch("/hub/session/logout", {
  • 发起网络请求
    ui/selector/app.mjs:107
    const res = await fetch("/hub/selector/nodes", {
扫描于 2026/11/4(0 天前) · 范围 入口闭包 + 全包扫描 · 来源 GitHub 仓库

静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。

给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个

功能亮点

  • 安全远程接入多节点
  • 锁定版本兼容升级
  • 集群管理

为 DeepSeek Harness 提供安全自托管与集群管理能力。

README

由 DeepSeek 最新模型翻译生成
DSH Orbit DSH Orbit 是一个由社区维护的自托管与集群层,面向 DeepSeek Harness。 该项目专注于安全远程访问、升级兼容性和多节点运行,同时将 DeepSeek Harness 保持为上游运行时。 DSH Orbit 是一个独立的社区项目。它与 DeepSeek AI 无关联,也未获得其认可。 自托管 · 多节点 · 安全优先 · 版本锁定兼容性 · 开放治理 初次接触?请从概览开始:了解 Orbit 是什么、v0.4 为你带来什么,以及它面向哪些用户。然后查看对比说明,了解它与隧道、VPN 和 Web 终端之间的差异。 问题、部署拓扑和升级经验请发布到 Discussions;Show and tell 汇集了其他运维人员正在运行的部署方案。 状态 DSH Orbit 0.4.1-rc.1 选择 DeepSeek Harness 0.1.5-rc.2 作为其发布基线。E9 发布兼容性鉴定已完成,因此 0.1.5-rc.2 已发布为 SUPPORTED;0.1.1-rc.2 仍保留为 LEGACY 配置档(保留兼容性,具备能力资格,但不在当前发布保证范围内)。请参阅 E9 鉴定证明。v0.4 Endpoint Selector 版本 v0.4.0-rc.1 在其最终评审通过后已作为预发布版本发布;请参阅标签与发布说明。
展开完整文档(README 剩余 1 节)
DSH Orbit

DSH Orbit 是一个由社区维护的自托管与集群层,面向 DeepSeek Harness。

该项目专注于安全远程访问、升级兼容性和多节点运行,同时将 DeepSeek Harness 保持为上游运行时。

DSH Orbit 是一个独立的社区项目。它与 DeepSeek AI 无关联,也未获得其认可。

自托管 · 多节点 · 安全优先 · 版本锁定兼容性 · 开放治理

初次接触?请从概览开始:了解 Orbit 是什么、v0.4 为你带来什么,以及它面向哪些用户。然后查看对比说明,了解它与隧道、VPN 和 Web 终端之间的差异。

问题、部署拓扑和升级经验请发布到 Discussions;Show and tell 汇集了其他运维人员正在运行的部署方案。

状态

DSH Orbit 0.4.1-rc.1 选择 DeepSeek Harness 0.1.5-rc.2 作为其发布基线。E9 发布兼容性鉴定已完成,因此 0.1.5-rc.2 已发布为 SUPPORTED;0.1.1-rc.2 仍保留为 LEGACY 配置档(保留兼容性,具备能力资格,但不在当前发布保证范围内)。请参阅 E9 鉴定证明。v0.4 Endpoint Selector 版本 v0.4.0-rc.1 在其最终评审通过后已作为预发布版本发布;请参阅标签与发布说明。
生产环境晋升尚未获授权,需要经过单独的审批关卡。

v0.4 版本提供了部署、兼容性和多节点路由层:

- Endpoint Selector:基于专用逐节点子域(n-.)的动态多节点浏览器路由,同时保留 selector 顶点域;
- Authenticated RouteIngress:从 Hub 到 Node 的加密逐跳 ORBIT-ROUTE-V1 请求签名,在不缓冲请求体的情况下验证身份;
- Per-Node Route Target:由运维人员分配的、可变的 DSH 实例目标源,并自动推导可达性;
- Hub Route Identity & Key Rotation:确定性的 Ed25519 路由密钥,具备重启安全的轮换重叠和计划性吊销;
- Duplex Routing:完整的 HTTP 和 WebSocket 双工流式传输,具备严格的容量限制、连接跟踪和提前中止清理;
- Deterministic 5-Condition Eligibility:仅当 state=active、authenticated=ok、dshHealthy=ok、orbitCompatible=pass 和 reachable=ok 时,节点才可路由;
- No Silent Failover:节点故障时以失败关闭方式处理,不会发生路由串扰或自动重定向到其他节点;
- Browser Context Isolation:严格的仅主机 Cookie,防止会话在不同 DSH 节点实例之间泄漏。

反向连接节点不属于 v0.4 的范围。它们仍属于 v0.5 的范围。
NAT 穿透、集群执行和反向隧道严格推迟到未来的里程碑。

发布文档:

- 概述 — 从这里开始
- 对比 — 隧道、VPN 和 Web 终端
- DSH 版本策略
- 架构
- 安全模型
- 兼容性
- 配置参考
- 运维 SOP
- 选择器与路由 SOP
- 节点注册 SOP
- 注册表备份/恢复 SOP
- 故障排查
- 生产环境晋升与回滚计划
- v0.4.0-rc.1 发布证明

未来版本计划增加反向连接节点(NAT 穿透)和集群级工作流。参见路线图。

原则

- 上游优先。 当官方 DSH 能力存在时,使用官方能力。兼容性补丁是后备方案,而非永久性分叉。
- 故障关闭。 未知的上游布局或补丁不匹配会停止构建或启动路径,而不是静默地削弱安全性。
- 不直接暴露 DSH。 远程管理应位于经过身份验证的代理或访问层之后。
- 版本化兼容性。 每个受支持的 DSH 版本都有明确的兼容性契约和测试覆盖。
- 可移植部署。 公开示例使用占位符和环境驱动的配置。站点特定的密钥和地址保留在仓库之外。

当前架构

Internet

Identity-aware access layer

Reverse proxy / authenticated gateway

DSH Orbit compatibility layer

DeepSeek Harness

DSH Orbit 不会整体替换 DSH 的身份验证或授权语义。当前的兼容层将远程配置访问收窄到满足已配置主机、HTTPS 转发、同源检查以及代理持有的共享密钥的请求。

有关信任边界和部署要求,请参见安全模型。

快速开始

1. 配置部署

复制示例环境文件并设置 DSH_PUBLIC_HOST:

cp .env.example .env

创建示例 Compose 文件所需的本地运行时目录和密钥:

mkdir -p secrets certs data workspace
openssl rand -hex 32 > secrets/dsh_proxy_auth
printf '%s' 'admin' > secrets/local_user
caddy hash-password --plaintext 'replace-this-password' > secrets/local_password_hash

将源站证书和私钥放置在 certs/fullchain.pem 和 certs/privkey.pem。如果上游代理验证 Caddy 源站,则该证书必须对 DSH_PUBLIC_HOST 有效。
secrets/、certs/ 和 data/ 下的文件被 Git 忽略,必须保留在本地。

2. 构建

docker compose -f docker/compose.example.yaml build

镜像构建会安装选定的 DSH 版本,并以 --build 模式运行兼容性补丁。不支持的源码布局会导致构建失败。

3. 启动

docker compose -f docker/compose.example.yaml up -d

在运行时,会在 Web 进程启动前检查并修补配置文件本地的 DSH 客户端包。这是必要的,因为 DSH 配置文件可能包含它们自己的 @deepseek-ai/dsh-client-connection 副本。

hooks/ 中的可选下游钩子会在每次 DSH Web 启动前运行。它们用于处理不属于共享项目的、特定于部署的兼容性工作。钩子失败会停止启动。

4. 配置反向代理

提供了以下示例:

- Caddy
- Nginx

这些示例有意将受信任的访问提供方路径与本地/基本认证路径分开。不要直接从任意客户端接受访问提供方断言头。

Caddy 示例使用两个不同的监听器,使身份边界明确:9443 是主机发布的本地/局域网基本认证路径,并且从不信任 Cf-Access-Jwt-Assertion;9444 是身份感知的 Access 路径,示例 Compose 文件有意不将其发布到主机。Cloudflare Tunnel sidecar(或其他受信任的连接器)应仅跨越私有容器网络边界访问 9444。不要将 9444 发布给任意的本地/局域网客户端。如果使用额外的局域网代理,请继续像 Nginx 示例中所示那样在那里剥离 Cf-Access-Jwt-Assertion。

5. 冒烟测试设置

在配置好认证和路由后,测试一次设置读取和一次空操作写入:

DSH_SMOKE_URL=https://dsh.example.com \
DSH_SMOKE_CONNECTION_PATCH=connection-v1 \
DSH_SMOKE_BASIC_USER=admin \
DSH_SMOKE_BASIC_PASSWORD='' \
node scripts/smoke-settings.mjs

DSH_SMOKE_CONNECTION_PATCH 必须指定部署的 DSH 版本所使用的、经过审查的连接补丁代次(connection-v1 或 connection-browser-auth-v1;参见 src/compatibility.mjs)。冒烟测试从该代次中选择其线路词汇——端点名称和负载结构——绝不通过版本比较来选择。该脚本不会打印凭据或设置机密。

当网关将 Host 头重写为与冒烟测试端点 URL 不同的公共权威(例如非默认的预演端口)时,请设置 DSH_SMOKE_ORIGIN=https://dsh.example.com,以便同源阳性对照与真实浏览器发送的内容相匹配。

6. 冒烟测试授权

针对特权 RPC 测试运行中部署的实时授权边界:

DSH_SMOKE_URL=https://dsh.example.com \
DSH_SMOKE_CONNECTION_PATCH=connection-v1 \
DSH_SMOKE_BASIC_USER=admin \
DSH_SMOKE_BASIC_PASSWORD='' \
npm run smoke:auth

两个凭据变量都是必需的:此套件支持的认证路径是网关的本地 Basic Auth 路径。该套件针对每个已审查的连接代际,在对应代际的设置读取端点上验证以下结果:

| 用例 | 预期结果 |
| 已认证,预期来源 | 允许 |
| 未认证 | 拒绝 |
| 无效凭据 | 拒绝 |
| 意外的 Origin | 拒绝 |
| Sec-Fetch-Site: cross-site | 拒绝 |
| 在本地路径上伪造的 Cf-Access-Jwt-Assertion | 拒绝 |

伪造断言用例与代际无关:它验证的是文档所述的代理拓扑,而非原始 DSH。客户端在本地/局域网路径上提供的身份提供方断言绝不能转化为已认证流量——拒绝由 DSH 前方的网关链产生(本地 Basic Auth 路径从不认可此类标头),而连接代际仅决定套件探测哪个设置端点以及何种载荷形态。

该套件从不需要内部代理密钥——网关在认证用户后注入该密钥,而将该密钥暴露给客户端本身即为失败。当任何用例不匹配时,它以非零状态退出,且正常输出和失败输出均不包含凭据或响应正文。

升级工作流

不要通过就地更改包版本来更新生产 DSH 实例。

推荐流程为:

1. 选择候选 DSH 版本;
2. 构建候选镜像;
3. 要求兼容性补丁完全匹配;
4. 使用复制的数据目录启动候选实例;
5. 运行设置、负向认证和升级前会话恢复冒烟测试;
6. 对生产数据进行快照;
7. 仅在测试通过后提升候选实例。

参见升级指南、兼容性和下游生产部署。

有关 DSH 版本选择和兼容性基线策略,参见 DSH 版本策略。

候选升级运行器

npm run upgrade -- 将手动升级序列编排为一条显式、故障关闭的命令。它从不提升生产:它最多只能达到 CANDIDATE PASSED - ELIGIBLE FOR MANUAL PROMOTION,提升仍由操作员执行。

npm run upgrade -- preflight # 验证配置而不触碰任何内容
npm run upgrade -- candidate # 生产快照、候选构建、隔离启动、验证、报告
npm run upgrade -- verify # 针对运行中的候选端点执行验证序列并生成报告
npm run upgrade -- report # 从运行目录重新生成报告
配置来自环境。候选身份:DSH_VERSION(候选 DSH 版本)、DSH_CANDIDATE_ORBIT_REVISION(候选构建所基于的 Orbit 修订版本)、DSH_CANDIDATE_IMAGE、DSH_CANDIDATE_DATA_ROOT、DSH_CANDIDATE_WORKSPACE_ROOT、DSH_UPGRADE_HOST_PORT(隔离的回环端口)。基线身份(回滚目标):DSH_BASELINE_IMAGE、DSH_BASELINE_ORBIT_REVISION、DSH_BASELINE_DSH_VERSION。网关与检查:DSH_PUBLIC_HOST、DSH_SMOKE_URL(候选端点)、DSH_SMOKE_BASIC_USER/DSH_SMOKE_BASIC_PASSWORD、DSH_SMOKE_SESSION_ID(升级前的会话)、DSH_SMOKE_ORIGIN(当网关重写 Host 时)、DSH_DATA_ROOT(生产数据)、DSH_SNAPSHOT_HOOK。可选:DSH_ORBIT_VERSION、DSH_UPGRADE_PROJECT、DSH_UPGRADE_COMPOSE、DSH_UPGRADE_WORKDIR、DSH_SNAPSHOT_TIMEOUT_SECONDS、DSH_UPGRADE_GATEWAY_SERVICE(默认 caddy)、DSH_UPGRADE_GATEWAY_CERT_TARGET(默认 /run/certs/fullchain.pem,与公开示例 compose 匹配)、DSH_UPGRADE_GATEWAY_KEY_TARGET(默认 /run/certs/privkey.pem)。网关从其他位置读取证书的部署必须设置这两个目标,并且基础 compose 网关必须已经将证书挂载到这些目标位置。

可选终端围栏(遗留第三方兼容性债务,ADR-0001——仅冻结,不新增功能,一旦 DSH 提供通用的受信任客户端/已认证代理能力即移除;参见 docs/third-party-debt.md):设置 DSH_ORBIT_PATCH_DSH_SSH=1(也会传入候选容器)以修补 @linxin666/dsh-ssh 的仅回环围栏,使已认证的 Orbit 代理路径能够打开远程 PTY 终端。该补丁固定版本(默认 0.3.2,可通过 DSH_SSH_PLUGIN_VERSION 覆盖),使用精确源码匹配,并保留回环访问以及所有其他拒绝规则不变;DSH_SSH_PLUGIN_ROOT 可覆盖插件位置。启用该围栏后,候选验证序列会运行终端授权冒烟测试(npm run smoke:terminal)——针对实时端点运行 6 个用例——终端门禁失败会阻止晋升资格。

运行器:

1. 运行生产快照钩子,并在其失败时拒绝晋升就绪状态;该失败会记录在运行证据中,因此重新生成报告无法恢复资格;
2. 根据候选规格(镜像、复制的数据和工作区根目录、隔离的回环端口)生成 compose 覆盖文件,并对照它验证解析后的 docker compose config——镜像、/data 和 /workspace 挂载、发布的回环端口(仅限 127.0.0.1 或 ::1)、项目名称以及每次运行的候选令牌都必须在构建或启动任何内容之前全部匹配;
3. 生成每次运行的网关身份证书(SAN 与候选端点主机匹配),并将其挂载到候选网关中以替代基础证书;
4. 在不替换最后一个已知良好镜像标签的情况下构建候选版本——构建会在不支持的版本或源码布局不匹配时失败——然后在隔离端点上针对复制的数据启动它;生产环境继续运行;
5. 在任何检查运行之前验证完整的身份链:运行中的栈携带本次运行的候选令牌,并且 DSH_SMOKE_URL 终止于候选网关(TLS 对等证书必须是每次运行的身份证书);
6. 以确定性顺序执行验证序列(运行时就绪、补丁验证、设置读取、空操作设置写入、实时授权冒烟测试、现有会话恢复、Web/插件路由,以及发布受限的长生命周期传输和终端检查),冒烟测试套件通过 NODE_EXTRA_CA_CERTS 信任每次运行的证书;
7. 在第一个必需失败处停止,将剩余检查标记为 not_run,并且仍然生成最终的脱敏报告;
8. 分别报告 compatibility 和 promotion readiness:仅当每个必需检查都通过、确切的候选版本和基线身份被记录,并且快照完成时,晋升就绪才符合条件;verify 从不评估晋升就绪;
9. 仅在候选版本通过或验证通过时退出 0,失败时退出 1,配置或绑定错误时退出 2。运行器环境需要 Docker、compose 插件和 OpenSSL。

开发

要求:

- Node.js 22 或更新版本

运行单元测试:

npm test

运行仓库验证:

npm run check

测试套件使用夹具和临时目录。它不需要实时 DSH 安装。

范围

范围内

- DSH 的安全自托管模式;
- 经过身份验证的反向代理兼容性;
- DSH 升级防护和兼容性检查;
- 部署示例和冒烟测试;
- 未来的多节点端点发现和选择。

范围外

- 维护 DeepSeek Harness 的分支;
- 为公共部署绕过身份验证;
- 存储或分发用户凭据;
- 承诺与未经测试的 DSH 版本兼容。

贡献

欢迎提交问题和拉取请求。涉及身份验证、代理信任或特权 DSH RPC 的更改应同时包含负面测试和成功路径测试。

使用 Discussions 讨论问题、部署拓扑和升级经验。在 Show and tell 中分享正在运行的设置——社区设置汇集于此,如果你正在决定 Orbit 是否适合你的机器,这是最有用的阅读内容。

参见 CONTRIBUTING.md。

许可证

MIT。参见 LICENSE。
预览分享卡片长什么样 ↓IkariKr/dsh-orbit
分享 · DeepSeek Harness Hub
IkariKr/dsh-orbit

安全远程接入多节点,锁定版本兼容升级

这是你的仓库?认领条目

回到顶部