@inkbox/sdk 0.2.16 → 0.3.1

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 (119) hide show
  1. package/README.md +10 -1
  2. package/dist/_http.d.ts +24 -5
  3. package/dist/_http.d.ts.map +1 -1
  4. package/dist/_http.js +21 -11
  5. package/dist/_http.js.map +1 -1
  6. package/dist/index.d.ts +4 -0
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +3 -0
  9. package/dist/index.js.map +1 -1
  10. package/dist/inkbox.d.ts +4 -0
  11. package/dist/inkbox.d.ts.map +1 -1
  12. package/dist/inkbox.js +5 -0
  13. package/dist/inkbox.js.map +1 -1
  14. package/dist/tunnels/_validation.d.ts +7 -0
  15. package/dist/tunnels/_validation.d.ts.map +1 -0
  16. package/dist/tunnels/_validation.js +27 -0
  17. package/dist/tunnels/_validation.js.map +1 -0
  18. package/dist/tunnels/client/_bridge.d.ts +35 -0
  19. package/dist/tunnels/client/_bridge.d.ts.map +1 -0
  20. package/dist/tunnels/client/_bridge.js +52 -0
  21. package/dist/tunnels/client/_bridge.js.map +1 -0
  22. package/dist/tunnels/client/_callable_streaming.d.ts +25 -0
  23. package/dist/tunnels/client/_callable_streaming.d.ts.map +1 -0
  24. package/dist/tunnels/client/_callable_streaming.js +158 -0
  25. package/dist/tunnels/client/_callable_streaming.js.map +1 -0
  26. package/dist/tunnels/client/_cert.d.ts +45 -0
  27. package/dist/tunnels/client/_cert.d.ts.map +1 -0
  28. package/dist/tunnels/client/_cert.js +193 -0
  29. package/dist/tunnels/client/_cert.js.map +1 -0
  30. package/dist/tunnels/client/_dispatch.d.ts +109 -0
  31. package/dist/tunnels/client/_dispatch.d.ts.map +1 -0
  32. package/dist/tunnels/client/_dispatch.js +314 -0
  33. package/dist/tunnels/client/_dispatch.js.map +1 -0
  34. package/dist/tunnels/client/_envelope.d.ts +55 -0
  35. package/dist/tunnels/client/_envelope.d.ts.map +1 -0
  36. package/dist/tunnels/client/_envelope.js +97 -0
  37. package/dist/tunnels/client/_envelope.js.map +1 -0
  38. package/dist/tunnels/client/_h1_server.d.ts +37 -0
  39. package/dist/tunnels/client/_h1_server.d.ts.map +1 -0
  40. package/dist/tunnels/client/_h1_server.js +433 -0
  41. package/dist/tunnels/client/_h1_server.js.map +1 -0
  42. package/dist/tunnels/client/_h2_transcode.d.ts +43 -0
  43. package/dist/tunnels/client/_h2_transcode.d.ts.map +1 -0
  44. package/dist/tunnels/client/_h2_transcode.js +488 -0
  45. package/dist/tunnels/client/_h2_transcode.js.map +1 -0
  46. package/dist/tunnels/client/_handler.d.ts +62 -0
  47. package/dist/tunnels/client/_handler.d.ts.map +1 -0
  48. package/dist/tunnels/client/_handler.js +121 -0
  49. package/dist/tunnels/client/_handler.js.map +1 -0
  50. package/dist/tunnels/client/_listener.d.ts +64 -0
  51. package/dist/tunnels/client/_listener.d.ts.map +1 -0
  52. package/dist/tunnels/client/_listener.js +113 -0
  53. package/dist/tunnels/client/_listener.js.map +1 -0
  54. package/dist/tunnels/client/_protocol.d.ts +67 -0
  55. package/dist/tunnels/client/_protocol.d.ts.map +1 -0
  56. package/dist/tunnels/client/_protocol.js +86 -0
  57. package/dist/tunnels/client/_protocol.js.map +1 -0
  58. package/dist/tunnels/client/_runtime.d.ts +143 -0
  59. package/dist/tunnels/client/_runtime.d.ts.map +1 -0
  60. package/dist/tunnels/client/_runtime.js +1679 -0
  61. package/dist/tunnels/client/_runtime.js.map +1 -0
  62. package/dist/tunnels/client/_state.d.ts +45 -0
  63. package/dist/tunnels/client/_state.d.ts.map +1 -0
  64. package/dist/tunnels/client/_state.js +165 -0
  65. package/dist/tunnels/client/_state.js.map +1 -0
  66. package/dist/tunnels/client/_tls.d.ts +50 -0
  67. package/dist/tunnels/client/_tls.d.ts.map +1 -0
  68. package/dist/tunnels/client/_tls.js +139 -0
  69. package/dist/tunnels/client/_tls.js.map +1 -0
  70. package/dist/tunnels/client/_upstream_tls.d.ts +25 -0
  71. package/dist/tunnels/client/_upstream_tls.d.ts.map +1 -0
  72. package/dist/tunnels/client/_upstream_tls.js +24 -0
  73. package/dist/tunnels/client/_upstream_tls.js.map +1 -0
  74. package/dist/tunnels/client/_url_forward.d.ts +92 -0
  75. package/dist/tunnels/client/_url_forward.d.ts.map +1 -0
  76. package/dist/tunnels/client/_url_forward.js +255 -0
  77. package/dist/tunnels/client/_url_forward.js.map +1 -0
  78. package/dist/tunnels/client/_validation.d.ts +27 -0
  79. package/dist/tunnels/client/_validation.d.ts.map +1 -0
  80. package/dist/tunnels/client/_validation.js +96 -0
  81. package/dist/tunnels/client/_validation.js.map +1 -0
  82. package/dist/tunnels/client/_ws.d.ts +149 -0
  83. package/dist/tunnels/client/_ws.d.ts.map +1 -0
  84. package/dist/tunnels/client/_ws.js +351 -0
  85. package/dist/tunnels/client/_ws.js.map +1 -0
  86. package/dist/tunnels/client/_ws_passthrough.d.ts +129 -0
  87. package/dist/tunnels/client/_ws_passthrough.d.ts.map +1 -0
  88. package/dist/tunnels/client/_ws_passthrough.js +432 -0
  89. package/dist/tunnels/client/_ws_passthrough.js.map +1 -0
  90. package/dist/tunnels/client/_ws_url_bridge.d.ts +71 -0
  91. package/dist/tunnels/client/_ws_url_bridge.d.ts.map +1 -0
  92. package/dist/tunnels/client/_ws_url_bridge.js +474 -0
  93. package/dist/tunnels/client/_ws_url_bridge.js.map +1 -0
  94. package/dist/tunnels/client/_ws_url_edge_bridge.d.ts +26 -0
  95. package/dist/tunnels/client/_ws_url_edge_bridge.d.ts.map +1 -0
  96. package/dist/tunnels/client/_ws_url_edge_bridge.js +256 -0
  97. package/dist/tunnels/client/_ws_url_edge_bridge.js.map +1 -0
  98. package/dist/tunnels/client/_wsframe.d.ts +142 -0
  99. package/dist/tunnels/client/_wsframe.d.ts.map +1 -0
  100. package/dist/tunnels/client/_wsframe.js +282 -0
  101. package/dist/tunnels/client/_wsframe.js.map +1 -0
  102. package/dist/tunnels/client/index.d.ts +101 -0
  103. package/dist/tunnels/client/index.d.ts.map +1 -0
  104. package/dist/tunnels/client/index.js +242 -0
  105. package/dist/tunnels/client/index.js.map +1 -0
  106. package/dist/tunnels/exceptions.d.ts +31 -0
  107. package/dist/tunnels/exceptions.d.ts.map +1 -0
  108. package/dist/tunnels/exceptions.js +68 -0
  109. package/dist/tunnels/exceptions.js.map +1 -0
  110. package/dist/tunnels/resources/tunnels.d.ts +73 -0
  111. package/dist/tunnels/resources/tunnels.d.ts.map +1 -0
  112. package/dist/tunnels/resources/tunnels.js +173 -0
  113. package/dist/tunnels/resources/tunnels.js.map +1 -0
  114. package/dist/tunnels/types.d.ts +99 -0
  115. package/dist/tunnels/types.d.ts.map +1 -0
  116. package/dist/tunnels/types.js +76 -0
  117. package/dist/tunnels/types.js.map +1 -0
  118. package/package.json +14 -5
  119. package/protocol/tunnel_protocol_constants.json +65 -0
@@ -0,0 +1,158 @@
1
+ /**
2
+ * inkbox-tunnels/client/_callable_streaming.ts
3
+ *
4
+ * Streaming wrapper for `InkboxHandler` used by `CallableDispatch` in
5
+ * passthrough mode. The Fetch-style handler returns a `Response`; this
6
+ * module pipes that response back through `DispatchResponseSink` so
7
+ * the third party gets a true streamed reply.
8
+ *
9
+ * WebSocket upgrades are handled separately by
10
+ * `_ws_passthrough.bridgeWsHandlerOverSink` — the h1 parser and h2
11
+ * transcoder route `isWebSocket=true` requests directly to
12
+ * `CallableDispatch.dispatchWebSocket` so they never reach this
13
+ * HTTP-only invoker.
14
+ */
15
+ import { HOP_BY_HOP_RESPONSE } from "./_protocol.js";
16
+ /**
17
+ * Build a minimal envelope-shaped object for the InkboxRequestContext.
18
+ * Passthrough callable dispatch doesn't have a true envelope (those
19
+ * are produced by the tunnel server's intake path), so we synthesize a
20
+ * placeholder. The handler can read the typed fields directly via
21
+ * `req` / `ctx` instead of poking at this for normal use.
22
+ */
23
+ function synthesizeEnvelope(req) {
24
+ return Object.freeze({
25
+ requestId: "",
26
+ method: req.method,
27
+ path: req.path,
28
+ routeKind: "webhook",
29
+ wsId: null,
30
+ forwardedHeaders: req.headers,
31
+ body: Buffer.alloc(0),
32
+ bodyUri: null,
33
+ forwardedForIp: req.forwardedForIp,
34
+ tcpId: null,
35
+ sniHost: req.sniHost,
36
+ extraMeta: {},
37
+ });
38
+ }
39
+ async function bodyToReadable(iter) {
40
+ const it = iter[Symbol.asyncIterator]();
41
+ let probed = await it.next();
42
+ if (probed.done)
43
+ return null;
44
+ const first = probed.value;
45
+ return new ReadableStream({
46
+ async start(controller) {
47
+ controller.enqueue(new Uint8Array(first));
48
+ try {
49
+ while (true) {
50
+ const r = await it.next();
51
+ if (r.done)
52
+ break;
53
+ controller.enqueue(new Uint8Array(r.value));
54
+ }
55
+ controller.close();
56
+ }
57
+ catch (err) {
58
+ controller.error(err);
59
+ }
60
+ },
61
+ });
62
+ }
63
+ export async function invokeHandlerStreaming(opts) {
64
+ const { handler, request, response, publicHost, maxOutboundBodyBytes } = opts;
65
+ // Build a Fetch Request from the DispatchRequest. Headers are flat,
66
+ // method/path provided. URL host is the public host.
67
+ const headers = new Headers();
68
+ for (const [k, v] of request.headers) {
69
+ if (k.startsWith(":"))
70
+ continue;
71
+ try {
72
+ headers.append(k, v);
73
+ }
74
+ catch {
75
+ /* invalid header value — skip */
76
+ }
77
+ }
78
+ if (!headers.has("host"))
79
+ headers.set("host", publicHost);
80
+ if (!headers.has("x-forwarded-host"))
81
+ headers.set("x-forwarded-host", publicHost);
82
+ if (!headers.has("x-forwarded-proto"))
83
+ headers.set("x-forwarded-proto", "https");
84
+ if (request.forwardedForIp != null) {
85
+ if (!headers.has("x-forwarded-for"))
86
+ headers.set("x-forwarded-for", request.forwardedForIp);
87
+ }
88
+ const url = `https://${publicHost}${request.path.startsWith("/") ? "" : "/"}${request.path}`;
89
+ const bodyStream = request.method === "GET" || request.method === "HEAD"
90
+ ? null
91
+ : await bodyToReadable(request.body);
92
+ const fetchReq = new Request(url, {
93
+ method: request.method,
94
+ headers,
95
+ body: bodyStream,
96
+ // Required when sending a stream body on Node fetch.
97
+ duplex: "half",
98
+ });
99
+ const ctx = {
100
+ signal: new AbortController().signal,
101
+ forwardedForIp: request.forwardedForIp,
102
+ sniHost: request.sniHost,
103
+ envelope: synthesizeEnvelope(request),
104
+ };
105
+ let resp;
106
+ try {
107
+ resp = await handler(fetchReq, ctx);
108
+ }
109
+ catch {
110
+ await response.sendHead({
111
+ status: 502,
112
+ headers: [["content-type", "text/plain"]],
113
+ });
114
+ await response.sendBody(Buffer.from("upstream error"));
115
+ await response.endBody();
116
+ return;
117
+ }
118
+ const respHeaders = [];
119
+ resp.headers.forEach((value, key) => {
120
+ if (HOP_BY_HOP_RESPONSE.has(key.toLowerCase()))
121
+ return;
122
+ respHeaders.push([key, value]);
123
+ });
124
+ await response.sendHead({ status: resp.status, headers: respHeaders });
125
+ if (resp.body == null) {
126
+ await response.endBody();
127
+ return;
128
+ }
129
+ let bytesOut = 0;
130
+ const reader = resp.body.getReader();
131
+ try {
132
+ while (true) {
133
+ const { done, value } = await reader.read();
134
+ if (done)
135
+ break;
136
+ const buf = Buffer.from(value);
137
+ bytesOut += buf.length;
138
+ if (bytesOut > maxOutboundBodyBytes) {
139
+ await response.reset("response-too-large");
140
+ return;
141
+ }
142
+ await response.sendBody(buf);
143
+ }
144
+ await response.endBody();
145
+ }
146
+ catch {
147
+ await response.reset("upstream-error");
148
+ }
149
+ finally {
150
+ try {
151
+ reader.releaseLock();
152
+ }
153
+ catch {
154
+ /* swallow */
155
+ }
156
+ }
157
+ }
158
+ //# sourceMappingURL=_callable_streaming.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"_callable_streaming.js","sourceRoot":"","sources":["../../../src/tunnels/client/_callable_streaming.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * inkbox-tunnels/client/_cert.ts
3
+ *
4
+ * Passthrough cert lifecycle: load-or-create EC P-256 keypair, build
5
+ * CSR, detect when the cached cert needs resigning. Mirrors Python
6
+ * `_cert.py` exactly so on-disk state interops between the two SDKs.
7
+ *
8
+ * IMPORTANT: This module statically imports `@peculiar/x509` at the
9
+ * top. The lazy-load boundary lives at the **call site** in
10
+ * `_runtime.ts` (or `index.ts`) so the edge-mode bundle never pulls
11
+ * `_cert.ts` into the module graph at all. See M5's bundle-size
12
+ * verification.
13
+ */
14
+ import "reflect-metadata";
15
+ export interface KeyPair {
16
+ privateKey: CryptoKey;
17
+ publicKey: CryptoKey;
18
+ /** PKCS8 PEM of the private key. */
19
+ privatePem: string;
20
+ }
21
+ /**
22
+ * Load EC P-256 key from disk or generate one. Returns the parsed
23
+ * keypair. The on-disk format is PKCS8 PEM, matching Python.
24
+ */
25
+ export declare function loadOrCreateKeypair(stateDir: string): Promise<KeyPair>;
26
+ /**
27
+ * Build a CSR with CN + SAN = `publicHost`. Signed with SHA-256.
28
+ */
29
+ export declare function buildCsr(key: KeyPair, publicHost: string): Promise<string>;
30
+ /** Return the cert's expiry (UTC) or null if missing/invalid. */
31
+ export declare function certExpiry(stateDir: string): Date | null;
32
+ /**
33
+ * Decide whether the cached cert needs resigning.
34
+ *
35
+ * - Missing cert / unreadable cert => resign.
36
+ * - Within the 14-day renewal window => resign.
37
+ * - Cert pubkey doesn't match the on-disk key (key regenerated since
38
+ * last signing) => resign.
39
+ */
40
+ export declare function certNeedsSign(stateDir: string, key: KeyPair): Promise<boolean>;
41
+ /** Persist the signed cert+chain (mode 0o600); return the bytes. */
42
+ export declare function writeCertChain(stateDir: string, certPem: string, chainPem: string): Buffer;
43
+ /** Serialize a private key as unencrypted PKCS8 PEM bytes. */
44
+ export declare function keyPemBytes(key: KeyPair): Promise<Buffer>;
45
+ //# sourceMappingURL=_cert.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"_cert.d.ts","sourceRoot":"","sources":["../../../src/tunnels/client/_cert.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAMH,OAAO,kBAAkB,CAAC;AAyB1B,MAAM,WAAW,OAAO;IACtB,UAAU,EAAE,SAAS,CAAC;IACtB,SAAS,EAAE,SAAS,CAAC;IACrB,oCAAoC;IACpC,UAAU,EAAE,MAAM,CAAC;CACpB;AAOD;;;GAGG;AACH,wBAAsB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAoB5E;AAED;;GAEG;AACH,wBAAsB,QAAQ,CAC5B,GAAG,EAAE,OAAO,EACZ,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,MAAM,CAAC,CAYjB;AAED,iEAAiE;AACjE,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAkBxD;AAED;;;;;;;GAOG;AACH,wBAAsB,aAAa,CACjC,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,OAAO,GACX,OAAO,CAAC,OAAO,CAAC,CAuBlB;AAED,oEAAoE;AACpE,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,GACf,MAAM,CAOR;AAED,8DAA8D;AAC9D,wBAAsB,WAAW,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAE/D"}
@@ -0,0 +1,193 @@
1
+ /**
2
+ * inkbox-tunnels/client/_cert.ts
3
+ *
4
+ * Passthrough cert lifecycle: load-or-create EC P-256 keypair, build
5
+ * CSR, detect when the cached cert needs resigning. Mirrors Python
6
+ * `_cert.py` exactly so on-disk state interops between the two SDKs.
7
+ *
8
+ * IMPORTANT: This module statically imports `@peculiar/x509` at the
9
+ * top. The lazy-load boundary lives at the **call site** in
10
+ * `_runtime.ts` (or `index.ts`) so the edge-mode bundle never pulls
11
+ * `_cert.ts` into the module graph at all. See M5's bundle-size
12
+ * verification.
13
+ */
14
+ // peculiar/x509 internally uses tsyringe, which needs the
15
+ // reflect-metadata polyfill to be loaded BEFORE the library imports.
16
+ // Keep this side-effect import first so the lazy-load boundary in
17
+ // _runtime.ts/index.ts pulls everything in correctly.
18
+ import "reflect-metadata";
19
+ import * as crypto from "node:crypto";
20
+ import * as fs from "node:fs";
21
+ import * as path from "node:path";
22
+ import * as x509 from "@peculiar/x509";
23
+ import { CERT_FILE, KEY_FILE, ensurePrivateStateDir, writePrivateFile, } from "./_state.js";
24
+ const RENEWAL_THRESHOLD_MS = 14 * 24 * 60 * 60 * 1000;
25
+ // peculiar/x509 reads the global cryptoProvider when its routines need
26
+ // crypto primitives. Wire Node's webcrypto in once at module load.
27
+ x509.cryptoProvider.set(crypto.webcrypto);
28
+ // Cast Node's webcrypto subtle to the DOM types used by peculiar/x509
29
+ // and our function signatures. The runtime APIs are identical; the
30
+ // type definitions diverge between `lib.dom.d.ts` and Node's own.
31
+ const subtle = crypto.webcrypto.subtle;
32
+ const ALGORITHM = {
33
+ name: "ECDSA",
34
+ namedCurve: "P-256",
35
+ };
36
+ /**
37
+ * Load EC P-256 key from disk or generate one. Returns the parsed
38
+ * keypair. The on-disk format is PKCS8 PEM, matching Python.
39
+ */
40
+ export async function loadOrCreateKeypair(stateDir) {
41
+ const keyPath = path.join(stateDir, KEY_FILE);
42
+ if (fs.existsSync(keyPath) && fs.statSync(keyPath).isFile()) {
43
+ const pem = fs.readFileSync(keyPath, "utf-8");
44
+ return await importPkcs8Pem(pem);
45
+ }
46
+ ensurePrivateStateDir(stateDir);
47
+ const generated = (await subtle.generateKey({ name: "ECDSA", namedCurve: "P-256" }, true, ["sign", "verify"]));
48
+ const pkcs8 = await subtle.exportKey("pkcs8", generated.privateKey);
49
+ const pem = pkcs8ToPem(Buffer.from(pkcs8));
50
+ writePrivateFile(keyPath, pem);
51
+ return {
52
+ privateKey: generated.privateKey,
53
+ publicKey: generated.publicKey,
54
+ privatePem: pem,
55
+ };
56
+ }
57
+ /**
58
+ * Build a CSR with CN + SAN = `publicHost`. Signed with SHA-256.
59
+ */
60
+ export async function buildCsr(key, publicHost) {
61
+ const csr = await x509.Pkcs10CertificateRequestGenerator.create({
62
+ name: `CN=${publicHost}`,
63
+ keys: { privateKey: key.privateKey, publicKey: key.publicKey },
64
+ signingAlgorithm: { name: "ECDSA", hash: "SHA-256" },
65
+ extensions: [
66
+ new x509.SubjectAlternativeNameExtension([
67
+ { type: "dns", value: publicHost },
68
+ ]),
69
+ ],
70
+ });
71
+ return csr.toString("pem");
72
+ }
73
+ /** Return the cert's expiry (UTC) or null if missing/invalid. */
74
+ export function certExpiry(stateDir) {
75
+ const certPath = path.join(stateDir, CERT_FILE);
76
+ if (!fs.existsSync(certPath))
77
+ return null;
78
+ let pem;
79
+ try {
80
+ pem = fs.readFileSync(certPath, "utf-8");
81
+ }
82
+ catch {
83
+ return null;
84
+ }
85
+ // Read the leaf cert (first PEM block).
86
+ const leafBlock = extractFirstPemBlock(pem, "CERTIFICATE");
87
+ if (leafBlock === null)
88
+ return null;
89
+ try {
90
+ const cert = new x509.X509Certificate(leafBlock);
91
+ return cert.notAfter;
92
+ }
93
+ catch {
94
+ return null;
95
+ }
96
+ }
97
+ /**
98
+ * Decide whether the cached cert needs resigning.
99
+ *
100
+ * - Missing cert / unreadable cert => resign.
101
+ * - Within the 14-day renewal window => resign.
102
+ * - Cert pubkey doesn't match the on-disk key (key regenerated since
103
+ * last signing) => resign.
104
+ */
105
+ export async function certNeedsSign(stateDir, key) {
106
+ const certPath = path.join(stateDir, CERT_FILE);
107
+ const expiry = certExpiry(stateDir);
108
+ if (!fs.existsSync(certPath) || expiry === null)
109
+ return true;
110
+ const now = new Date();
111
+ if (expiry.getTime() - now.getTime() < RENEWAL_THRESHOLD_MS)
112
+ return true;
113
+ try {
114
+ const pem = fs.readFileSync(certPath, "utf-8");
115
+ const leafBlock = extractFirstPemBlock(pem, "CERTIFICATE");
116
+ if (leafBlock === null)
117
+ return true;
118
+ const cert = new x509.X509Certificate(leafBlock);
119
+ const certPubKey = (await cert.publicKey.export(crypto.webcrypto));
120
+ const certPubSpki = await subtle.exportKey("spki", certPubKey);
121
+ const keyPubSpki = await subtle.exportKey("spki", key.publicKey);
122
+ if (Buffer.compare(Buffer.from(certPubSpki), Buffer.from(keyPubSpki)) !== 0) {
123
+ return true;
124
+ }
125
+ return false;
126
+ }
127
+ catch {
128
+ return true;
129
+ }
130
+ }
131
+ /** Persist the signed cert+chain (mode 0o600); return the bytes. */
132
+ export function writeCertChain(stateDir, certPem, chainPem) {
133
+ // PEM cross-language interop policy: leaf first, then intermediates,
134
+ // \n line endings, trailing single \n. Mirror Python `_cert.py`.
135
+ const fullChain = ensureSinglePemTerminator(certPem) + ensureSinglePemTerminator(chainPem);
136
+ const certPath = path.join(stateDir, CERT_FILE);
137
+ writePrivateFile(certPath, fullChain);
138
+ return Buffer.from(fullChain, "ascii");
139
+ }
140
+ /** Serialize a private key as unencrypted PKCS8 PEM bytes. */
141
+ export async function keyPemBytes(key) {
142
+ return Buffer.from(key.privatePem, "ascii");
143
+ }
144
+ // --- internals -----------------------------------------------------------
145
+ async function importPkcs8Pem(pem) {
146
+ const block = extractFirstPemBlock(pem, "PRIVATE KEY");
147
+ if (block === null) {
148
+ throw new Error("private_key.pem: missing PRIVATE KEY block");
149
+ }
150
+ const der = pemBlockToDer(block);
151
+ // Cast to BufferSource's Uint8Array variant — Node's Buffer is
152
+ // assignable but lib.dom.d.ts disagrees on ArrayBufferLike vs
153
+ // ArrayBuffer.
154
+ const privateKey = await subtle.importKey("pkcs8", new Uint8Array(der), ALGORITHM, true, ["sign"]);
155
+ // Derive the public key from the private — JOSE jwk round-trip.
156
+ const jwk = await subtle.exportKey("jwk", privateKey);
157
+ const pubJwk = { ...jwk };
158
+ delete pubJwk.d;
159
+ pubJwk.key_ops = ["verify"];
160
+ const publicKey = await subtle.importKey("jwk", pubJwk, ALGORITHM, true, ["verify"]);
161
+ return { privateKey, publicKey, privatePem: pem };
162
+ }
163
+ function pkcs8ToPem(der) {
164
+ const b64 = der.toString("base64");
165
+ const lines = [];
166
+ for (let i = 0; i < b64.length; i += 64)
167
+ lines.push(b64.slice(i, i + 64));
168
+ return `-----BEGIN PRIVATE KEY-----\n${lines.join("\n")}\n-----END PRIVATE KEY-----\n`;
169
+ }
170
+ function extractFirstPemBlock(pem, label) {
171
+ const begin = `-----BEGIN ${label}-----`;
172
+ const end = `-----END ${label}-----`;
173
+ const beginIdx = pem.indexOf(begin);
174
+ const endIdx = pem.indexOf(end);
175
+ if (beginIdx < 0 || endIdx < 0 || endIdx < beginIdx)
176
+ return null;
177
+ return pem.slice(beginIdx, endIdx + end.length);
178
+ }
179
+ function pemBlockToDer(block) {
180
+ const lines = block.split(/\r?\n/).filter((l) => l.length > 0 && !l.startsWith("-----"));
181
+ return Buffer.from(lines.join(""), "base64");
182
+ }
183
+ function ensureSinglePemTerminator(pem) {
184
+ // Strip Windows line endings, trailing whitespace, and excess
185
+ // newlines; ensure a single \n after the final -----END...-----.
186
+ let out = pem.replace(/\r\n/g, "\n").replace(/[ \t]+\n/g, "\n");
187
+ while (out.endsWith("\n\n"))
188
+ out = out.slice(0, -1);
189
+ if (!out.endsWith("\n"))
190
+ out += "\n";
191
+ return out;
192
+ }
193
+ //# sourceMappingURL=_cert.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"_cert.js","sourceRoot":"","sources":["../../../src/tunnels/client/_cert.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,109 @@
1
+ /**
2
+ * inkbox-tunnels/client/_dispatch.ts
3
+ *
4
+ * The Dispatch interface — both the in-process h1 parser and the h2
5
+ * transcoder hand parsed requests to a Dispatch impl. The interface is
6
+ * transport-neutral; the same impl serves an h1 inbound and an h2 inbound.
7
+ *
8
+ * UpstreamUrlDispatch forwards requests to a customer-supplied URL via
9
+ * undici (one Pool per dispatcher). CallableDispatch invokes an
10
+ * in-process Fetch-style handler. ``dispatchWebSocket`` on either impl
11
+ * routes WS upgrades (h1 ``Upgrade: websocket`` or h2 Extended CONNECT)
12
+ * without going through the HTTP response sink.
13
+ */
14
+ /**
15
+ * Wire-shaped request handed to a Dispatch impl. Both transports
16
+ * populate the same shape; pseudo-headers and h2-only headers are
17
+ * stripped before this point.
18
+ */
19
+ export interface DispatchRequest {
20
+ method: string;
21
+ path: string;
22
+ /** Lower-case header name + value, preserving order and duplicates. */
23
+ headers: Array<[string, string]>;
24
+ /** Streaming body. Resolves with empty Buffer on end-of-body. */
25
+ body: AsyncIterable<Buffer>;
26
+ forwardedForIp: string | null;
27
+ sniHost: string | null;
28
+ isWebSocket: boolean;
29
+ wsSubprotocol: string | null;
30
+ /**
31
+ * Inbound transport: "h1" or "h2". Populated by the parser /
32
+ * transcoder so dispatchers can emit structured telemetry with the
33
+ * full ``dispatch=url-h1|url-h2|callable-h1|callable-h2`` field.
34
+ */
35
+ transport?: "h1" | "h2";
36
+ }
37
+ export interface DispatchResponseHead {
38
+ status: number;
39
+ headers: Array<[string, string]>;
40
+ }
41
+ /** Streamed response sink the Dispatch impl writes to. */
42
+ export interface DispatchResponseSink {
43
+ sendHead(head: DispatchResponseHead): Promise<void>;
44
+ sendBody(chunk: Buffer): Promise<void>;
45
+ endBody(): Promise<void>;
46
+ reset(reason: string): Promise<void>;
47
+ }
48
+ /** Stateless-per-call dispatcher invoked by the parser/transcoder. */
49
+ export interface Dispatch {
50
+ dispatch(request: DispatchRequest, response: DispatchResponseSink): Promise<void>;
51
+ /**
52
+ * Optional WS upgrade entry-point. Transports check for this method
53
+ * before routing a WS upgrade; if absent, they fall back to sending
54
+ * a 501 via the regular HTTP sink.
55
+ */
56
+ dispatchWebSocket?(request: DispatchRequest, ws: import("./_ws_passthrough.js").WebSocketSink): Promise<void>;
57
+ aclose(): Promise<void>;
58
+ }
59
+ export interface UpstreamUrlDispatchOpts {
60
+ forwardTo: string;
61
+ publicHost: string;
62
+ maxOutboundBodyBytes: number;
63
+ maxInboundBodyBytes: number;
64
+ /** Verify upstream TLS certs. Only consulted when forwardTo is https://. */
65
+ verifyTls?: boolean;
66
+ /** PEM CA bundle to trust for the upstream TLS connection. */
67
+ caBundle?: Buffer | string | null;
68
+ }
69
+ /**
70
+ * Forward requests to a customer-supplied URL via undici.
71
+ *
72
+ * One ``undici.Pool`` per dispatcher. The pool's connect options carry
73
+ * the upstream-TLS options when the URL is https://. We always speak
74
+ * h1 to the upstream — we transcode at this boundary.
75
+ */
76
+ export declare class UpstreamUrlDispatch implements Dispatch {
77
+ private readonly pool;
78
+ private readonly forwardTo;
79
+ private readonly publicHost;
80
+ private readonly maxOutboundBodyBytes;
81
+ private readonly maxInboundBodyBytes;
82
+ private readonly verifyTls;
83
+ private readonly caBundle;
84
+ constructor(opts: UpstreamUrlDispatchOpts);
85
+ aclose(): Promise<void>;
86
+ dispatch(request: DispatchRequest, response: DispatchResponseSink): Promise<void>;
87
+ dispatchWebSocket(request: DispatchRequest, ws: import("./_ws_passthrough.js").WebSocketSink): Promise<void>;
88
+ }
89
+ export interface CallableDispatchOpts {
90
+ handler: import("./_handler.js").InkboxHandler;
91
+ /** Optional WebSocket handler — receives `accept`/`send`/iterator. */
92
+ wsHandler?: import("./_ws.js").InkboxWsHandler;
93
+ publicHost: string;
94
+ maxOutboundBodyBytes: number;
95
+ }
96
+ /**
97
+ * Dispatch impl that invokes an in-process `InkboxHandler` (Fetch-style).
98
+ * Used by passthrough mode when the user supplied a `handler` instead
99
+ * of a `forwardTo` URL. When `wsHandler` is also supplied, WebSocket
100
+ * upgrades route through `dispatchWebSocket`.
101
+ */
102
+ export declare class CallableDispatch implements Dispatch {
103
+ private readonly opts;
104
+ constructor(opts: CallableDispatchOpts);
105
+ aclose(): Promise<void>;
106
+ dispatch(request: DispatchRequest, response: DispatchResponseSink): Promise<void>;
107
+ dispatchWebSocket(request: DispatchRequest, ws: import("./_ws_passthrough.js").WebSocketSink): Promise<void>;
108
+ }
109
+ //# sourceMappingURL=_dispatch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"_dispatch.d.ts","sourceRoot":"","sources":["../../../src/tunnels/client/_dispatch.ts"],"names":[],"mappings":"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"}