dsh-plugin-remote-connect-beta 0.1.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +115 -0
- package/CONTRIBUTING.md +50 -0
- package/LICENSE +21 -0
- package/README.md +279 -0
- package/README.zh.md +298 -0
- package/SECURITY.md +41 -0
- package/bin/dsh-remote.js +768 -0
- package/docs/client-preview.png +0 -0
- package/docs/market-submission.md +82 -0
- package/docs/multi-tenant.md +136 -0
- package/docs/multi-tenant.zh.md +127 -0
- package/docs/self-host.md +398 -0
- package/docs/self-host.zh.md +369 -0
- package/docs/tenants-preview.png +0 -0
- package/lib/client.js +1120 -0
- package/lib/core/assets/server-setup.sh.tpl +344 -0
- package/lib/core/credential.js +123 -0
- package/lib/core/instance.js +360 -0
- package/lib/core/messages.js +549 -0
- package/lib/core/paths.js +59 -0
- package/lib/core/preflight.js +410 -0
- package/lib/core/proxy.js +924 -0
- package/lib/core/serversetup.js +133 -0
- package/lib/core/snippets.js +193 -0
- package/lib/core/tailscale.js +158 -0
- package/lib/core/tenancy.js +267 -0
- package/lib/core/tenant.js +272 -0
- package/lib/core/tunnel.js +320 -0
- package/lib/index.js +1097 -0
- package/package.json +76 -0
|
Binary file
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# 上架材料(插件市场 / 目录提交用)
|
|
2
|
+
|
|
3
|
+
这份文件是**给你(fork 者)复制粘贴用的**,不是给别人安装用的。所有方括号 `<...>` 都是占位符。
|
|
4
|
+
|
|
5
|
+
## 0. 先改这些(否则会被拒或被当成隐私事故)
|
|
6
|
+
|
|
7
|
+
| 位置 | 现在 | 改成 |
|
|
8
|
+
| --- | --- | --- |
|
|
9
|
+
| `package.json` → `name` | `dsh-plugin-remote-connect-beta` | `@<your-scope>/dsh-plugin-remote-connect-beta`(或确认无 scope 名可用) |
|
|
10
|
+
| `package.json` → `repository/homepage/bugs/author` | 空 | 你的仓库与联系方式 |
|
|
11
|
+
| `LICENSE` | 版权行留空 | `<你的名字或组织>` |
|
|
12
|
+
| `SECURITY.md` | 通用 | 你的漏洞披露渠道(邮箱 / GitHub Security Advisory) |
|
|
13
|
+
| `README.md` / `README.zh.md` 顶部 | 相对链接 | 若有官网,补一行项目地址 |
|
|
14
|
+
|
|
15
|
+
改完跑:`npm run gate && npm test && npm pack --dry-run`。
|
|
16
|
+
|
|
17
|
+
## 1. 一句话简介(≤ 80 字)
|
|
18
|
+
|
|
19
|
+
- 中文:把 DSH Harness 安全地开放到局域网或你自己的域名上,手机与别的电脑都能用;不经过任何第三方中转。
|
|
20
|
+
- English: Expose your DSH Harness safely on your LAN or your own domain — usable from your phone and any other computer, with no third-party relay.
|
|
21
|
+
|
|
22
|
+
## 2. 长描述(可直接贴)
|
|
23
|
+
|
|
24
|
+
中文:
|
|
25
|
+
|
|
26
|
+
> DSH Harness 默认只监听 `127.0.0.1`,这是对的,但你就没法用手机或另一台电脑打开它。
|
|
27
|
+
> 本插件在 Harness 前面加一层反向代理:监听地址、端口、绑定网卡、上游端口全部由 Harness 自己上报,不写死;访问入口带一层密钥门(`?k=...` 换取签名 Cookie,之后不再出现在 URL 里),并且永远不会把 Harness 改成监听 `0.0.0.0`。
|
|
28
|
+
> 两条通道:
|
|
29
|
+
> - **局域网**:一条命令起服务,同网段手机直接访问 `http://192.168.x.x:8787`,附二维码。
|
|
30
|
+
> - **公网**:用你自己的服务器和域名。插件只负责在你机器上把流量加密隧道送到服务器;服务器侧配置由插件生成的、可审计的 bash 脚本完成(不碰 80 端口、不覆盖你已有的站点)。
|
|
31
|
+
> 附带 `doctor`(四处键门 + 证书 SHA-256 固定)与 `check`(本机连通性自检),配置只来自 Harness 的配置校验结果,不写死端口(CLI 3080 / Desktop 43129 都对)。
|
|
32
|
+
|
|
33
|
+
English:
|
|
34
|
+
|
|
35
|
+
> DSH Harness listens on `127.0.0.1` only — correct, but it means you cannot open it from your phone or another computer.
|
|
36
|
+
> This plugin puts a reverse proxy in front of the Harness. Listen address, port, bind interface and upstream port are all reported by the Harness itself and never hardcoded; the entry point has a key gate (`?k=...` is exchanged for a signed cookie and never stays in the URL), and the Harness is never reconfigured to listen on `0.0.0.0`.
|
|
37
|
+
> Two channels:
|
|
38
|
+
> - **LAN**: one command, then any phone on the same network opens `http://192.168.x.x:8787`, with a QR code.
|
|
39
|
+
> - **Public**: bring your own server and domain. The plugin only tunnels encrypted traffic from your machine to your server; the server side is a generated, auditable bash script (it does not touch port 80 and never overwrites an existing site).
|
|
40
|
+
> Ships with `doctor` (four key-gate checks plus certificate SHA-256 pinning) and `check` (local reachability self-test). Every tunable comes from the Harness config validator — nothing hardcodes a port, so both the CLI (3080) and Desktop (43129) work.
|
|
41
|
+
|
|
42
|
+
## 3. 分类与标签
|
|
43
|
+
|
|
44
|
+
- 分类:网络 / 远程访问 / 开发者工具
|
|
45
|
+
- 标签:`lan`、`remote-access`、`reverse-proxy`、`tunnel`、`mobile`、`no-relay`、`self-hosted`、`ssh-tunnel`、`tailscale`、`cloudflared`
|
|
46
|
+
|
|
47
|
+
## 4. 需要的权限 / 能力声明(填表用)
|
|
48
|
+
|
|
49
|
+
| 项 | 值 | 说明 |
|
|
50
|
+
| --- | --- | --- |
|
|
51
|
+
| 网络监听 | 是 | 默认 `0.0.0.0:<lan.port>`,**仅在你显式启动局域网服务时**;`lan.enabled` 默认 `false` |
|
|
52
|
+
| 出站连接 | 是 | 仅公网模式,且仅连你自己配置的服务器(`ssh.host`) |
|
|
53
|
+
| 读文件 | 有限 | 仅读 `logPaths` 指定的 Harness 日志以匹配启动 token,以及调用方给的证书文件 |
|
|
54
|
+
| 写文件 | 是 | 仅写状态文件(`stateDir`)与生成的服务器脚本;服务器脚本默认 `--dry-run` |
|
|
55
|
+
| 子进程 | 是 | `ssh` / `tailscale` / `cloudflared` / `ssh-keygen`;不在系统里装任何服务 |
|
|
56
|
+
| 遥测 | 无 | 不发任何请求到第三方,除非你启动对应隧道后端 |
|
|
57
|
+
| Harness 配置改动 | 无 | 不修改 Harness 监听配置,不写 `0.0.0.0` |
|
|
58
|
+
|
|
59
|
+
## 5. 审核预期问答
|
|
60
|
+
|
|
61
|
+
**Q:它是不是"把 Harness 暴露到公网"的一键危险按钮?**
|
|
62
|
+
A:不是。公网模式需要你自己有服务器和域名;插件不会给你一个共享入口,也不提供任何中转。默认配置下 `public.enabled` 与 `lan.enabled` 都是 `false`。
|
|
63
|
+
|
|
64
|
+
**Q:密钥怎么存的?**
|
|
65
|
+
A:只在内存与状态文件里;`?k=` 首次访问后立刻换成 HttpOnly + SameSite=Lax 的签名 Cookie 并 303 重定向,`Referer` 不会带走它。日志与错误信息里会脱敏。
|
|
66
|
+
|
|
67
|
+
**Q:为什么不用 `0.0.0.0` 监听 Harness 本身?**
|
|
68
|
+
A:因为那样 Harness 自己就成了暴露面。代理可以只做转发、可随时关掉,且保留了密钥门。
|
|
69
|
+
|
|
70
|
+
**Q:会上传我的域名 / IP 吗?**
|
|
71
|
+
A:不会。`npm run gate` 会拒绝把作者自己的域名、服务器 IP、本机路径或真实公钥提交进仓库 —— 这也是本仓库演示配置里用 `example.com` 与 `192.0.2.x` 的原因。
|
|
72
|
+
|
|
73
|
+
## 6. 截图与素材
|
|
74
|
+
|
|
75
|
+
- `docs/client-preview.png` —— 侧栏入口与面板(已脱敏:域名/IP/密钥均为演示值)
|
|
76
|
+
- 需要更多截图时用 `npm run preview` 起的本地预览页截,不要用真实部署页
|
|
77
|
+
|
|
78
|
+
## 7. 提交前自检命令
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
npm run gate && npm test && npm pack --dry-run --json
|
|
82
|
+
```
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
# Multi-tenancy: one gateway, one Harness per person
|
|
2
|
+
|
|
3
|
+
[English](multi-tenant.md) | [中文](multi-tenant.zh.md)
|
|
4
|
+
|
|
5
|
+
> **Read this first.** Every tenant's Harness runs **on the machine where you installed this
|
|
6
|
+
> plugin**, and a Harness can execute commands and touch files there. Giving someone a tenant link
|
|
7
|
+
> is therefore equivalent to giving them an account on that computer. Do this only for people you
|
|
8
|
+
> would let sit down at it.
|
|
9
|
+
>
|
|
10
|
+
> If someone just wants their own remote entry, the right answer is the cheap one: **they install
|
|
11
|
+
> this plugin on their own machine** (MIT, `npx dsh-plugin-remote-connect-beta serve --public ...`). That
|
|
12
|
+
> is what this project is built for; the multi-tenant layer below exists for the case where one
|
|
13
|
+
> machine really does serve several people (a home lab, a shared workstation, a family box), and it
|
|
14
|
+
> is off by default.
|
|
15
|
+
|
|
16
|
+
A DSH Harness instance is **single-user by construction**: its sessions, credentials, settings,
|
|
17
|
+
workspace and launch token all live inside one `DSH_HOME`. There is no account system inside it
|
|
18
|
+
(the shipped `anonymous-user-id` is a telemetry UUID, not an identity). So multi-tenancy cannot be
|
|
19
|
+
a switch on one instance — it is this plugin acting as a **gateway** and giving every tenant
|
|
20
|
+
**their own Harness process**:
|
|
21
|
+
|
|
22
|
+
```
|
|
23
|
+
┌─ tenant alice ─► Harness 127.0.0.1:58581 DSH_HOME=~/DSH-tenants/alice
|
|
24
|
+
browser ─?k=<alice key>─►│ (own sessions / credentials / token)
|
|
25
|
+
gateway :8787/:8788 └─ tenant bob ─► Harness 127.0.0.1:58582 DSH_HOME=~/DSH-tenants/bob
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
What is isolated, and by what:
|
|
29
|
+
|
|
30
|
+
| Thing | Boundary |
|
|
31
|
+
| --- | --- |
|
|
32
|
+
| Sessions, settings, credentials, `storages/` | separate `DSH_HOME` per tenant (separate directories) |
|
|
33
|
+
| Process, memory, agent loop | separate OS process per tenant |
|
|
34
|
+
| Launch token | each instance prints its own; the gateway injects **only** that tenant's token |
|
|
35
|
+
| Network exposure | every instance binds `127.0.0.1` only; the only way in is the gateway |
|
|
36
|
+
| Model billing / API keys | each tenant configures their own credentials inside their instance — nothing is inherited |
|
|
37
|
+
|
|
38
|
+
Verified end to end in `test/e2e-isolated.sh`: two real instances on separate loopback ports with
|
|
39
|
+
separate `DSH_HOME`s, each key routed to its own instance, anonymous requests and unknown keys
|
|
40
|
+
answered with `404`, and **one tenant's launch token against the other tenant's port → `401`**.
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+

|
|
45
|
+
|
|
46
|
+
## Turn it on
|
|
47
|
+
|
|
48
|
+
```yaml
|
|
49
|
+
# profiles/web/cordis.patch.yml
|
|
50
|
+
- insert:
|
|
51
|
+
- id: dsh-plugin-remote-connect-beta
|
|
52
|
+
name: dsh-plugin-remote-connect-beta
|
|
53
|
+
config:
|
|
54
|
+
lan: { enabled: true, port: 8787 }
|
|
55
|
+
tenants:
|
|
56
|
+
enabled: true
|
|
57
|
+
baseDir: ~/DSH-tenants # one DSH_HOME per tenant goes here
|
|
58
|
+
registry: ~/.dsh/remote-connect/tenants.json
|
|
59
|
+
harness:
|
|
60
|
+
node: /opt/homebrew/bin/node # a REAL node, see the gotcha below
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Restart DSH, open the panel: there is now a **Tenants** card. Add a person by name, press the QR
|
|
64
|
+
button, and send them that link. That is the whole flow — the link carries their access key, the
|
|
65
|
+
gateway exchanges it for a cookie, and every request from then on lands in *their* Harness.
|
|
66
|
+
|
|
67
|
+
⚠️ **One gotcha that costs an hour if you skip it.** DSH Desktop puts its own `node` shim in `PATH`
|
|
68
|
+
(`ELECTRON_RUN_AS_NODE=1` + the Electron helper). That shim rejects `NODE_OPTIONS`, so the tenant
|
|
69
|
+
instances never boot if you point `harness.node` at it. Use a real Node (Homebrew's is fine) — the
|
|
70
|
+
panel says so explicitly when it cannot start them, and `tenants.harness.bin` can pin the Harness
|
|
71
|
+
entry point if auto-detection picks the wrong one.
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## Command line
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
# registry editing (does NOT start anything)
|
|
79
|
+
npx dsh-plugin-remote-connect-beta tenant add --name "Alice" # prints the key + LAN link + QR
|
|
80
|
+
npx dsh-plugin-remote-connect-beta tenant list
|
|
81
|
+
npx dsh-plugin-remote-connect-beta tenant key --id alice
|
|
82
|
+
npx dsh-plugin-remote-connect-beta tenant rotate alice # old links die immediately
|
|
83
|
+
npx dsh-plugin-remote-connect-beta tenant rm alice # data directory is left in place
|
|
84
|
+
|
|
85
|
+
# run the gateway + the instances in this process (for machines without a DSH UI)
|
|
86
|
+
npx dsh-plugin-remote-connect-beta serve --multi --port 8787
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
`tenant` only edits the registry. The Harness instances belong to whichever process runs the
|
|
90
|
+
gateway — the DSH plugin, or `serve --multi`. Start/stop them from the host window panel (`serve
|
|
91
|
+
--multi` stops them when it exits), because an instance started by a short-lived CLI command would
|
|
92
|
+
be orphaned the moment that command returned.
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## Registry format
|
|
97
|
+
|
|
98
|
+
`~/.dsh/remote-connect/tenants.json`, mode `0600`, written atomically:
|
|
99
|
+
|
|
100
|
+
```json
|
|
101
|
+
{
|
|
102
|
+
"version": 1,
|
|
103
|
+
"tenants": [
|
|
104
|
+
{
|
|
105
|
+
"id": "alice", // 2–32 lowercase letters/digits/hyphens; also the DSH profile name
|
|
106
|
+
"name": "Alice",
|
|
107
|
+
"accessKey": "…32 url-safe chars…",
|
|
108
|
+
"home": "/home/you/DSH-tenants/alice",
|
|
109
|
+
"profile": "alice",
|
|
110
|
+
"port": 0, // 0 = let the OS pick; the real port is read from the instance
|
|
111
|
+
"autostart": true,
|
|
112
|
+
"enabled": true,
|
|
113
|
+
"note": "",
|
|
114
|
+
"createdAt": "2026-09-19T03:44:53.370Z"
|
|
115
|
+
}
|
|
116
|
+
]
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Hand-editing is fine: invalid entries are reported and skipped (the plugin still boots), and
|
|
121
|
+
duplicate ids or duplicate access keys are rejected so two tenants can never share a key.
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
## Operating notes
|
|
126
|
+
|
|
127
|
+
| Question | Answer |
|
|
128
|
+
| --- | --- |
|
|
129
|
+
| Where do I see what a tenant's instance is doing? | `<tenant home>/instance.log` (its stdout/stderr), and the per-tenant state in the panel |
|
|
130
|
+
| A tenant's instance keeps crashing | the supervisor retries with exponential backoff, then stops and shows the last error instead of looping forever |
|
|
131
|
+
| I rotated a key | old `?k=` links stop working immediately; already-issued cookies for that tenant stop matching |
|
|
132
|
+
| I removed a tenant | the instance is stopped and unregistered; **their data directory is not deleted** |
|
|
133
|
+
| Two people on one laptop | one browser profile each (the cookie decides the tenant) |
|
|
134
|
+
| Public entry | each tenant gets `https://<your-domain>/?k=<their key>`; the edge password still applies to everyone |
|
|
135
|
+
| Can a tenant reach another tenant's instance? | no: the gateway only ever connects to the upstream resolved from *that* request's cookie, and instances accept only their own token |
|
|
136
|
+
| Do tenants share my API credentials? | no — by design each instance has its own credentials file. Give them their own key, or copy yours deliberately if you want to pay for them |
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# 多租户:一个网关,每人一个独立 Harness
|
|
2
|
+
|
|
3
|
+
[English](multi-tenant.md) | [中文](multi-tenant.zh.md)
|
|
4
|
+
|
|
5
|
+
> **先读这段。** 每个租户的 Harness 都跑在**你装这个插件的那台机器上**,而 Harness 能在那里执行命令、
|
|
6
|
+
> 读写文件。所以把一条租户链接给出去,等于在那台电脑上给他开了一个账号。只给你愿意让他直接坐到这台
|
|
7
|
+
> 电脑前的人用。
|
|
8
|
+
>
|
|
9
|
+
> 如果对方只是想要自己的远程入口,最省事的答案是:**让他把本插件装在他自己的机器上**
|
|
10
|
+
> (MIT,`npx dsh-plugin-remote-connect-beta serve --public ...`)—— 这也是本项目的主要用途。下面的多租户
|
|
11
|
+
> 是为「一台机器确实要给多个人用」(家庭服务器、共用工作站)准备的,默认关闭。
|
|
12
|
+
|
|
13
|
+
一个 DSH Harness 实例**本身就是单用户的**:会话、凭据、设置、工作区、启动令牌全都在同一个
|
|
14
|
+
`DSH_HOME` 里,实例内部没有账号体系(自带的 `anonymous-user-id` 只是遥测用的 UUID,不是身份)。
|
|
15
|
+
所以多租户不可能是"给单个实例加个开关",而是本插件当**网关**,给每个租户**一个独立的 Harness 进程**:
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
┌─ 租户 alice ─► Harness 127.0.0.1:58581 DSH_HOME=~/DSH-tenants/alice
|
|
19
|
+
浏览器 ─?k=<alice 密钥>─►│ (自己的会话 / 凭据 / 令牌)
|
|
20
|
+
网关 :8787/:8788 └─ 租户 bob ─► Harness 127.0.0.1:58582 DSH_HOME=~/DSH-tenants/bob
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
隔离的是什么、靠什么隔离:
|
|
24
|
+
|
|
25
|
+
| 隔离对象 | 边界 |
|
|
26
|
+
| --- | --- |
|
|
27
|
+
| 会话、设置、凭据、`storages/` | 每个租户一个独立 `DSH_HOME`(独立目录) |
|
|
28
|
+
| 进程、内存、agent 循环 | 每个租户一个独立操作系统进程 |
|
|
29
|
+
| 启动令牌 | 各自实例自己打印;网关**只**注入该请求所属租户的令牌 |
|
|
30
|
+
| 网络暴露面 | 所有实例只绑 `127.0.0.1`;唯一入口是网关 |
|
|
31
|
+
| 模型计费 / API 密钥 | 各自在自己的实例里配置凭据,不继承任何人的 |
|
|
32
|
+
|
|
33
|
+
`test/e2e-isolated.sh` 已端到端验证:两个真实实例各自监听独立回环端口、各自独立 `DSH_HOME`,
|
|
34
|
+
各自密钥进各自实例,匿名请求与无效密钥一律 `404`,并且**把一个租户的令牌打到另一个实例上返回 `401`**。
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+

|
|
39
|
+
|
|
40
|
+
## 打开它
|
|
41
|
+
|
|
42
|
+
```yaml
|
|
43
|
+
# profiles/web/cordis.patch.yml
|
|
44
|
+
- insert:
|
|
45
|
+
- id: dsh-plugin-remote-connect-beta
|
|
46
|
+
name: dsh-plugin-remote-connect-beta
|
|
47
|
+
config:
|
|
48
|
+
lan: { enabled: true, port: 8787 }
|
|
49
|
+
tenants:
|
|
50
|
+
enabled: true
|
|
51
|
+
baseDir: ~/DSH-tenants # 每个租户的 DSH_HOME 放这里
|
|
52
|
+
registry: ~/.dsh/remote-connect/tenants.json
|
|
53
|
+
harness:
|
|
54
|
+
node: /opt/homebrew/bin/node # 必须是真 node,见下面的坑
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
重启 DSH,打开面板:多了一张**租户**卡片。输入名字新增,点二维码,把人那条链接发给他 —— 就这些。
|
|
58
|
+
链接里带他的访问密钥,网关换成 Cookie 后,他之后的每个请求都落在**他自己**的 Harness 里。
|
|
59
|
+
|
|
60
|
+
⚠️ **一个能白花一小时的坑**:DSH Desktop 会把自己的 `node` shim(`ELECTRON_RUN_AS_NODE=1` + Electron
|
|
61
|
+
Helper)放进 `PATH`,而它会拒绝 `NODE_OPTIONS` —— 把 `harness.node` 指向它,租户实例一律起不来。
|
|
62
|
+
请指向真正的 Node(Homebrew 那个就行)。起不来时面板会把这句话直接摊出来;
|
|
63
|
+
自动探测找错入口时可以用 `tenants.harness.bin` 钉住。
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## 命令行
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
# 只改注册表,不启动任何东西
|
|
71
|
+
npx dsh-plugin-remote-connect-beta tenant add --name "张三" # 打印密钥 + 局域网链接 + 二维码
|
|
72
|
+
npx dsh-plugin-remote-connect-beta tenant list
|
|
73
|
+
npx dsh-plugin-remote-connect-beta tenant key --id zhangsan
|
|
74
|
+
npx dsh-plugin-remote-connect-beta tenant rotate zhangsan # 旧链接立刻失效
|
|
75
|
+
npx dsh-plugin-remote-connect-beta tenant rm zhangsan # 数据目录保留,不替你删
|
|
76
|
+
|
|
77
|
+
# 在没有 DSH 界面的机器上:网关 + 实例都在这个进程里
|
|
78
|
+
npx dsh-plugin-remote-connect-beta serve --multi --port 8787
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
`tenant` 只动注册表。Harness 实例归"跑网关的那个进程"所有 —— DSH 插件,或 `serve --multi`。
|
|
82
|
+
启停请在宿主窗口的面板里做(`serve --multi` 退出时会一起收掉),因为短命 CLI 命令拉起的实例
|
|
83
|
+
会在命令返回的瞬间变成孤儿进程。
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## 注册表格式
|
|
88
|
+
|
|
89
|
+
`~/.dsh/remote-connect/tenants.json`,权限 `0600`,原子写入:
|
|
90
|
+
|
|
91
|
+
```json
|
|
92
|
+
{
|
|
93
|
+
"version": 1,
|
|
94
|
+
"tenants": [
|
|
95
|
+
{
|
|
96
|
+
"id": "alice", // 2–32 位小写字母/数字/连字符;同时是 DSH profile 名
|
|
97
|
+
"name": "Alice",
|
|
98
|
+
"accessKey": "…32 位 URL 安全字符…",
|
|
99
|
+
"home": "/home/you/DSH-tenants/alice",
|
|
100
|
+
"profile": "alice",
|
|
101
|
+
"port": 0, // 0 = 由系统分配;真实端口从实例输出里读
|
|
102
|
+
"autostart": true,
|
|
103
|
+
"enabled": true,
|
|
104
|
+
"note": "",
|
|
105
|
+
"createdAt": "2026-09-19T03:44:53.370Z"
|
|
106
|
+
}
|
|
107
|
+
]
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
手工编辑也可以:非法条目会被逐条报告并跳过(插件照样能起来),重复 id 或重复密钥会被拒 ——
|
|
112
|
+
两个租户不可能共用一把密钥。
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## 运维问答
|
|
117
|
+
|
|
118
|
+
| 问题 | 答案 |
|
|
119
|
+
| --- | --- |
|
|
120
|
+
| 某个租户的实例在干什么? | `<租户 home>/instance.log`(它的 stdout/stderr),面板里也有该租户的状态 |
|
|
121
|
+
| 实例反复崩溃怎么办? | 看护会指数退避重试,到上限就停手并显示最后的错误,不会无限重启刷屏 |
|
|
122
|
+
| 轮换了密钥会怎样? | 旧的 `?k=` 链接立刻失效;该租户已发的 Cookie 也不再匹配 |
|
|
123
|
+
| 删除租户会删数据吗? | 不会。只停实例 + 从注册表移除,**他的数据目录原样保留** |
|
|
124
|
+
| 两个人共用一台电脑? | 各用各自的浏览器 profile(Cookie 决定租户) |
|
|
125
|
+
| 公网入口呢? | 每人一条 `https://<你的域名>/?k=<他的密钥>`;边缘口令对所有人一样生效 |
|
|
126
|
+
| 租户能摸到别人的实例吗? | 不能:网关只连"当前请求 Cookie 解析出的那个上游",而实例只认自己的令牌 |
|
|
127
|
+
| 租户会用我的 API 凭据吗? | 不会 —— 按设计每个实例有自己的凭据文件。要用你的额度,得你主动把凭据放进去 |
|