@camstack/server 1.1.60 → 1.1.62
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/dist/api/core/auth.router.js +15 -2
- package/dist/api/trpc/client-ip.js +132 -29
- package/dist/api/trpc/generated-cap-mounts.js +26 -1
- package/dist/api/trpc/generated-cap-routers.js +1186 -748
- package/dist/api/trpc/trpc.router.js +81 -17
- package/package.json +14 -14
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.enrichInputWithUserAgent = enrichInputWithUserAgent;
|
|
4
|
+
exports.enrichInputWithRelayClass = enrichInputWithRelayClass;
|
|
4
5
|
exports.wrapWebrtcSessionProviderWithRelay = wrapWebrtcSessionProviderWithRelay;
|
|
5
6
|
exports.buildAppRouter = buildAppRouter;
|
|
6
7
|
const addon_settings_router_js_1 = require("../core/addon-settings.router.js");
|
|
@@ -39,30 +40,93 @@ function enrichInputWithUserAgent(input, userAgent) {
|
|
|
39
40
|
};
|
|
40
41
|
}
|
|
41
42
|
/**
|
|
42
|
-
*
|
|
43
|
+
* Operator gate for FORCING TURN-relay-only ICE on genuinely-remote viewers.
|
|
44
|
+
* DEFAULT OFF.
|
|
43
45
|
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
* is fixed.
|
|
46
|
+
* The forced relay↔relay path is disabled by default because it was NEVER
|
|
47
|
+
* validated to deliver media. It was originally disabled on 2026-05-26 after a
|
|
48
|
+
* connected relay session produced a black picture — ICE/DTLS completed but no
|
|
49
|
+
* decodable media was forwarded over the relay (a MEDIA-FORWARD reliability
|
|
50
|
+
* bug, not a signalling one). The later werift+0.22.9 patch
|
|
51
|
+
* (`patches/werift+0.22.9.patch`) fixed a DIFFERENT bug — a host/srflx
|
|
52
|
+
* candidate LEAK in the forced-relay SDP — and does nothing for relay
|
|
53
|
+
* media-forward, so the connected-but-black regression remains unproven-fixed.
|
|
53
54
|
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
55
|
+
* Until an operator confirms that ONE real remote relay session (4G /
|
|
56
|
+
* Cloudflare-tunnel) actually delivers media, remote viewers stay on the same
|
|
57
|
+
* `relayOnly:false` path as everyone else. Set `CAMSTACK_FORCE_RELAY_REMOTE=1`
|
|
58
|
+
* (or `true`) to re-enable remote→relay once validated; the broker's pairClass
|
|
59
|
+
* logging is the instrument for that validation.
|
|
60
|
+
*/
|
|
61
|
+
const FORCE_RELAY_REMOTE = process.env.CAMSTACK_FORCE_RELAY_REMOTE === '1' ||
|
|
62
|
+
process.env.CAMSTACK_FORCE_RELAY_REMOTE === 'true';
|
|
63
|
+
/**
|
|
64
|
+
* Inject the SERVER-computed `relayOnly` ICE override.
|
|
65
|
+
*
|
|
66
|
+
* The client's three-way classification (`lan | vpn | remote`, see
|
|
67
|
+
* `client-ip.ts`) is ALWAYS computed by the caller and passed here so the
|
|
68
|
+
* broker's pairClass logging can observe the viewer class for every session.
|
|
69
|
+
* Whether that class actually forces a relay is gated by
|
|
70
|
+
* `FORCE_RELAY_REMOTE` (default OFF — see above):
|
|
71
|
+
* - gate OFF → `relayOnly: false` for ALL classes (today's effective,
|
|
72
|
+
* validated behaviour: nobody is forced onto the unproven relay path).
|
|
73
|
+
* - gate ON → `deriveRelayOnly(clientClass)` — only genuinely-remote
|
|
74
|
+
* viewers (public / CGNAT-4G / Cloudflare-tunnel) get `relayOnly: true`;
|
|
75
|
+
* `lan` and `vpn` (Tailscale) keep the low-latency direct host/srflx path.
|
|
76
|
+
*
|
|
77
|
+
* When the class is `null` (mesh-originated call — no request context) the
|
|
78
|
+
* input passes through UNCHANGED so a trusted addon-set `relayOnly` (e.g.
|
|
79
|
+
* Alexa) is never clobbered. Any client-supplied `relayOnly` on a
|
|
80
|
+
* request-bound call IS overwritten — the hub trusts only the request context.
|
|
81
|
+
*
|
|
82
|
+
* Immutable — builds a NEW input object, never mutates the caller's.
|
|
83
|
+
*/
|
|
84
|
+
function enrichInputWithRelayClass(input, clientClass) {
|
|
85
|
+
if (clientClass === null)
|
|
86
|
+
return input;
|
|
87
|
+
const relayOnly = FORCE_RELAY_REMOTE ? (0, client_ip_js_1.deriveRelayOnly)(clientClass) : false;
|
|
88
|
+
return { ...input, relayOnly };
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Per-request wrapper around the resolved `webrtc-session` broker singleton.
|
|
92
|
+
*
|
|
93
|
+
* Two SERVER-side enrichments, both read from the tRPC request context (the
|
|
94
|
+
* broker is a forked addon that cannot see the HTTP request):
|
|
95
|
+
* 1. `relayOnly` — the ICE policy. The client is classified three ways
|
|
96
|
+
* (`lan | vpn | remote`, 2026-07-15): Tailscale (`vpn`) is distinguished
|
|
97
|
+
* from genuine internet (`remote`) and Cloudflare Tunnel's
|
|
98
|
+
* `CF-Connecting-IP` on the loopback connector path is honoured, closing
|
|
99
|
+
* the two blind spots the boolean classifier had (a tailnet client on the
|
|
100
|
+
* hub's own 100.64/10 address is `vpn`; a Cloudflare-tunneled viewer with
|
|
101
|
+
* a loopback socket peer is `remote`, not misread as LAN). The class is
|
|
102
|
+
* ALWAYS computed and threaded so the broker's pairClass logging can
|
|
103
|
+
* observe it — but FORCING remote viewers onto a relay↔relay path is
|
|
104
|
+
* GATED OFF by default (`CAMSTACK_FORCE_RELAY_REMOTE`, see
|
|
105
|
+
* `enrichInputWithRelayClass`).
|
|
106
|
+
*
|
|
107
|
+
* Why gated: the forced-relay MEDIA-FORWARD path was disabled on
|
|
108
|
+
* 2026-05-26 because a connected relay session delivered NO decodable
|
|
109
|
+
* media (ICE/DTLS up, picture black). The werift+0.22.9 patch
|
|
110
|
+
* (`patches/werift+0.22.9.patch`) fixed a DIFFERENT bug — the forced-relay
|
|
111
|
+
* SDP host/srflx candidate LEAK — and does NOT address relay
|
|
112
|
+
* media-forward, so the connected-but-black regression is still unproven-
|
|
113
|
+
* fixed. The flag lets the operator re-enable remote→relay only after
|
|
114
|
+
* validating that ONE real 4G / Cloudflare-tunnel relay session actually
|
|
115
|
+
* delivers media (the broker pairClass logging is the instrument). Until
|
|
116
|
+
* then every class gets `relayOnly:false`.
|
|
117
|
+
* 2. `consumerAttribution.userAgent` — the browser UA for the broker's
|
|
118
|
+
* client list.
|
|
119
|
+
*
|
|
120
|
+
* All OTHER methods delegate straight through — auth, the remote-proxy
|
|
121
|
+
* factory and every signaling behaviour are untouched.
|
|
59
122
|
*/
|
|
60
123
|
function wrapWebrtcSessionProviderWithRelay(provider, ctx) {
|
|
61
124
|
const userAgent = (0, client_ip_js_1.extractUserAgent)(ctx.req);
|
|
125
|
+
const clientClass = (0, client_ip_js_1.classifyClientRequest)(ctx.req);
|
|
62
126
|
return {
|
|
63
127
|
...provider,
|
|
64
|
-
createSession: (input) => provider.createSession(enrichInputWithUserAgent(input, userAgent)),
|
|
65
|
-
handleOffer: (input) => provider.handleOffer(enrichInputWithUserAgent(input, userAgent)),
|
|
128
|
+
createSession: (input) => provider.createSession(enrichInputWithRelayClass(enrichInputWithUserAgent(input, userAgent), clientClass)),
|
|
129
|
+
handleOffer: (input) => provider.handleOffer(enrichInputWithRelayClass(enrichInputWithUserAgent(input, userAgent), clientClass)),
|
|
66
130
|
};
|
|
67
131
|
}
|
|
68
132
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@camstack/server",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.62",
|
|
4
4
|
"private": false,
|
|
5
5
|
"files": [
|
|
6
6
|
"dist",
|
|
@@ -23,19 +23,19 @@
|
|
|
23
23
|
"test:watch": "vitest"
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"@camstack/addon-admin-ui": "1.1.
|
|
27
|
-
"@camstack/addon-advanced-notifier": "1.1.
|
|
28
|
-
"@camstack/addon-auth": "1.1.
|
|
29
|
-
"@camstack/addon-decoder-nodeav": "1.1.
|
|
30
|
-
"@camstack/addon-notifiers": "1.1.
|
|
31
|
-
"@camstack/addon-pipeline": "1.1.
|
|
32
|
-
"@camstack/addon-pipeline-orchestrator": "1.1.
|
|
33
|
-
"@camstack/addon-post-analysis": "1.1.
|
|
34
|
-
"@camstack/sdk": "1.1.
|
|
35
|
-
"@camstack/shm-ring": "1.0.
|
|
36
|
-
"@camstack/system": "1.1.
|
|
37
|
-
"@camstack/types": "1.1.
|
|
38
|
-
"@camstack/ui-library": "1.1.
|
|
26
|
+
"@camstack/addon-admin-ui": "1.1.51",
|
|
27
|
+
"@camstack/addon-advanced-notifier": "1.1.24",
|
|
28
|
+
"@camstack/addon-auth": "1.1.9",
|
|
29
|
+
"@camstack/addon-decoder-nodeav": "1.1.11",
|
|
30
|
+
"@camstack/addon-notifiers": "1.1.23",
|
|
31
|
+
"@camstack/addon-pipeline": "1.1.56",
|
|
32
|
+
"@camstack/addon-pipeline-orchestrator": "1.1.46",
|
|
33
|
+
"@camstack/addon-post-analysis": "1.1.28",
|
|
34
|
+
"@camstack/sdk": "1.1.24",
|
|
35
|
+
"@camstack/shm-ring": "1.0.23",
|
|
36
|
+
"@camstack/system": "1.1.49",
|
|
37
|
+
"@camstack/types": "1.1.44",
|
|
38
|
+
"@camstack/ui-library": "1.1.36",
|
|
39
39
|
"@fastify/compress": "^9.0.0",
|
|
40
40
|
"@fastify/cookie": "^11.0.2",
|
|
41
41
|
"@fastify/multipart": "^10.0.0",
|