@falling-ts/dsh-local-no-auth 0.2.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 the dsh-local-no-auth contributors
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.cn.md ADDED
@@ -0,0 +1,89 @@
1
+ # dsh-local-no-auth
2
+
3
+ 一个 [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) 的
4
+ Cordis 插件:让 `dsh web` 的**仅回环本地实例**免浏览器会话认证——不需要
5
+ launch token、不需要 cookie——而且**不改上游源码的一个字**。
6
+
7
+ | | |
8
+ |---|---|
9
+ | 包名 | `@falling-ts/dsh-local-no-auth` |
10
+ | License | MIT |
11
+ | 平台 | 纯 Host 插件(无客户端半部、无 UI) |
12
+
13
+ ## 适用场景
14
+
15
+ `dsh web` 用"启动 URL 里的 `?token=...` 交换 + 签名 cookie"守卫全部访问。
16
+ 这在远程/共享部署下是对的;但对只监听回环的本地实例纯属仪式——能连到这个
17
+ 端口的主机,本来就是这台机器本身。
18
+
19
+ ## 实现原理(零源码改动)
20
+
21
+ 上游所有认证判定都经由 `ctx.connection` 服务**实例**的三个方法:
22
+
23
+ | 方法 | 管辖 |
24
+ |---|---|
25
+ | `requestRejection` | `/api` 面(RPC / 流 / 网关)的 401/403 判定 |
26
+ | `authorizeIndex` | 前端 index 请求的 token/cookie 认证 |
27
+ | `authenticatedUrl` | 打印的启动 URL(追加 launch token) |
28
+
29
+ 本插件在自身 `apply` 里先读取实时 `webServer` 绑定地址:**只有回环字面量才
30
+ 继续**,否则 fail-loud 抛错拒载;然后才把这三个**实例方法**在运行时替换掉:
31
+
32
+ ```js
33
+ connection.requestRejection = () => undefined // 所有 /api 请求直接放行
34
+ connection.authorizeIndex = () => true // index 立即返回
35
+ connection.authenticatedUrl = (url) => url // 打印的 URL 保持干净
36
+ ```
37
+
38
+ 各消费点(`/api` 路由、frontend-static 兜底席位、API 网关、web-app 的 URL
39
+ 公告)都是按实例引用调用这三个方法的,因此整个本地面一次性变为免 token、
40
+ 免 cookie。插件卸载(`dispose`)时恢复原方法。
41
+
42
+ ## 安全边界
43
+
44
+ - **绑定地址闸门**:`apply` 读取实时 `webServer` 主机并校验为
45
+ `127.0.0.1` / `localhost` 才放行,否则抛错拒载(fail-loud,无静默兜底)。
46
+ 上游 webserver 的 Config schema 只接受 `'127.0.0.1' | '0.0.0.0'`,因此
47
+ `0.0.0.0`(所有网卡)必然被拒——插件绝不会在"其它主机可达"的监听上静默生效。
48
+ - `dsh web` 只绑回环,CLI **拒绝 `--host 0.0.0.0`**,所以本插件自身不会把
49
+ 服务暴露给其它主机。
50
+ - **不要**与任何让该端口可达性超出本机的手段搭配(共享机器上的 SSH 转发、
51
+ VLAN 回环、NAT hairpin)。
52
+ - 若上游未来允许非回环绑定,本插件会拒绝启动,而不是静默放行。
53
+
54
+ ## 安装
55
+
56
+ ```sh
57
+ # 本地目录安装(支持目录路径):
58
+ pnpm dsh plugin --profile web add <本目录绝对路径>
59
+
60
+ # 发布后:
61
+ dsh plugin --profile web add github:falling-ts/dsh-local-no-auth
62
+ ```
63
+
64
+ `dsh web` 没有 `--patch` 命令行叠加;插件通过 profile 自身的
65
+ `cordis.patch.yml` 引用本包的 `dsh.bundle.patch` 层激活。装完重启
66
+ `dsh web`。
67
+
68
+ ## 验证
69
+
70
+ ```sh
71
+ curl -i http://127.0.0.1:<port>/ # 200 + index.html,无需 token
72
+ curl -i -X POST http://127.0.0.1:<port>/api/session/list \
73
+ -H 'content-type: application/json' \
74
+ -d '{"type":"client-request","rpcId":"probe","method":"session/list","payload":{}}'
75
+ # 返回业务信封(2xx,result.ok 为 true/false)—— 绝不是 401
76
+ ```
77
+
78
+ ## 卸载
79
+
80
+ ```sh
81
+ dsh plugin --profile web remove dsh-local-no-auth
82
+ ```
83
+
84
+ ## 已知限制
85
+
86
+ - 放行是进程级的:同一组合里的所有插件都会看到免认证的 `/api`——这正是
87
+ 本地开发机想要的。
88
+ - 启动日志仍打印 `dsh web: http://...`(干净 URL);配合
89
+ `--trusted-host` 的 LAN 候选地址同样以干净 URL 打印。
package/README.md ADDED
@@ -0,0 +1,97 @@
1
+ # dsh-local-no-auth
2
+
3
+ A [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) Cordis
4
+ plugin that bypasses the `dsh web` browser-session authentication for the
5
+ **loopback-local** instance — no launch token, no cookie — **without modifying
6
+ a single line of upstream source**.
7
+
8
+ | | |
9
+ |---|---|
10
+ | Package | `@falling-ts/dsh-local-no-auth` |
11
+ | License | MIT |
12
+ | Platform | Host-only (no client half, no UI) |
13
+
14
+ ## Why
15
+
16
+ `dsh web` guards its surface with a launch-token exchange (the `?token=...`
17
+ on the printed URL) plus a signed browser cookie. That is correct for remote
18
+ or shared deployments, but for a loopback-only local instance it is pure
19
+ ceremony: any host that can reach the port is a host already on the machine.
20
+
21
+ ## How it works (no source changes)
22
+
23
+ Upstream routes every authentication decision through the live
24
+ `ctx.connection` service instance's methods:
25
+
26
+ | method | gate |
27
+ |---|---|
28
+ | `requestRejection` | `/api` surface (RPC, streams, gateway) — 401/403 |
29
+ | `authorizeIndex` | frontend index request — token/cookie exchange |
30
+ | `authenticatedUrl` | printed startup URL — appends the launch token |
31
+
32
+ This plugin, in its own `apply`, first checks the live webServer bind host and
33
+ **refuses to start** unless it is a loopback literal, then replaces those three
34
+ **instance methods** at runtime:
35
+
36
+ ```js
37
+ connection.requestRejection = () => undefined // every /api request proceeds
38
+ connection.authorizeIndex = () => true // index serves immediately
39
+ connection.authenticatedUrl = (url) => url // printed URL stays clean
40
+ ```
41
+
42
+ Every consumer (the `/api` route, the frontend-static fallback seat, the API
43
+ gateway, the web-app URL announcement) calls them by instance reference, so the
44
+ whole local surface becomes token- and cookie-free at once. On unload
45
+ (`dispose`) the original methods are restored.
46
+
47
+ ## Safety boundary
48
+
49
+ - **Bind-host gate:** `apply` reads the live `webServer` bind host and throws
50
+ (fails loud, no timeout fallback) unless it is `127.0.0.1` or `localhost`.
51
+ The upstream webserver Config schema only accepts `'127.0.0.1' | '0.0.0.0'`,
52
+ so `0.0.0.0` (all interfaces) always fails and the bypass never silently
53
+ activates against a reachable-by-others listen.
54
+ - `dsh web` binds loopback and the CLI **rejects `--host 0.0.0.0`**, so this
55
+ bypass cannot, by itself, expose the server to other hosts.
56
+ - Never combine with anything that makes the reach accessible beyond the local
57
+ machine (SSH forwarders on shared boxes, VLAN loopback, NAT hairpin).
58
+ - Authorization of *remote* requests is unchanged only because there are none
59
+ on a loopback bind; if upstream ever allows non-loopback binds, this plugin
60
+ refuses to start rather than silently bypassing.
61
+
62
+ ## Install
63
+
64
+ ```sh
65
+ # local development install (directory path is accepted):
66
+ pnpm dsh plugin --profile web add <absolute-path-to-this-directory>
67
+
68
+ # after publishing:
69
+ dsh plugin --profile web add github:falling-ts/dsh-local-no-auth
70
+ ```
71
+
72
+ `dsh web` has no `--patch` CLI overlay; the plugin activates through the
73
+ profile's own `cordis.patch.yml` referencing this package's `dsh.bundle.patch`
74
+ layer. Restart `dsh web` afterwards.
75
+
76
+ ## Verify
77
+
78
+ ```sh
79
+ curl -i http://127.0.0.1:<port>/ # 200 + index.html, no token needed
80
+ curl -i -X POST http://127.0.0.1:<port>/api/session/list \
81
+ -H 'content-type: application/json' \
82
+ -d '{"type":"client-request","rpcId":"probe","method":"session/list","payload":{}}'
83
+ # a business envelope arrives (2xx, result.ok true/false) — never 401
84
+ ```
85
+
86
+ ## Uninstall
87
+
88
+ ```sh
89
+ dsh plugin --profile web remove dsh-local-no-auth
90
+ ```
91
+
92
+ ## Known limitations
93
+
94
+ - The bypass is process-wide: every plugin in the same composition sees
95
+ unauthenticated `/api`. That is the point for a local dev box.
96
+ - Startup log line still says `dsh web: http://...` (clean URL) and, with
97
+ `--trusted-host` LAN candidates, prints the LAN URL clean as well.
@@ -0,0 +1,13 @@
1
+ # Opt-in layer: mount the dsh-local-no-auth function plugin over the current
2
+ # composition without changing shipped defaults or any upstream source.
3
+ #
4
+ # pnpm dsh plugin --profile web add <this-directory> # local dev install
5
+ # dsh plugin --profile web add github:falling-ts/dsh-local-no-auth # once published
6
+ #
7
+ # The plugin bypasses browser-session authentication for the loopback-local dsh
8
+ # web instance by replacing the live `connection` instance methods
9
+ # (requestRejection / authorizeIndex / authenticatedUrl); unload restores them.
10
+
11
+ - insert:
12
+ - id: dsh-local-no-auth
13
+ name: '@falling-ts/dsh-local-no-auth'
package/index.js ADDED
@@ -0,0 +1,82 @@
1
+ /**
2
+ * dsh-local-no-auth — a DSH Cordis function plugin.
3
+ *
4
+ * Bypasses the dsh web browser-session authentication for the loopback-local
5
+ * instance WITHOUT touching any upstream source. It replaces the three
6
+ * authentication methods on the live `ctx.connection` service instance:
7
+ *
8
+ * - `requestRejection` — the `/api` surface's 401/403 gate (RPC, streams, gateway)
9
+ * - `authorizeIndex` — the frontend index request's token/cookie exchange
10
+ * - `authenticatedUrl` — the startup URL line (drops the launch `?token=...`)
11
+ *
12
+ * Every authentication decision in the running app goes through these instance
13
+ * methods by instance reference (the `/api` route, the frontend-static fallback
14
+ * seat, the API gateway, and the web-app URL announcement), so replacing them
15
+ * here makes the whole local surface token- and cookie-free:
16
+ *
17
+ * http://127.0.0.1:<port>/ serves index.html immediately, no login round-trip
18
+ * /api/* answers RPC without a signed browser cookie
19
+ * the printed URL stays clean (no launch token appended)
20
+ *
21
+ * Safety boundary: `apply` refuses to start unless the live webServer bind host
22
+ * is a loopback literal. The upstream webserver Config schema only accepts
23
+ * `'127.0.0.1' | '0.0.0.0'`, so in practice `127.0.0.1` passes and `0.0.0.0`
24
+ * (all interfaces) fails; `localhost` is kept as a loopback alias for future
25
+ * schema latitude. Any other value fails loud — the bypass never silently
26
+ * activates against a reachable-by-others listen. Never enable the plugin when
27
+ * the reach in question can be accessed by other hosts anyway (SSH forwarders,
28
+ * VLAN-reachable loopback, NAT looping). Unload restores the original methods,
29
+ * so a disposed plugin leaves the instance identical to an unpatched run.
30
+ *
31
+ * @module @falling-ts/dsh-local-no-auth
32
+ */
33
+
34
+ /** Stable Cordis plugin name. */
35
+ export const name = 'dsh-local-no-auth'
36
+
37
+ /** Connection and webServer must be active before their members can be read. */
38
+ export const inject = ['connection', 'webServer']
39
+
40
+ /** The connection instance methods that own every auth decision in this app. */
41
+ const AUTH_METHODS = ['requestRejection', 'authorizeIndex', 'authenticatedUrl']
42
+
43
+ /** Bind hosts this bypass may operate under: loopback literals only. */
44
+ const LOOPBACK_HOSTS = new Set(['127.0.0.1', 'localhost'])
45
+
46
+ /**
47
+ * Replace the connection instance's authentication methods for this process,
48
+ * but only while the instance listens on a loopback-only bind host.
49
+ * @param {import('@deepseek-ai/cordis').Context} ctx - plugin context with `connection` and `webServer` active.
50
+ */
51
+ export function apply(ctx) {
52
+ const connection = ctx.connection
53
+ if (!connection || typeof connection !== 'object') {
54
+ throw new Error('dsh-local-no-auth: connection service unavailable')
55
+ }
56
+ const webServer = ctx.webServer
57
+ const bindHost = webServer && typeof webServer.host === 'string' ? webServer.host : undefined
58
+ if (bindHost === undefined) {
59
+ throw new Error('dsh-local-no-auth: webServer bind host unavailable; refusing to start')
60
+ }
61
+ if (!LOOPBACK_HOSTS.has(bindHost)) {
62
+ throw new Error(
63
+ `dsh-local-no-auth: refusing to start — webServer bind host "${bindHost}" is not loopback-only `
64
+ + `(allowed: ${[...LOOPBACK_HOSTS].join(', ')}). The no-auth bypass is only safe when `
65
+ + `no other host can reach the instance.`,
66
+ )
67
+ }
68
+ const originals = {}
69
+ for (const method of AUTH_METHODS) {
70
+ if (typeof connection[method] !== 'function') {
71
+ throw new Error(`dsh-local-no-auth: connection.${method} is not a function; refusing to start`)
72
+ }
73
+ originals[method] = connection[method]
74
+ }
75
+ connection.requestRejection = () => undefined
76
+ connection.authorizeIndex = () => true
77
+ connection.authenticatedUrl = (baseUrl) => baseUrl
78
+ ctx.on('dispose', () => {
79
+ for (const method of AUTH_METHODS) connection[method] = originals[method]
80
+ })
81
+ console.log(`[dsh-local-no-auth] active: browser token/cookie checks bypassed; URLs printed clean (bind host "${bindHost}", loopback-only)`)
82
+ }
package/package.json ADDED
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "@falling-ts/dsh-local-no-auth",
3
+ "version": "0.2.0",
4
+ "description": "DSH Cordis plugin: loopback-local no-auth for the dsh web GUI. Replaces the live ctx.connection instance's three authentication methods (requestRejection / authorizeIndex / authenticatedUrl) at runtime so the loopback-bound dsh web instance serves the frontend and /api without a launch token or browser cookie, and prints clean URLs. Refuses to start unless the live webServer bind host is loopback-only (127.0.0.1 / localhost). Touches no upstream source; dispose restores the original methods.",
5
+ "type": "module",
6
+ "main": "./index.js",
7
+ "exports": {
8
+ ".": "./index.js",
9
+ "./package.json": "./package.json"
10
+ },
11
+ "files": [
12
+ "index.js",
13
+ "cordis.patch.yml"
14
+ ],
15
+ "license": "MIT",
16
+ "peerDependencies": {
17
+ "@deepseek-ai/cordis": "^4.0.2"
18
+ },
19
+ "publishConfig": {
20
+ "access": "public"
21
+ },
22
+ "dsh": {
23
+ "bundle": {
24
+ "patch": "./cordis.patch.yml"
25
+ }
26
+ }
27
+ }