@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,199 +0,0 @@
1
- import { FlowersecError } from "../utils/errors.js";
2
- import { emitObserverDiagnostic } from "../observability/observer.js";
3
- export const RequireTLS = "require_tls";
4
- export const AllowPlaintextForLoopback = "allow_plaintext_for_loopback";
5
- export const PlaintextRiskAcceptance = {
6
- acceptPreE2ECredentialExposure: "accept_pre_e2ee_credential_exposure",
7
- };
8
- export function createNetworkPlaintextPolicy(options) {
9
- if (options?.riskAcceptance !== PlaintextRiskAcceptance.acceptPreE2ECredentialExposure) {
10
- throw new Error("network plaintext policy requires explicit pre-E2EE credential exposure acceptance");
11
- }
12
- if (!Array.isArray(options.allowedHosts) || options.allowedHosts.length === 0) {
13
- throw new Error("network plaintext policy requires at least one allowed host");
14
- }
15
- const allowedHosts = new Set(options.allowedHosts.map(canonicalNetworkPlaintextHost));
16
- return (input) => input.scheme === "wss" || (input.scheme === "ws" && allowedHosts.has(input.host));
17
- }
18
- export async function enforceTransportSecurity(args) {
19
- let target;
20
- try {
21
- target = parseWebSocketTarget(args.rawUrl);
22
- }
23
- catch (cause) {
24
- throw denied(args.path, cause);
25
- }
26
- const input = {
27
- path: args.path,
28
- scheme: target.scheme,
29
- host: target.host,
30
- runtime: detectRuntime(),
31
- };
32
- let allowed = false;
33
- const policy = args.policy ?? RequireTLS;
34
- try {
35
- if (typeof policy === "function") {
36
- allowed = await policy(input) === true;
37
- }
38
- else {
39
- allowed = evaluatePreset(policy, target);
40
- }
41
- }
42
- catch (cause) {
43
- throw denied(args.path, cause);
44
- }
45
- if (!allowed)
46
- throw denied(args.path);
47
- if (target.scheme === "ws") {
48
- emitObserverDiagnostic(args.observer, {
49
- path: args.path,
50
- stage: "transport",
51
- code_domain: "event",
52
- code: "plaintext_transport",
53
- result: "skip",
54
- });
55
- }
56
- }
57
- function denied(path, cause) {
58
- return new FlowersecError({
59
- path,
60
- stage: "validate",
61
- code: "transport_policy_denied",
62
- message: "transport security policy denied websocket URL",
63
- ...(cause === undefined ? {} : { cause }),
64
- });
65
- }
66
- function evaluatePreset(policy, target) {
67
- switch (policy) {
68
- case RequireTLS:
69
- return target.scheme === "wss";
70
- case AllowPlaintextForLoopback:
71
- return target.scheme === "wss" || isLiteralLoopbackHost(target.host);
72
- }
73
- }
74
- function parseWebSocketTarget(rawUrl) {
75
- const raw = rawUrl.trim();
76
- const match = /^([A-Za-z][A-Za-z0-9+.-]*):\/\/([^/?#]*)(?:[/?#]|$)/.exec(raw);
77
- const scheme = match?.[1]?.toLowerCase();
78
- const authority = match?.[2] ?? "";
79
- if ((scheme !== "ws" && scheme !== "wss") || authority === "" || authority.includes("@")) {
80
- throw new Error("invalid websocket URL");
81
- }
82
- let host;
83
- if (authority.startsWith("[")) {
84
- const end = authority.indexOf("]");
85
- if (end <= 1 || (authority.slice(end + 1) !== "" && !/^:\d+$/.test(authority.slice(end + 1)))) {
86
- throw new Error("invalid websocket URL");
87
- }
88
- host = authority.slice(1, end).toLowerCase();
89
- }
90
- else {
91
- const pieces = authority.split(":");
92
- if (pieces.length > 2 || (pieces.length === 2 && !/^\d+$/.test(pieces[1] ?? ""))) {
93
- throw new Error("invalid websocket URL");
94
- }
95
- host = (pieces[0] ?? "").toLowerCase();
96
- }
97
- if (host === "")
98
- throw new Error("invalid websocket URL");
99
- return { scheme, host };
100
- }
101
- function isLiteralLoopbackHost(host) {
102
- if (host === "localhost" || host === "::1")
103
- return true;
104
- const parts = host.split(".");
105
- if (parts.length !== 4)
106
- return false;
107
- const octets = [];
108
- for (const part of parts) {
109
- if (!/^(0|[1-9]\d{0,2})$/.test(part))
110
- return false;
111
- const value = Number(part);
112
- if (value > 255)
113
- return false;
114
- octets.push(value);
115
- }
116
- return octets[0] === 127;
117
- }
118
- function canonicalNetworkPlaintextHost(rawHost) {
119
- const host = String(rawHost ?? "").trim();
120
- if (host === "" || host !== host.toLowerCase() || /[@/?#%\[\]]/.test(host)) {
121
- throw new Error(`invalid network plaintext allowed host ${JSON.stringify(rawHost)}`);
122
- }
123
- if (host.includes(":")) {
124
- return canonicalNetworkIPv6Host(host);
125
- }
126
- const octets = canonicalIPv4Octets(host);
127
- const firstOctet = octets[0];
128
- const secondOctet = octets[1];
129
- if (firstOctet == null || secondOctet == null) {
130
- throw new Error(`network plaintext allowed host must be a canonical IP literal: ${JSON.stringify(rawHost)}`);
131
- }
132
- if (firstOctet === 127 || (firstOctet === 169 && secondOctet === 254) || firstOctet >= 224 || octets.every((value) => value === 0)) {
133
- throw new Error(`network plaintext allowed host must be a non-loopback unicast IP literal: ${JSON.stringify(rawHost)}`);
134
- }
135
- return host;
136
- }
137
- function canonicalIPv4Octets(host) {
138
- const parts = host.split(".");
139
- if (parts.length !== 4) {
140
- throw new Error(`network plaintext allowed host must be a canonical IP literal: ${JSON.stringify(host)}`);
141
- }
142
- const octets = parts.map((part) => {
143
- if (!/^(0|[1-9]\d{0,2})$/.test(part)) {
144
- throw new Error(`network plaintext allowed host must be a canonical IP literal: ${JSON.stringify(host)}`);
145
- }
146
- const value = Number(part);
147
- if (value > 255) {
148
- throw new Error(`network plaintext allowed host must be a canonical IP literal: ${JSON.stringify(host)}`);
149
- }
150
- return value;
151
- });
152
- return octets;
153
- }
154
- function canonicalNetworkIPv6Host(host) {
155
- let parsed;
156
- try {
157
- parsed = new URL(`http://[${host}]/`);
158
- }
159
- catch {
160
- throw new Error(`network plaintext allowed host must be a canonical IP literal: ${JSON.stringify(host)}`);
161
- }
162
- const canonical = parsed.hostname.replace(/^\[|\]$/g, "").toLowerCase();
163
- if (canonical !== host) {
164
- throw new Error(`network plaintext allowed host must be a canonical IP literal: ${JSON.stringify(host)}`);
165
- }
166
- const words = expandIPv6Words(canonical);
167
- const unspecified = words.every((word) => word === 0);
168
- const loopback = words.slice(0, 7).every((word) => word === 0) && words[7] === 1;
169
- const mappedIPv4 = words.slice(0, 5).every((word) => word === 0) && words[5] === 0xffff;
170
- const multicast = (words[0] & 0xff00) === 0xff00;
171
- const linkLocal = (words[0] & 0xffc0) === 0xfe80;
172
- if (unspecified || loopback || mappedIPv4 || multicast || linkLocal) {
173
- throw new Error(`network plaintext allowed host must be a non-loopback unicast IP literal: ${JSON.stringify(host)}`);
174
- }
175
- return canonical;
176
- }
177
- function expandIPv6Words(host) {
178
- const halves = host.split("::");
179
- if (halves.length > 2)
180
- throw new Error("invalid IPv6 host");
181
- const left = halves[0] === "" ? [] : halves[0].split(":");
182
- const right = halves.length === 1 || halves[1] === "" ? [] : halves[1].split(":");
183
- const missing = 8 - left.length - right.length;
184
- if ((halves.length === 1 && missing !== 0) || (halves.length === 2 && missing < 1)) {
185
- throw new Error("invalid IPv6 host");
186
- }
187
- const words = [...left, ...Array.from({ length: missing }, () => "0"), ...right].map((part) => Number.parseInt(part, 16));
188
- if (words.length !== 8 || words.some((word) => !Number.isInteger(word) || word < 0 || word > 0xffff)) {
189
- throw new Error("invalid IPv6 host");
190
- }
191
- return words;
192
- }
193
- function detectRuntime() {
194
- if (typeof window !== "undefined" && typeof window.document !== "undefined")
195
- return "browser";
196
- if (typeof process !== "undefined" && process.versions?.node != null)
197
- return "node";
198
- return "other";
199
- }
@@ -1,3 +0,0 @@
1
- export declare const tunnelAttachCloseReasons: readonly ["too_many_connections", "expected_attach", "invalid_attach", "invalid_token", "channel_mismatch", "init_exp_mismatch", "idle_timeout_mismatch", "role_mismatch", "token_replay", "tenant_mismatch", "policy_denied", "policy_error", "replace_rate_limited", "attach_failed", "timeout", "canceled"];
2
- export type TunnelAttachCloseReason = (typeof tunnelAttachCloseReasons)[number];
3
- export declare function isTunnelAttachCloseReason(v: string | undefined): v is TunnelAttachCloseReason;
@@ -1,23 +0,0 @@
1
- export const tunnelAttachCloseReasons = [
2
- "too_many_connections",
3
- "expected_attach",
4
- "invalid_attach",
5
- "invalid_token",
6
- "channel_mismatch",
7
- "init_exp_mismatch",
8
- "idle_timeout_mismatch",
9
- "role_mismatch",
10
- "token_replay",
11
- "tenant_mismatch",
12
- "policy_denied",
13
- "policy_error",
14
- "replace_rate_limited",
15
- "attach_failed",
16
- "timeout",
17
- "canceled",
18
- ];
19
- export function isTunnelAttachCloseReason(v) {
20
- if (v == null)
21
- return false;
22
- return tunnelAttachCloseReasons.includes(v);
23
- }
package/dist/client.d.ts DELETED
@@ -1,23 +0,0 @@
1
- import type { SecureChannel } from "./e2ee/secureChannel.js";
2
- import type { RpcClient } from "./rpc/client.js";
3
- import type { YamuxSession } from "./yamux/session.js";
4
- import type { YamuxStream } from "./yamux/stream.js";
5
- export type ClientPath = "tunnel" | "direct";
6
- export type Client = Readonly<{
7
- path: ClientPath;
8
- endpointInstanceId?: string;
9
- rpc: RpcClient;
10
- openStream: (kind: string, opts?: Readonly<{
11
- signal?: AbortSignal;
12
- }>) => Promise<YamuxStream>;
13
- ping: () => Promise<void>;
14
- /** Emits an authenticated rekey record and advances the E2EE send key. */
15
- rekey: () => Promise<void>;
16
- /** Performs an acknowledged Yamux round trip and returns RTT in milliseconds. */
17
- probeLiveness: () => Promise<number>;
18
- close: () => void;
19
- }>;
20
- export type ClientInternal = Client & Readonly<{
21
- secure: SecureChannel;
22
- mux: YamuxSession;
23
- }>;
@@ -1,36 +0,0 @@
1
- import { type ChannelInitGrant } from "../gen/flowersec/controlplane/v1.gen.js";
2
- import { type DirectConnectInfo } from "../gen/flowersec/direct/v1.gen.js";
3
- export type ConnectArtifactTransport = "tunnel" | "direct";
4
- export type CorrelationKV = Readonly<{
5
- key: string;
6
- value: string;
7
- }>;
8
- export type CorrelationContext = Readonly<{
9
- v: 1;
10
- trace_id?: string;
11
- session_id?: string;
12
- tags: readonly CorrelationKV[];
13
- }>;
14
- export type ScopePayload = Record<string, unknown>;
15
- export type ScopeMetadataEntry = Readonly<{
16
- scope: string;
17
- scope_version: number;
18
- critical: boolean;
19
- payload: ScopePayload;
20
- }>;
21
- export type TunnelClientConnectArtifact = Readonly<{
22
- v: 1;
23
- transport: "tunnel";
24
- tunnel_grant: ChannelInitGrant;
25
- scoped?: readonly ScopeMetadataEntry[];
26
- correlation?: CorrelationContext;
27
- }>;
28
- export type DirectClientConnectArtifact = Readonly<{
29
- v: 1;
30
- transport: "direct";
31
- direct_info: DirectConnectInfo;
32
- scoped?: readonly ScopeMetadataEntry[];
33
- correlation?: CorrelationContext;
34
- }>;
35
- export type ConnectArtifact = TunnelClientConnectArtifact | DirectClientConnectArtifact;
36
- export declare function assertConnectArtifact(value: unknown): ConnectArtifact;
@@ -1,213 +0,0 @@
1
- import { assertChannelInitGrant, Role as ControlRole } from "../gen/flowersec/controlplane/v1.gen.js";
2
- import { assertDirectConnectInfo } from "../gen/flowersec/direct/v1.gen.js";
3
- const SCOPE_NAME_RE = /^[a-z][a-z0-9._-]{0,63}$/;
4
- const TAG_KEY_RE = /^[a-z][a-z0-9._-]{0,31}$/;
5
- const CORRELATION_ID_RE = /^[A-Za-z0-9._~-]{8,128}$/;
6
- const encoder = new TextEncoder();
7
- const TUNNEL_ARTIFACT_KEYS = new Set(["v", "transport", "tunnel_grant", "scoped", "correlation"]);
8
- const DIRECT_ARTIFACT_KEYS = new Set(["v", "transport", "direct_info", "scoped", "correlation"]);
9
- function isRecord(v) {
10
- return typeof v === "object" && v != null && !Array.isArray(v);
11
- }
12
- function hasOwn(o, key) {
13
- return Object.prototype.hasOwnProperty.call(o, key);
14
- }
15
- function assertNoUnknownFields(kind, obj, allowed) {
16
- for (const key of Object.keys(obj)) {
17
- if (!allowed.has(key))
18
- throw new Error(`bad ${kind}.${key}`);
19
- }
20
- }
21
- function assertPositiveInt(name, value, min, max) {
22
- if (typeof value !== "number" || !Number.isSafeInteger(value))
23
- throw new Error(`bad ${name}`);
24
- if (value < min || value > max)
25
- throw new Error(`bad ${name}`);
26
- return value;
27
- }
28
- function utf8Len(value) {
29
- return encoder.encode(value).length;
30
- }
31
- function normalizedJSONForSize(value) {
32
- return serializeNormalizedJSON(normalizeJSONValue(value));
33
- }
34
- function normalizeJSONValue(value) {
35
- if (value === null)
36
- return null;
37
- switch (typeof value) {
38
- case "string":
39
- case "boolean":
40
- return value;
41
- case "number":
42
- if (!Number.isFinite(value))
43
- throw new Error("bad payload.number");
44
- return value;
45
- case "object": {
46
- if (Array.isArray(value)) {
47
- return value.map((entry) => normalizeJSONValue(entry));
48
- }
49
- if (!isRecord(value))
50
- throw new Error("bad payload.object");
51
- const out = {};
52
- for (const key of Object.keys(value).sort()) {
53
- out[key] = normalizeJSONValue(value[key]);
54
- }
55
- return out;
56
- }
57
- default:
58
- throw new Error("bad payload.value");
59
- }
60
- }
61
- function serializeNormalizedJSON(value) {
62
- return JSON.stringify(value);
63
- }
64
- function maxContainerDepth(value) {
65
- if (Array.isArray(value)) {
66
- let best = 1;
67
- for (const entry of value)
68
- best = Math.max(best, 1 + maxContainerDepth(entry));
69
- return best;
70
- }
71
- if (isRecord(value)) {
72
- let best = 1;
73
- for (const entry of Object.values(value))
74
- best = Math.max(best, 1 + maxContainerDepth(entry));
75
- return best;
76
- }
77
- return 0;
78
- }
79
- function sanitizeCorrelationID(value) {
80
- if (typeof value !== "string")
81
- return undefined;
82
- const trimmed = value.trim();
83
- if (!CORRELATION_ID_RE.test(trimmed))
84
- return undefined;
85
- return trimmed;
86
- }
87
- function assertCorrelationKV(value) {
88
- if (!isRecord(value))
89
- throw new Error("bad CorrelationKV");
90
- assertNoUnknownFields("CorrelationKV", value, new Set(["key", "value"]));
91
- if (typeof value.key !== "string" || !TAG_KEY_RE.test(value.key))
92
- throw new Error("bad CorrelationKV.key");
93
- if (typeof value.value !== "string")
94
- throw new Error("bad CorrelationKV.value");
95
- if (utf8Len(value.key) > 32)
96
- throw new Error("bad CorrelationKV.key");
97
- if (utf8Len(value.value) > 128)
98
- throw new Error("bad CorrelationKV.value");
99
- return Object.freeze({ key: value.key, value: value.value });
100
- }
101
- function assertCorrelationContext(value) {
102
- if (!isRecord(value))
103
- throw new Error("bad CorrelationContext");
104
- assertNoUnknownFields("CorrelationContext", value, new Set(["v", "trace_id", "session_id", "tags"]));
105
- if (value.v !== 1)
106
- throw new Error("bad CorrelationContext.v");
107
- const rawTags = value.tags;
108
- if (rawTags !== undefined && !Array.isArray(rawTags))
109
- throw new Error("bad CorrelationContext.tags");
110
- const tags = (rawTags ?? []).map((entry) => assertCorrelationKV(entry));
111
- const seen = new Set();
112
- for (const tag of tags) {
113
- if (seen.has(tag.key))
114
- throw new Error("bad CorrelationContext.tags");
115
- seen.add(tag.key);
116
- }
117
- if (tags.length > 8)
118
- throw new Error("bad CorrelationContext.tags");
119
- const traceId = sanitizeCorrelationID(value.trace_id);
120
- const sessionId = sanitizeCorrelationID(value.session_id);
121
- return Object.freeze({
122
- v: 1,
123
- ...(traceId === undefined ? {} : { trace_id: traceId }),
124
- ...(sessionId === undefined ? {} : { session_id: sessionId }),
125
- tags: Object.freeze(tags),
126
- });
127
- }
128
- function assertPayloadObject(value) {
129
- if (!isRecord(value))
130
- throw new Error("bad ScopeMetadataEntry.payload");
131
- const normalized = normalizedJSONForSize(value);
132
- if (utf8Len(normalized) > 8192)
133
- throw new Error("bad ScopeMetadataEntry.payload");
134
- if (maxContainerDepth(value) > 8)
135
- throw new Error("bad ScopeMetadataEntry.payload");
136
- return value;
137
- }
138
- function assertScopeMetadataEntry(value) {
139
- if (!isRecord(value))
140
- throw new Error("bad ScopeMetadataEntry");
141
- assertNoUnknownFields("ScopeMetadataEntry", value, new Set(["scope", "scope_version", "critical", "payload"]));
142
- if (typeof value.scope !== "string" || !SCOPE_NAME_RE.test(value.scope))
143
- throw new Error("bad ScopeMetadataEntry.scope");
144
- if (typeof value.critical !== "boolean")
145
- throw new Error("bad ScopeMetadataEntry.critical");
146
- const scopeVersion = assertPositiveInt("ScopeMetadataEntry.scope_version", value.scope_version, 1, 65535);
147
- const payload = assertPayloadObject(value.payload);
148
- return Object.freeze({
149
- scope: value.scope,
150
- scope_version: scopeVersion,
151
- critical: value.critical,
152
- payload,
153
- });
154
- }
155
- function assertScopedEntries(value) {
156
- if (!Array.isArray(value))
157
- throw new Error("bad ConnectArtifact.scoped");
158
- if (value.length > 8)
159
- throw new Error("bad ConnectArtifact.scoped");
160
- const out = value.map((entry) => assertScopeMetadataEntry(entry));
161
- const seen = new Set();
162
- for (const entry of out) {
163
- if (seen.has(entry.scope))
164
- throw new Error("bad ConnectArtifact.scoped");
165
- seen.add(entry.scope);
166
- }
167
- return Object.freeze(out);
168
- }
169
- function assertArtifactObject(value) {
170
- if (!isRecord(value))
171
- throw new Error("bad ConnectArtifact");
172
- return value;
173
- }
174
- function assertArtifactTransport(value) {
175
- if (value !== "tunnel" && value !== "direct")
176
- throw new Error("bad ConnectArtifact.transport");
177
- return value;
178
- }
179
- export function assertConnectArtifact(value) {
180
- const record = assertArtifactObject(value);
181
- if (record.v !== 1)
182
- throw new Error("bad ConnectArtifact.v");
183
- const transport = assertArtifactTransport(record.transport);
184
- const scoped = record.scoped === undefined ? undefined : assertScopedEntries(record.scoped);
185
- const correlation = record.correlation === undefined ? undefined : assertCorrelationContext(record.correlation);
186
- if (transport === "tunnel") {
187
- assertNoUnknownFields("TunnelClientConnectArtifact", record, TUNNEL_ARTIFACT_KEYS);
188
- if (!hasOwn(record, "tunnel_grant"))
189
- throw new Error("bad TunnelClientConnectArtifact.tunnel_grant");
190
- const tunnelGrant = assertChannelInitGrant(record.tunnel_grant);
191
- if (tunnelGrant.role !== ControlRole.Role_client) {
192
- throw new Error("bad TunnelClientConnectArtifact.tunnel_grant.role");
193
- }
194
- return Object.freeze({
195
- v: 1,
196
- transport,
197
- tunnel_grant: tunnelGrant,
198
- ...(scoped === undefined ? {} : { scoped }),
199
- ...(correlation === undefined ? {} : { correlation }),
200
- });
201
- }
202
- assertNoUnknownFields("DirectClientConnectArtifact", record, DIRECT_ARTIFACT_KEYS);
203
- if (!hasOwn(record, "direct_info"))
204
- throw new Error("bad DirectClientConnectArtifact.direct_info");
205
- const directInfo = assertDirectConnectInfo(record.direct_info);
206
- return Object.freeze({
207
- v: 1,
208
- transport,
209
- direct_info: directInfo,
210
- ...(scoped === undefined ? {} : { scoped }),
211
- ...(correlation === undefined ? {} : { correlation }),
212
- });
213
- }
@@ -1,22 +0,0 @@
1
- import type { ChannelInitGrant } from "../gen/flowersec/controlplane/v1.gen.js";
2
- import type { DirectConnectInfo } from "../gen/flowersec/direct/v1.gen.js";
3
- import { type ClientObserverLike } from "../observability/observer.js";
4
- import { type ConnectArtifact, type ScopeMetadataEntry } from "./artifact.js";
5
- export type ConnectScopeResolver = (entry: ScopeMetadataEntry) => void | Promise<void>;
6
- export type ConnectScopeResolverMap = Readonly<Record<string, ConnectScopeResolver>>;
7
- type ResolveOptions = Readonly<{
8
- observer?: ClientObserverLike;
9
- scopeResolvers?: ConnectScopeResolverMap;
10
- relaxedOptionalScopeValidation?: boolean;
11
- }>;
12
- export type ResolvedConnectArtifact = Readonly<{
13
- kind: "tunnel";
14
- input: ChannelInitGrant;
15
- observer?: ClientObserverLike;
16
- }> | Readonly<{
17
- kind: "direct";
18
- input: DirectConnectInfo;
19
- observer?: ClientObserverLike;
20
- }>;
21
- export declare function resolveConnectArtifact(input: ConnectArtifact, opts?: ResolveOptions): Promise<ResolvedConnectArtifact>;
22
- export {};
@@ -1,87 +0,0 @@
1
- import { emitObserverDiagnostic, normalizeObserver, withObserverContext } from "../observability/observer.js";
2
- import { FlowersecError } from "../utils/errors.js";
3
- import { assertConnectArtifact, } from "./artifact.js";
4
- async function validateArtifactScopes(artifact, opts, observer) {
5
- const scoped = artifact.scoped ?? [];
6
- if (scoped.length === 0)
7
- return;
8
- const path = artifact.transport;
9
- for (const entry of scoped) {
10
- const resolver = opts.scopeResolvers?.[entry.scope];
11
- if (resolver == null) {
12
- if (entry.critical) {
13
- throw new FlowersecError({
14
- path,
15
- stage: "validate",
16
- code: "resolve_failed",
17
- message: `missing scope resolver for ${entry.scope}@${entry.scope_version}`,
18
- });
19
- }
20
- emitObserverDiagnostic(observer, {
21
- path,
22
- stage: "scope",
23
- code_domain: "event",
24
- code: "scope_ignored_missing_resolver",
25
- result: "skip",
26
- });
27
- continue;
28
- }
29
- try {
30
- await resolver(entry);
31
- }
32
- catch (error) {
33
- if (!entry.critical && opts.relaxedOptionalScopeValidation === true) {
34
- emitObserverDiagnostic(observer, {
35
- path,
36
- stage: "scope",
37
- code_domain: "event",
38
- code: "scope_ignored_relaxed_validation",
39
- result: "skip",
40
- });
41
- continue;
42
- }
43
- throw new FlowersecError({
44
- path,
45
- stage: "validate",
46
- code: "resolve_failed",
47
- message: `scope validation failed for ${entry.scope}@${entry.scope_version}`,
48
- cause: error,
49
- });
50
- }
51
- }
52
- }
53
- export async function resolveConnectArtifact(input, opts = {}) {
54
- let artifact;
55
- try {
56
- artifact = assertConnectArtifact(input);
57
- }
58
- catch (error) {
59
- throw new FlowersecError({
60
- path: "auto",
61
- stage: "validate",
62
- code: "invalid_input",
63
- message: "invalid ConnectArtifact",
64
- cause: error,
65
- });
66
- }
67
- const observer = opts.observer == null
68
- ? undefined
69
- : normalizeObserver(withObserverContext(opts.observer, {
70
- path: artifact.transport,
71
- ...(artifact.correlation?.trace_id === undefined ? {} : { traceId: artifact.correlation.trace_id }),
72
- ...(artifact.correlation?.session_id === undefined ? {} : { sessionId: artifact.correlation.session_id }),
73
- }), { path: artifact.transport });
74
- await validateArtifactScopes(artifact, opts, observer);
75
- if (artifact.transport === "direct") {
76
- return {
77
- kind: "direct",
78
- input: artifact.direct_info,
79
- ...(observer === undefined ? {} : { observer }),
80
- };
81
- }
82
- return {
83
- kind: "tunnel",
84
- input: artifact.tunnel_grant,
85
- ...(observer === undefined ? {} : { observer }),
86
- };
87
- }
@@ -1,27 +0,0 @@
1
- import type { ChannelInitGrant, Suite } from "../gen/flowersec/controlplane/v1.gen.js";
2
- import type { IssuerKeyset } from "./issuer.js";
3
- export declare const CHANNEL_INIT_WINDOW_SECONDS = 120;
4
- export declare const DEFAULT_IDLE_TIMEOUT_SECONDS = 60;
5
- export declare const DEFAULT_TOKEN_EXP_SECONDS = 60;
6
- export type ChannelInitParams = Readonly<{
7
- tunnelUrl: string;
8
- tunnelAudience: string;
9
- issuerId: string;
10
- tokenExpSeconds?: number;
11
- idleTimeoutSeconds?: number;
12
- clockSkewMs?: number;
13
- allowedSuites?: readonly Suite[];
14
- defaultSuite?: Suite;
15
- }>;
16
- export declare class ChannelInitService {
17
- private readonly issuer;
18
- private readonly params;
19
- private readonly nowUnixS;
20
- constructor(issuer: IssuerKeyset, params: ChannelInitParams, nowUnixS?: () => number);
21
- issue(channelIdInput: string): Readonly<{
22
- client: ChannelInitGrant;
23
- server: ChannelInitGrant;
24
- }>;
25
- reissue(grant: ChannelInitGrant): ChannelInitGrant;
26
- private signRoleToken;
27
- }