@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.
- package/README.md +10 -1
- package/dist/_http.d.ts +24 -5
- package/dist/_http.d.ts.map +1 -1
- package/dist/_http.js +21 -11
- package/dist/_http.js.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/inkbox.d.ts +4 -0
- package/dist/inkbox.d.ts.map +1 -1
- package/dist/inkbox.js +5 -0
- package/dist/inkbox.js.map +1 -1
- package/dist/tunnels/_validation.d.ts +7 -0
- package/dist/tunnels/_validation.d.ts.map +1 -0
- package/dist/tunnels/_validation.js +27 -0
- package/dist/tunnels/_validation.js.map +1 -0
- package/dist/tunnels/client/_bridge.d.ts +35 -0
- package/dist/tunnels/client/_bridge.d.ts.map +1 -0
- package/dist/tunnels/client/_bridge.js +52 -0
- package/dist/tunnels/client/_bridge.js.map +1 -0
- package/dist/tunnels/client/_callable_streaming.d.ts +25 -0
- package/dist/tunnels/client/_callable_streaming.d.ts.map +1 -0
- package/dist/tunnels/client/_callable_streaming.js +158 -0
- package/dist/tunnels/client/_callable_streaming.js.map +1 -0
- package/dist/tunnels/client/_cert.d.ts +45 -0
- package/dist/tunnels/client/_cert.d.ts.map +1 -0
- package/dist/tunnels/client/_cert.js +193 -0
- package/dist/tunnels/client/_cert.js.map +1 -0
- package/dist/tunnels/client/_dispatch.d.ts +109 -0
- package/dist/tunnels/client/_dispatch.d.ts.map +1 -0
- package/dist/tunnels/client/_dispatch.js +314 -0
- package/dist/tunnels/client/_dispatch.js.map +1 -0
- package/dist/tunnels/client/_envelope.d.ts +55 -0
- package/dist/tunnels/client/_envelope.d.ts.map +1 -0
- package/dist/tunnels/client/_envelope.js +97 -0
- package/dist/tunnels/client/_envelope.js.map +1 -0
- package/dist/tunnels/client/_h1_server.d.ts +37 -0
- package/dist/tunnels/client/_h1_server.d.ts.map +1 -0
- package/dist/tunnels/client/_h1_server.js +433 -0
- package/dist/tunnels/client/_h1_server.js.map +1 -0
- package/dist/tunnels/client/_h2_transcode.d.ts +43 -0
- package/dist/tunnels/client/_h2_transcode.d.ts.map +1 -0
- package/dist/tunnels/client/_h2_transcode.js +488 -0
- package/dist/tunnels/client/_h2_transcode.js.map +1 -0
- package/dist/tunnels/client/_handler.d.ts +62 -0
- package/dist/tunnels/client/_handler.d.ts.map +1 -0
- package/dist/tunnels/client/_handler.js +121 -0
- package/dist/tunnels/client/_handler.js.map +1 -0
- package/dist/tunnels/client/_listener.d.ts +64 -0
- package/dist/tunnels/client/_listener.d.ts.map +1 -0
- package/dist/tunnels/client/_listener.js +113 -0
- package/dist/tunnels/client/_listener.js.map +1 -0
- package/dist/tunnels/client/_protocol.d.ts +67 -0
- package/dist/tunnels/client/_protocol.d.ts.map +1 -0
- package/dist/tunnels/client/_protocol.js +86 -0
- package/dist/tunnels/client/_protocol.js.map +1 -0
- package/dist/tunnels/client/_runtime.d.ts +143 -0
- package/dist/tunnels/client/_runtime.d.ts.map +1 -0
- package/dist/tunnels/client/_runtime.js +1679 -0
- package/dist/tunnels/client/_runtime.js.map +1 -0
- package/dist/tunnels/client/_state.d.ts +45 -0
- package/dist/tunnels/client/_state.d.ts.map +1 -0
- package/dist/tunnels/client/_state.js +165 -0
- package/dist/tunnels/client/_state.js.map +1 -0
- package/dist/tunnels/client/_tls.d.ts +50 -0
- package/dist/tunnels/client/_tls.d.ts.map +1 -0
- package/dist/tunnels/client/_tls.js +139 -0
- package/dist/tunnels/client/_tls.js.map +1 -0
- package/dist/tunnels/client/_upstream_tls.d.ts +25 -0
- package/dist/tunnels/client/_upstream_tls.d.ts.map +1 -0
- package/dist/tunnels/client/_upstream_tls.js +24 -0
- package/dist/tunnels/client/_upstream_tls.js.map +1 -0
- package/dist/tunnels/client/_url_forward.d.ts +92 -0
- package/dist/tunnels/client/_url_forward.d.ts.map +1 -0
- package/dist/tunnels/client/_url_forward.js +255 -0
- package/dist/tunnels/client/_url_forward.js.map +1 -0
- package/dist/tunnels/client/_validation.d.ts +27 -0
- package/dist/tunnels/client/_validation.d.ts.map +1 -0
- package/dist/tunnels/client/_validation.js +96 -0
- package/dist/tunnels/client/_validation.js.map +1 -0
- package/dist/tunnels/client/_ws.d.ts +149 -0
- package/dist/tunnels/client/_ws.d.ts.map +1 -0
- package/dist/tunnels/client/_ws.js +351 -0
- package/dist/tunnels/client/_ws.js.map +1 -0
- package/dist/tunnels/client/_ws_passthrough.d.ts +129 -0
- package/dist/tunnels/client/_ws_passthrough.d.ts.map +1 -0
- package/dist/tunnels/client/_ws_passthrough.js +432 -0
- package/dist/tunnels/client/_ws_passthrough.js.map +1 -0
- package/dist/tunnels/client/_ws_url_bridge.d.ts +71 -0
- package/dist/tunnels/client/_ws_url_bridge.d.ts.map +1 -0
- package/dist/tunnels/client/_ws_url_bridge.js +474 -0
- package/dist/tunnels/client/_ws_url_bridge.js.map +1 -0
- package/dist/tunnels/client/_ws_url_edge_bridge.d.ts +26 -0
- package/dist/tunnels/client/_ws_url_edge_bridge.d.ts.map +1 -0
- package/dist/tunnels/client/_ws_url_edge_bridge.js +256 -0
- package/dist/tunnels/client/_ws_url_edge_bridge.js.map +1 -0
- package/dist/tunnels/client/_wsframe.d.ts +142 -0
- package/dist/tunnels/client/_wsframe.d.ts.map +1 -0
- package/dist/tunnels/client/_wsframe.js +282 -0
- package/dist/tunnels/client/_wsframe.js.map +1 -0
- package/dist/tunnels/client/index.d.ts +101 -0
- package/dist/tunnels/client/index.d.ts.map +1 -0
- package/dist/tunnels/client/index.js +242 -0
- package/dist/tunnels/client/index.js.map +1 -0
- package/dist/tunnels/exceptions.d.ts +31 -0
- package/dist/tunnels/exceptions.d.ts.map +1 -0
- package/dist/tunnels/exceptions.js +68 -0
- package/dist/tunnels/exceptions.js.map +1 -0
- package/dist/tunnels/resources/tunnels.d.ts +73 -0
- package/dist/tunnels/resources/tunnels.d.ts.map +1 -0
- package/dist/tunnels/resources/tunnels.js +173 -0
- package/dist/tunnels/resources/tunnels.js.map +1 -0
- package/dist/tunnels/types.d.ts +99 -0
- package/dist/tunnels/types.d.ts.map +1 -0
- package/dist/tunnels/types.js +76 -0
- package/dist/tunnels/types.js.map +1 -0
- package/package.json +14 -5
- 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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"}
|