@floegence/flowersec-core 0.26.0 → 2.0.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 (270) 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 -30
  34. package/dist/facade.js +6 -30
  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 -83
  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 -12
  148. package/dist/browser/connect.js +0 -31
  149. package/dist/browser/controlplane.d.ts +0 -15
  150. package/dist/browser/controlplane.js +0 -64
  151. package/dist/browser/reconnectConfig.d.ts +0 -15
  152. package/dist/browser/reconnectConfig.js +0 -20
  153. package/dist/client-connect/common.d.ts +0 -26
  154. package/dist/client-connect/common.js +0 -167
  155. package/dist/client-connect/connectCore.d.ts +0 -61
  156. package/dist/client-connect/connectCore.js +0 -572
  157. package/dist/client-connect/contract.d.ts +0 -8
  158. package/dist/client-connect/contract.js +0 -51
  159. package/dist/client-connect/termination.d.ts +0 -6
  160. package/dist/client-connect/termination.js +0 -7
  161. package/dist/client-connect/transportSecurity.d.ts +0 -27
  162. package/dist/client-connect/transportSecurity.js +0 -199
  163. package/dist/client-connect/tunnelAttachCloseReason.d.ts +0 -3
  164. package/dist/client-connect/tunnelAttachCloseReason.js +0 -23
  165. package/dist/client.d.ts +0 -23
  166. package/dist/connect/artifact.d.ts +0 -37
  167. package/dist/connect/artifact.js +0 -217
  168. package/dist/connect/internalNormalize.d.ts +0 -22
  169. package/dist/connect/internalNormalize.js +0 -173
  170. package/dist/controlplane/channelInit.d.ts +0 -27
  171. package/dist/controlplane/channelInit.js +0 -116
  172. package/dist/controlplane/http.d.ts +0 -16
  173. package/dist/controlplane/http.js +0 -59
  174. package/dist/controlplane/index.d.ts +0 -6
  175. package/dist/controlplane/index.js +0 -5
  176. package/dist/controlplane/issuer.d.ts +0 -18
  177. package/dist/controlplane/issuer.js +0 -113
  178. package/dist/controlplane/request.d.ts +0 -51
  179. package/dist/controlplane/request.js +0 -273
  180. package/dist/controlplane/token.d.ts +0 -33
  181. package/dist/controlplane/token.js +0 -119
  182. package/dist/direct-client/connect.d.ts +0 -9
  183. package/dist/direct-client/connect.js +0 -90
  184. package/dist/direct-client/index.d.ts +0 -1
  185. package/dist/direct-client/index.js +0 -1
  186. package/dist/e2ee/constants.d.ts +0 -9
  187. package/dist/e2ee/constants.js +0 -18
  188. package/dist/e2ee/errors.d.ts +0 -5
  189. package/dist/e2ee/errors.js +0 -8
  190. package/dist/e2ee/framing.d.ts +0 -12
  191. package/dist/e2ee/framing.js +0 -57
  192. package/dist/e2ee/handshake.d.ts +0 -88
  193. package/dist/e2ee/handshake.js +0 -353
  194. package/dist/e2ee/index.d.ts +0 -7
  195. package/dist/e2ee/index.js +0 -7
  196. package/dist/e2ee/kdf.d.ts +0 -15
  197. package/dist/e2ee/kdf.js +0 -39
  198. package/dist/e2ee/record.d.ts +0 -11
  199. package/dist/e2ee/record.js +0 -76
  200. package/dist/e2ee/secureChannel.d.ts +0 -92
  201. package/dist/e2ee/secureChannel.js +0 -328
  202. package/dist/e2ee/transcript.d.ts +0 -23
  203. package/dist/e2ee/transcript.js +0 -31
  204. package/dist/endpoint/index.d.ts +0 -84
  205. package/dist/endpoint/index.js +0 -557
  206. package/dist/endpoint/node.d.ts +0 -9
  207. package/dist/endpoint/node.js +0 -51
  208. package/dist/gen/flowersec/controlplane/v1.gen.d.ts +0 -36
  209. package/dist/gen/flowersec/controlplane/v1.gen.js +0 -135
  210. package/dist/gen/flowersec/direct/v1.gen.d.ts +0 -21
  211. package/dist/gen/flowersec/direct/v1.gen.js +0 -101
  212. package/dist/gen/flowersec/e2ee/v1.gen.d.ts +0 -68
  213. package/dist/gen/flowersec/e2ee/v1.gen.js +0 -194
  214. package/dist/gen/flowersec/rpc/v1.gen.d.ts +0 -30
  215. package/dist/gen/flowersec/rpc/v1.gen.js +0 -107
  216. package/dist/gen/flowersec/tunnel/v1.gen.d.ts +0 -23
  217. package/dist/gen/flowersec/tunnel/v1.gen.js +0 -104
  218. package/dist/index.d.ts +0 -22
  219. package/dist/index.js +0 -22
  220. package/dist/node/connect.d.ts +0 -12
  221. package/dist/node/connect.js +0 -29
  222. package/dist/node/reconnectConfig.d.ts +0 -16
  223. package/dist/node/reconnectConfig.js +0 -20
  224. package/dist/observability/index.d.ts +0 -1
  225. package/dist/observability/index.js +0 -1
  226. package/dist/observability/observer.d.ts +0 -52
  227. package/dist/observability/observer.js +0 -314
  228. package/dist/proxy/appWindow.d.ts +0 -37
  229. package/dist/proxy/appWindow.js +0 -273
  230. package/dist/proxy/bootstrap.d.ts +0 -35
  231. package/dist/proxy/bootstrap.js +0 -119
  232. package/dist/proxy/constants.d.ts +0 -7
  233. package/dist/proxy/constants.js +0 -8
  234. package/dist/proxy/controllerWindow.d.ts +0 -12
  235. package/dist/proxy/controllerWindow.js +0 -384
  236. package/dist/proxy/cookieJar.d.ts +0 -7
  237. package/dist/proxy/cookieJar.js +0 -136
  238. package/dist/proxy/headerPolicy.d.ts +0 -14
  239. package/dist/proxy/headerPolicy.js +0 -134
  240. package/dist/proxy/portStream.d.ts +0 -5
  241. package/dist/proxy/portStream.js +0 -256
  242. package/dist/proxy/preset.d.ts +0 -29
  243. package/dist/proxy/preset.js +0 -111
  244. package/dist/proxy/runtimeScope.d.ts +0 -57
  245. package/dist/proxy/runtimeScope.js +0 -218
  246. package/dist/proxy/server.d.ts +0 -28
  247. package/dist/proxy/server.js +0 -833
  248. package/dist/proxy/windowBridgeProtocol.d.ts +0 -65
  249. package/dist/proxy/windowBridgeProtocol.js +0 -11
  250. package/dist/reconnect/artifactControlplane.d.ts +0 -16
  251. package/dist/reconnect/artifactControlplane.js +0 -35
  252. package/dist/reconnect/index.d.ts +0 -36
  253. package/dist/reconnect/index.js +0 -388
  254. package/dist/rpc-proxy/index.d.ts +0 -1
  255. package/dist/rpc-proxy/index.js +0 -1
  256. package/dist/rpc-proxy/rpcProxy.d.ts +0 -1
  257. package/dist/rpc-proxy/rpcProxy.js +0 -1
  258. package/dist/streamhello/index.d.ts +0 -1
  259. package/dist/streamhello/index.js +0 -1
  260. package/dist/streamhello/streamHello.d.ts +0 -3
  261. package/dist/streamhello/streamHello.js +0 -13
  262. package/dist/streamio/index.d.ts +0 -32
  263. package/dist/streamio/index.js +0 -87
  264. package/dist/tunnel-client/connect.d.ts +0 -9
  265. package/dist/tunnel-client/connect.js +0 -157
  266. package/dist/tunnel-client/index.d.ts +0 -1
  267. package/dist/tunnel-client/index.js +0 -1
  268. package/dist/ws/index.d.ts +0 -1
  269. package/dist/ws/index.js +0 -1
  270. /package/dist/{client.js → rpc/wire.js} +0 -0
@@ -1,173 +0,0 @@
1
- import { Role as ControlRole } from "../gen/flowersec/controlplane/v1.gen.js";
2
- import { emitObserverDiagnostic, normalizeObserver, withObserverContext } from "../observability/observer.js";
3
- import { FlowersecError } from "../utils/errors.js";
4
- import { assertConnectArtifact, hasArtifactOnlyFields, } from "./artifact.js";
5
- function maybeParseJSON(input) {
6
- if (typeof input !== "string")
7
- return input;
8
- const s = input.trim();
9
- if (s === "")
10
- return input;
11
- if (s[0] !== "{" && s[0] !== "[")
12
- return input;
13
- try {
14
- return JSON.parse(s);
15
- }
16
- catch (e) {
17
- throw new FlowersecError({
18
- path: "auto",
19
- stage: "validate",
20
- code: "invalid_input",
21
- message: "invalid JSON string",
22
- cause: e,
23
- });
24
- }
25
- }
26
- async function validateArtifactScopes(artifact, opts, observer) {
27
- const scoped = artifact.scoped ?? [];
28
- if (scoped.length === 0)
29
- return;
30
- const path = artifact.transport;
31
- for (const entry of scoped) {
32
- const resolver = opts.scopeResolvers?.[entry.scope];
33
- if (resolver == null) {
34
- if (entry.critical) {
35
- throw new FlowersecError({
36
- path,
37
- stage: "validate",
38
- code: "resolve_failed",
39
- message: `missing scope resolver for ${entry.scope}@${entry.scope_version}`,
40
- });
41
- }
42
- emitObserverDiagnostic(observer, {
43
- path,
44
- stage: "scope",
45
- code_domain: "event",
46
- code: "scope_ignored_missing_resolver",
47
- result: "skip",
48
- });
49
- continue;
50
- }
51
- try {
52
- await resolver(entry);
53
- }
54
- catch (e) {
55
- if (!entry.critical && opts.relaxedOptionalScopeValidation === true) {
56
- emitObserverDiagnostic(observer, {
57
- path,
58
- stage: "scope",
59
- code_domain: "event",
60
- code: "scope_ignored_relaxed_validation",
61
- result: "skip",
62
- });
63
- continue;
64
- }
65
- throw new FlowersecError({
66
- path,
67
- stage: "validate",
68
- code: "resolve_failed",
69
- message: `scope validation failed for ${entry.scope}@${entry.scope_version}`,
70
- cause: e,
71
- });
72
- }
73
- }
74
- }
75
- export async function normalizeConnectInput(input, opts = {}) {
76
- const v = maybeParseJSON(input);
77
- if (v == null || typeof v !== "object") {
78
- throw new FlowersecError({
79
- path: "auto",
80
- stage: "validate",
81
- code: "invalid_input",
82
- message: "invalid input: expected an object or a JSON string",
83
- });
84
- }
85
- const o = v;
86
- const hasWsUrl = Object.prototype.hasOwnProperty.call(o, "ws_url");
87
- const hasTunnelUrl = Object.prototype.hasOwnProperty.call(o, "tunnel_url");
88
- const hasGrantClient = Object.prototype.hasOwnProperty.call(o, "grant_client");
89
- const hasGrantServer = Object.prototype.hasOwnProperty.call(o, "grant_server");
90
- const isArtifactCandidate = hasArtifactOnlyFields(o);
91
- if ((hasWsUrl && (hasTunnelUrl || hasGrantClient || hasGrantServer)) || (hasTunnelUrl && (hasGrantClient || hasGrantServer))) {
92
- throw new FlowersecError({
93
- path: "auto",
94
- stage: "validate",
95
- code: "invalid_input",
96
- message: "hybrid connect input is not allowed",
97
- });
98
- }
99
- if (isArtifactCandidate && (hasWsUrl || hasTunnelUrl || hasGrantClient || hasGrantServer)) {
100
- throw new FlowersecError({
101
- path: "auto",
102
- stage: "validate",
103
- code: "invalid_input",
104
- message: "artifact fields cannot be mixed with legacy connect inputs",
105
- });
106
- }
107
- if (hasGrantServer) {
108
- throw new FlowersecError({
109
- path: "tunnel",
110
- stage: "validate",
111
- code: "role_mismatch",
112
- message: "expected role=client",
113
- });
114
- }
115
- if (hasGrantClient)
116
- return { kind: "tunnel", input: v };
117
- if (hasWsUrl)
118
- return { kind: "direct", input: v };
119
- if (hasTunnelUrl) {
120
- if (typeof o.role === "number" && Number.isSafeInteger(o.role) && o.role === ControlRole.Role_server) {
121
- throw new FlowersecError({
122
- path: "tunnel",
123
- stage: "validate",
124
- code: "role_mismatch",
125
- message: "expected role=client",
126
- });
127
- }
128
- return { kind: "tunnel", input: v };
129
- }
130
- if (isArtifactCandidate) {
131
- let artifact;
132
- try {
133
- artifact = assertConnectArtifact(v);
134
- }
135
- catch (e) {
136
- throw new FlowersecError({
137
- path: "auto",
138
- stage: "validate",
139
- code: "invalid_input",
140
- message: "invalid ConnectArtifact",
141
- cause: e,
142
- });
143
- }
144
- const observer = opts.observer == null
145
- ? undefined
146
- : normalizeObserver(withObserverContext(opts.observer, {
147
- path: artifact.transport,
148
- ...(artifact.correlation?.trace_id === undefined ? {} : { traceId: artifact.correlation.trace_id }),
149
- ...(artifact.correlation?.session_id === undefined ? {} : { sessionId: artifact.correlation.session_id }),
150
- }), { path: artifact.transport });
151
- await validateArtifactScopes(artifact, opts, observer);
152
- if (artifact.transport === "direct") {
153
- return {
154
- kind: "direct",
155
- input: artifact.direct_info,
156
- ...(artifact.correlation === undefined ? {} : { correlation: artifact.correlation }),
157
- ...(observer === undefined ? {} : { observer }),
158
- };
159
- }
160
- return {
161
- kind: "tunnel",
162
- input: artifact.tunnel_grant,
163
- ...(artifact.correlation === undefined ? {} : { correlation: artifact.correlation }),
164
- ...(observer === undefined ? {} : { observer }),
165
- };
166
- }
167
- throw new FlowersecError({
168
- path: "auto",
169
- stage: "validate",
170
- code: "invalid_input",
171
- message: "invalid input: expected DirectConnectInfo, ChannelInitGrant, wrapper, or ConnectArtifact",
172
- });
173
- }
@@ -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
- }
@@ -1,116 +0,0 @@
1
- import { Role } from "../gen/flowersec/controlplane/v1.gen.js";
2
- import { base64urlEncode } from "../utils/base64url.js";
3
- export const CHANNEL_INIT_WINDOW_SECONDS = 120;
4
- export const DEFAULT_IDLE_TIMEOUT_SECONDS = 60;
5
- export const DEFAULT_TOKEN_EXP_SECONDS = 60;
6
- export class ChannelInitService {
7
- issuer;
8
- params;
9
- nowUnixS;
10
- constructor(issuer, params, nowUnixS = () => Math.floor(Date.now() / 1000)) {
11
- this.issuer = issuer;
12
- this.params = params;
13
- this.nowUnixS = nowUnixS;
14
- }
15
- issue(channelIdInput) {
16
- const params = normalizeParams(this.params);
17
- const channelId = normalizeChannelId(channelIdInput);
18
- const psk = crypto.getRandomValues(new Uint8Array(32));
19
- let pskB64u;
20
- try {
21
- pskB64u = base64urlEncode(psk);
22
- }
23
- finally {
24
- psk.fill(0);
25
- }
26
- const now = normalizeNow(this.nowUnixS());
27
- const initExp = now + CHANNEL_INIT_WINDOW_SECONDS;
28
- const grant = (role, token) => ({
29
- tunnel_url: params.tunnelUrl,
30
- channel_id: channelId,
31
- channel_init_expire_at_unix_s: initExp,
32
- idle_timeout_seconds: params.idleTimeoutSeconds,
33
- role,
34
- token,
35
- e2ee_psk_b64u: pskB64u,
36
- allowed_suites: [...params.allowedSuites],
37
- default_suite: params.defaultSuite,
38
- });
39
- return {
40
- client: grant(Role.Role_client, this.signRoleToken(channelId, Role.Role_client, initExp, params.idleTimeoutSeconds, params.tokenExpSeconds, now, params)),
41
- server: grant(Role.Role_server, this.signRoleToken(channelId, Role.Role_server, initExp, params.idleTimeoutSeconds, params.tokenExpSeconds, now, params)),
42
- };
43
- }
44
- reissue(grant) {
45
- const params = normalizeParams(this.params);
46
- if (grant.idle_timeout_seconds <= 0 || (grant.role !== Role.Role_client && grant.role !== Role.Role_server))
47
- throw new Error("invalid grant");
48
- const now = normalizeNow(this.nowUnixS());
49
- const skewSeconds = Math.ceil(params.clockSkewMs / 1000);
50
- if (now > grant.channel_init_expire_at_unix_s + skewSeconds)
51
- throw new Error("channel init expired");
52
- return {
53
- ...grant,
54
- token: this.signRoleToken(normalizeChannelId(grant.channel_id), grant.role, grant.channel_init_expire_at_unix_s, grant.idle_timeout_seconds, params.tokenExpSeconds, now, params),
55
- };
56
- }
57
- signRoleToken(channelId, role, initExp, idleTimeoutSeconds, tokenExpSeconds, now, params) {
58
- const iat = Math.min(now, initExp);
59
- const exp = Math.min(initExp, iat + tokenExpSeconds);
60
- const tokenId = crypto.getRandomValues(new Uint8Array(24));
61
- try {
62
- return this.issuer.sign({
63
- aud: params.tunnelAudience,
64
- iss: params.issuerId,
65
- channel_id: channelId,
66
- role: role,
67
- token_id: base64urlEncode(tokenId),
68
- init_exp: initExp,
69
- idle_timeout_seconds: idleTimeoutSeconds,
70
- iat,
71
- exp,
72
- });
73
- }
74
- finally {
75
- tokenId.fill(0);
76
- }
77
- }
78
- }
79
- function normalizeParams(params) {
80
- const tunnelUrl = params.tunnelUrl.trim();
81
- const tunnelAudience = params.tunnelAudience.trim();
82
- const issuerId = params.issuerId.trim();
83
- if (tunnelUrl === "")
84
- throw new Error("missing tunnel URL");
85
- if (tunnelAudience === "")
86
- throw new Error("missing tunnel audience");
87
- if (issuerId === "")
88
- throw new Error("missing issuer ID");
89
- const tokenExpSeconds = normalizeNonNegative("tokenExpSeconds", params.tokenExpSeconds ?? 0) || DEFAULT_TOKEN_EXP_SECONDS;
90
- const idleTimeoutSeconds = normalizeNonNegative("idleTimeoutSeconds", params.idleTimeoutSeconds ?? 0) || DEFAULT_IDLE_TIMEOUT_SECONDS;
91
- const clockSkewMs = normalizeNonNegative("clockSkewMs", params.clockSkewMs ?? 0);
92
- const allowedSuites = [...new Set(params.allowedSuites?.length ? params.allowedSuites : [1])];
93
- for (const suite of allowedSuites)
94
- if (suite !== 1 && suite !== 2)
95
- throw new Error("unsupported suite");
96
- const defaultSuite = params.defaultSuite ?? allowedSuites[0];
97
- if (!allowedSuites.includes(defaultSuite))
98
- throw new Error("default suite not allowed");
99
- return { tunnelUrl, tunnelAudience, issuerId, tokenExpSeconds, idleTimeoutSeconds, clockSkewMs, allowedSuites, defaultSuite };
100
- }
101
- function normalizeChannelId(input) {
102
- const value = input.trim();
103
- if (value === "" || new TextEncoder().encode(value).length > 256)
104
- throw new Error("invalid channel ID");
105
- return value;
106
- }
107
- function normalizeNonNegative(name, value) {
108
- if (!Number.isSafeInteger(value) || value < 0)
109
- throw new Error(`${name} must be a non-negative integer`);
110
- return value;
111
- }
112
- function normalizeNow(value) {
113
- if (!Number.isSafeInteger(value) || value <= 0)
114
- throw new Error("invalid clock");
115
- return value;
116
- }
@@ -1,16 +0,0 @@
1
- export type ArtifactRequest = Readonly<{
2
- endpoint_id: string;
3
- payload?: Record<string, unknown>;
4
- correlation?: Readonly<{
5
- trace_id?: string;
6
- }>;
7
- }>;
8
- export declare class ControlplaneCodecError extends Error {
9
- readonly status: number;
10
- readonly code: string;
11
- constructor(status: number, code: string, message: string);
12
- }
13
- export declare function decodeArtifactRequest(contentType: string, body: Uint8Array, maxBodyBytes?: number): ArtifactRequest;
14
- export declare function encodeArtifactEnvelope(artifact: unknown): Uint8Array;
15
- export declare function encodeControlplaneError(code: string, message: string): Uint8Array;
16
- export declare function bearerToken(authorization: string): string | undefined;
@@ -1,59 +0,0 @@
1
- import { assertConnectArtifact } from "../connect/artifact.js";
2
- import { SDK_DEFAULTS } from "../defaults.js";
3
- export class ControlplaneCodecError extends Error {
4
- status;
5
- code;
6
- constructor(status, code, message) {
7
- super(message);
8
- this.status = status;
9
- this.code = code;
10
- this.name = "ControlplaneCodecError";
11
- }
12
- }
13
- export function decodeArtifactRequest(contentType, body, maxBodyBytes = SDK_DEFAULTS.controlplane.maxRequestBodyBytes) {
14
- if (contentType.split(";", 1)[0]?.trim().toLowerCase() !== "application/json") {
15
- throw new ControlplaneCodecError(415, "unsupported_media_type", "content type must be application/json");
16
- }
17
- if (body.length > maxBodyBytes)
18
- throw new ControlplaneCodecError(413, "body_too_large", `request body exceeds ${maxBodyBytes} bytes`);
19
- let value;
20
- try {
21
- value = JSON.parse(new TextDecoder().decode(body));
22
- }
23
- catch {
24
- throw new ControlplaneCodecError(400, "invalid_json", "malformed JSON request body");
25
- }
26
- if (typeof value !== "object" || value == null || Array.isArray(value))
27
- throw new ControlplaneCodecError(400, "invalid_json", "request body must be a JSON object");
28
- const input = value;
29
- const endpointId = typeof input["endpoint_id"] === "string" ? input["endpoint_id"].trim() : "";
30
- if (endpointId === "")
31
- throw new ControlplaneCodecError(400, "invalid_request", "bad endpoint_id");
32
- const payload = input["payload"];
33
- if (payload !== undefined && (typeof payload !== "object" || payload == null || Array.isArray(payload)))
34
- throw new ControlplaneCodecError(400, "invalid_request", "bad payload");
35
- const correlation = input["correlation"];
36
- if (correlation !== undefined && (typeof correlation !== "object" || correlation == null || Array.isArray(correlation)))
37
- throw new ControlplaneCodecError(400, "invalid_request", "bad correlation");
38
- const traceId = typeof correlation?.["trace_id"] === "string"
39
- ? String(correlation["trace_id"]).trim()
40
- : "";
41
- return {
42
- endpoint_id: endpointId,
43
- ...(payload === undefined ? {} : { payload: payload }),
44
- ...(traceId === "" ? {} : { correlation: { trace_id: traceId } }),
45
- };
46
- }
47
- export function encodeArtifactEnvelope(artifact) {
48
- return new TextEncoder().encode(JSON.stringify({ connect_artifact: assertConnectArtifact(artifact) }));
49
- }
50
- export function encodeControlplaneError(code, message) {
51
- return new TextEncoder().encode(JSON.stringify({ error: { code, message } }));
52
- }
53
- export function bearerToken(authorization) {
54
- const value = authorization.trim();
55
- if (!value.startsWith("Bearer "))
56
- return undefined;
57
- const token = value.slice("Bearer ".length).trim();
58
- return token === "" ? undefined : token;
59
- }
@@ -1,6 +0,0 @@
1
- export type { ArtifactRequestCorrelation, ConnectArtifactEnvelope, ControlplaneBaseConfig, ControlplaneErrorEnvelope, RequestConnectArtifactInput, RequestEntryConnectArtifactInput, } from "./request.js";
2
- export { ControlplaneRequestError, DEFAULT_CONNECT_ARTIFACT_PATH, DEFAULT_ENTRY_CONNECT_ARTIFACT_PATH, requestConnectArtifact, requestEntryConnectArtifact, } from "./request.js";
3
- export * from "./token.js";
4
- export * from "./issuer.js";
5
- export * from "./channelInit.js";
6
- export * from "./http.js";
@@ -1,5 +0,0 @@
1
- export { ControlplaneRequestError, DEFAULT_CONNECT_ARTIFACT_PATH, DEFAULT_ENTRY_CONNECT_ARTIFACT_PATH, requestConnectArtifact, requestEntryConnectArtifact, } from "./request.js";
2
- export * from "./token.js";
3
- export * from "./issuer.js";
4
- export * from "./channelInit.js";
5
- export * from "./http.js";
@@ -1,18 +0,0 @@
1
- import { type TokenPayload } from "./token.js";
2
- export declare class IssuerKeyset {
3
- private active;
4
- private readonly verificationKeys;
5
- constructor(kid: string, signingSeed: Uint8Array);
6
- static random(kid: string): IssuerKeyset;
7
- currentKID(): string;
8
- publicKeys(): ReadonlyMap<string, Uint8Array>;
9
- sign(payload: Omit<TokenPayload, "kid"> & Readonly<{
10
- kid?: string;
11
- }>): string;
12
- addVerificationKey(kid: string, publicKey: Uint8Array): void;
13
- rotate(kid: string, signingSeed: Uint8Array): void;
14
- retireVerificationKey(kid: string): void;
15
- exportTunnelKeyset(): Uint8Array;
16
- dispose(): void;
17
- private requireActive;
18
- }
@@ -1,113 +0,0 @@
1
- import { ed25519 } from "@noble/curves/ed25519";
2
- import { base64urlEncode } from "../utils/base64url.js";
3
- import { signToken } from "./token.js";
4
- export class IssuerKeyset {
5
- active;
6
- verificationKeys = new Map();
7
- constructor(kid, signingSeed) {
8
- const normalizedKID = normalizeKid(kid);
9
- const retainedSeed = copySigningSeed(signingSeed);
10
- this.active = { kid: normalizedKID, signingSeed: retainedSeed };
11
- this.verificationKeys.set(normalizedKID, ed25519.getPublicKey(retainedSeed));
12
- }
13
- static random(kid) {
14
- const seed = crypto.getRandomValues(new Uint8Array(32));
15
- try {
16
- return new IssuerKeyset(kid, seed);
17
- }
18
- finally {
19
- seed.fill(0);
20
- }
21
- }
22
- currentKID() {
23
- return this.requireActive().kid;
24
- }
25
- publicKeys() {
26
- this.requireActive();
27
- return new Map([...this.verificationKeys.entries()]
28
- .sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0)
29
- .map(([kid, publicKey]) => [kid, publicKey.slice()]));
30
- }
31
- sign(payload) {
32
- const active = this.requireActive();
33
- return signToken(active.signingSeed, { ...payload, kid: active.kid });
34
- }
35
- addVerificationKey(kid, publicKey) {
36
- this.requireActive();
37
- const normalizedKID = normalizeKid(kid);
38
- const retainedKey = copyPublicKey(publicKey);
39
- const existing = this.verificationKeys.get(normalizedKID);
40
- if (existing != null) {
41
- if (!equalBytes(existing, retainedKey))
42
- throw new Error(`key ID ${normalizedKID} is already bound to a different public key`);
43
- return;
44
- }
45
- this.verificationKeys.set(normalizedKID, retainedKey);
46
- }
47
- rotate(kid, signingSeed) {
48
- const active = this.requireActive();
49
- const nextKID = normalizeKid(kid);
50
- const nextSeed = copySigningSeed(signingSeed);
51
- const nextPublicKey = ed25519.getPublicKey(nextSeed);
52
- const prepublishedKey = this.verificationKeys.get(nextKID);
53
- if (prepublishedKey == null) {
54
- nextSeed.fill(0);
55
- throw new Error(`key ID ${nextKID} must be prepublished before rotation`);
56
- }
57
- if (!equalBytes(prepublishedKey, nextPublicKey)) {
58
- nextSeed.fill(0);
59
- throw new Error(`key ID ${nextKID} is bound to a different public key`);
60
- }
61
- this.active = { kid: nextKID, signingSeed: nextSeed };
62
- active.signingSeed.fill(0);
63
- }
64
- retireVerificationKey(kid) {
65
- const active = this.requireActive();
66
- const normalizedKID = normalizeKid(kid);
67
- if (normalizedKID === active.kid)
68
- throw new Error("the active signing key cannot be retired");
69
- if (!this.verificationKeys.delete(normalizedKID))
70
- throw new Error(`key ID ${normalizedKID} is not published`);
71
- }
72
- exportTunnelKeyset() {
73
- const keys = [...this.publicKeys()].map(([kid, publicKey]) => ({ kid, pubkey_b64u: base64urlEncode(publicKey) }));
74
- return new TextEncoder().encode(JSON.stringify({ keys }, null, 2));
75
- }
76
- dispose() {
77
- const active = this.active;
78
- if (active == null)
79
- return;
80
- active.signingSeed.fill(0);
81
- this.active = undefined;
82
- this.verificationKeys.clear();
83
- }
84
- requireActive() {
85
- if (this.active == null)
86
- throw new Error("issuer keyset is disposed");
87
- return this.active;
88
- }
89
- }
90
- function copySigningSeed(signingSeed) {
91
- if (signingSeed.length !== 32)
92
- throw new TypeError("Ed25519 signing seed must be 32 bytes");
93
- return signingSeed.slice();
94
- }
95
- function copyPublicKey(publicKey) {
96
- if (publicKey.length !== 32)
97
- throw new TypeError("Ed25519 public key must be 32 bytes");
98
- return publicKey.slice();
99
- }
100
- function equalBytes(left, right) {
101
- if (left.length !== right.length)
102
- return false;
103
- let diff = 0;
104
- for (let index = 0; index < left.length; index++)
105
- diff |= left[index] ^ right[index];
106
- return diff === 0;
107
- }
108
- function normalizeKid(kid) {
109
- const value = kid.trim();
110
- if (value === "")
111
- throw new TypeError("key ID is required");
112
- return value;
113
- }
@@ -1,51 +0,0 @@
1
- import { type ConnectArtifact } from "../connect/artifact.js";
2
- type FetchLike = typeof fetch;
3
- export type ControlplaneBaseConfig = Readonly<{
4
- baseUrl?: string;
5
- path?: string;
6
- headers?: HeadersInit;
7
- credentials?: RequestCredentials;
8
- fetch?: FetchLike;
9
- signal?: AbortSignal;
10
- allowLoopbackHTTP?: boolean;
11
- }>;
12
- export type ArtifactRequestCorrelation = Readonly<{
13
- traceId?: string;
14
- }>;
15
- export type RequestConnectArtifactInput = ControlplaneBaseConfig & Readonly<{
16
- endpointId: string;
17
- payload?: Record<string, unknown>;
18
- correlation?: ArtifactRequestCorrelation;
19
- }>;
20
- export type RequestEntryConnectArtifactInput = RequestConnectArtifactInput & Readonly<{
21
- entryTicket: string;
22
- }>;
23
- export type ConnectArtifactEnvelope = Readonly<{
24
- connect_artifact: ConnectArtifact;
25
- }>;
26
- export type ControlplaneErrorEnvelope = Readonly<{
27
- error: Readonly<{
28
- code: string;
29
- message: string;
30
- }>;
31
- }>;
32
- export declare class ControlplaneRequestError extends Error {
33
- readonly status: number;
34
- readonly code: string;
35
- readonly responseBody: unknown;
36
- constructor(args: Readonly<{
37
- status: number;
38
- message: string;
39
- code?: string;
40
- responseBody?: unknown;
41
- }>);
42
- }
43
- export declare const DEFAULT_CONNECT_ARTIFACT_PATH = "/v1/connect/artifact";
44
- export declare const DEFAULT_ENTRY_CONNECT_ARTIFACT_PATH = "/v1/connect/artifact/entry";
45
- export declare function buildControlplaneURL(baseUrl: string | undefined, path: string): string;
46
- export declare function requestControlplaneJSON(url: string, init: RequestInit & Readonly<{
47
- fetch?: FetchLike;
48
- }>): Promise<unknown>;
49
- export declare function requestConnectArtifact(config: RequestConnectArtifactInput): Promise<ConnectArtifact>;
50
- export declare function requestEntryConnectArtifact(config: RequestEntryConnectArtifactInput): Promise<ConnectArtifact>;
51
- export {};