@floegence/flowersec-core 3.2.0 → 4.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 (133) hide show
  1. package/README.md +57 -200
  2. package/THIRD_PARTY_NOTICES.md +11 -11
  3. package/dist/browser/index.d.ts +0 -5
  4. package/dist/browser/index.js +0 -3
  5. package/dist/cli.js +93 -64
  6. package/dist/facade.d.ts +0 -17
  7. package/dist/facade.js +0 -11
  8. package/dist/generated/unicode151.d.ts +2 -0
  9. package/dist/generated/unicode151.js +83 -0
  10. package/dist/interop/proxyServerPeer.js +34 -20
  11. package/dist/interop/serverParityPeer.js +11 -252
  12. package/dist/node/acceptor.d.ts +1 -57
  13. package/dist/node/acceptor.js +3 -420
  14. package/dist/node/acceptorV3.js +2 -2
  15. package/dist/node/connectSessionV3.js +2 -2
  16. package/dist/node/index.d.ts +2 -17
  17. package/dist/node/index.js +1 -9
  18. package/dist/node/nativeTransportAddon.d.ts +7 -36
  19. package/dist/node/nativeTransportAddon.js +7 -27
  20. package/dist/node/proxyServer.d.ts +2 -2
  21. package/dist/node/proxyServer.js +2 -2
  22. package/dist/node/rawQuicAdapterV3.d.ts +2 -2
  23. package/dist/node/rawQuicAdapterV3.js +1 -1
  24. package/dist/node/rawQuicServerV3.d.ts +2 -2
  25. package/dist/node/rawQuicServerV3.js +1 -1
  26. package/dist/node/rawQuicTls.d.ts +2 -0
  27. package/dist/node/{rawQuicAdapter.js → rawQuicTls.js} +30 -45
  28. package/dist/node/tunnelRuntimeV3.js +2 -2
  29. package/dist/proxy/integration.d.ts +5 -5
  30. package/dist/proxy/integration.js +2 -2
  31. package/dist/public/streamHandlers.d.ts +0 -3
  32. package/dist/public/streamHandlers.js +2 -4
  33. package/dist/public/streamMetadata.js +2 -2
  34. package/dist/runtime/errors.d.ts +2 -2
  35. package/dist/testSupport/tlsFixture.d.ts +2 -0
  36. package/dist/testSupport/tlsFixture.js +8 -0
  37. package/dist/utils/errors.d.ts +0 -2
  38. package/dist/utils/errors.js +0 -80
  39. package/dist/v3/artifact.js +17 -3
  40. package/dist/v3/carrier.d.ts +2 -2
  41. package/dist/v3/protocol.js +2 -103
  42. package/dist/v3/retryDisposition.d.ts +2 -2
  43. package/dist/v3/retryDisposition.js +0 -7
  44. package/dist/vendor/tr46.d.ts +4 -0
  45. package/dist/vendor/tr46.js +37 -32
  46. package/package.json +21 -19
  47. package/sbom/cyclonedx.json +62 -62
  48. package/sbom/spdx.json +65 -65
  49. package/dist/browser/connectSession.d.ts +0 -13
  50. package/dist/browser/connectSession.js +0 -40
  51. package/dist/browser/runtimeCapability.d.ts +0 -13
  52. package/dist/browser/runtimeCapability.js +0 -27
  53. package/dist/browser/sessionRuntime.d.ts +0 -2
  54. package/dist/browser/sessionRuntime.js +0 -21
  55. package/dist/browser/v2.d.ts +0 -3
  56. package/dist/browser/v2.js +0 -2
  57. package/dist/browser/webTransportClient.d.ts +0 -8
  58. package/dist/browser/webTransportClient.js +0 -55
  59. package/dist/connectionController.d.ts +0 -55
  60. package/dist/connectionController.js +0 -441
  61. package/dist/connector/adapters/rawQuicCandidate.d.ts +0 -5
  62. package/dist/connector/adapters/rawQuicCandidate.js +0 -71
  63. package/dist/connector/adapters/webSocketCandidate.d.ts +0 -7
  64. package/dist/connector/adapters/webSocketCandidate.js +0 -116
  65. package/dist/connector/adapters/webTransportCandidate.d.ts +0 -5
  66. package/dist/connector/adapters/webTransportCandidate.js +0 -73
  67. package/dist/connector/admissionCommit.d.ts +0 -31
  68. package/dist/connector/admissionCommit.js +0 -239
  69. package/dist/connector/sessionAcceptor.d.ts +0 -38
  70. package/dist/connector/sessionAcceptor.js +0 -158
  71. package/dist/connector/sessionConfig.d.ts +0 -3
  72. package/dist/connector/sessionConfig.js +0 -30
  73. package/dist/connector/sessionConnector.d.ts +0 -47
  74. package/dist/connector/sessionConnector.js +0 -606
  75. package/dist/node/connectSession.d.ts +0 -23
  76. package/dist/node/connectSession.js +0 -93
  77. package/dist/node/controlplane.d.ts +0 -137
  78. package/dist/node/controlplane.js +0 -443
  79. package/dist/node/rawQuicAdapter.d.ts +0 -9
  80. package/dist/node/rawQuicServer.d.ts +0 -24
  81. package/dist/node/rawQuicServer.js +0 -37
  82. package/dist/node/runtimeCapability.d.ts +0 -14
  83. package/dist/node/runtimeCapability.js +0 -24
  84. package/dist/node/sessionRuntime.d.ts +0 -2
  85. package/dist/node/sessionRuntime.js +0 -13
  86. package/dist/node/tunnelRuntime.d.ts +0 -42
  87. package/dist/node/tunnelRuntime.js +0 -559
  88. package/dist/node/v2.d.ts +0 -11
  89. package/dist/node/v2.js +0 -6
  90. package/dist/node/webSocketServer.d.ts +0 -24
  91. package/dist/node/webSocketServer.js +0 -135
  92. package/dist/public/artifact.d.ts +0 -11
  93. package/dist/public/artifact.js +0 -44
  94. package/dist/public/artifactLease.d.ts +0 -10
  95. package/dist/public/artifactLease.js +0 -41
  96. package/dist/public/connectError.d.ts +0 -6
  97. package/dist/public/connectError.js +0 -9
  98. package/dist/transport/webSocketAdapter.d.ts +0 -31
  99. package/dist/transport/webSocketAdapter.js +0 -370
  100. package/dist/transport/webTransportAdapter.d.ts +0 -27
  101. package/dist/transport/webTransportAdapter.js +0 -534
  102. package/dist/v2/admissionError.d.ts +0 -4
  103. package/dist/v2/admissionError.js +0 -8
  104. package/dist/v2/artifact.d.ts +0 -128
  105. package/dist/v2/artifact.js +0 -1131
  106. package/dist/v2/artifactLease.d.ts +0 -1
  107. package/dist/v2/artifactLease.js +0 -7
  108. package/dist/v2/capability.d.ts +0 -29
  109. package/dist/v2/capability.js +0 -190
  110. package/dist/v2/carrier.d.ts +0 -84
  111. package/dist/v2/carrier.js +0 -411
  112. package/dist/v2/contract.d.ts +0 -46
  113. package/dist/v2/contract.js +0 -1
  114. package/dist/v2/handshake.d.ts +0 -80
  115. package/dist/v2/handshake.js +0 -470
  116. package/dist/v2/index.d.ts +0 -14
  117. package/dist/v2/index.js +0 -7
  118. package/dist/v2/opaqueArtifact.d.ts +0 -1
  119. package/dist/v2/opaqueArtifact.js +0 -2
  120. package/dist/v2/protocol.d.ts +0 -99
  121. package/dist/v2/protocol.js +0 -737
  122. package/dist/v2/publicSession.d.ts +0 -1
  123. package/dist/v2/publicSession.js +0 -281
  124. package/dist/v2/retryDisposition.d.ts +0 -16
  125. package/dist/v2/retryDisposition.js +0 -30
  126. package/dist/v2/session.d.ts +0 -310
  127. package/dist/v2/session.js +0 -2127
  128. package/dist/v2/streamLifetimeLedger.d.ts +0 -31
  129. package/dist/v2/streamLifetimeLedger.js +0 -131
  130. package/dist/v2/streamMetadata.d.ts +0 -1
  131. package/dist/v2/streamMetadata.js +0 -7
  132. package/dist/v2/unreliableMessage.d.ts +0 -43
  133. package/dist/v2/unreliableMessage.js +0 -254
@@ -1,6 +1,6 @@
1
1
  import { createRequire } from "node:module";
2
- import { CarrierError, } from "../v2/carrier.js";
3
- export const NATIVE_TRANSPORT_CONTRACT_VERSION = 2;
2
+ import { CarrierError } from "../v3/carrier.js";
3
+ export const NATIVE_TRANSPORT_CONTRACT_VERSION = 3;
4
4
  const NATIVE_PACKAGE = "@floegence/flowersec-node-native";
5
5
  const SERVER_PARITY_NATIVE_ADDON = "FLOWERSEC_SERVER_PARITY_NATIVE_ADDON";
6
6
  export class NativeTransportUnavailableError extends Error {
@@ -45,31 +45,13 @@ export function createNativeRawQuicDriver(addon = loadNativeTransportAddon()) {
45
45
  return Object.freeze({
46
46
  async connectRawQuic(options, operation = {}) {
47
47
  const native = await settle(addon.connectRawQuic(options), operation.signal);
48
- return wrapSession(native, 2);
48
+ return wrapSession(native);
49
49
  },
50
50
  async bindRawQuic(options) {
51
51
  const native = await addon.bindRawQuic(options);
52
52
  return Object.freeze({
53
53
  address: () => native.address(),
54
- accept: async (operation = {}) => wrapSession(await settle(native.accept(), operation.signal), 2),
55
- async close() {
56
- await native.close();
57
- },
58
- });
59
- },
60
- });
61
- }
62
- export function createNativeRawQuicDriverV3(addon = loadNativeTransportAddon()) {
63
- return Object.freeze({
64
- async connectRawQuic(options, operation = {}) {
65
- const native = await settle(addon.connectRawQuicV3(options), operation.signal);
66
- return wrapSession(native, 3);
67
- },
68
- async bindRawQuic(options) {
69
- const native = await addon.bindRawQuicV3(options);
70
- return Object.freeze({
71
- address: () => native.address(),
72
- accept: async (operation = {}) => wrapSession(await settle(native.accept(), operation.signal), 3),
54
+ accept: async (operation = {}) => wrapSession(await settle(native.accept(), operation.signal)),
73
55
  async close() {
74
56
  await native.close();
75
57
  },
@@ -92,12 +74,10 @@ function isNativeTransportAddon(value) {
92
74
  }
93
75
  return contractVersion === NATIVE_TRANSPORT_CONTRACT_VERSION &&
94
76
  typeof candidate.connectRawQuic === "function" &&
95
- typeof candidate.bindRawQuic === "function" &&
96
- typeof candidate.connectRawQuicV3 === "function" &&
97
- typeof candidate.bindRawQuicV3 === "function";
77
+ typeof candidate.bindRawQuic === "function";
98
78
  }
99
- function wrapSession(native, expectedWireVersion) {
100
- if (native.wireVersion !== expectedWireVersion) {
79
+ function wrapSession(native) {
80
+ if (native.wireVersion !== 3) {
101
81
  native.abort();
102
82
  throw new NativeTransportUnavailableError();
103
83
  }
@@ -1,6 +1,6 @@
1
1
  import { StreamHandlers } from "../public/streamHandlers.js";
2
- import { SessionHandlers } from "./acceptor.js";
3
- export type StreamHandlerRegistrar = StreamHandlers | SessionHandlers;
2
+ import { SessionHandlersV3 } from "./acceptor.js";
3
+ export type StreamHandlerRegistrar = StreamHandlers | SessionHandlersV3;
4
4
  export type ProxyServerOptions = Readonly<{
5
5
  upstream: string;
6
6
  upstreamOrigin: string;
@@ -3,7 +3,7 @@ import { SessionError } from "../public/contract.js";
3
3
  import { writeJsonFrame } from "../framing/jsonframe.js";
4
4
  import { ProxyByteReader, writeAll } from "../proxy/stream.js";
5
5
  import { StreamHandlers, registerStreamHandlersAtomically, } from "../public/streamHandlers.js";
6
- import { SessionHandlers, registerSessionStreamHandlersAtomically, } from "./acceptor.js";
6
+ import { SessionHandlersV3, registerSessionStreamHandlersAtomically, } from "./acceptor.js";
7
7
  const HTTP_KIND = "flowersec-proxy/http1";
8
8
  const WS_KIND = "flowersec-proxy/ws";
9
9
  const WIRE_VERSION = 1;
@@ -47,7 +47,7 @@ export class ProxyServer {
47
47
  if (handlers instanceof StreamHandlers) {
48
48
  registerStreamHandlersAtomically(handlers, registrations);
49
49
  }
50
- else if (handlers instanceof SessionHandlers) {
50
+ else if (handlers instanceof SessionHandlersV3) {
51
51
  registerSessionStreamHandlersAtomically(handlers, registrations);
52
52
  }
53
53
  else {
@@ -1,8 +1,8 @@
1
1
  import type { ArtifactV3, CanonicalArtifactCandidateV3 } from "../v3/artifact.js";
2
2
  import type { NativeCarrierSessionV3 } from "../v3/carrier.js";
3
- import type { NativeRawQuicDriverV3 } from "./nativeTransportAddon.js";
3
+ import type { NativeRawQuicDriver } from "./nativeTransportAddon.js";
4
4
  type NodeTLSRootsV3 = string | Uint8Array | readonly (string | Uint8Array)[];
5
- export declare function createNodeRawQuicClientV3(driver: NativeRawQuicDriverV3, candidate: CanonicalArtifactCandidateV3, artifact: ArtifactV3, attemptNowUnixSeconds: number, options?: Readonly<{
5
+ export declare function createNodeRawQuicClientV3(driver: NativeRawQuicDriver, candidate: CanonicalArtifactCandidateV3, artifact: ArtifactV3, attemptNowUnixSeconds: number, options?: Readonly<{
6
6
  roots?: NodeTLSRootsV3;
7
7
  signal?: AbortSignal;
8
8
  handshakeTimeoutMs?: number;
@@ -1,6 +1,6 @@
1
1
  import { rootCertificates } from "node:tls";
2
2
  import { snapshotTransportSecurityPolicyV3, TransportFailureV3, } from "../v3/security.js";
3
- import { normalizeCertificateChain } from "./rawQuicAdapter.js";
3
+ import { normalizeCertificateChain } from "./rawQuicTls.js";
4
4
  import { alpnForPathV3 } from "../v3/transportConstants.js";
5
5
  const DEFAULT_HANDSHAKE_TIMEOUT_MS = 10_000;
6
6
  export async function createNodeRawQuicClientV3(driver, candidate, artifact, attemptNowUnixSeconds, options = {}) {
@@ -1,6 +1,6 @@
1
1
  import { type CarrierSessionV3 } from "../v3/carrier.js";
2
2
  import type { PathKind } from "../v3/contract.js";
3
- import type { NativeRawQuicDriverV3 } from "./nativeTransportAddon.js";
3
+ import type { NativeRawQuicDriver } from "./nativeTransportAddon.js";
4
4
  export type NodeRawQuicListenerOptionsV3 = Readonly<{
5
5
  host: string;
6
6
  port: number;
@@ -21,4 +21,4 @@ export type NodeRawQuicListenerV3 = Readonly<{
21
21
  }>): Promise<CarrierSessionV3>;
22
22
  close(): Promise<void>;
23
23
  }>;
24
- export declare function startNodeRawQuicListenerV3(driver: NativeRawQuicDriverV3, options: NodeRawQuicListenerOptionsV3): Promise<NodeRawQuicListenerV3>;
24
+ export declare function startNodeRawQuicListenerV3(driver: NativeRawQuicDriver, options: NodeRawQuicListenerOptionsV3): Promise<NodeRawQuicListenerV3>;
@@ -1,5 +1,5 @@
1
1
  import { adaptNativeCarrierSessionV3 } from "../v3/carrier.js";
2
- import { normalizeCertificateChain, normalizePrivateKey } from "./rawQuicAdapter.js";
2
+ import { normalizeCertificateChain, normalizePrivateKey } from "./rawQuicTls.js";
3
3
  export async function startNodeRawQuicListenerV3(driver, options) {
4
4
  validateOptions(options);
5
5
  let listener;
@@ -0,0 +1,2 @@
1
+ export declare function normalizeCertificateChain(input: string | Uint8Array | readonly (string | Uint8Array)[]): readonly Uint8Array[];
2
+ export declare function normalizePrivateKey(input: string | Uint8Array): Uint8Array;
@@ -1,25 +1,8 @@
1
1
  import { X509Certificate, createPrivateKey } from "node:crypto";
2
- const DEFAULT_HANDSHAKE_TIMEOUT_MS = 10_000;
3
2
  const CERTIFICATE_PEM_BEGIN = "-----BEGIN CERTIFICATE-----";
4
3
  const CERTIFICATE_PEM_END = "-----END CERTIFICATE-----";
5
4
  const MAX_CERTIFICATE_PEM_BYTES = 256 * 1024;
6
5
  const MAX_CERTIFICATE_CHAIN_LENGTH = 32;
7
- export async function createNodeRawQuicClientV2(driver, candidate, artifact, tls, signal, handshakeTimeoutMs = DEFAULT_HANDSHAKE_TIMEOUT_MS) {
8
- const url = new URL(candidate.normalized_url);
9
- if (candidate.carrier !== "raw_quic" || url.protocol !== "quic:" || url.port === "") {
10
- throw new TypeError("invalid raw QUIC candidate");
11
- }
12
- const options = {
13
- host: unbracket(url.hostname),
14
- port: Number(url.port),
15
- serverName: unbracket(url.hostname),
16
- path: artifact.path.kind,
17
- trustRootsDer: normalizeCertificateChain(tls.ca),
18
- inboundBidirectionalStreamCapacity: artifact.session.max_inbound_streams + 2,
19
- handshakeTimeoutMs,
20
- };
21
- return await driver.connectRawQuic(options, { signal });
22
- }
23
6
  export function normalizeCertificateChain(input) {
24
7
  const values = Array.isArray(input) ? input : [input];
25
8
  const certificates = [];
@@ -28,32 +11,46 @@ export function normalizeCertificateChain(input) {
28
11
  if (typeof value !== "string" && !(value instanceof Uint8Array)) {
29
12
  throw new TypeError("invalid raw QUIC certificate");
30
13
  }
31
- totalInputBytes += typeof value === "string" ? Buffer.byteLength(value, "utf8") : value.byteLength;
32
- if (totalInputBytes > MAX_CERTIFICATE_PEM_BYTES)
14
+ totalInputBytes += typeof value === "string"
15
+ ? Buffer.byteLength(value, "utf8")
16
+ : value.byteLength;
17
+ if (totalInputBytes > MAX_CERTIFICATE_PEM_BYTES) {
33
18
  throw new TypeError("invalid raw QUIC certificate");
19
+ }
34
20
  if (typeof value === "string") {
35
21
  const blocks = splitCertificatePEM(value);
36
- if (blocks.length === 0)
37
- throw new TypeError("invalid raw QUIC certificate");
38
- if (certificates.length + blocks.length > MAX_CERTIFICATE_CHAIN_LENGTH) {
22
+ if (blocks.length === 0 || certificates.length + blocks.length > MAX_CERTIFICATE_CHAIN_LENGTH) {
39
23
  throw new TypeError("invalid raw QUIC certificate");
40
24
  }
41
25
  for (const block of blocks)
42
26
  certificates.push(parseCertificate(block));
43
27
  }
44
- else if (value instanceof Uint8Array && value.length > 0) {
45
- if (certificates.length >= MAX_CERTIFICATE_CHAIN_LENGTH)
28
+ else if (value.length > 0) {
29
+ if (certificates.length >= MAX_CERTIFICATE_CHAIN_LENGTH) {
46
30
  throw new TypeError("invalid raw QUIC certificate");
31
+ }
47
32
  certificates.push(parseCertificate(value));
48
33
  }
49
34
  else {
50
35
  throw new TypeError("invalid raw QUIC certificate");
51
36
  }
52
37
  }
53
- if (certificates.length === 0)
38
+ if (certificates.length === 0) {
54
39
  throw new TypeError("raw QUIC requires explicit trust roots");
40
+ }
55
41
  return certificates;
56
42
  }
43
+ export function normalizePrivateKey(input) {
44
+ if (input.length === 0)
45
+ throw new TypeError("invalid raw QUIC private key");
46
+ try {
47
+ const key = createPrivateKey(input);
48
+ return new Uint8Array(key.export({ format: "der", type: "pkcs8" }));
49
+ }
50
+ catch {
51
+ throw new TypeError("invalid raw QUIC private key");
52
+ }
53
+ }
57
54
  function splitCertificatePEM(value) {
58
55
  if (Buffer.byteLength(value, "utf8") > MAX_CERTIFICATE_PEM_BYTES) {
59
56
  throw new TypeError("invalid raw QUIC certificate");
@@ -63,37 +60,28 @@ function splitCertificatePEM(value) {
63
60
  while (cursor < value.length) {
64
61
  const begin = value.indexOf(CERTIFICATE_PEM_BEGIN, cursor);
65
62
  if (begin < 0) {
66
- if (value.slice(cursor).trim().length !== 0)
63
+ if (value.slice(cursor).trim().length !== 0) {
67
64
  throw new TypeError("invalid raw QUIC certificate");
65
+ }
68
66
  break;
69
67
  }
70
- if (value.slice(cursor, begin).trim().length !== 0)
68
+ if (value.slice(cursor, begin).trim().length !== 0) {
71
69
  throw new TypeError("invalid raw QUIC certificate");
70
+ }
72
71
  const contentStart = begin + CERTIFICATE_PEM_BEGIN.length;
73
72
  const end = value.indexOf(CERTIFICATE_PEM_END, contentStart);
74
- if (end < 0 || value.indexOf(CERTIFICATE_PEM_BEGIN, contentStart) >= 0 && value.indexOf(CERTIFICATE_PEM_BEGIN, contentStart) < end) {
73
+ const nested = value.indexOf(CERTIFICATE_PEM_BEGIN, contentStart);
74
+ if (end < 0 || nested >= 0 && nested < end) {
75
75
  throw new TypeError("invalid raw QUIC certificate");
76
76
  }
77
77
  blocks.push(value.slice(begin, end + CERTIFICATE_PEM_END.length));
78
- if (blocks.length > MAX_CERTIFICATE_CHAIN_LENGTH)
78
+ if (blocks.length > MAX_CERTIFICATE_CHAIN_LENGTH) {
79
79
  throw new TypeError("invalid raw QUIC certificate");
80
+ }
80
81
  cursor = end + CERTIFICATE_PEM_END.length;
81
82
  }
82
83
  return blocks;
83
84
  }
84
- export function normalizePrivateKey(input) {
85
- if ((typeof input === "string" && input.length === 0) ||
86
- (input instanceof Uint8Array && input.length === 0)) {
87
- throw new TypeError("invalid raw QUIC private key");
88
- }
89
- try {
90
- const key = createPrivateKey(input);
91
- return new Uint8Array(key.export({ format: "der", type: "pkcs8" }));
92
- }
93
- catch {
94
- throw new TypeError("invalid raw QUIC private key");
95
- }
96
- }
97
85
  function parseCertificate(input) {
98
86
  try {
99
87
  return new Uint8Array(new X509Certificate(input).raw);
@@ -102,6 +90,3 @@ function parseCertificate(input) {
102
90
  throw new TypeError("invalid raw QUIC certificate");
103
91
  }
104
92
  }
105
- function unbracket(host) {
106
- return host.startsWith("[") && host.endsWith("]") ? host.slice(1, -1) : host;
107
- }
@@ -2,7 +2,7 @@ import { createAdmissionReasonRegistryV3, receiveSessionAdmissionV3, rejectSessi
2
2
  import { configureServerWebSocketCarrierRoleV3 } from "../v3/webSocketCarrier.js";
3
3
  import { AdmissionStatusV3, encodeFSA3ResponseV3, } from "../v3/artifact.js";
4
4
  import { startNodeWebSocketListenerV3 } from "./webSocketServerV3.js";
5
- import { createNativeRawQuicDriverV3 } from "./nativeTransportAddon.js";
5
+ import { createNativeRawQuicDriver } from "./nativeTransportAddon.js";
6
6
  import { startNodeRawQuicListenerV3 } from "./rawQuicServerV3.js";
7
7
  import { consumeTunnelAuthorizationGrantV3, inspectTunnelAuthorizationGrantV3, retireTunnelAuthorizationGrantV3, runtimeAuthorizationRequestV3FromDecoded, validateTunnelAuthorizationGrantV3, } from "./runtimeAuthorizationV3.js";
8
8
  const DEFAULT_PAIR_TIMEOUT_MS = 10_000;
@@ -107,7 +107,7 @@ async function startTunnelRuntimeV3(state) {
107
107
  pendingSessionTimeoutMs: state.limits.admissionTimeoutMs,
108
108
  cleanupTimeoutMs: state.limits.cleanupTimeoutMs,
109
109
  })
110
- : await startNodeRawQuicListenerV3(rawQuicDriver ??= createNativeRawQuicDriverV3(), {
110
+ : await startNodeRawQuicListenerV3(rawQuicDriver ??= createNativeRawQuicDriver(), {
111
111
  ...listener,
112
112
  path: "tunnel",
113
113
  inboundBidirectionalStreamCapacity: state.options.maxInboundStreams + 2,
@@ -1,10 +1,10 @@
1
- import { type SessionOptions } from "../browser/connectSession.js";
2
- import type { ArtifactLease } from "../public/artifactLease.js";
1
+ import { type SessionOptionsV3 } from "../browser/connectSessionV3.js";
2
+ import type { ArtifactLeaseV3 } from "../v3/artifactLease.js";
3
3
  import type { Session } from "../public/contract.js";
4
4
  import type { ProxyRuntime, ProxyRuntimeOptions } from "./types.js";
5
5
  import { type ProxyControllerWindowHandle, type RegisterProxyControllerWindowOptions } from "./windowBridge.js";
6
6
  export type ProxyBrowserConnectOptions = Readonly<{
7
- connect?: SessionOptions;
7
+ connect?: SessionOptionsV3;
8
8
  runtime?: Omit<ProxyRuntimeOptions, "session">;
9
9
  serviceWorker?: Readonly<{
10
10
  scriptUrl: string;
@@ -19,11 +19,11 @@ export type ProxyBrowserHandle = Readonly<{
19
19
  runtime: ProxyRuntime;
20
20
  dispose(): Promise<void>;
21
21
  }>;
22
- export declare function connectProxyBrowser(lease: ArtifactLease, options?: ProxyBrowserConnectOptions): Promise<ProxyBrowserHandle>;
22
+ export declare function connectProxyBrowser(lease: ArtifactLeaseV3, options?: ProxyBrowserConnectOptions): Promise<ProxyBrowserHandle>;
23
23
  export type ProxyControllerBrowserConnectOptions = ProxyBrowserConnectOptions & Readonly<{
24
24
  controller: Omit<RegisterProxyControllerWindowOptions, "runtime">;
25
25
  }>;
26
26
  export type ProxyControllerBrowserHandle = ProxyBrowserHandle & Readonly<{
27
27
  controller: ProxyControllerWindowHandle;
28
28
  }>;
29
- export declare function connectProxyControllerBrowser(lease: ArtifactLease, options: ProxyControllerBrowserConnectOptions): Promise<ProxyControllerBrowserHandle>;
29
+ export declare function connectProxyControllerBrowser(lease: ArtifactLeaseV3, options: ProxyControllerBrowserConnectOptions): Promise<ProxyControllerBrowserHandle>;
@@ -1,9 +1,9 @@
1
- import { connect } from "../browser/connectSession.js";
1
+ import { connectV3 } from "../browser/connectSessionV3.js";
2
2
  import { createProxyRuntime } from "./runtime.js";
3
3
  import { registerServiceWorkerAndEnsureControl } from "./registerServiceWorker.js";
4
4
  import { registerProxyControllerWindow, } from "./windowBridge.js";
5
5
  export async function connectProxyBrowser(lease, options = {}) {
6
- const session = await connect(lease, options.connect);
6
+ const session = await connectV3(lease, options.connect);
7
7
  let runtime;
8
8
  try {
9
9
  if (options.serviceWorker !== undefined)
@@ -18,6 +18,3 @@ export declare class StreamHandlers {
18
18
  handleStream(kind: string, handler: StreamHandler): void;
19
19
  serve(session: Session, options?: OperationOptions): Promise<void>;
20
20
  }
21
- /** Explicit-compatibility application-stream handlers. */
22
- export declare class LegacyStreamHandlers extends StreamHandlers {
23
- }
@@ -1,5 +1,5 @@
1
1
  import { SessionError, } from "./contract.js";
2
- import { validApplicationStreamKind } from "../v2/protocol.js";
2
+ import { validApplicationStreamKind } from "../v3/protocol.js";
3
3
  const DEFAULT_MAX_CONCURRENT_STREAMS = 64;
4
4
  const MAX_CONCURRENT_STREAMS = 128;
5
5
  export class HandlerRegistrationError extends Error {
@@ -11,6 +11,7 @@ export class HandlerRegistrationError extends Error {
11
11
  }
12
12
  }
13
13
  const streamHandlerStates = new WeakMap();
14
+ // The v2 identifier is denied so it cannot be repurposed as an application stream.
14
15
  const RESERVED_RPC_KINDS = new Set(["flowersec.rpc.v2", "flowersec.rpc.v3"]);
15
16
  /** Carrier-neutral application-stream handlers for any established Session. */
16
17
  export class StreamHandlers {
@@ -38,9 +39,6 @@ export class StreamHandlers {
38
39
  await serveFrozenStreamHandlers(freezeStreamHandlers(this), session, options, async () => await session.close());
39
40
  }
40
41
  }
41
- /** Explicit-compatibility application-stream handlers. */
42
- export class LegacyStreamHandlers extends StreamHandlers {
43
- }
44
42
  function mutableStreamHandlerState(handlers) {
45
43
  const state = streamHandlerStates.get(handlers);
46
44
  if (state === undefined)
@@ -1,4 +1,4 @@
1
- import { canonicalStreamMetadataJSONV2Internal } from "../v2/protocol.js";
1
+ import { canonicalStreamMetadataJSONV3Internal } from "../v3/protocol.js";
2
2
  export class StreamMetadataError extends Error {
3
3
  constructor() {
4
4
  super("invalid Flowersec stream metadata");
@@ -18,7 +18,7 @@ export class StreamMetadata {
18
18
  }
19
19
  export function createStreamMetadata(values) {
20
20
  try {
21
- const canonical = canonicalStreamMetadataJSONV2Internal(values);
21
+ const canonical = canonicalStreamMetadataJSONV3Internal(values);
22
22
  return StreamMetadata.fromCanonicalValues(deepFreeze(JSON.parse(canonical)));
23
23
  }
24
24
  catch {
@@ -1,6 +1,6 @@
1
1
  export type RuntimeErrorCode = "runtime_unsupported" | "runtime_start_failed" | "runtime_crashed";
2
2
  export declare class RuntimeError extends Error {
3
3
  readonly code: RuntimeErrorCode;
4
- readonly cause?: unknown | undefined;
5
- constructor(code: RuntimeErrorCode, message: string, cause?: unknown | undefined);
4
+ readonly cause?: unknown;
5
+ constructor(code: RuntimeErrorCode, message: string, cause?: unknown);
6
6
  }
@@ -0,0 +1,2 @@
1
+ export declare const testCertificatePEM: string;
2
+ export declare const testPrivateKeyPEM: string;
@@ -0,0 +1,8 @@
1
+ const certificateDER = Buffer.from("MIIBjzCCAUGgAwIBAgIUW8hQEpQsUJN9a6qqF2g6hsNpSm8wBQYDK2VwMBQxEjAQBgNVBAMMCWxvY2FsaG9zdDAeFw0yNjA3MjAxOTAxMjFaFw0zNjA3MTcxOTAxMjFaMBQxEjAQBgNVBAMMCWxvY2FsaG9zdDAqMAUGAytlcAMhAAihki/Jec+1EaC6E6PsSxjMYFAazrgkNiUIlbj/+A/0o4GkMIGhMB0GA1UdDgQWBBQCuKxQmMQkAAy9KkfuD+WOmrrMbTAfBgNVHSMEGDAWgBQCuKxQmMQkAAy9KkfuD+WOmrrMbTAsBgNVHREEJTAjgglsb2NhbGhvc3SHBH8AAAGHEAAAAAAAAAAAAAAAAAAAAAEwDAYDVR0TAQH/BAIwADAOBgNVHQ8BAf8EBAMCB4AwEwYDVR0lBAwwCgYIKwYBBQUHAwEwBQYDK2VwA0EArZng3XitiH2E1pW/NTxQvEOBXJYpYE8coQmLV4yTjfI43CWHMG6lIrwk/so67oe6Z2R4iHGjUm3Tuy50Fl8hBw==", "base64");
2
+ const privateKeyDER = Buffer.from("MC4CAQAwBQYDK2VwBCIEICxYUWHqGoh0CBBohsaNg/NThm1n3UeWCzYuq6jS+Qi6", "base64");
3
+ export const testCertificatePEM = pem("CERTIFICATE", certificateDER);
4
+ export const testPrivateKeyPEM = pem("PRIVATE KEY", privateKeyDER);
5
+ function pem(label, der) {
6
+ const lines = Buffer.from(der).toString("base64").match(/.{1,64}/gu) ?? [];
7
+ return `-----BEGIN ${label}-----\n${lines.join("\n")}\n-----END ${label}-----\n`;
8
+ }
@@ -4,5 +4,3 @@ export declare class TimeoutError extends Error {
4
4
  export declare class AbortError extends Error {
5
5
  constructor(message?: string);
6
6
  }
7
- export type ConnectErrorPath = "auto" | "tunnel" | "direct";
8
- export type ConnectErrorStage = "validate" | "connect" | "session" | "rpc" | "close";
@@ -1,4 +1,3 @@
1
- import { ConnectError } from "../public/connectError.js";
2
1
  export class TimeoutError extends Error {
3
2
  constructor(message = "timeout") {
4
3
  super(message);
@@ -11,87 +10,8 @@ export class AbortError extends Error {
11
10
  this.name = "AbortError";
12
11
  }
13
12
  }
14
- const internalDetails = new WeakMap();
15
- /** @internal */
16
- export function createConnectErrorInternal(args) {
17
- const error = new ConnectError(publicCode(args.code));
18
- internalDetails.set(error, Object.freeze({
19
- code: args.code,
20
- stage: args.stage,
21
- ...(args.cause === undefined ? {} : { cause: args.cause }),
22
- diagnostics: Object.freeze((args.diagnostics ?? []).map((diagnostic) => Object.freeze({ ...diagnostic }))),
23
- }));
24
- return error;
25
- }
26
- /** @internal */
27
- export function connectErrorDetailsInternal(error) {
28
- return internalDetails.get(error) ?? Object.freeze({
29
- code: internalCode(error.code),
30
- stage: "connect",
31
- diagnostics: Object.freeze([]),
32
- });
33
- }
34
- /** @internal */
35
- export function isTimeoutError(e) {
36
- return e instanceof TimeoutError;
37
- }
38
- /** @internal */
39
- export function isAbortError(e) {
40
- return e instanceof AbortError;
41
- }
42
- /** @internal */
43
- export function isConnectError(e) {
44
- return e instanceof ConnectError;
45
- }
46
13
  /** @internal */
47
14
  export function throwIfAborted(signal, message) {
48
15
  if (signal?.aborted)
49
16
  throw new AbortError(message);
50
17
  }
51
- function publicCode(code) {
52
- switch (code) {
53
- case "timeout": return "timeout";
54
- case "canceled": return "canceled";
55
- case "resolve_failed": return "resolve_failed";
56
- case "credential_commit_failed": return "credential_spend_failed";
57
- case "handshake_failed": return "handshake_failed";
58
- case "rpc_failed": return "rpc_failed";
59
- case "resource_exhausted": return "resource_exhausted";
60
- case "not_connected": return "not_connected";
61
- case "invalid_option": return "invalid_options";
62
- case "timestamp_after_init_exp": return "expired_artifact";
63
- case "dial_failed":
64
- case "upgrade_failed":
65
- case "attach_failed":
66
- case "unsupported_capability":
67
- case "too_many_connections":
68
- case "policy_denied":
69
- case "policy_error":
70
- case "replace_rate_limited":
71
- case "ping_failed":
72
- case "rekey_failed":
73
- case "mux_failed":
74
- case "accept_stream_failed":
75
- case "open_stream_failed":
76
- case "stream_hello_failed":
77
- return "connection_failed";
78
- default:
79
- return "invalid_input";
80
- }
81
- }
82
- function internalCode(code) {
83
- switch (code) {
84
- case "invalid_input": return "invalid_input";
85
- case "invalid_options": return "invalid_option";
86
- case "expired_artifact": return "timestamp_after_init_exp";
87
- case "resolve_failed": return "resolve_failed";
88
- case "credential_spend_failed": return "credential_commit_failed";
89
- case "connection_failed": return "dial_failed";
90
- case "timeout": return "timeout";
91
- case "canceled": return "canceled";
92
- case "handshake_failed": return "handshake_failed";
93
- case "rpc_failed": return "rpc_failed";
94
- case "resource_exhausted": return "resource_exhausted";
95
- case "not_connected": return "not_connected";
96
- }
97
- }
@@ -1,7 +1,8 @@
1
1
  import { sha256 } from "@noble/hashes/sha2.js";
2
2
  import { base64urlDecode, base64urlEncode } from "../utils/base64url.js";
3
3
  import { concatBytes, readU32be, u32be } from "../utils/bin.js";
4
- import { toASCII } from "../vendor/tr46.js";
4
+ import { unicode151Assigned } from "../generated/unicode151.js";
5
+ import { toASCII, toUnicode } from "../vendor/tr46.js";
5
6
  import { canonicalizeJCSV3 } from "./jcs.js";
6
7
  import { preflightJSONV3 } from "./jsonPreflight.js";
7
8
  import { FLOWERSEC_V3_CRYPTO_LABELS, FLOWERSEC_V3_PROFILE, wireProfileForPathV3, } from "./transportConstants.js";
@@ -671,14 +672,20 @@ function normalizeDNSOrIPv4(host) {
671
672
  }
672
673
  return parts.map((part) => String(Number(part))).join(".");
673
674
  }
674
- const ascii = toASCII(host, {
675
+ const options = {
675
676
  checkHyphens: true,
676
677
  checkBidi: true,
677
678
  checkJoiners: true,
678
679
  useSTD3ASCIIRules: true,
679
680
  verifyDNSLength: true,
680
681
  transitionalProcessing: false,
681
- });
682
+ };
683
+ if (!hasOnlyUnicode151Scalars(host))
684
+ throw invalidCandidate("DNS label");
685
+ const decoded = toUnicode(host, options);
686
+ if (decoded.error || !hasOnlyUnicode151Scalars(decoded.domain))
687
+ throw invalidCandidate("DNS label");
688
+ const ascii = toASCII(host, options);
682
689
  if (ascii === null || ascii.endsWith("."))
683
690
  throw invalidCandidate("DNS label");
684
691
  const normalized = ascii.toLowerCase();
@@ -688,6 +695,13 @@ function normalizeDNSOrIPv4(host) {
688
695
  }
689
696
  return normalized;
690
697
  }
698
+ function hasOnlyUnicode151Scalars(value) {
699
+ for (const scalar of value) {
700
+ if (!unicode151Assigned(scalar.codePointAt(0)))
701
+ return false;
702
+ }
703
+ return true;
704
+ }
691
705
  function requestFromValidatedArtifact(artifact, candidateSet, chosenCandidateID) {
692
706
  const common = {
693
707
  profile: PROFILE,
@@ -2,8 +2,8 @@ import type { CarrierKind, OperationOptionsV3, PathKind } from "./contract.js";
2
2
  export type CarrierErrorCode = "aborted" | "closed" | "reset" | "stop_sending" | "stop_sending_unavailable" | "write_closed" | "datagram_unavailable";
3
3
  export declare class CarrierError extends Error {
4
4
  readonly code: CarrierErrorCode;
5
- readonly cause?: unknown | undefined;
6
- constructor(code: CarrierErrorCode, message: string, cause?: unknown | undefined);
5
+ readonly cause?: unknown;
6
+ constructor(code: CarrierErrorCode, message: string, cause?: unknown);
7
7
  }
8
8
  export interface CarrierStreamV3 {
9
9
  read(options?: OperationOptionsV3): Promise<Uint8Array | null>;