dsh-relay-phone 0.11.1 → 0.11.2
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/package.json +1 -1
- package/src/panel.js +18 -3
- package/src/pump.js +19 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-relay-phone",
|
|
3
|
-
"version": "0.11.
|
|
3
|
+
"version": "0.11.2",
|
|
4
4
|
"description": "Remote-control your local DeepSeek Harness from a phone through your own zero-knowledge relay: phone-first page, QR pairing, end-to-end encryption, approvals, full desktop UI fallback.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.js",
|
package/src/panel.js
CHANGED
|
@@ -89,9 +89,17 @@ export async function registerPanel(ctx, service) {
|
|
|
89
89
|
kind: 'exact',
|
|
90
90
|
path: '/relay-bridge/panel',
|
|
91
91
|
handler: async (req, res) => {
|
|
92
|
-
//
|
|
93
|
-
//
|
|
94
|
-
//
|
|
92
|
+
// Desktop-only. This page hands out the pairing entry URL, which embeds
|
|
93
|
+
// the device PSK (`#k=`); a relayed request is a phone, and a phone that
|
|
94
|
+
// could read it would be able to pair unlimited extra devices. The pump
|
|
95
|
+
// marks relayed requests, so this cannot be forged by the client.
|
|
96
|
+
if (req.headers['x-dsh-relay']) {
|
|
97
|
+
res.writeHead(403, { 'content-type': 'text/plain; charset=utf-8', 'cache-control': 'no-store' })
|
|
98
|
+
res.end('dsh-relay: the management panel is desktop-only. Open it on 127.0.0.1.\n')
|
|
99
|
+
return
|
|
100
|
+
}
|
|
101
|
+
// Same ticket policy as /relay-bridge/api: a local page load mints one so
|
|
102
|
+
// its write actions authenticate.
|
|
95
103
|
if (!service.pairing.sessionForHeaders(req.headers)) {
|
|
96
104
|
if (!isLoopback(req.socket?.remoteAddress ?? '')) {
|
|
97
105
|
res.writeHead(401, { 'content-type': 'text/plain; charset=utf-8' })
|
|
@@ -117,6 +125,13 @@ export async function registerPanel(ctx, service) {
|
|
|
117
125
|
res.end(JSON.stringify(value))
|
|
118
126
|
}
|
|
119
127
|
|
|
128
|
+
// Desktop-only, for the same reason as the page: the snapshot contains
|
|
129
|
+
// the pairing URL with the device PSK, and the relay key can be changed
|
|
130
|
+
// from here.
|
|
131
|
+
if (req.headers['x-dsh-relay']) {
|
|
132
|
+
return reply(403, { ok: false, error: 'desktop-only' })
|
|
133
|
+
}
|
|
134
|
+
|
|
120
135
|
// Pairing-ticket gate. Requests arriving through the relay already
|
|
121
136
|
// carry a valid ticket (the pump refuses ticketless traffic), so this
|
|
122
137
|
// check only ever fires for DIRECT local access — the local user's own
|
package/src/pump.js
CHANGED
|
@@ -365,6 +365,17 @@ export class PumpManager {
|
|
|
365
365
|
return
|
|
366
366
|
}
|
|
367
367
|
|
|
368
|
+
// The plugin's own console is a desktop tool: it prints the pairing URL
|
|
369
|
+
// (which carries the device PSK) and can change the relay key. A phone that
|
|
370
|
+
// reached it would be able to pair unlimited extra devices, so relayed
|
|
371
|
+
// requests never see it, ticketed or not.
|
|
372
|
+
if (stream.relay && info.path.startsWith('/relay-bridge/')) {
|
|
373
|
+
auditLog(`relayed request to the management panel blocked: ${info.path}`)
|
|
374
|
+
this.respondInline(sid, 403, 'Forbidden', 'dsh-relay-phone: the management panel is desktop-only\n')
|
|
375
|
+
this.closeStream(sid)
|
|
376
|
+
return
|
|
377
|
+
}
|
|
378
|
+
|
|
368
379
|
if (!session) {
|
|
369
380
|
auditLog(`unauthenticated ${info.method} ${info.path} -> pairing page`)
|
|
370
381
|
this.respond(sid, 200, 'text/html; charset=utf-8',
|
|
@@ -530,10 +541,12 @@ export class PumpManager {
|
|
|
530
541
|
if (lower.startsWith('connection:')) continue
|
|
531
542
|
if (lower.startsWith('accept-encoding:')) continue
|
|
532
543
|
if (lower.startsWith('x-relay-enc:') || lower.startsWith('x-relay-ct:')) continue
|
|
544
|
+
if (lower.startsWith('x-dsh-relay:')) continue // the pump sets its own
|
|
533
545
|
headers.push(line)
|
|
534
546
|
}
|
|
535
547
|
headers.unshift(`Host: ${target.authority}`)
|
|
536
548
|
headers.unshift(`Origin: ${target.origin}`)
|
|
549
|
+
headers.push('x-dsh-relay: 1')
|
|
537
550
|
if (authCookie) headers.push(`Cookie: ${authCookie}`)
|
|
538
551
|
headers.push(`Content-Type: ${info.headers.get('x-relay-ct') ?? 'text/plain;charset=UTF-8'}`)
|
|
539
552
|
headers.push(`Content-Length: ${plain.length}`)
|
|
@@ -603,6 +616,8 @@ export class PumpManager {
|
|
|
603
616
|
hasHost = true
|
|
604
617
|
} else if (/^origin:/i.test(line)) {
|
|
605
618
|
rewritten.push(`Origin: ${target.origin}`)
|
|
619
|
+
} else if (/^x-dsh-relay:/i.test(line)) {
|
|
620
|
+
// Dropped: the pump adds its own marker below.
|
|
606
621
|
} else if (/^cookie:/i.test(line)) {
|
|
607
622
|
// The phone's relay ticket authenticates it to the pump, not to DSH;
|
|
608
623
|
// upstream speaks the browser-session credential instead.
|
|
@@ -621,6 +636,10 @@ export class PumpManager {
|
|
|
621
636
|
}
|
|
622
637
|
if (!hasHost) rewritten.unshift(`Host: ${target.authority}`)
|
|
623
638
|
if (!isUpgrade && !hasConnection) rewritten.push('Connection: close')
|
|
639
|
+
// Mark the request as relayed. The plugin's own console uses this to refuse
|
|
640
|
+
// phone-side access: it hands out pairing links, which embed the device PSK.
|
|
641
|
+
// Any inbound copy was dropped in the loop above, so a client cannot forge it.
|
|
642
|
+
rewritten.push('x-dsh-relay: 1')
|
|
624
643
|
if (authCookie) rewritten.push(`Cookie: ${authCookie}`)
|
|
625
644
|
const out = `${lines[0]}\r\n${rewritten.join('\r\n')}\r\n\r\n`
|
|
626
645
|
|