@floegence/flowersec-core 0.27.0 → 2.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.
Files changed (268) hide show
  1. package/README.md +91 -22
  2. package/THIRD_PARTY_NOTICES.md +141 -0
  3. package/dist/browser/connectSession.d.ts +13 -0
  4. package/dist/browser/connectSession.js +40 -0
  5. package/dist/browser/index.d.ts +3 -10
  6. package/dist/browser/index.js +2 -5
  7. package/dist/browser/runtimeCapability.d.ts +13 -0
  8. package/dist/browser/runtimeCapability.js +27 -0
  9. package/dist/browser/sessionRuntime.d.ts +2 -0
  10. package/dist/browser/sessionRuntime.js +21 -0
  11. package/dist/browser/webTransportClient.d.ts +8 -0
  12. package/dist/browser/webTransportClient.js +55 -0
  13. package/dist/cli.d.ts +2 -0
  14. package/dist/cli.js +126 -0
  15. package/dist/cliSpendMarker.d.ts +1 -0
  16. package/dist/cliSpendMarker.js +22 -0
  17. package/dist/connectionController.d.ts +54 -0
  18. package/dist/connectionController.js +438 -0
  19. package/dist/connector/adapters/webSocketCandidate.d.ts +7 -0
  20. package/dist/connector/adapters/webSocketCandidate.js +116 -0
  21. package/dist/connector/adapters/webTransportCandidate.d.ts +5 -0
  22. package/dist/connector/adapters/webTransportCandidate.js +73 -0
  23. package/dist/connector/admissionCommit.d.ts +31 -0
  24. package/dist/connector/admissionCommit.js +239 -0
  25. package/dist/connector/sessionAcceptor.d.ts +16 -0
  26. package/dist/connector/sessionAcceptor.js +114 -0
  27. package/dist/connector/sessionConfig.d.ts +3 -0
  28. package/dist/connector/sessionConfig.js +30 -0
  29. package/dist/connector/sessionConnector.d.ts +45 -0
  30. package/dist/connector/sessionConnector.js +603 -0
  31. package/dist/defaults.d.ts +7 -7
  32. package/dist/defaults.js +7 -7
  33. package/dist/facade.d.ts +12 -27
  34. package/dist/facade.js +6 -24
  35. package/dist/node/connectSession.d.ts +21 -0
  36. package/dist/node/connectSession.js +62 -0
  37. package/dist/node/index.d.ts +3 -11
  38. package/dist/node/index.js +2 -8
  39. package/dist/node/runtimeCapability.d.ts +7 -0
  40. package/dist/node/runtimeCapability.js +10 -0
  41. package/dist/node/sessionRuntime.d.ts +2 -0
  42. package/dist/node/sessionRuntime.js +13 -0
  43. package/dist/node/webTransportClient.d.ts +9 -0
  44. package/dist/node/webTransportClient.js +56 -0
  45. package/dist/node/webTransportServer.d.ts +22 -0
  46. package/dist/node/webTransportServer.js +100 -0
  47. package/dist/node/wsFactory.d.ts +3 -1
  48. package/dist/node/wsFactory.js +4 -3
  49. package/dist/proxy/controllerGuard.d.ts +4 -4
  50. package/dist/proxy/controllerGuard.js +83 -177
  51. package/dist/proxy/disableUpstreamServiceWorkerRegister.d.ts +3 -1
  52. package/dist/proxy/disableUpstreamServiceWorkerRegister.js +19 -17
  53. package/dist/proxy/index.d.ts +16 -15
  54. package/dist/proxy/index.js +8 -14
  55. package/dist/proxy/integration.d.ts +23 -71
  56. package/dist/proxy/integration.js +37 -340
  57. package/dist/proxy/registerServiceWorker.d.ts +1 -10
  58. package/dist/proxy/registerServiceWorker.js +43 -105
  59. package/dist/proxy/runtime.d.ts +3 -63
  60. package/dist/proxy/runtime.js +349 -462
  61. package/dist/proxy/scope.d.ts +6 -0
  62. package/dist/proxy/scope.js +131 -0
  63. package/dist/proxy/serviceWorker.d.ts +5 -16
  64. package/dist/proxy/serviceWorker.js +230 -617
  65. package/dist/proxy/stream.d.ts +9 -0
  66. package/dist/proxy/stream.js +40 -0
  67. package/dist/proxy/types.d.ts +78 -30
  68. package/dist/proxy/windowBridge.d.ts +61 -0
  69. package/dist/proxy/windowBridge.js +344 -0
  70. package/dist/proxy/wsPatch.d.ts +4 -14
  71. package/dist/proxy/wsPatch.js +173 -268
  72. package/dist/public/artifact.d.ts +11 -0
  73. package/dist/public/artifact.js +44 -0
  74. package/dist/public/artifactLease.d.ts +10 -0
  75. package/dist/public/artifactLease.js +48 -0
  76. package/dist/public/connectError.d.ts +6 -0
  77. package/dist/public/connectError.js +9 -0
  78. package/dist/public/contract.d.ts +83 -0
  79. package/dist/public/contract.js +17 -0
  80. package/dist/public/streamMetadata.d.ts +10 -0
  81. package/dist/public/streamMetadata.js +40 -0
  82. package/dist/rpc/caller.d.ts +1 -1
  83. package/dist/rpc/client.d.ts +1 -4
  84. package/dist/rpc/client.js +0 -21
  85. package/dist/rpc/rpcProxy.d.ts +1 -1
  86. package/dist/rpc/server.d.ts +1 -1
  87. package/dist/rpc/validate.d.ts +1 -1
  88. package/dist/rpc/wire.d.ts +15 -0
  89. package/dist/runtime/errors.d.ts +6 -0
  90. package/dist/runtime/errors.js +10 -0
  91. package/dist/transport/webSocketAdapter.d.ts +24 -0
  92. package/dist/transport/webSocketAdapter.js +234 -0
  93. package/dist/transport/webTransportAdapter.d.ts +27 -0
  94. package/dist/transport/webTransportAdapter.js +519 -0
  95. package/dist/utils/errors.d.ts +2 -20
  96. package/dist/utils/errors.js +73 -20
  97. package/dist/v2/admissionError.d.ts +4 -0
  98. package/dist/v2/admissionError.js +8 -0
  99. package/dist/v2/artifact.d.ts +128 -0
  100. package/dist/v2/artifact.js +1126 -0
  101. package/dist/v2/artifactLease.d.ts +1 -0
  102. package/dist/v2/artifactLease.js +7 -0
  103. package/dist/v2/capability.d.ts +29 -0
  104. package/dist/v2/capability.js +190 -0
  105. package/dist/v2/carrier.d.ts +79 -0
  106. package/dist/v2/carrier.js +405 -0
  107. package/dist/v2/contract.d.ts +37 -0
  108. package/dist/v2/contract.js +1 -0
  109. package/dist/v2/handshake.d.ts +80 -0
  110. package/dist/v2/handshake.js +470 -0
  111. package/dist/v2/opaqueArtifact.d.ts +1 -0
  112. package/dist/v2/opaqueArtifact.js +2 -0
  113. package/dist/v2/protocol.d.ts +99 -0
  114. package/dist/v2/protocol.js +739 -0
  115. package/dist/v2/publicSession.d.ts +1 -0
  116. package/dist/v2/publicSession.js +179 -0
  117. package/dist/v2/retryDisposition.d.ts +16 -0
  118. package/dist/v2/retryDisposition.js +30 -0
  119. package/dist/v2/session.d.ts +277 -0
  120. package/dist/v2/session.js +1879 -0
  121. package/dist/v2/streamLifetimeLedger.d.ts +31 -0
  122. package/dist/v2/streamLifetimeLedger.js +131 -0
  123. package/dist/v2/streamMetadata.d.ts +1 -0
  124. package/dist/v2/streamMetadata.js +7 -0
  125. package/dist/v2/unreliableMessage.d.ts +43 -0
  126. package/dist/v2/unreliableMessage.js +252 -0
  127. package/dist/vendor/tr46.d.ts +10 -0
  128. package/dist/vendor/tr46.js +501 -0
  129. package/dist/ws-client/binaryTransport.d.ts +0 -3
  130. package/dist/ws-client/binaryTransport.js +15 -39
  131. package/dist/yamux/errors.d.ts +0 -4
  132. package/dist/yamux/errors.js +0 -9
  133. package/dist/yamux/session.d.ts +0 -5
  134. package/dist/yamux/session.js +4 -70
  135. package/dist/yamux/stream.d.ts +1 -0
  136. package/dist/yamux/stream.js +4 -0
  137. package/package.json +29 -81
  138. package/sbom/cyclonedx.json +2764 -0
  139. package/sbom/spdx.json +1947 -0
  140. package/YAMUX_ALIGNMENT.md +0 -127
  141. package/dist/_examples/flowersec/demo/v1.facade.gen.d.ts +0 -12
  142. package/dist/_examples/flowersec/demo/v1.facade.gen.js +0 -15
  143. package/dist/_examples/flowersec/demo/v1.gen.d.ts +0 -16
  144. package/dist/_examples/flowersec/demo/v1.gen.js +0 -86
  145. package/dist/_examples/flowersec/demo/v1.rpc.gen.d.ts +0 -11
  146. package/dist/_examples/flowersec/demo/v1.rpc.gen.js +0 -22
  147. package/dist/browser/connect.d.ts +0 -13
  148. package/dist/browser/connect.js +0 -31
  149. package/dist/browser/reconnectConfig.d.ts +0 -15
  150. package/dist/browser/reconnectConfig.js +0 -20
  151. package/dist/client-connect/common.d.ts +0 -26
  152. package/dist/client-connect/common.js +0 -167
  153. package/dist/client-connect/connectCore.d.ts +0 -61
  154. package/dist/client-connect/connectCore.js +0 -572
  155. package/dist/client-connect/contract.d.ts +0 -8
  156. package/dist/client-connect/contract.js +0 -51
  157. package/dist/client-connect/termination.d.ts +0 -6
  158. package/dist/client-connect/termination.js +0 -7
  159. package/dist/client-connect/transportSecurity.d.ts +0 -27
  160. package/dist/client-connect/transportSecurity.js +0 -199
  161. package/dist/client-connect/tunnelAttachCloseReason.d.ts +0 -3
  162. package/dist/client-connect/tunnelAttachCloseReason.js +0 -23
  163. package/dist/client.d.ts +0 -23
  164. package/dist/connect/artifact.d.ts +0 -36
  165. package/dist/connect/artifact.js +0 -213
  166. package/dist/connect/resolveArtifact.d.ts +0 -22
  167. package/dist/connect/resolveArtifact.js +0 -87
  168. package/dist/controlplane/channelInit.d.ts +0 -27
  169. package/dist/controlplane/channelInit.js +0 -116
  170. package/dist/controlplane/http.d.ts +0 -16
  171. package/dist/controlplane/http.js +0 -59
  172. package/dist/controlplane/index.d.ts +0 -6
  173. package/dist/controlplane/index.js +0 -5
  174. package/dist/controlplane/issuer.d.ts +0 -18
  175. package/dist/controlplane/issuer.js +0 -113
  176. package/dist/controlplane/request.d.ts +0 -51
  177. package/dist/controlplane/request.js +0 -273
  178. package/dist/controlplane/token.d.ts +0 -33
  179. package/dist/controlplane/token.js +0 -119
  180. package/dist/direct-client/connect.d.ts +0 -9
  181. package/dist/direct-client/connect.js +0 -90
  182. package/dist/direct-client/index.d.ts +0 -1
  183. package/dist/direct-client/index.js +0 -1
  184. package/dist/e2ee/constants.d.ts +0 -9
  185. package/dist/e2ee/constants.js +0 -18
  186. package/dist/e2ee/errors.d.ts +0 -5
  187. package/dist/e2ee/errors.js +0 -8
  188. package/dist/e2ee/framing.d.ts +0 -12
  189. package/dist/e2ee/framing.js +0 -57
  190. package/dist/e2ee/handshake.d.ts +0 -88
  191. package/dist/e2ee/handshake.js +0 -353
  192. package/dist/e2ee/index.d.ts +0 -7
  193. package/dist/e2ee/index.js +0 -7
  194. package/dist/e2ee/kdf.d.ts +0 -15
  195. package/dist/e2ee/kdf.js +0 -39
  196. package/dist/e2ee/record.d.ts +0 -11
  197. package/dist/e2ee/record.js +0 -80
  198. package/dist/e2ee/secureChannel.d.ts +0 -93
  199. package/dist/e2ee/secureChannel.js +0 -342
  200. package/dist/e2ee/transcript.d.ts +0 -23
  201. package/dist/e2ee/transcript.js +0 -31
  202. package/dist/endpoint/index.d.ts +0 -84
  203. package/dist/endpoint/index.js +0 -557
  204. package/dist/endpoint/node.d.ts +0 -9
  205. package/dist/endpoint/node.js +0 -51
  206. package/dist/gen/flowersec/controlplane/v1.gen.d.ts +0 -36
  207. package/dist/gen/flowersec/controlplane/v1.gen.js +0 -135
  208. package/dist/gen/flowersec/direct/v1.gen.d.ts +0 -21
  209. package/dist/gen/flowersec/direct/v1.gen.js +0 -101
  210. package/dist/gen/flowersec/e2ee/v1.gen.d.ts +0 -68
  211. package/dist/gen/flowersec/e2ee/v1.gen.js +0 -194
  212. package/dist/gen/flowersec/rpc/v1.gen.d.ts +0 -30
  213. package/dist/gen/flowersec/rpc/v1.gen.js +0 -107
  214. package/dist/gen/flowersec/tunnel/v1.gen.d.ts +0 -23
  215. package/dist/gen/flowersec/tunnel/v1.gen.js +0 -104
  216. package/dist/index.d.ts +0 -22
  217. package/dist/index.js +0 -22
  218. package/dist/node/connect.d.ts +0 -10
  219. package/dist/node/connect.js +0 -29
  220. package/dist/node/reconnectConfig.d.ts +0 -16
  221. package/dist/node/reconnectConfig.js +0 -20
  222. package/dist/observability/index.d.ts +0 -1
  223. package/dist/observability/index.js +0 -1
  224. package/dist/observability/observer.d.ts +0 -52
  225. package/dist/observability/observer.js +0 -314
  226. package/dist/proxy/appWindow.d.ts +0 -37
  227. package/dist/proxy/appWindow.js +0 -273
  228. package/dist/proxy/bootstrap.d.ts +0 -35
  229. package/dist/proxy/bootstrap.js +0 -119
  230. package/dist/proxy/constants.d.ts +0 -7
  231. package/dist/proxy/constants.js +0 -8
  232. package/dist/proxy/controllerWindow.d.ts +0 -12
  233. package/dist/proxy/controllerWindow.js +0 -384
  234. package/dist/proxy/cookieJar.d.ts +0 -7
  235. package/dist/proxy/cookieJar.js +0 -136
  236. package/dist/proxy/headerPolicy.d.ts +0 -14
  237. package/dist/proxy/headerPolicy.js +0 -134
  238. package/dist/proxy/portStream.d.ts +0 -5
  239. package/dist/proxy/portStream.js +0 -256
  240. package/dist/proxy/preset.d.ts +0 -29
  241. package/dist/proxy/preset.js +0 -111
  242. package/dist/proxy/runtimeScope.d.ts +0 -57
  243. package/dist/proxy/runtimeScope.js +0 -218
  244. package/dist/proxy/server.d.ts +0 -28
  245. package/dist/proxy/server.js +0 -833
  246. package/dist/proxy/windowBridgeProtocol.d.ts +0 -65
  247. package/dist/proxy/windowBridgeProtocol.js +0 -11
  248. package/dist/reconnect/artifactControlplane.d.ts +0 -16
  249. package/dist/reconnect/artifactControlplane.js +0 -35
  250. package/dist/reconnect/index.d.ts +0 -36
  251. package/dist/reconnect/index.js +0 -388
  252. package/dist/rpc-proxy/index.d.ts +0 -1
  253. package/dist/rpc-proxy/index.js +0 -1
  254. package/dist/rpc-proxy/rpcProxy.d.ts +0 -1
  255. package/dist/rpc-proxy/rpcProxy.js +0 -1
  256. package/dist/streamhello/index.d.ts +0 -1
  257. package/dist/streamhello/index.js +0 -1
  258. package/dist/streamhello/streamHello.d.ts +0 -3
  259. package/dist/streamhello/streamHello.js +0 -13
  260. package/dist/streamio/index.d.ts +0 -32
  261. package/dist/streamio/index.js +0 -87
  262. package/dist/tunnel-client/connect.d.ts +0 -9
  263. package/dist/tunnel-client/connect.js +0 -147
  264. package/dist/tunnel-client/index.d.ts +0 -1
  265. package/dist/tunnel-client/index.js +0 -1
  266. package/dist/ws/index.d.ts +0 -1
  267. package/dist/ws/index.js +0 -1
  268. /package/dist/{client.js → rpc/wire.js} +0 -0
@@ -1,557 +0,0 @@
1
- import { Role as ControlRole, assertChannelInitGrant } from "../gen/flowersec/controlplane/v1.gen.js";
2
- import { Role as TunnelRole } from "../gen/flowersec/tunnel/v1.gen.js";
3
- import { withAbortAndTimeout } from "../client-connect/common.js";
4
- import { assertTunnelGrantContract, assertValidPSK, prepareChannelId } from "../client-connect/contract.js";
5
- import { enforceTransportSecurity } from "../client-connect/transportSecurity.js";
6
- import { SDK_DEFAULTS } from "../defaults.js";
7
- import { serverHandshake, ServerHandshakeCache } from "../e2ee/handshake.js";
8
- import { decodeHandshakeFrame } from "../e2ee/framing.js";
9
- import { HANDSHAKE_TYPE_INIT, PROTOCOL_VERSION } from "../e2ee/constants.js";
10
- import { readStreamHello, writeStreamHello } from "../streamhello/streamHello.js";
11
- import { ByteReader } from "../yamux/byteReader.js";
12
- import { isYamuxPingTimeoutError } from "../yamux/errors.js";
13
- import { YamuxSession } from "../yamux/session.js";
14
- import { RpcServer } from "../rpc/server.js";
15
- import { base64urlDecode, base64urlEncode } from "../utils/base64url.js";
16
- import { AbortError, FlowersecError, TimeoutError, throwIfAborted } from "../utils/errors.js";
17
- import { WebSocketBinaryTransport } from "../ws-client/binaryTransport.js";
18
- export class Session {
19
- secure;
20
- mux;
21
- path;
22
- endpointInstanceId;
23
- streams = [];
24
- waiters = [];
25
- terminalError;
26
- constructor(path, secure, mux, endpointInstanceId) {
27
- this.secure = secure;
28
- this.mux = mux;
29
- this.path = path;
30
- this.endpointInstanceId = endpointInstanceId;
31
- }
32
- static create(path, secure, options = {}) {
33
- let session;
34
- const mux = new YamuxSession({
35
- read: () => secure.read(),
36
- write: (bytes) => secure.write(bytes),
37
- close: () => secure.close(),
38
- }, {
39
- client: false,
40
- ...(options.yamuxLimits === undefined ? {} : { limits: options.yamuxLimits }),
41
- onIncomingStream: (stream) => session.pushStream(stream),
42
- onTerminal: (error) => session.fail(error),
43
- });
44
- session = new Session(path, secure, mux, options.endpointInstanceId);
45
- return session;
46
- }
47
- async openStream(kind, options = {}) {
48
- const streamKind = normalizeStreamKind(kind, this.path);
49
- const stream = await this.mux.openStream(options);
50
- try {
51
- await writeStreamHello((bytes) => stream.write(bytes), streamKind);
52
- return stream;
53
- }
54
- catch (error) {
55
- await stream.reset(asError(error));
56
- throw new FlowersecError({ path: this.path, stage: "rpc", code: "stream_hello_failed", message: "failed to write stream hello", cause: error });
57
- }
58
- }
59
- async acceptStream(options = {}) {
60
- const stream = await this.acceptRawStream(options.signal);
61
- try {
62
- const reader = new ByteReader(() => stream.read());
63
- const hello = await readStreamHello((length) => reader.readExactly(length));
64
- return { kind: hello.kind, stream };
65
- }
66
- catch (error) {
67
- await stream.reset(asError(error));
68
- throw new FlowersecError({ path: this.path, stage: "rpc", code: "stream_hello_failed", message: "failed to read stream hello", cause: error });
69
- }
70
- }
71
- async serveRPC(router, options = {}) {
72
- while (true) {
73
- const accepted = await this.acceptStream(options.signal === undefined ? {} : { signal: options.signal });
74
- if (accepted.kind !== "rpc") {
75
- await accepted.stream.reset(new Error(`unexpected stream kind ${accepted.kind}`));
76
- continue;
77
- }
78
- const reader = new ByteReader(() => accepted.stream.read());
79
- const server = new RpcServer({
80
- readExactly: (length) => reader.readExactly(length),
81
- write: (bytes) => accepted.stream.write(bytes),
82
- close: (error) => { void accepted.stream.reset(asError(error)); },
83
- }, options, router);
84
- await server.serve(options.signal);
85
- return;
86
- }
87
- }
88
- async probeLiveness(timeoutMs = SDK_DEFAULTS.transport.handshakeTimeoutMs) {
89
- try {
90
- return await this.mux.probeLiveness(timeoutMs);
91
- }
92
- catch (error) {
93
- throw new FlowersecError({
94
- path: this.path,
95
- stage: "yamux",
96
- code: isYamuxPingTimeoutError(error) ? "timeout" : "ping_failed",
97
- message: "endpoint liveness probe failed",
98
- cause: error,
99
- });
100
- }
101
- }
102
- async rekey() {
103
- try {
104
- await this.secure.rekeyNow();
105
- }
106
- catch (error) {
107
- throw new FlowersecError({ path: this.path, stage: "secure", code: "rekey_failed", message: "endpoint rekey failed", cause: error });
108
- }
109
- }
110
- close() {
111
- this.fail(new Error("endpoint session closed"));
112
- this.mux.close();
113
- this.secure.close();
114
- }
115
- pushStream(stream) {
116
- const waiter = this.waiters.shift();
117
- if (waiter == null) {
118
- this.streams.push(stream);
119
- return;
120
- }
121
- cleanupWaiter(waiter);
122
- waiter.resolve(stream);
123
- }
124
- acceptRawStream(signal) {
125
- if (signal?.aborted)
126
- return Promise.reject(new AbortError("accept stream aborted"));
127
- if (this.terminalError != null)
128
- return Promise.reject(this.terminalError);
129
- const stream = this.streams.shift();
130
- if (stream != null)
131
- return Promise.resolve(stream);
132
- return new Promise((resolve, reject) => {
133
- const waiter = { resolve, reject, ...(signal === undefined ? {} : { signal }) };
134
- waiter.onAbort = () => {
135
- const index = this.waiters.indexOf(waiter);
136
- if (index >= 0)
137
- this.waiters.splice(index, 1);
138
- cleanupWaiter(waiter);
139
- reject(new AbortError("accept stream aborted"));
140
- };
141
- signal?.addEventListener("abort", waiter.onAbort, { once: true });
142
- this.waiters.push(waiter);
143
- });
144
- }
145
- fail(error) {
146
- if (this.terminalError != null)
147
- return;
148
- this.terminalError = error;
149
- for (const waiter of this.waiters.splice(0)) {
150
- cleanupWaiter(waiter);
151
- waiter.reject(error);
152
- }
153
- }
154
- }
155
- export async function acceptDirect(websocket, handshake, options = {}) {
156
- const handshakeTimeoutMs = options.handshakeTimeoutMs ?? SDK_DEFAULTS.transport.handshakeTimeoutMs;
157
- if (!Number.isFinite(handshakeTimeoutMs) || handshakeTimeoutMs < 0) {
158
- throw new FlowersecError({
159
- path: "direct",
160
- stage: "validate",
161
- code: "invalid_option",
162
- message: "handshakeTimeoutMs must be a non-negative number",
163
- });
164
- }
165
- await enforceIncomingDirectTransport(options);
166
- const deadline = createHandshakeDeadline(handshakeTimeoutMs);
167
- const transport = new WebSocketBinaryTransport(websocket, webSocketTransportOptions(options));
168
- return await establishSession("direct", transport, handshake, options, undefined, deadline);
169
- }
170
- export async function acceptDirectResolved(websocket, resolver, options = {}) {
171
- const handshakeTimeoutMs = options.handshakeTimeoutMs ?? SDK_DEFAULTS.transport.handshakeTimeoutMs;
172
- if (!Number.isFinite(handshakeTimeoutMs) || handshakeTimeoutMs < 0) {
173
- throw new FlowersecError({
174
- path: "direct",
175
- stage: "validate",
176
- code: "invalid_option",
177
- message: "handshakeTimeoutMs must be a non-negative number",
178
- });
179
- }
180
- await enforceIncomingDirectTransport(options);
181
- const deadline = createHandshakeDeadline(handshakeTimeoutMs);
182
- const transport = new WebSocketBinaryTransport(websocket, webSocketTransportOptions(options));
183
- let first;
184
- try {
185
- first = await runHandshakeStep(deadline, options.signal, () => transport.readBinary({
186
- ...(options.signal === undefined ? {} : { signal: options.signal }),
187
- timeoutMs: remainingHandshakeTimeoutMs(deadline),
188
- }));
189
- }
190
- catch (error) {
191
- transport.close();
192
- throw endpointHandshakeError("direct", error, "failed to read handshake init");
193
- }
194
- let init;
195
- let channelId;
196
- try {
197
- const decoded = decodeHandshakeFrame(first, options.maxHandshakePayload ?? SDK_DEFAULTS.e2ee.maxHandshakePayloadBytes);
198
- if (decoded.handshakeType !== HANDSHAKE_TYPE_INIT)
199
- throw new Error("expected handshake init");
200
- init = JSON.parse(new TextDecoder().decode(decoded.payloadJsonUtf8));
201
- if (init.version !== PROTOCOL_VERSION || init.role !== 1 || (init.suite !== 1 && init.suite !== 2))
202
- throw new Error("invalid handshake init");
203
- channelId = prepareChannelId(init.channel_id, "direct");
204
- if (channelId !== init.channel_id) {
205
- throw new FlowersecError({
206
- path: "direct",
207
- stage: "validate",
208
- code: "invalid_input",
209
- message: "channel_id must not have leading or trailing whitespace",
210
- });
211
- }
212
- }
213
- catch (error) {
214
- transport.close();
215
- if (error instanceof FlowersecError)
216
- throw error;
217
- throw new FlowersecError({ path: "direct", stage: "handshake", code: "handshake_failed", message: "invalid handshake init", cause: error });
218
- }
219
- let credential;
220
- try {
221
- credential = await runHandshakeStep(deadline, options.signal, () => resolver({
222
- channelId,
223
- version: init.version,
224
- suite: init.suite,
225
- clientFeatures: init.client_features >>> 0,
226
- }));
227
- }
228
- catch (error) {
229
- transport.close();
230
- const interrupted = endpointHandshakeInterruption("direct", error);
231
- if (interrupted != null)
232
- throw interrupted;
233
- throw new FlowersecError({ path: "direct", stage: "validate", code: "resolve_failed", message: "credential resolution failed", cause: error });
234
- }
235
- const replay = new PrefetchedTransport(transport, first);
236
- return await establishSession("direct", replay, {
237
- channelId,
238
- suite: init.suite,
239
- ...credential,
240
- }, options, undefined, deadline);
241
- }
242
- export async function connectTunnel(grantInput, options) {
243
- let grant;
244
- try {
245
- grant = assertChannelInitGrant(unwrapServerGrant(grantInput));
246
- }
247
- catch (error) {
248
- throw new FlowersecError({ path: "tunnel", stage: "validate", code: "invalid_input", message: "invalid ChannelInitGrant", cause: error });
249
- }
250
- assertTunnelGrantContract(grant, ControlRole.Role_server);
251
- const channelId = prepareChannelId(grant.channel_id, "tunnel");
252
- const tunnelUrl = grant.tunnel_url.trim();
253
- if (tunnelUrl === "")
254
- throw new FlowersecError({ path: "tunnel", stage: "validate", code: "missing_tunnel_url", message: "missing tunnel_url" });
255
- if (grant.token.trim() === "")
256
- throw new FlowersecError({ path: "tunnel", stage: "validate", code: "missing_token", message: "missing token" });
257
- if (grant.channel_init_expire_at_unix_s <= 0)
258
- throw new FlowersecError({ path: "tunnel", stage: "validate", code: "missing_init_exp", message: "missing channel init expiry" });
259
- const origin = options.origin.trim();
260
- if (origin === "")
261
- throw new FlowersecError({ path: "tunnel", stage: "validate", code: "missing_origin", message: "missing origin" });
262
- const connectTimeoutMs = options.connectTimeoutMs ?? SDK_DEFAULTS.transport.connectTimeoutMs;
263
- if (!Number.isFinite(connectTimeoutMs) || connectTimeoutMs < 0) {
264
- throw new FlowersecError({
265
- path: "tunnel",
266
- stage: "validate",
267
- code: "invalid_option",
268
- message: "connectTimeoutMs must be a non-negative number",
269
- });
270
- }
271
- const handshakeTimeoutMs = options.handshakeTimeoutMs ?? SDK_DEFAULTS.transport.handshakeTimeoutMs;
272
- if (!Number.isFinite(handshakeTimeoutMs) || handshakeTimeoutMs < 0) {
273
- throw new FlowersecError({
274
- path: "tunnel",
275
- stage: "validate",
276
- code: "invalid_option",
277
- message: "handshakeTimeoutMs must be a non-negative number",
278
- });
279
- }
280
- const psk = assertValidPSK(grant.e2ee_psk_b64u, "tunnel");
281
- await enforceTransportSecurity({ rawUrl: tunnelUrl, path: "tunnel", ...(options.transportSecurityPolicy === undefined ? {} : { policy: options.transportSecurityPolicy }) });
282
- const endpointInstanceId = normalizeEndpointInstanceId(options.endpointInstanceId);
283
- let transport;
284
- try {
285
- throwIfAborted(options.signal, "connect aborted");
286
- const websocket = options.wsFactory(tunnelUrl, origin);
287
- transport = new WebSocketBinaryTransport(websocket, webSocketTransportOptions(options));
288
- await waitForOpen(websocket, connectTimeoutMs, options.signal);
289
- throwIfAborted(options.signal, "connect aborted");
290
- const attach = {
291
- v: 1,
292
- channel_id: channelId,
293
- role: TunnelRole.Role_server,
294
- token: grant.token.trim(),
295
- endpoint_instance_id: endpointInstanceId,
296
- };
297
- websocket.send(JSON.stringify(attach));
298
- return await establishSession("tunnel", transport, {
299
- channelId,
300
- suite: grant.default_suite,
301
- psk,
302
- initExpireAtUnixS: grant.channel_init_expire_at_unix_s,
303
- }, options, endpointInstanceId);
304
- }
305
- catch (error) {
306
- transport?.close();
307
- if (error instanceof FlowersecError)
308
- throw error;
309
- if (error instanceof TimeoutError) {
310
- throw new FlowersecError({ path: "tunnel", stage: "connect", code: "timeout", message: "endpoint tunnel connect timed out", cause: error });
311
- }
312
- if (error instanceof AbortError) {
313
- throw new FlowersecError({ path: "tunnel", stage: "connect", code: "canceled", message: "endpoint tunnel connect canceled", cause: error });
314
- }
315
- throw new FlowersecError({ path: "tunnel", stage: "connect", code: "dial_failed", message: "endpoint tunnel connect failed", cause: error });
316
- }
317
- }
318
- async function establishSession(path, transport, handshake, options, endpointInstanceId, deadline) {
319
- let psk;
320
- try {
321
- psk = normalizePSK(handshake.psk, path);
322
- }
323
- catch (error) {
324
- transport.close();
325
- throw error;
326
- }
327
- try {
328
- const startHandshake = () => serverHandshake(transport, options.handshakeCache ?? new ServerHandshakeCache(), {
329
- channelId: prepareChannelId(handshake.channelId, path),
330
- suite: handshake.suite,
331
- psk,
332
- serverFeatures: options.serverFeatures ?? 0,
333
- initExpireAtUnixS: handshake.initExpireAtUnixS,
334
- clockSkewSeconds: Math.ceil((options.handshakeClockSkewMs ?? SDK_DEFAULTS.transport.handshakeClockSkewMs) / 1000),
335
- maxHandshakePayload: options.maxHandshakePayload ?? SDK_DEFAULTS.e2ee.maxHandshakePayloadBytes,
336
- maxRecordBytes: options.maxRecordBytes ?? SDK_DEFAULTS.e2ee.maxRecordBytes,
337
- outboundRecordChunkBytes: options.outboundRecordChunkBytes ?? SDK_DEFAULTS.e2ee.outboundRecordChunkBytes,
338
- maxBufferedBytes: options.maxBufferedBytes ?? SDK_DEFAULTS.e2ee.maxInboundBufferedBytes,
339
- maxOutboundBufferedBytes: options.maxOutboundBufferedBytes ?? SDK_DEFAULTS.e2ee.maxOutboundBufferedBytes,
340
- timeoutMs: deadline == null
341
- ? options.handshakeTimeoutMs ?? SDK_DEFAULTS.transport.handshakeTimeoutMs
342
- : remainingHandshakeTimeoutMs(deadline),
343
- ...(options.signal === undefined ? {} : { signal: options.signal }),
344
- });
345
- const secure = deadline == null
346
- ? await startHandshake()
347
- : await runHandshakeStep(deadline, options.signal, startHandshake);
348
- try {
349
- if (handshake.commitAuthenticated != null) {
350
- if (deadline == null) {
351
- await handshake.commitAuthenticated();
352
- }
353
- else {
354
- await runHandshakeStep(deadline, options.signal, handshake.commitAuthenticated);
355
- }
356
- }
357
- }
358
- catch (error) {
359
- secure.close();
360
- const interrupted = endpointHandshakeInterruption(path, error);
361
- if (interrupted != null)
362
- throw interrupted;
363
- throw new FlowersecError({ path, stage: "handshake", code: "credential_commit_failed", message: "credential commit failed", cause: error });
364
- }
365
- try {
366
- throwIfAborted(options.signal, "handshake canceled");
367
- if (deadline != null)
368
- remainingHandshakeTimeoutMs(deadline);
369
- return Session.create(path, secure, {
370
- ...(options.yamuxLimits === undefined ? {} : { yamuxLimits: options.yamuxLimits }),
371
- ...(endpointInstanceId === undefined ? {} : { endpointInstanceId }),
372
- });
373
- }
374
- catch (error) {
375
- secure.close();
376
- throw error;
377
- }
378
- }
379
- catch (error) {
380
- transport.close();
381
- if (error instanceof FlowersecError)
382
- throw error;
383
- const interrupted = endpointHandshakeInterruption(path, error);
384
- if (interrupted != null)
385
- throw interrupted;
386
- throw new FlowersecError({ path, stage: "handshake", code: "handshake_failed", message: "endpoint handshake failed", cause: error });
387
- }
388
- finally {
389
- psk.fill(0);
390
- }
391
- }
392
- class PrefetchedTransport {
393
- inner;
394
- first;
395
- constructor(inner, first) {
396
- this.inner = inner;
397
- this.first = first;
398
- }
399
- readBinary(options) {
400
- if (this.first != null) {
401
- const first = this.first;
402
- this.first = undefined;
403
- return Promise.resolve(first);
404
- }
405
- return this.inner.readBinary(options);
406
- }
407
- writeBinary(frame, options) {
408
- return this.inner.writeBinary(frame, options);
409
- }
410
- close() {
411
- this.inner.close();
412
- }
413
- }
414
- function normalizePSK(input, path) {
415
- try {
416
- const psk = typeof input === "string" ? base64urlDecode(input.trim()) : input.slice();
417
- if (psk.length !== 32)
418
- throw new Error("psk must be 32 bytes");
419
- return psk;
420
- }
421
- catch (error) {
422
- throw new FlowersecError({ path, stage: "validate", code: "invalid_psk", message: "invalid psk", cause: error });
423
- }
424
- }
425
- function normalizeEndpointInstanceId(input) {
426
- const value = input ?? randomEndpointInstanceId();
427
- try {
428
- const bytes = base64urlDecode(value);
429
- if (bytes.length < 16 || bytes.length > 32)
430
- throw new Error("endpoint instance ID must decode to 16..32 bytes");
431
- }
432
- catch (error) {
433
- throw new FlowersecError({ path: "tunnel", stage: "validate", code: "invalid_endpoint_instance_id", message: "invalid endpoint instance ID", cause: error });
434
- }
435
- return value;
436
- }
437
- function randomEndpointInstanceId() {
438
- const bytes = new Uint8Array(24);
439
- crypto.getRandomValues(bytes);
440
- return base64urlEncode(bytes);
441
- }
442
- function normalizeStreamKind(kind, path) {
443
- const value = kind.trim();
444
- if (value === "")
445
- throw new FlowersecError({ path, stage: "rpc", code: "missing_stream_kind", message: "missing stream kind" });
446
- return value;
447
- }
448
- function unwrapServerGrant(input) {
449
- if (typeof input !== "object" || input == null || Array.isArray(input))
450
- return input;
451
- const record = input;
452
- return record["grant_server"] ?? input;
453
- }
454
- function webSocketTransportOptions(options) {
455
- return options.webSocketLimits === undefined ? {} : { webSocketLimits: options.webSocketLimits };
456
- }
457
- async function enforceIncomingDirectTransport(options) {
458
- const rawUrl = options.secureTransport === false ? "ws://127.0.0.1/" : "wss://127.0.0.1/";
459
- await enforceTransportSecurity({ rawUrl, path: "direct", ...(options.transportSecurityPolicy === undefined ? {} : { policy: options.transportSecurityPolicy }) });
460
- }
461
- function waitForOpen(websocket, timeoutMs, signal) {
462
- if (!Number.isFinite(timeoutMs) || timeoutMs < 0)
463
- return Promise.reject(new RangeError("connectTimeoutMs must be non-negative"));
464
- try {
465
- throwIfAborted(signal, "connect aborted");
466
- }
467
- catch (error) {
468
- return Promise.reject(error);
469
- }
470
- if (websocket.readyState === 1)
471
- return Promise.resolve();
472
- if (websocket.readyState >= 2)
473
- return Promise.reject(new Error("websocket closed before open"));
474
- return new Promise((resolve, reject) => {
475
- let settled = false;
476
- let timer;
477
- const cleanup = () => {
478
- if (timer != null)
479
- clearTimeout(timer);
480
- websocket.removeEventListener("open", onOpen);
481
- websocket.removeEventListener("error", onError);
482
- websocket.removeEventListener("close", onClose);
483
- signal?.removeEventListener("abort", onAbort);
484
- };
485
- const finish = (error) => {
486
- if (settled)
487
- return;
488
- settled = true;
489
- cleanup();
490
- if (error == null)
491
- resolve();
492
- else
493
- reject(error);
494
- };
495
- const onOpen = () => finish();
496
- const onError = () => finish(new Error("websocket open failed"));
497
- const onClose = () => finish(new Error("websocket closed before open"));
498
- const onAbort = () => finish(new AbortError("connect aborted"));
499
- websocket.addEventListener("open", onOpen);
500
- websocket.addEventListener("error", onError);
501
- websocket.addEventListener("close", onClose);
502
- signal?.addEventListener("abort", onAbort, { once: true });
503
- if (signal?.aborted) {
504
- onAbort();
505
- }
506
- else if (websocket.readyState === 1) {
507
- onOpen();
508
- }
509
- else if (websocket.readyState >= 2) {
510
- onClose();
511
- }
512
- if (!settled && timeoutMs > 0)
513
- timer = setTimeout(() => finish(new TimeoutError("websocket open timeout")), timeoutMs);
514
- });
515
- }
516
- function cleanupWaiter(waiter) {
517
- if (waiter.onAbort != null)
518
- waiter.signal?.removeEventListener("abort", waiter.onAbort);
519
- }
520
- function createHandshakeDeadline(timeoutMs) {
521
- return timeoutMs > 0 ? { expiresAtMs: Date.now() + timeoutMs } : {};
522
- }
523
- function remainingHandshakeTimeoutMs(deadline) {
524
- if (deadline.expiresAtMs === undefined)
525
- return 0;
526
- const remaining = deadline.expiresAtMs - Date.now();
527
- if (remaining <= 0)
528
- throw new TimeoutError("handshake timeout");
529
- return remaining;
530
- }
531
- async function runHandshakeStep(deadline, signal, operation) {
532
- throwIfAborted(signal, "handshake canceled");
533
- const timeoutMs = remainingHandshakeTimeoutMs(deadline);
534
- const result = await withAbortAndTimeout(Promise.resolve().then(operation), {
535
- timeoutMs,
536
- ...(signal === undefined ? {} : { signal }),
537
- });
538
- throwIfAborted(signal, "handshake canceled");
539
- remainingHandshakeTimeoutMs(deadline);
540
- return result;
541
- }
542
- function endpointHandshakeInterruption(path, error) {
543
- if (error instanceof TimeoutError) {
544
- return new FlowersecError({ path, stage: "handshake", code: "timeout", message: "endpoint handshake timed out", cause: error });
545
- }
546
- if (error instanceof AbortError) {
547
- return new FlowersecError({ path, stage: "handshake", code: "canceled", message: "endpoint handshake canceled", cause: error });
548
- }
549
- return undefined;
550
- }
551
- function endpointHandshakeError(path, error, message) {
552
- return endpointHandshakeInterruption(path, error)
553
- ?? new FlowersecError({ path, stage: "handshake", code: "handshake_failed", message, cause: error });
554
- }
555
- function asError(error) {
556
- return error instanceof Error ? error : new Error(String(error));
557
- }
@@ -1,9 +0,0 @@
1
- import type { WebSocketLike } from "../ws-client/binaryTransport.js";
2
- import { type DirectAcceptOptions, type DirectCredentialResolver, type DirectHandshakeCredential, type Session, type Suite, type TunnelEndpointOptions } from "./index.js";
3
- export declare function adaptNodeWebSocket(websocket: unknown): WebSocketLike;
4
- export declare function acceptDirectNode(websocket: unknown, handshake: Readonly<{
5
- channelId: string;
6
- suite: Suite;
7
- }> & DirectHandshakeCredential, options?: DirectAcceptOptions): Promise<Session>;
8
- export declare function acceptDirectResolvedNode(websocket: unknown, resolver: DirectCredentialResolver, options?: DirectAcceptOptions): Promise<Session>;
9
- export declare function connectTunnelEndpointNode(grant: unknown, options: Omit<TunnelEndpointOptions, "wsFactory">): Promise<Session>;
@@ -1,51 +0,0 @@
1
- import { createNodeWsFactory } from "../node/wsFactory.js";
2
- import { acceptDirect, acceptDirectResolved, connectTunnel, } from "./index.js";
3
- export function adaptNodeWebSocket(websocket) {
4
- const raw = websocket;
5
- if (raw == null || typeof raw.send !== "function" || typeof raw.close !== "function") {
6
- throw new TypeError("a Node WebSocket is required");
7
- }
8
- if (typeof raw.addEventListener === "function" && typeof raw.removeEventListener === "function") {
9
- return raw;
10
- }
11
- const listeners = new Map();
12
- return {
13
- get binaryType() { return String(raw.binaryType ?? "nodebuffer"); },
14
- set binaryType(value) { raw.binaryType = value; },
15
- get readyState() { return Number(raw.readyState); },
16
- get bufferedAmount() { return Number(raw.bufferedAmount ?? 0); },
17
- send(data) { raw.send(data); },
18
- close(code, reason) { raw.close(code, reason); },
19
- addEventListener(type, listener) {
20
- const wrapped = (...args) => {
21
- if (type === "message")
22
- listener({ data: args[0] });
23
- else if (type === "close")
24
- listener({ code: args[0], reason: args[1]?.toString?.() ?? "" });
25
- else
26
- listener(args[0]);
27
- };
28
- const byListener = listeners.get(type) ?? new Map();
29
- byListener.set(listener, wrapped);
30
- listeners.set(type, byListener);
31
- raw.on(type, wrapped);
32
- },
33
- removeEventListener(type, listener) {
34
- const byListener = listeners.get(type);
35
- const wrapped = byListener?.get(listener);
36
- if (wrapped == null)
37
- return;
38
- byListener.delete(listener);
39
- raw.off(type, wrapped);
40
- },
41
- };
42
- }
43
- export function acceptDirectNode(websocket, handshake, options = {}) {
44
- return acceptDirect(adaptNodeWebSocket(websocket), handshake, options);
45
- }
46
- export function acceptDirectResolvedNode(websocket, resolver, options = {}) {
47
- return acceptDirectResolved(adaptNodeWebSocket(websocket), resolver, options);
48
- }
49
- export function connectTunnelEndpointNode(grant, options) {
50
- return connectTunnel(grant, { ...options, wsFactory: createNodeWsFactory() });
51
- }
@@ -1,36 +0,0 @@
1
- /** Endpoint role for tunnel attach. */
2
- export declare enum Role {
3
- /** Client endpoint. */
4
- Role_client = 1,
5
- /** Server endpoint. */
6
- Role_server = 2
7
- }
8
- /** E2EE cipher suite identifier. */
9
- export declare enum Suite {
10
- /** P-256 + HKDF-SHA256 + AES-256-GCM. */
11
- Suite_P256_HKDF_SHA256_AES_256_GCM = 2,
12
- /** X25519 + HKDF-SHA256 + AES-256-GCM. */
13
- Suite_X25519_HKDF_SHA256_AES_256_GCM = 1
14
- }
15
- /** Grant issued by controlplane to attach and start E2EE. */
16
- export interface ChannelInitGrant {
17
- /** WebSocket URL of the tunnel server. */
18
- tunnel_url: string;
19
- /** Channel identifier shared by both endpoints. */
20
- channel_id: string;
21
- /** Unix timestamp when the grant expires. */
22
- channel_init_expire_at_unix_s: number;
23
- /** Server idle timeout hint in seconds. */
24
- idle_timeout_seconds: number;
25
- /** Role for this grant (client or server). */
26
- role: Role;
27
- /** Signed tunnel attach token. */
28
- token: string;
29
- /** Base64url-encoded 32-byte PSK. */
30
- e2ee_psk_b64u: string;
31
- /** Allowed E2EE cipher suites. */
32
- allowed_suites: Suite[];
33
- /** Default E2EE cipher suite. */
34
- default_suite: Suite;
35
- }
36
- export declare function assertChannelInitGrant(v: unknown): ChannelInitGrant;