@eduwork/dsh-remote-access 0.0.0-stage → 0.1.0
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 +19 -0
- package/LICENSE +21 -0
- package/README.md +92 -2
- package/README_EN.md +93 -0
- package/THIRD_PARTY_NOTICES.md +9 -0
- package/cordis.patch.yml +5 -0
- package/docs/PROTOCOL.md +129 -0
- package/lib/THIRD_PARTY_LICENSES.txt +728 -0
- package/lib/client.js +105 -0
- package/lib/index.js +22275 -0
- package/lib/relay.js +4247 -0
- package/lib/typert.js +38 -0
- package/lib/web/icon.png +0 -0
- package/lib/web/icon.svg +1 -0
- package/lib/web/index.html +18 -0
- package/lib/web/loader.css +24 -0
- package/lib/web/loader.js +1 -0
- package/lib/web/manifest.webmanifest +21 -0
- package/lib/web/preamble.js +1 -0
- package/lib/web/sw.js +1 -0
- package/package.json +150 -4
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# 变更记录 / Changelog
|
|
2
|
+
|
|
3
|
+
## 0.1.0 — 2026-10-23
|
|
4
|
+
|
|
5
|
+
- 首个版本:在手机或其他电脑的浏览器中使用本机的 DSH,包含 DSH 插件与设置页、可自托管的中继和网页客户端。
|
|
6
|
+
- 连接端到端加密;新设备须在电脑上核对配对码并允许,可逐台移除。
|
|
7
|
+
- 远程设备可查看与新建会话、继续或停止任务、审批与回答提问、预览与下载成果;设置只能查看,账号、凭据、插件管理等仅限本机。
|
|
8
|
+
- 手机上沿用 DSH 自己的界面,按窄屏调整侧栏与设置弹窗。
|
|
9
|
+
- 中继可凭机构登录的 ID Token 或接入密钥接纳电脑,并限制每个账号的电脑数和连接频率;发行版可预置机构的中继,用户登录后即可使用。
|
|
10
|
+
- 支持带路径前缀的中继地址;通过 `RELAY_FRAME_ANCESTORS` 配置允许嵌入的门户来源,默认禁止嵌入。
|
|
11
|
+
- 随包提供协议文档;Node.js 最低版本为 24.22.1,与部署说明中的安全更新基线一致。
|
|
12
|
+
|
|
13
|
+
- First version: use DSH on this computer from a browser on a phone or another computer, with the DSH plugin and its settings page, a self-hostable relay and the web client.
|
|
14
|
+
- Connections are end-to-end encrypted; a new device must be allowed on the computer after checking its pairing code, and devices can be removed one by one.
|
|
15
|
+
- Remote devices can view and start sessions, continue or stop tasks, approve and answer questions, and preview and download results; settings are view only, and accounts, credentials and plugin management stay on the computer.
|
|
16
|
+
- Phones get DSH's own interface, with the sidebar and the settings dialog fitted to narrow screens.
|
|
17
|
+
- The relay admits computers by an organization's ID Token or by an access key, and limits computers per account and connection rates; a distribution can preset its organization's relay, ready once the user signs in.
|
|
18
|
+
- Relay URLs support path prefixes; `RELAY_FRAME_ANCESTORS` configures which portals may embed the page, with embedding denied by default.
|
|
19
|
+
- Includes the protocol documentation; requires Node.js 24.22.1 or newer, matching the security update baseline in the deployment guide.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 East China Normal University
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,93 @@
|
|
|
1
|
-
#
|
|
1
|
+
# 远程访问
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[English](README_EN.md)
|
|
4
|
+
|
|
5
|
+
`@eduwork/dsh-remote-access` 让你在手机或其他电脑的浏览器里使用另一台电脑上的 DeepSeek Harness(DSH):查看和新建会话、继续或取消任务、审批、回答提问、查看和下载结果。连接经自托管的中继服务器转发,端到端加密,中继看不到内容。每台设备都要在电脑上核对配对码、确认后才能连接,也可以随时移除。
|
|
6
|
+
|
|
7
|
+
包里有两部分:
|
|
8
|
+
|
|
9
|
+
- **DSH 插件**:运行在被访问的电脑上,在设置页开关远程访问、添加和移除设备,并让 DSH 的界面适合手机屏幕。
|
|
10
|
+
- **中继服务**:部署在有公网地址的服务器上,转发密文,并提供手机上打开的网页。
|
|
11
|
+
|
|
12
|
+
## 使用
|
|
13
|
+
|
|
14
|
+
### 部署中继
|
|
15
|
+
|
|
16
|
+
需要 Node.js 24.22.1 或更新版本,以及为中继域名提供 HTTPS 并转发 WebSocket 的反向代理。
|
|
17
|
+
|
|
18
|
+
```sh
|
|
19
|
+
RELAY_URL=https://remote.example.edu RELAY_ACCESS_KEYS=key-1,key-2 \
|
|
20
|
+
npx -p @eduwork/dsh-remote-access eduwork-remote-relay
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
`RELAY_URL` 是用户访问中继的公开地址;`RELAY_ACCESS_KEYS` 是发给电脑主人的接入密钥,多个用逗号分隔。中继默认监听 `127.0.0.1:8787`,可用 `HOST`、`PORT` 修改;`<RELAY_URL>healthz` 返回 `ok`,可用于监控。
|
|
24
|
+
|
|
25
|
+
中继也可以和其他网站共用域名,放在单独的路径下,例如 `RELAY_URL=https://example.edu/eduwork-remote/`。反向代理须按原路径转发、不去掉这段前缀;中继的网页、Service Worker 和连接都只在这个路径下。电脑上填写同一个地址。共用域名有安全风险(见[安全](#安全)),只适合同一域名下没有需要登录的网站时使用;同一域名下若有作用于整个域名的 Service Worker,还须让它放过这段路径。
|
|
26
|
+
|
|
27
|
+
学校或企业也可以让成员凭自己的登录使用中继,不必逐个发接入密钥:`RELAY_OIDC_ISSUER` 填机构 OpenID Connect 登录的 issuer(例如 `https://sso.example.edu`),`RELAY_OIDC_CLIENT_IDS` 填桌面应用在该登录中的 client ID,多个用逗号分隔。电脑以登录得到的 ID Token 注册,中继用 issuer 公布的公钥验证,只记录用户标识,不记录 Token。Token 到期前电脑会自动换新的;到期仍未换新时,中继断开这台电脑现有的远程连接。机构登录失效、需要重新登录时,电脑停止连接中继,设置页提示重新登录。两种方式可以同时启用。
|
|
28
|
+
|
|
29
|
+
中继限制每个账号最多同时注册 5 台电脑,每台电脑每分钟最多接受 60 个设备连接,同一地址一分钟内注册被拒 10 次后要等待。中继在反向代理后面时,用 `RELAY_CLIENT_IP_HEADER` 指定代理传递客户端地址的请求头(例如 `x-real-ip`),否则所有用户共用代理的地址。
|
|
30
|
+
|
|
31
|
+
中继的网页默认不允许被其他页面嵌入。机构网站需要用 iframe 嵌入它时,用 `RELAY_FRAME_ANCESTORS` 列出允许嵌入的页面来源,多个用空格分隔:与中继同源的页面写 `self`,其他页面写它的来源,例如 `https://portal.example.edu`。命令行和 env 文件都会去掉引号,所以 `self` 与 `'self'` 等效;写多个来源时整个值加双引号,例如 `RELAY_FRAME_ANCESTORS="self https://portal.example.edu"`。iframe 不要加 `sandbox` 属性,中继网页需要运行脚本、注册 Service Worker 和下载文件。嵌入的页面应与中继属于同一站点(主域名相同,例如 `portal.example.edu` 与 `remote.example.edu`):跨站嵌入时,Safari 等 WebKit 浏览器无法在其中加载电脑的界面。
|
|
32
|
+
|
|
33
|
+
### 在电脑上开启
|
|
34
|
+
|
|
35
|
+
在 DSH 中安装插件后重启:
|
|
36
|
+
|
|
37
|
+
```sh
|
|
38
|
+
dsh plugin --profile web add @eduwork/dsh-remote-access
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
打开“设置 → 远程访问”,填写中继服务器和接入密钥,勾选“允许远程访问”。显示“已连接”后即可添加设备。远程访问默认关闭。
|
|
42
|
+
|
|
43
|
+
机构的发行版可以预置自己的中继和登录(插件设置 `organization: { relay, oidcProfileId }`,`oidcProfileId` 是 [dsh-oidc](../dsh-oidc/README.md) 中的机构配置)。这时设置页不显示中继和接入密钥:登录机构账号并勾选“允许远程访问”即可,退出登录后远程访问随之停止。
|
|
44
|
+
|
|
45
|
+
机构登录模式还要求宿主安装提供 `identityToken(profileID)` 接口的 `@eduwork/dsh-oidc@0.3.1` 或后续兼容版本;旧版 `0.3.0` 不包含该接口。使用接入密钥的独立部署不依赖 OIDC 插件。
|
|
46
|
+
|
|
47
|
+
### 添加和移除设备
|
|
48
|
+
|
|
49
|
+
1. 在电脑上点“添加设备”,出现二维码和链接。链接 10 分钟内有效,只能用一次。
|
|
50
|
+
2. 在手机上扫码或打开链接,填写设备名称,点“开始配对”。
|
|
51
|
+
3. 确认手机和电脑上显示的 6 位配对码一致后,在电脑上点“允许”。
|
|
52
|
+
|
|
53
|
+
之后在这台设备上打开中继地址就会连接到电脑;断线后自动重连。iPhone 和 iPad 上请先把页面添加到主屏幕,从主屏幕打开后再粘贴配对链接:只在 Safari 里配对的设备,7 天不用会丢失配对。
|
|
54
|
+
|
|
55
|
+
在电脑的“已配对设备”里点“移除”,该设备立即断开,之后不能再连接。
|
|
56
|
+
|
|
57
|
+
## 远程设备能做什么
|
|
58
|
+
|
|
59
|
+
可以选择已有工作区,查看或新建会话,继续和取消任务,审批或回答提问,查看和下载结果;在 EduWork 中还可以使用 Studio 成果和日历。
|
|
60
|
+
|
|
61
|
+
手机上看到的就是 DSH 自己的界面,只按窄屏做了调整:会话列表(工作区目录、搜索、新会话等)从左上角打开并铺满屏幕,选中会话或面板后自动收起;设置同样铺满屏幕,各分区排成一行标签,在远程设备上只能查看;对话中的耗时、重试、审批和提问与电脑上一致。
|
|
62
|
+
|
|
63
|
+
不能在电脑上打开窗口、程序或发出通知,不能注册或删除工作区,不能修改设置或管理账号、插件、凭据、技能和记忆,不能使用终端或执行代码,也不能管理远程访问本身。完整清单见 [src/gate/gate.ts](src/gate/gate.ts),未列出的接口一律拒绝。
|
|
64
|
+
|
|
65
|
+
## 安全
|
|
66
|
+
|
|
67
|
+
- 电脑和设备之间端到端加密,中继只转发密文。配对链接固定了电脑的公钥,中继无法冒充电脑。
|
|
68
|
+
- 设备密钥由浏览器生成且不可导出,电脑只保存设备公钥。每次连接都要用设备密钥签名。
|
|
69
|
+
- 接入密钥和机构登录只决定谁能使用这台中继,不能用来连接别人的电脑。接入密钥保存在 DSH 的凭据里(`EDUWORK_REMOTE_ACCESS_KEY`),不写进插件设置;机构登录的 ID Token 只发给发行版预置的中继,设置页不能更改这个地址。
|
|
70
|
+
- 手机上打开的网页由中继提供,部署中继的人可以修改它。请只使用你信任的中继。
|
|
71
|
+
- 手机上显示的 DSH 界面由所连的电脑提供,与中继的网页运行在同一个域名下,而浏览器只按域名隔离数据。因此一台电脑提供的界面,能使用此浏览器与其他电脑的配对去连接它们。请只与自己的电脑配对。
|
|
72
|
+
- 同理,中继与其他网站共用域名时,那些网站的脚本能使用这里的配对连接电脑;任何一台被配对的电脑提供的界面,也能以用户的身份访问那些网站、读取它们保存在浏览器里的数据。请为中继使用单独的域名(例如子域名),不要与登录服务或其他需要登录的网站共用。
|
|
73
|
+
- 允许其他页面嵌入中继的网页后,那些页面能把它显示在自己的页面里并加以遮挡,诱导用户点击(例如批准命令)。只允许你信任的页面嵌入。
|
|
74
|
+
|
|
75
|
+
协议细节见[协议文档](docs/PROTOCOL.md)。
|
|
76
|
+
|
|
77
|
+
## 贡献来源
|
|
78
|
+
|
|
79
|
+
本插件由 [@JunieXD](https://github.com/JunieXD) 在 [#192](https://github.com/ECNU/EduWork/pull/192) 中实现,承接了 [@Paraso42](https://github.com/Paraso42) 在 [#180](https://github.com/ECNU/EduWork/pull/180) 中的远程访问原型贡献:DSH 宿主接入点梳理、接口白名单与拒绝原因、设备信任与撤销模型,以及断线重连、避免重复执行和跨网络部署的验收场景。@Paraso42 是远程访问功能的合作贡献者;当前协议与实现以 #192 为准。
|
|
80
|
+
|
|
81
|
+
## 开发
|
|
82
|
+
|
|
83
|
+
在本包目录运行:
|
|
84
|
+
|
|
85
|
+
```sh
|
|
86
|
+
npm ci
|
|
87
|
+
npm run check
|
|
88
|
+
node scripts/check-web.ts
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
`check-web.ts` 在 Chromium 和 WebKit 中走完配对、加载、请求、断线重连和移除设备,需要本机已安装 Playwright 的浏览器,不在 CI 中运行。
|
|
92
|
+
|
|
93
|
+
[包维护与发布](https://github.com/ecnu/EduWork/blob/main/docs/PACKAGES.md) · [变更记录](CHANGELOG.md) · [代码许可](LICENSE) · [第三方声明](THIRD_PARTY_NOTICES.md)
|
package/README_EN.md
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# Remote access
|
|
2
|
+
|
|
3
|
+
[简体中文](README.md)
|
|
4
|
+
|
|
5
|
+
`@eduwork/dsh-remote-access` lets you use DeepSeek Harness (DSH) on another computer from a browser on your phone or a second computer: view and start conversations, continue or cancel tasks, approve, answer questions, and view and download results. The connection passes through a self-hosted relay server and is end-to-end encrypted, so the relay cannot read it. Each device connects only after you compare a pairing code and confirm it on the computer, and you can remove it at any time.
|
|
6
|
+
|
|
7
|
+
The package has two parts:
|
|
8
|
+
|
|
9
|
+
- **DSH plugin**: runs on the computer being accessed. Its settings page switches remote access on and off and adds and removes devices, and it fits DSH's interface to phone screens.
|
|
10
|
+
- **Relay service**: runs on a server with a public address. It forwards ciphertext and serves the page you open on your phone.
|
|
11
|
+
|
|
12
|
+
## Use
|
|
13
|
+
|
|
14
|
+
### Deploy the relay
|
|
15
|
+
|
|
16
|
+
You need Node.js 24.22.1 or later and a reverse proxy that serves HTTPS for the relay's domain and forwards WebSockets.
|
|
17
|
+
|
|
18
|
+
```sh
|
|
19
|
+
RELAY_URL=https://remote.example.edu RELAY_ACCESS_KEYS=key-1,key-2 \
|
|
20
|
+
npx -p @eduwork/dsh-remote-access eduwork-remote-relay
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
`RELAY_URL` is the public address users reach the relay at; `RELAY_ACCESS_KEYS` are the comma-separated access keys you give to computer owners. The relay listens on `127.0.0.1:8787` by default; change it with `HOST` and `PORT`. `<RELAY_URL>healthz` answers `ok` for monitoring.
|
|
24
|
+
|
|
25
|
+
The relay can also share a domain with other sites under a path of its own, for example `RELAY_URL=https://example.edu/eduwork-remote/`. The reverse proxy must pass the path on unchanged, keeping that prefix; the relay's pages, Service Worker and connections all stay below it. Enter the same address on the computer. Sharing a domain has security risks (see [Security](#security)): do it only when no site on that domain needs signing in to, and if a Service Worker there covers the whole domain, make it leave this path alone.
|
|
26
|
+
|
|
27
|
+
A school or company can instead let its members use the relay with their own sign-in, without handing out access keys: set `RELAY_OIDC_ISSUER` to the issuer of the organization's OpenID Connect sign-in (for example `https://sso.example.edu`) and `RELAY_OIDC_CLIENT_IDS` to the desktop application's client IDs in it, separated by commas. Computers register with the ID Token their sign-in issued; the relay checks it against the keys the issuer publishes and keeps only the user's identifier, never the token. Computers switch to a new token before theirs expires; if one expires without being renewed, the relay ends that computer's remote connections. When the organization sign-in has lapsed and needs signing in again, the computer stops connecting to the relay and the settings page asks the user to sign in again. Both ways can be enabled together.
|
|
28
|
+
|
|
29
|
+
The relay lets each account register at most 5 computers at a time and each computer accept at most 60 device connections a minute; an address with 10 refused registrations in a minute has to wait. Behind a reverse proxy, set `RELAY_CLIENT_IP_HEADER` to the header in which the proxy passes the client's address (for example `x-real-ip`); otherwise all users share the proxy's address.
|
|
30
|
+
|
|
31
|
+
By default no other page may embed the relay's page. When an organization's site needs to embed it in an iframe, list the origins of the pages allowed to in `RELAY_FRAME_ANCESTORS`, separated by spaces: `self` for pages of the relay's own origin, or an origin such as `https://portal.example.edu` for others. Shells and env files strip quotes, so `self` and `'self'` are the same; quote the whole value when it lists several, for example `RELAY_FRAME_ANCESTORS="self https://portal.example.edu"`. Do not give the iframe a `sandbox` attribute: the relay's page runs scripts, registers a Service Worker and downloads files. The embedding page should be on the same site as the relay (the same registrable domain, such as `portal.example.edu` and `remote.example.edu`): embedded across sites, Safari and other WebKit browsers cannot load the computer's interface in it.
|
|
32
|
+
|
|
33
|
+
### Turn it on on the computer
|
|
34
|
+
|
|
35
|
+
Install the plugin in DSH and restart it:
|
|
36
|
+
|
|
37
|
+
```sh
|
|
38
|
+
dsh plugin --profile web add @eduwork/dsh-remote-access
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Open Settings → Remote access, enter the relay server and the access key, and select "Allow remote access". Once it shows "Connected", you can add devices. Remote access is off by default.
|
|
42
|
+
|
|
43
|
+
An organization's distribution can preset its relay and sign-in (plugin setting `organization: { relay, oidcProfileId }`, where `oidcProfileId` names the organization's [dsh-oidc](../dsh-oidc/README_EN.md) profile). The settings page then shows no relay or access key: sign in with the organization's account and select "Allow remote access". Signing out stops remote access.
|
|
44
|
+
|
|
45
|
+
Organization sign-in also requires `@eduwork/dsh-oidc@0.3.1` or a later compatible version on the host, providing `identityToken(profileID)`; the older `0.3.0` does not include this API. Standalone deployments using an access key do not depend on the OIDC plugin.
|
|
46
|
+
|
|
47
|
+
### Add and remove devices
|
|
48
|
+
|
|
49
|
+
1. On the computer, choose "Add a device". A QR code and a link appear; the link works once, for 10 minutes.
|
|
50
|
+
2. On the phone, scan the code or open the link, enter a device name and choose "Pair".
|
|
51
|
+
3. Check that the phone and the computer show the same 6-digit pairing code, then choose "Allow" on the computer.
|
|
52
|
+
|
|
53
|
+
From then on, opening the relay address on that device connects to the computer, and it reconnects by itself after a drop. On iPhone and iPad, add the page to the Home Screen first, open it from there and paste the pairing link: a pairing made only in Safari is lost after 7 days without use.
|
|
54
|
+
|
|
55
|
+
Choose "Remove" under "Paired devices" on the computer to disconnect a device at once and keep it from connecting again.
|
|
56
|
+
|
|
57
|
+
## What remote devices can do
|
|
58
|
+
|
|
59
|
+
They can choose an existing workspace, view and start conversations, continue and cancel tasks, approve or answer questions, and view and download results; in EduWork they can also use Studio results and the calendar.
|
|
60
|
+
|
|
61
|
+
A phone shows DSH's own interface, only fitted to the narrow screen: the session list (workspace folders, search, new session and so on) opens full screen from the top-left corner and closes once you pick a session or a panel; settings also fill the screen, with the sections as a row of tabs, and are view only on remote devices; conversations show durations, retries, approvals and questions as on the computer.
|
|
62
|
+
|
|
63
|
+
They cannot open windows or programs or show notifications on the computer, register or remove workspaces, change settings or manage accounts, plugins, credentials, skills or memories, use terminals or run code, or manage remote access itself. The full list is in [src/gate/gate.ts](src/gate/gate.ts); anything not listed is refused.
|
|
64
|
+
|
|
65
|
+
## Security
|
|
66
|
+
|
|
67
|
+
- Traffic between the computer and the device is end-to-end encrypted; the relay only forwards ciphertext. The pairing link pins the computer's public key, so the relay cannot impersonate the computer.
|
|
68
|
+
- The browser generates the device key as non-extractable; the computer stores only its public key. Every connection is signed with the device key.
|
|
69
|
+
- An access key or an organization sign-in only decides who may use the relay; neither can be used to connect to someone else's computer. The access key is kept in DSH's credentials (`EDUWORK_REMOTE_ACCESS_KEY`), not in the plugin's settings; the ID Token of an organization sign-in goes only to the relay the distribution preset, which the settings page cannot change.
|
|
70
|
+
- The relay serves the page you open on your phone, so whoever runs the relay can change it. Use only relays you trust.
|
|
71
|
+
- The DSH interface on your phone is provided by the computer it connects to and runs on the relay's domain, and browsers separate data only by domain. So the interface one computer provides can use this browser's pairings with other computers to connect to them. Pair only with your own computers.
|
|
72
|
+
- Likewise, when the relay shares a domain with other sites, their scripts can use the pairings here to connect to computers, and the interface of any paired computer can use those sites as the user and read the data they keep in the browser. Give the relay a domain of its own (a subdomain, for example), and never share one with a sign-in service or other sites that you sign in to.
|
|
73
|
+
- Pages allowed to embed the relay's page can show it within their own and cover parts of it to trick users into clicking (approving a command, say). Allow only pages you trust.
|
|
74
|
+
|
|
75
|
+
See the [protocol document](docs/PROTOCOL.md) (Chinese) for details.
|
|
76
|
+
|
|
77
|
+
## Contribution origins
|
|
78
|
+
|
|
79
|
+
[@JunieXD](https://github.com/JunieXD) implemented this plugin in [#192](https://github.com/ECNU/EduWork/pull/192), building on [@Paraso42](https://github.com/Paraso42)'s remote-access prototype in [#180](https://github.com/ECNU/EduWork/pull/180): mapping DSH host integration points, endpoint allowlists and refusal reasons, the device trust and revocation model, and validation scenarios for reconnection, avoiding duplicate execution, and deployment across networks. @Paraso42 is a co-contributor to remote access; #192 defines the current protocol and implementation.
|
|
80
|
+
|
|
81
|
+
## Development
|
|
82
|
+
|
|
83
|
+
From this package directory:
|
|
84
|
+
|
|
85
|
+
```sh
|
|
86
|
+
npm ci
|
|
87
|
+
npm run check
|
|
88
|
+
node scripts/check-web.ts
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
`check-web.ts` goes through pairing, loading, requests, reconnection and device removal in Chromium and WebKit. It needs Playwright's browsers installed locally and does not run in CI.
|
|
92
|
+
|
|
93
|
+
[Package maintenance and publishing](https://github.com/ecnu/EduWork/blob/main/docs/PACKAGES_EN.md) · [Changelog](CHANGELOG.md) · [License](LICENSE) · [Third-party notices](THIRD_PARTY_NOTICES.md)
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# 第三方声明
|
|
2
|
+
|
|
3
|
+
`@eduwork/dsh-remote-access` 使用 MIT 许可证。发布产物打包了下列软件包的代码;构建脚本从实际打包的依赖收集各包的许可证全文,随包放在 `lib/THIRD_PARTY_LICENSES.txt`。精确版本见 `package-lock.json`。
|
|
4
|
+
|
|
5
|
+
- 宿主插件与网页客户端的加密:`noise-handshake`、`b4a`(Apache-2.0),`sodium-javascript`、`sodium-universal`、`blake2b-wasm`、`siphash24`、`xsalsa20`(MIT),`blake2b`、`chacha20-universal`、`nanoassert`、`sha256-universal`、`sha256-wasm`、`sha512-universal`、`sha512-wasm`(ISC)。
|
|
6
|
+
- 中继服务:`ws`(MIT)。
|
|
7
|
+
- 设置页:`zod`、`uqr`(MIT)。
|
|
8
|
+
|
|
9
|
+
DeepSeek Harness 软件包、`@deepseek-ai/cordis`、`@deepseek-ai/schemastery` 和 React 由宿主提供,作为 peer dependency 出现,不打包进本包,也不表示其作者认可本项目。网页图标取自 EduWork 自有 Logo,随本项目 MIT 许可提供。
|
package/cordis.patch.yml
ADDED
package/docs/PROTOCOL.md
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# 远程访问协议 `eduwork-remote/1`
|
|
2
|
+
|
|
3
|
+
本文描述当前版本的消息格式、状态转换和版本兼容规则。实现位于 [`src/protocol/`](../src/protocol/),浏览器和宿主共用同一份代码。
|
|
4
|
+
|
|
5
|
+
## 参与方
|
|
6
|
+
|
|
7
|
+
- **宿主**:运行 DSH 和本插件的电脑。长期持有两把密钥:Ed25519 签名密钥(向中继证明身份)和 X25519 Noise 静态密钥(向设备证明身份)。
|
|
8
|
+
- **设备**:手机或其他电脑上的浏览器。每个配对的宿主对应一把 WebCrypto 生成的不可导出 ECDSA P-256 密钥。
|
|
9
|
+
- **中继**:转发密文的公共服务,同时提供网页客户端。它只验证宿主的接入资格,看不到明文。
|
|
10
|
+
|
|
11
|
+
宿主 ID(hostId)是 Ed25519 公钥原文的 base64url 编码(43 个字符)。
|
|
12
|
+
|
|
13
|
+
## 中继
|
|
14
|
+
|
|
15
|
+
中继有一个公开的基础地址,可以带路径,例如 `https://example.edu/eduwork-remote/`;规范形式以 `/` 结尾,不含用户名、查询和片段。下文的路径都在这个地址之下,网页客户端的页面和 Service Worker 也一样。中继的 WebSocket 路径如下。文本帧是控制消息(JSON,带字段 `t`),二进制帧是端到端密文,单条消息不超过 65535 字节。
|
|
16
|
+
|
|
17
|
+
| 路径 | 连接方 | 过程 |
|
|
18
|
+
| --- | --- | --- |
|
|
19
|
+
| `v1/host` | 宿主 | 中继发 `challenge {nonce}`;宿主回 `register {protocol, hostId, sig, accessKey}` 或 `register {protocol, hostId, sig, idToken}`;中继回 `registered`。此后每有一台设备接入,中继发 `connect {id}`。宿主可随时发 `ping`,中继回 `pong`。 |
|
|
20
|
+
| `v1/host/accept` | 宿主 | 宿主第一条消息发 `accept {id}`,此后这条连接与对应设备的连接成对转发。 |
|
|
21
|
+
| `v1/connect/<hostId>` | 设备 | 宿主接受后中继发 `open`,此后成对转发。 |
|
|
22
|
+
|
|
23
|
+
注册签名覆盖 `` `eduwork-remote/1 relay-register\0<中继基础地址>\0<nonce>` ``,用 hostId 对应的 Ed25519 密钥签名。nonce 每次连接新生成,签名绑定中继的完整基础地址,不能在别处重放,包括同一域名下其他路径的中继。同一 hostId 的新注册会顶替旧的,旧连接以 4409 关闭。
|
|
24
|
+
|
|
25
|
+
注册凭据二选一:`accessKey` 是中继运营者发的接入密钥;`idToken` 是中继接纳的机构登录签发的 OpenID Connect ID Token(RS256,issuer 和 audience 由中继配置)。凭据只决定谁能使用这台中继,不决定谁能连接哪台电脑。Token 须由中继配置的 issuer 签发,签发对象(有 `azp` 时为 `azp`,否则为唯一的 `aud`)须是配置的 client ID 之一并在 `aud` 中;`aud` 有多个值时必须带 `azp`。以 ID Token 注册时,中继记下其中的 `sub` 作为账号;Token 过期时,中继以 4408 关闭这台宿主的控制连接,以及在它名下配对的全部设备连接。宿主在过期前 5 分钟用新的 Token 在另一条控制连接上重新注册,新注册顶替旧的,过期时间随之顺延,已连接的设备不受影响;重新注册失败时宿主保留原注册,稍后再试;机构登录已失效、只能重新登录时,宿主停止注册,等用户重新登录。控制连接因网络中断时,已配对的设备连接继续转发,直到 Token 过期。
|
|
26
|
+
|
|
27
|
+
中继的限制:同一来源地址一分钟内被拒的注册超过上限后,之后的注册直接以 4429 关闭;每个账号同时注册的电脑数有上限,超过以 4401 拒绝;每台电脑每分钟接受的设备连接数有上限,超过以 4429 关闭设备连接。机构登录服务暂时取不到公钥时以 4503 关闭,宿主稍后重试。
|
|
28
|
+
|
|
29
|
+
每台设备的每条连接都对应宿主新开的一条 `accept` 连接,因此各条连接分别做 TCP 背压,一台慢设备不会拖住其他设备。成对连接的一端关闭时,中继用同一关闭码关闭另一端(1000 和 4000–4999 原样转发,其余改为 4410)。
|
|
30
|
+
|
|
31
|
+
## 加密信道
|
|
32
|
+
|
|
33
|
+
设备在 `open` 之后发起 Noise 握手,模式为 `Noise_IK_25519_ChaChaPoly_BLAKE2b`:
|
|
34
|
+
|
|
35
|
+
- 宿主静态公钥来自配对链接,之后每次连接都固定使用它,所以中继无法冒充宿主。
|
|
36
|
+
- 设备的发起方静态密钥每条连接随机生成,不代表设备身份;设备身份由下一节的签名证明。
|
|
37
|
+
- prologue 为 `` `eduwork-remote/1\0relay\0<hostId>` ``。
|
|
38
|
+
|
|
39
|
+
握手后每条 WebSocket 二进制消息是一条 Noise 传输消息。明文第一个字节为 1 表示后面还有分片,为 0 表示帧结束;其余是帧的一段。一帧由 4 字节大端头部长度、JSON 头部和原始正文组成。一帧最大 16 MiB;任一方积压未发出的数据超过 64 MiB 时关闭连接。Noise 计数器为 32 位,单向达到 2³² 条消息后拒绝继续收发,实际使用中达不到。
|
|
40
|
+
|
|
41
|
+
## 认证与配对
|
|
42
|
+
|
|
43
|
+
握手完成后,设备的第一帧必须是 `auth`:
|
|
44
|
+
|
|
45
|
+
| 字段 | 说明 |
|
|
46
|
+
| --- | --- |
|
|
47
|
+
| `key` | 设备 P-256 公钥(未压缩格式原文)的 base64url |
|
|
48
|
+
| `sig` | 对 `` `eduwork-remote/1 device-auth\0` `` 加握手哈希的 ECDSA P-256/SHA-256 签名(IEEE P1363 格式) |
|
|
49
|
+
| `invite` | 仅首次配对:配对链接中的一次性邀请 |
|
|
50
|
+
| `name` | 仅首次配对:设备名称,最多 64 个字符 |
|
|
51
|
+
|
|
52
|
+
签名覆盖本次连接的握手哈希,换一条连接就不能重放。宿主的回复:
|
|
53
|
+
|
|
54
|
+
| 回复 | 含义 |
|
|
55
|
+
| --- | --- |
|
|
56
|
+
| `ready {host}` | 已认证,可以开始交换 |
|
|
57
|
+
| `pending {host}` | 新设备,等待宿主主人确认 |
|
|
58
|
+
| `rejected {reason}` | 拒绝后关闭连接,原因为 `unknown-device`、`invalid-invite`、`declined` 或 `timeout` |
|
|
59
|
+
| `revoked` | 已连接的设备被移除,随后以 4403 关闭 |
|
|
60
|
+
|
|
61
|
+
状态转换:
|
|
62
|
+
|
|
63
|
+
```text
|
|
64
|
+
握手 ──auth──▶ 已登记设备 ──────────────────────────▶ ready
|
|
65
|
+
未登记、无邀请或邀请无效 ─────────────▶ rejected
|
|
66
|
+
邀请有效 ──▶ pending ──主人允许──────▶ ready
|
|
67
|
+
──拒绝或 5 分钟未答─▶ rejected
|
|
68
|
+
ready ──主人移除设备──▶ revoked
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
设备在 10 秒内不发 `auth` 时连接关闭。收到 `ready` 前,设备除 `auth` 外不发其他帧;宿主在发出 `ready` 前不处理任何交换。
|
|
72
|
+
|
|
73
|
+
**配对码**:双方各自计算 `SHA-256("eduwork-remote/1 pairing-code\0" ‖ 握手哈希)` 前 4 字节(大端)对 1 000 000 取模,补足 6 位显示。两边的码一致,说明设备连到的正是这台宿主,中间没有第三方。
|
|
74
|
+
|
|
75
|
+
**配对链接**:`<中继基础地址>#<hostId>.<宿主 Noise 公钥>.<邀请>`,三段都是 base64url。浏览器不会把 `#` 之后的内容发给中继。邀请为 16 字节随机数,10 分钟内有效、只能用一次;新生成的邀请使旧邀请作废。
|
|
76
|
+
|
|
77
|
+
## 隧道
|
|
78
|
+
|
|
79
|
+
`ready` 之后,信道上承载两类交换:HTTP 请求和 DSH Gateway 流。帧名称沿用 DSH worker 隧道的约定。每个交换由设备分配一个正整数 `id`,同一连接内严格递增。
|
|
80
|
+
|
|
81
|
+
| 帧 | 方向 | 说明 |
|
|
82
|
+
| --- | --- | --- |
|
|
83
|
+
| `req {id, method, url, headers, body}` | 设备 → 宿主 | `url` 为以 `/` 开头的路径和查询;`body` 为布尔值,表示随后是否有请求正文 |
|
|
84
|
+
| `req-chunk {id}` + 正文 | 设备 → 宿主 | 请求正文片段 |
|
|
85
|
+
| `req-end {id}` | 设备 → 宿主 | 请求正文结束 |
|
|
86
|
+
| `res-head {id, status, headers}` | 宿主 → 设备 | 响应状态与头部 |
|
|
87
|
+
| `res-chunk {id}` + 正文 | 宿主 → 设备 | 响应正文片段 |
|
|
88
|
+
| `res-end {id}` | 宿主 → 设备 | 响应结束 |
|
|
89
|
+
| `res-err {id, message}` | 宿主 → 设备 | 宿主无法给出响应(区别于返回错误状态码) |
|
|
90
|
+
| `stream-open {id, endpoint, payload}` | 设备 → 宿主 | 打开 Gateway 流,等同 DSH 的 `RpcStreamOpen` |
|
|
91
|
+
| `stream-uplink-item {id, value}` / `stream-uplink-end {id}` | 设备 → 宿主 | 流的上行数据 |
|
|
92
|
+
| `stream-item {id, value}` | 宿主 → 设备 | 流的一项 |
|
|
93
|
+
| `stream-end {id}` | 宿主 → 设备 | 流正常结束 |
|
|
94
|
+
| `stream-error {id, code, message, details}` | 宿主 → 设备 | 流以 DSH 的稳定错误结束 |
|
|
95
|
+
| `credit {id, bytes}` | 双向 | 正文接收方追加的额度 |
|
|
96
|
+
| `abort {id}` | 设备 → 宿主 | 设备不再需要该交换 |
|
|
97
|
+
| `ping` / `pong` | 设备 → 宿主 / 宿主 → 设备 | 不带 `id` 的存活探测 |
|
|
98
|
+
|
|
99
|
+
**压缩**:设备请求前端文件(首页、静态资源、插件包)时可在头部带 `accept-encoding: gzip`,宿主据此把其中的文本文件压缩后返回,并标上 `content-encoding: gzip`。RPC、文件下载和流一律不压缩:这些内容可能涉及隐私,压缩后的长度可能向中继透露内容特征。
|
|
100
|
+
|
|
101
|
+
**流控**:每个正文开始时有 1 MiB 额度,每片不超过 64000 字节;接收方读走四分之一窗口后补发 `credit`。超额发送是协议错误。
|
|
102
|
+
|
|
103
|
+
**存活**:设备每 20 秒发一次 `ping`,10 秒内没有 `pong` 就关闭并重连。宿主 90 秒没收到任何帧时以 4408 关闭连接。宿主到中继的控制连接也用 `ping`/`pong` 探测。
|
|
104
|
+
|
|
105
|
+
**失败语义**:交换只属于它所在的连接。连接关闭时,设备端未完成的交换立即失败,宿主端的处理被中止,之后产生的结果随连接丢弃。隧道不重发;重试由 DSH 客户端按自身规则处理(RPC 不重试,流按退避重开,prompt 由宿主按 `requestId` 去重)。
|
|
106
|
+
|
|
107
|
+
**宿主上的执行边界**:远程请求以宿主主人的身份进入 DSH,每条连接有独立的 Peer,连接关闭或设备被移除时 Peer 随即释放。可用的接口由 [`src/gate/gate.ts`](../src/gate/gate.ts) 列出,未列出的一律拒绝:RPC 拒绝时返回 DSH 的响应信封,错误码 `remote-access/forbidden`;流以同一错误码结束。个别接口只在参数符合时放行:`settings/mutate` 只能记录已读 DSH 的预览版说明。桌面端不显示这条说明,网页端不确认就无法继续,其他设置仍不能修改。
|
|
108
|
+
|
|
109
|
+
## 关闭码
|
|
110
|
+
|
|
111
|
+
| 码 | 含义 |
|
|
112
|
+
| --- | --- |
|
|
113
|
+
| 1000 | 正常关闭(例如宿主关闭了远程访问) |
|
|
114
|
+
| 4400 | 协议错误 |
|
|
115
|
+
| 4401 | 中继拒绝注册凭据或签名,或该账号注册的电脑已达上限 |
|
|
116
|
+
| 4403 | 设备被拒绝或被移除 |
|
|
117
|
+
| 4404 | 宿主不在线 |
|
|
118
|
+
| 4408 | 超时,或注册所用的 ID Token 过期 |
|
|
119
|
+
| 4409 | 同一宿主身份在别处注册,本连接被顶替 |
|
|
120
|
+
| 4410 | 对端连接断开 |
|
|
121
|
+
| 4426 | 协议版本不受支持 |
|
|
122
|
+
| 4429 | 超出频率、连接数或积压上限 |
|
|
123
|
+
| 4503 | 服务停止或内部错误 |
|
|
124
|
+
|
|
125
|
+
## 版本兼容
|
|
126
|
+
|
|
127
|
+
协议名 `eduwork-remote/1` 出现在中继注册、握手 prologue、设备签名和配对码的上下文中,所以不同版本的两端无法误连:中继以 4426 拒绝它不支持的宿主协议,宿主与设备版本不一致时握手或验签失败。
|
|
128
|
+
|
|
129
|
+
同一版本内只做兼容的扩展:新增可选字段,接收方忽略不认识的字段。改变已有帧的含义、增删必需字段或调整密码学构造时,协议名升级为 `eduwork-remote/2`。中继提供的网页客户端和宿主插件分别发布,二者只需协议名一致。网页客户端在远程设备的 DSH 页面根元素上标记 `data-eduwork-remote`,宿主插件据此按手机宽度调整 DSH 的布局,并且不显示远程访问设置;任一端较旧时只是少了这项调整。
|