@antasphere/chassis-server 1.1.0 → 1.3.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.
- package/dist/api/apikeys.d.ts +4 -1
- package/dist/api/apikeys.d.ts.map +1 -1
- package/dist/api/apikeys.js +11 -1
- package/dist/api/apikeys.js.map +1 -1
- package/dist/api/create-api.d.ts +3 -0
- package/dist/api/create-api.d.ts.map +1 -1
- package/dist/api/create-api.js +8 -3
- package/dist/api/create-api.js.map +1 -1
- package/dist/api/invitations.d.ts.map +1 -1
- package/dist/api/invitations.js +6 -0
- package/dist/api/invitations.js.map +1 -1
- package/dist/api/members.d.ts +5 -2
- package/dist/api/members.d.ts.map +1 -1
- package/dist/api/members.js +1 -0
- package/dist/api/members.js.map +1 -1
- package/dist/api/projects.d.ts +3 -0
- package/dist/api/projects.d.ts.map +1 -1
- package/dist/api/projects.js +13 -1
- package/dist/api/projects.js.map +1 -1
- package/dist/api/webhooks.d.ts +24 -0
- package/dist/api/webhooks.d.ts.map +1 -0
- package/dist/api/webhooks.js +170 -0
- package/dist/api/webhooks.js.map +1 -0
- package/dist/app.d.ts.map +1 -1
- package/dist/app.js +11 -5
- package/dist/app.js.map +1 -1
- package/dist/boot.d.ts.map +1 -1
- package/dist/boot.js +47 -4
- package/dist/boot.js.map +1 -1
- package/dist/email/index.d.ts +1 -0
- package/dist/email/index.d.ts.map +1 -1
- package/dist/email/index.js +1 -0
- package/dist/email/index.js.map +1 -1
- package/dist/env.d.ts +9 -0
- package/dist/env.d.ts.map +1 -1
- package/dist/env.js +11 -0
- package/dist/env.js.map +1 -1
- package/dist/identity/demo-pass-rules.d.ts.map +1 -1
- package/dist/identity/demo-pass-rules.js +6 -1
- package/dist/identity/demo-pass-rules.js.map +1 -1
- package/dist/identity/hub-reconcile.d.ts +2 -2
- package/dist/identity/hub-reconcile.d.ts.map +1 -1
- package/dist/identity/hub-reconcile.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/jobs/index.d.ts +1 -1
- package/dist/jobs/index.d.ts.map +1 -1
- package/dist/jobs/index.js.map +1 -1
- package/dist/jobs/pgboss.d.ts +34 -0
- package/dist/jobs/pgboss.d.ts.map +1 -1
- package/dist/jobs/pgboss.js +32 -9
- package/dist/jobs/pgboss.js.map +1 -1
- package/dist/mcp/index.d.ts +1 -0
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +1 -0
- package/dist/mcp/index.js.map +1 -1
- package/dist/mcp/server.d.ts +1 -1
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +11 -2
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/tool-kit.d.ts +2 -0
- package/dist/mcp/tool-kit.d.ts.map +1 -1
- package/dist/mcp/tool-kit.js +5 -1
- package/dist/mcp/tool-kit.js.map +1 -1
- package/dist/mcp/webhooks.d.ts +16 -0
- package/dist/mcp/webhooks.d.ts.map +1 -0
- package/dist/mcp/webhooks.js +226 -0
- package/dist/mcp/webhooks.js.map +1 -0
- package/dist/members/removal.d.ts +10 -2
- package/dist/members/removal.d.ts.map +1 -1
- package/dist/members/removal.js +3 -0
- package/dist/members/removal.js.map +1 -1
- package/dist/middleware/idempotency.d.ts.map +1 -1
- package/dist/middleware/idempotency.js +3 -1
- package/dist/middleware/idempotency.js.map +1 -1
- package/dist/middleware/index.d.ts +1 -0
- package/dist/middleware/index.d.ts.map +1 -1
- package/dist/middleware/index.js +1 -0
- package/dist/middleware/index.js.map +1 -1
- package/dist/middleware/scopes.d.ts +9 -1
- package/dist/middleware/scopes.d.ts.map +1 -1
- package/dist/middleware/scopes.js +8 -1
- package/dist/middleware/scopes.js.map +1 -1
- package/dist/middleware/static-assets.d.ts +63 -0
- package/dist/middleware/static-assets.d.ts.map +1 -0
- package/dist/middleware/static-assets.js +125 -0
- package/dist/middleware/static-assets.js.map +1 -0
- package/dist/secret-box.d.ts +10 -0
- package/dist/secret-box.d.ts.map +1 -0
- package/dist/secret-box.js +87 -0
- package/dist/secret-box.js.map +1 -0
- package/dist/testing/helpers.d.ts +8 -0
- package/dist/testing/helpers.d.ts.map +1 -1
- package/dist/testing/helpers.js.map +1 -1
- package/dist/tool-definition.d.ts +24 -0
- package/dist/tool-definition.d.ts.map +1 -1
- package/dist/tool-definition.js.map +1 -1
- package/dist/webhooks/catalog.d.ts +80 -0
- package/dist/webhooks/catalog.d.ts.map +1 -0
- package/dist/webhooks/catalog.js +134 -0
- package/dist/webhooks/catalog.js.map +1 -0
- package/dist/webhooks/index.d.ts +9 -0
- package/dist/webhooks/index.d.ts.map +1 -0
- package/dist/webhooks/index.js +9 -0
- package/dist/webhooks/index.js.map +1 -0
- package/dist/webhooks/jobs.d.ts +12 -0
- package/dist/webhooks/jobs.d.ts.map +1 -0
- package/dist/webhooks/jobs.js +77 -0
- package/dist/webhooks/jobs.js.map +1 -0
- package/dist/webhooks/mail.d.ts +24 -0
- package/dist/webhooks/mail.d.ts.map +1 -0
- package/dist/webhooks/mail.js +21 -0
- package/dist/webhooks/mail.js.map +1 -0
- package/dist/webhooks/match.d.ts +22 -0
- package/dist/webhooks/match.d.ts.map +1 -0
- package/dist/webhooks/match.js +40 -0
- package/dist/webhooks/match.js.map +1 -0
- package/dist/webhooks/safe-fetch.d.ts +94 -0
- package/dist/webhooks/safe-fetch.d.ts.map +1 -0
- package/dist/webhooks/safe-fetch.js +263 -0
- package/dist/webhooks/safe-fetch.js.map +1 -0
- package/dist/webhooks/service.d.ts +245 -0
- package/dist/webhooks/service.d.ts.map +1 -0
- package/dist/webhooks/service.js +861 -0
- package/dist/webhooks/service.js.map +1 -0
- package/dist/webhooks/signing.d.ts +40 -0
- package/dist/webhooks/signing.d.ts.map +1 -0
- package/dist/webhooks/signing.js +108 -0
- package/dist/webhooks/signing.js.map +1 -0
- package/dist/webhooks/tx-send.d.ts +31 -0
- package/dist/webhooks/tx-send.d.ts.map +1 -0
- package/dist/webhooks/tx-send.js +22 -0
- package/dist/webhooks/tx-send.js.map +1 -0
- package/package.json +7 -3
- package/src/api/apikeys.ts +12 -1
- package/src/api/create-api.ts +17 -3
- package/src/api/invitations.ts +6 -0
- package/src/api/members.ts +6 -2
- package/src/api/projects.ts +16 -1
- package/src/api/webhooks.ts +205 -0
- package/src/app.ts +17 -4
- package/src/boot.ts +48 -4
- package/src/email/index.ts +1 -0
- package/src/env.ts +11 -0
- package/src/identity/demo-pass-rules.ts +6 -1
- package/src/identity/hub-reconcile.ts +2 -2
- package/src/index.ts +13 -1
- package/src/jobs/index.ts +1 -0
- package/src/jobs/pgboss.ts +71 -9
- package/src/mcp/index.ts +1 -0
- package/src/mcp/server.ts +10 -2
- package/src/mcp/tool-kit.ts +7 -1
- package/src/mcp/webhooks.ts +370 -0
- package/src/members/removal.ts +21 -1
- package/src/middleware/idempotency.ts +3 -1
- package/src/middleware/index.ts +11 -0
- package/src/middleware/scopes.ts +14 -0
- package/src/middleware/static-assets.ts +144 -0
- package/src/secret-box.ts +93 -0
- package/src/testing/helpers.ts +8 -0
- package/src/tool-definition.ts +29 -0
- package/src/webhooks/catalog.ts +205 -0
- package/src/webhooks/index.ts +58 -0
- package/src/webhooks/jobs.ts +92 -0
- package/src/webhooks/mail.ts +48 -0
- package/src/webhooks/match.ts +52 -0
- package/src/webhooks/safe-fetch.ts +335 -0
- package/src/webhooks/service.ts +1089 -0
- package/src/webhooks/signing.ts +132 -0
- package/src/webhooks/tx-send.ts +53 -0
- package/test/host/drizzle/0002_webhooks.sql +49 -0
- package/test/host/drizzle/meta/0002_snapshot.json +3952 -0
- package/test/host/drizzle/meta/_journal.json +8 -1
- package/test/host/identity.ts +6 -1
- package/test/host/minimal-host.ts +2 -1
- package/test/host/minimal-tool.ts +32 -4
- package/test/integration/mcp-webhooks.test.ts +360 -0
- package/test/integration/webhooks.test.ts +1684 -0
- package/test/unit/secret-box.test.ts +124 -0
- package/test/unit/static-assets.test.ts +227 -0
- package/test/unit/tx-send.test.ts +23 -0
- package/test/unit/webhook-catalog.test.ts +151 -0
- package/test/unit/webhook-env.test.ts +60 -0
- package/test/unit/webhook-jobs.test.ts +92 -0
- package/test/unit/webhook-match.test.ts +81 -0
- package/test/unit/webhook-safe-fetch.test.ts +330 -0
- package/test/unit/webhook-signing.test.ts +169 -0
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
import { promises as dns } from 'node:dns';
|
|
2
|
+
import http from 'node:http';
|
|
3
|
+
import https from 'node:https';
|
|
4
|
+
import { isIP } from 'node:net';
|
|
5
|
+
import { addAbortSignal } from 'node:stream';
|
|
6
|
+
const REFUSAL_MESSAGES = {
|
|
7
|
+
scheme: 'the webhook URL must use http: or https:',
|
|
8
|
+
port: "the webhook URL must use its scheme's default port",
|
|
9
|
+
userinfo: 'the webhook URL must not carry a user name or a password',
|
|
10
|
+
private_address: 'the webhook URL resolves to an address that is not on the public internet',
|
|
11
|
+
https_required: 'the webhook URL must use https:',
|
|
12
|
+
unresolvable: 'the webhook URL names a host that resolves to no address',
|
|
13
|
+
malformed: 'the webhook URL is not a valid URL'
|
|
14
|
+
};
|
|
15
|
+
/** A webhook URL the guard refused (a scheme, a port, userinfo, an address). */
|
|
16
|
+
export class WebhookUrlRefusedError extends Error {
|
|
17
|
+
code;
|
|
18
|
+
constructor(code, message) {
|
|
19
|
+
super(message ?? REFUSAL_MESSAGES[code]);
|
|
20
|
+
this.name = 'WebhookUrlRefusedError';
|
|
21
|
+
this.code = code;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
const DEFAULT_TIMEOUT_MS = 10_000;
|
|
25
|
+
const DEFAULT_MAX_RESPONSE_BYTES = 65_536;
|
|
26
|
+
// ── The address check ────────────────────────────────────────────────────────
|
|
27
|
+
function ipv4(ip) {
|
|
28
|
+
const parts = ip.split('.');
|
|
29
|
+
if (parts.length !== 4)
|
|
30
|
+
return null;
|
|
31
|
+
const bytes = parts.map((p) => (/^\d{1,3}$/.test(p) ? Number(p) : NaN));
|
|
32
|
+
return bytes.every((b) => b >= 0 && b <= 255) ? bytes : null;
|
|
33
|
+
}
|
|
34
|
+
function isPublicV4(b) {
|
|
35
|
+
const [a, c] = [b[0], b[1]];
|
|
36
|
+
if (a === 0 || a === 10 || a === 127)
|
|
37
|
+
return false; // this network, private, loopback
|
|
38
|
+
if (a === 100 && c >= 64 && c <= 127)
|
|
39
|
+
return false; // carrier-grade NAT
|
|
40
|
+
if (a === 169 && c === 254)
|
|
41
|
+
return false; // link-local, the cloud metadata address
|
|
42
|
+
if (a === 172 && c >= 16 && c <= 31)
|
|
43
|
+
return false; // private
|
|
44
|
+
if (a === 192 && c === 0 && b[2] === 0)
|
|
45
|
+
return false; // IETF protocol assignments
|
|
46
|
+
if (a === 192 && c === 0 && b[2] === 2)
|
|
47
|
+
return false; // documentation
|
|
48
|
+
if (a === 192 && c === 168)
|
|
49
|
+
return false; // private
|
|
50
|
+
if (a === 198 && (c === 18 || c === 19))
|
|
51
|
+
return false; // benchmarking
|
|
52
|
+
if (a === 198 && c === 51 && b[2] === 100)
|
|
53
|
+
return false; // documentation
|
|
54
|
+
if (a === 203 && c === 0 && b[2] === 113)
|
|
55
|
+
return false; // documentation
|
|
56
|
+
return a < 224; // multicast, reserved, broadcast
|
|
57
|
+
}
|
|
58
|
+
/** The eight 16-bit groups of an IPv6 address (a trailing dotted IPv4 folded in), or null. */
|
|
59
|
+
function ipv6(ip) {
|
|
60
|
+
let s = ip.toLowerCase().replace(/%.*$/, '');
|
|
61
|
+
const dotted = /(\d+\.\d+\.\d+\.\d+)$/.exec(s);
|
|
62
|
+
if (dotted) {
|
|
63
|
+
const v4 = ipv4(dotted[1]);
|
|
64
|
+
if (!v4)
|
|
65
|
+
return null;
|
|
66
|
+
s = `${s.slice(0, -dotted[1].length)}${((v4[0] << 8) | v4[1]).toString(16)}:${((v4[2] << 8) | v4[3]).toString(16)}`;
|
|
67
|
+
}
|
|
68
|
+
const halves = s.split('::');
|
|
69
|
+
if (halves.length > 2)
|
|
70
|
+
return null;
|
|
71
|
+
const parse = (h) => (h === '' ? [] : h.split(':'));
|
|
72
|
+
const head = parse(halves[0]);
|
|
73
|
+
const tail = halves.length === 2 ? parse(halves[1]) : [];
|
|
74
|
+
const fill = 8 - head.length - tail.length;
|
|
75
|
+
if (halves.length === 2 ? fill < 1 : fill !== 0)
|
|
76
|
+
return null;
|
|
77
|
+
const groups = [...head, ...Array(halves.length === 2 ? fill : 0).fill('0'), ...tail];
|
|
78
|
+
if (!groups.every((g) => /^[0-9a-f]{1,4}$/.test(g)))
|
|
79
|
+
return null;
|
|
80
|
+
return groups.map((g) => Number.parseInt(g, 16));
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Whether an address is on the public internet: false for loopback, private,
|
|
84
|
+
* link-local (the metadata address), carrier-grade NAT, multicast, reserved,
|
|
85
|
+
* and every IPv6 outside global unicast (`2000::/3`): `::`, `::1`, `fc00::/7`,
|
|
86
|
+
* `fe80::/10`, `ff00::/8`. An IPv4-mapped (`::ffff:a.b.c.d`) or a 6to4
|
|
87
|
+
* (`2002::/16`) address is judged by the IPv4 inside it. Unparseable: false.
|
|
88
|
+
*/
|
|
89
|
+
export function isPublicAddress(ip) {
|
|
90
|
+
if (isIP(ip) === 4) {
|
|
91
|
+
const b = ipv4(ip);
|
|
92
|
+
return b !== null && isPublicV4(b);
|
|
93
|
+
}
|
|
94
|
+
const g = ipv6(ip);
|
|
95
|
+
if (!g)
|
|
96
|
+
return false;
|
|
97
|
+
const embedded = (hi, lo) => isPublicV4([hi >> 8, hi & 255, lo >> 8, lo & 255]);
|
|
98
|
+
if (g.slice(0, 5).every((x) => x === 0) && g[5] === 0xffff)
|
|
99
|
+
return embedded(g[6], g[7]);
|
|
100
|
+
if ((g[0] & 0xe000) !== 0x2000)
|
|
101
|
+
return false; // not global unicast
|
|
102
|
+
if (g[0] === 0x2002)
|
|
103
|
+
return embedded(g[1], g[2]); // 6to4
|
|
104
|
+
if (g[0] === 0x2001 && (g[1] === 0 || g[1] === 0xdb8))
|
|
105
|
+
return false; // Teredo, documentation
|
|
106
|
+
return true;
|
|
107
|
+
}
|
|
108
|
+
/** The URL's host without the brackets of an IPv6 literal. */
|
|
109
|
+
function bareHost(hostname) {
|
|
110
|
+
return hostname.replace(/^\[|\]$/g, '');
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* The checks that need no lookup: `http:`/`https:` only (`https:` alone under
|
|
114
|
+
* `httpsOnly`), the scheme's default port only (`url.port === ''`, so an
|
|
115
|
+
* explicit default port, which the URL parser drops, passes), no userinfo, a
|
|
116
|
+
* non-empty hostname, a literal IP public unless `allowPrivateNetworks`.
|
|
117
|
+
* Throws `WebhookUrlRefusedError`; returns the parsed URL.
|
|
118
|
+
*/
|
|
119
|
+
export function checkWebhookUrl(url, policy) {
|
|
120
|
+
let parsed;
|
|
121
|
+
try {
|
|
122
|
+
parsed = new URL(url);
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
throw new WebhookUrlRefusedError('malformed');
|
|
126
|
+
}
|
|
127
|
+
if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:')
|
|
128
|
+
throw new WebhookUrlRefusedError('scheme');
|
|
129
|
+
if (policy.httpsOnly && parsed.protocol !== 'https:')
|
|
130
|
+
throw new WebhookUrlRefusedError('https_required');
|
|
131
|
+
if (parsed.username || parsed.password)
|
|
132
|
+
throw new WebhookUrlRefusedError('userinfo');
|
|
133
|
+
// The scheme's default port only on the public internet; an operator who
|
|
134
|
+
// opened private networks addresses services on their own ports (an n8n on
|
|
135
|
+
// 5678), so the port rule lifts with the address rule.
|
|
136
|
+
if (parsed.port !== '' && !policy.allowPrivateNetworks)
|
|
137
|
+
throw new WebhookUrlRefusedError('port');
|
|
138
|
+
const host = bareHost(parsed.hostname);
|
|
139
|
+
if (host === '')
|
|
140
|
+
throw new WebhookUrlRefusedError('malformed');
|
|
141
|
+
if (isIP(host) && !policy.allowPrivateNetworks && !isPublicAddress(host)) {
|
|
142
|
+
throw new WebhookUrlRefusedError('private_address');
|
|
143
|
+
}
|
|
144
|
+
return parsed;
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* The address to connect to, once EVERY address the name resolves to is
|
|
148
|
+
* public (or the policy opens private networks); an empty resolution is
|
|
149
|
+
* `unresolvable`. A failing resolver's own error passes through.
|
|
150
|
+
*/
|
|
151
|
+
async function checkedAddress(hostname, policy, lookup) {
|
|
152
|
+
const host = bareHost(hostname);
|
|
153
|
+
const family = isIP(host);
|
|
154
|
+
if (family) {
|
|
155
|
+
if (!policy.allowPrivateNetworks && !isPublicAddress(host)) {
|
|
156
|
+
throw new WebhookUrlRefusedError('private_address');
|
|
157
|
+
}
|
|
158
|
+
return { address: host, family };
|
|
159
|
+
}
|
|
160
|
+
const all = await lookup(host);
|
|
161
|
+
if (all.length === 0)
|
|
162
|
+
throw new WebhookUrlRefusedError('unresolvable');
|
|
163
|
+
if (!policy.allowPrivateNetworks && !all.every((a) => isPublicAddress(a.address))) {
|
|
164
|
+
throw new WebhookUrlRefusedError('private_address');
|
|
165
|
+
}
|
|
166
|
+
return all[0];
|
|
167
|
+
}
|
|
168
|
+
/** The promise, or the signal's reason once it aborts first. */
|
|
169
|
+
function abortable(promise, signal) {
|
|
170
|
+
if (signal.aborted)
|
|
171
|
+
return Promise.reject(signal.reason);
|
|
172
|
+
return new Promise((resolve, reject) => {
|
|
173
|
+
const onAbort = () => reject(signal.reason);
|
|
174
|
+
signal.addEventListener('abort', onAbort, { once: true });
|
|
175
|
+
promise.then((v) => {
|
|
176
|
+
signal.removeEventListener('abort', onAbort);
|
|
177
|
+
resolve(v);
|
|
178
|
+
}, (e) => {
|
|
179
|
+
signal.removeEventListener('abort', onAbort);
|
|
180
|
+
reject(e);
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
/** At most `max` bytes of the body; the rest is never read. */
|
|
185
|
+
async function readCapped(body, max) {
|
|
186
|
+
const chunks = [];
|
|
187
|
+
let size = 0;
|
|
188
|
+
for await (const chunk of body) {
|
|
189
|
+
const buf = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
190
|
+
if (size + buf.length >= max) {
|
|
191
|
+
chunks.push(buf.subarray(0, max - size));
|
|
192
|
+
break;
|
|
193
|
+
}
|
|
194
|
+
chunks.push(buf);
|
|
195
|
+
size += buf.length;
|
|
196
|
+
}
|
|
197
|
+
return Buffer.concat(chunks);
|
|
198
|
+
}
|
|
199
|
+
function nodeTransport(policy, resolve) {
|
|
200
|
+
return (url, init, signal) => {
|
|
201
|
+
const lookup = (hostname, options, callback) => {
|
|
202
|
+
checkedAddress(hostname, policy, resolve).then((first) => {
|
|
203
|
+
if (options.all)
|
|
204
|
+
callback(null, [first]);
|
|
205
|
+
else
|
|
206
|
+
callback(null, first.address, first.family);
|
|
207
|
+
}, (e) => callback(e, '', 0));
|
|
208
|
+
};
|
|
209
|
+
const request = url.protocol === 'https:' ? https.request : http.request;
|
|
210
|
+
return new Promise((done, fail) => {
|
|
211
|
+
const req = request(url, { method: init.method, headers: init.headers, lookup, signal, agent: false }, (res) => done({ status: res.statusCode ?? 0, headers: res.headers, body: res }));
|
|
212
|
+
req.on('error', fail);
|
|
213
|
+
req.end(init.body);
|
|
214
|
+
});
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* One POST, no redirect followed (a 3xx comes back as its status), the address
|
|
219
|
+
* check inside the socket's own lookup (ALL resolved addresses must be public
|
|
220
|
+
* unless `allowPrivateNetworks`; an empty resolution is `unresolvable`),
|
|
221
|
+
* `AbortSignal.timeout(timeoutMs)` over the whole call, the response body read
|
|
222
|
+
* up to `maxResponseBytes` then the stream destroyed. Rejects with
|
|
223
|
+
* `WebhookUrlRefusedError` on a refused address, with the transport's error
|
|
224
|
+
* (ECONNREFUSED, the timeout's AbortError…) otherwise. Sets no `user-agent`:
|
|
225
|
+
* that is the caller's.
|
|
226
|
+
*/
|
|
227
|
+
export async function safePost(url, headers, body, policy, options = {}) {
|
|
228
|
+
const started = performance.now();
|
|
229
|
+
const target = checkWebhookUrl(url, policy);
|
|
230
|
+
const lookup = options.lookup ?? ((host) => dns.lookup(host, { all: true }));
|
|
231
|
+
const signal = AbortSignal.timeout(options.timeoutMs ?? DEFAULT_TIMEOUT_MS);
|
|
232
|
+
const maxBytes = options.maxResponseBytes ?? DEFAULT_MAX_RESPONSE_BYTES;
|
|
233
|
+
const sent = {};
|
|
234
|
+
for (const [name, value] of Object.entries(headers)) {
|
|
235
|
+
if (name.toLowerCase() !== 'content-length')
|
|
236
|
+
sent[name] = value;
|
|
237
|
+
}
|
|
238
|
+
sent['content-length'] = String(Buffer.byteLength(body, 'utf8'));
|
|
239
|
+
const init = { method: 'POST', headers: sent, body };
|
|
240
|
+
let transport;
|
|
241
|
+
if (options.transport) {
|
|
242
|
+
// The test seam: the same check the socket's lookup makes, made first.
|
|
243
|
+
await abortable(checkedAddress(target.hostname, policy, lookup), signal);
|
|
244
|
+
transport = options.transport;
|
|
245
|
+
}
|
|
246
|
+
else {
|
|
247
|
+
transport = nodeTransport(policy, lookup);
|
|
248
|
+
}
|
|
249
|
+
const res = await abortable(transport(target, init, signal), signal);
|
|
250
|
+
try {
|
|
251
|
+
addAbortSignal(signal, res.body);
|
|
252
|
+
const bytes = await readCapped(res.body, maxBytes);
|
|
253
|
+
return {
|
|
254
|
+
status: res.status,
|
|
255
|
+
bodyText: bytes.toString('utf8'),
|
|
256
|
+
durationMs: Math.round(performance.now() - started)
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
finally {
|
|
260
|
+
res.body.destroy();
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
//# sourceMappingURL=safe-fetch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"safe-fetch.js","sourceRoot":"","sources":["../../src/webhooks/safe-fetch.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import type PgBoss from 'pg-boss';
|
|
2
|
+
import { Counter } from 'prom-client';
|
|
3
|
+
import { type Db, type DbConn, type WebhookDeliveryRow, type WebhookEndpointRow } from '@antasphere/chassis-db';
|
|
4
|
+
import type { ToolIdentity, WebhookDelivery, WebhookEndpoint, WebhookEndpointCreate, WebhookEndpointUpdate } from '@antasphere/chassis-contract';
|
|
5
|
+
import type { Logger } from '../logger.js';
|
|
6
|
+
import type { EmailDriver } from '../email/driver.js';
|
|
7
|
+
import type { MailBrand } from '../email/shell.js';
|
|
8
|
+
import type { EventBus } from '../platform/events.js';
|
|
9
|
+
import { type WebhookCatalogIndex, type WebhookSubjectValue } from './catalog.js';
|
|
10
|
+
import { type SafeFetchOptions, type SafeFetchPolicy } from './safe-fetch.js';
|
|
11
|
+
/**
|
|
12
|
+
* Outbound webhooks (PRDCT-3482): the chassis's machinery, tool-neutral.
|
|
13
|
+
*
|
|
14
|
+
* EMIT: a domain write calls `emit(tx, …)` INSIDE its own transaction; one
|
|
15
|
+
* pg-boss job (the event's envelope) is written by the same connection, so
|
|
16
|
+
* the event is durable with the write and never exists for a rolled-back
|
|
17
|
+
* one. A per-replica cache of "does any enabled endpoint of this workspace
|
|
18
|
+
* want this type" (`listenerTtlMs`) makes an emit nobody listens to cost
|
|
19
|
+
* one map lookup: a view with no endpoint costs no job.
|
|
20
|
+
*
|
|
21
|
+
* FAN-OUT (`webhook-events`): the event is matched against the workspace's
|
|
22
|
+
* enabled endpoints (type, then filters); one `webhook_deliveries` row and
|
|
23
|
+
* one delivery job per match, in one transaction.
|
|
24
|
+
*
|
|
25
|
+
* DELIVERY (`webhook-deliveries`): the envelope is signed (Standard
|
|
26
|
+
* Webhooks, two signatures during a rotation overlap) and POSTed through the
|
|
27
|
+
* guarded fetch. A 2xx is `delivered`; anything else fails the job, which
|
|
28
|
+
* pg-boss retries on the queue's budget (ten retries from thirty seconds,
|
|
29
|
+
* about eight and a half hours), then moves to `webhook-deliveries-held`:
|
|
30
|
+
* the row is marked `held`, logged, counted, and queued again an hour later
|
|
31
|
+
* with the whole budget once more, round after round, until the receiver
|
|
32
|
+
* takes it or the endpoint is disabled. A streak of
|
|
33
|
+
* `disableAfterFailures` failures spanning `disableAfterMs` switches the
|
|
34
|
+
* endpoint off and mails its creator once.
|
|
35
|
+
*
|
|
36
|
+
* The secret is stored sealed (`secret-box.ts`, info `chassis:webhook-endpoint`)
|
|
37
|
+
* and shown once, at creation and at rotation.
|
|
38
|
+
*/
|
|
39
|
+
export declare const WEBHOOK_EVENTS_QUEUE = "webhook-events";
|
|
40
|
+
export declare const WEBHOOK_DELIVERIES_QUEUE = "webhook-deliveries";
|
|
41
|
+
export declare const WEBHOOK_DELIVERIES_HELD_QUEUE = "webhook-deliveries-held";
|
|
42
|
+
export declare const WEBHOOK_PURGE_QUEUE = "webhook-purge";
|
|
43
|
+
/** The HKDF info of the endpoints' secret box: never the idempotency one. */
|
|
44
|
+
export declare const WEBHOOK_SECRET_BOX_INFO = "chassis:webhook-endpoint";
|
|
45
|
+
export interface WebhookDials {
|
|
46
|
+
/** Delivery retries before a hold (pg-boss `retryLimit`). */
|
|
47
|
+
retryLimit: number;
|
|
48
|
+
/** The first retry's delay in seconds, doubled each time. */
|
|
49
|
+
retryDelaySeconds: number;
|
|
50
|
+
/** How long a held delivery waits before its next round. */
|
|
51
|
+
heldDelaySeconds: number;
|
|
52
|
+
/** The failure streak that switches an endpoint off… */
|
|
53
|
+
disableAfterFailures: number;
|
|
54
|
+
/** …once it has lasted this long. */
|
|
55
|
+
disableAfterMs: number;
|
|
56
|
+
/** How long "anyone listening?" is remembered per workspace and replica. */
|
|
57
|
+
listenerTtlMs: number;
|
|
58
|
+
/** How long the previous secret keeps signing after a rotation. */
|
|
59
|
+
previousSecretOverlapMs: number;
|
|
60
|
+
/** The guarded fetch's timeout. */
|
|
61
|
+
timeoutMs: number;
|
|
62
|
+
}
|
|
63
|
+
export declare const DEFAULT_WEBHOOK_DIALS: WebhookDials;
|
|
64
|
+
/** What `emit` takes: the workspace, the type, the subjects by KEY, the data. */
|
|
65
|
+
export interface WebhookEmit {
|
|
66
|
+
workspaceId: string;
|
|
67
|
+
type: string;
|
|
68
|
+
subjects?: Readonly<Record<string, WebhookSubjectValue>>;
|
|
69
|
+
data: Record<string, unknown>;
|
|
70
|
+
}
|
|
71
|
+
/** The job the fan-out queue carries: the event as the envelope will say it (minus `attempt`). */
|
|
72
|
+
export interface WebhookEventJob {
|
|
73
|
+
id: string;
|
|
74
|
+
type: string;
|
|
75
|
+
occurredAt: string;
|
|
76
|
+
workspaceId: string;
|
|
77
|
+
/** By subject KEY (`project`), the envelope writes them under each subject's FIELD. */
|
|
78
|
+
subjects: Record<string, WebhookSubjectValue>;
|
|
79
|
+
data: Record<string, unknown>;
|
|
80
|
+
}
|
|
81
|
+
export interface WebhookDeliveryJob {
|
|
82
|
+
deliveryId: string;
|
|
83
|
+
}
|
|
84
|
+
/** The emitter a tool's services receive (`ServiceCore.webhooks`). */
|
|
85
|
+
export interface WebhookEmitter {
|
|
86
|
+
/**
|
|
87
|
+
* Record that something happened, inside the caller's transaction (or on
|
|
88
|
+
* a plain handle, for a write the chassis does not own). Answers the
|
|
89
|
+
* event id, or null when no endpoint of the workspace listens to the type,
|
|
90
|
+
* or the data does not match the declared schema (logged, never thrown:
|
|
91
|
+
* a webhook must never break the write it reports).
|
|
92
|
+
*/
|
|
93
|
+
emit(conn: DbConn, event: WebhookEmit): Promise<string | null>;
|
|
94
|
+
/**
|
|
95
|
+
* Whether any enabled endpoint of the workspace subscribes to the type:
|
|
96
|
+
* the cheap exit a tool takes BEFORE it computes the facts of an event
|
|
97
|
+
* that is frequent and costs something to describe (a view). Cached per
|
|
98
|
+
* replica; `emit` runs the same check itself.
|
|
99
|
+
*/
|
|
100
|
+
wants(workspaceId: string, type: string): Promise<boolean>;
|
|
101
|
+
}
|
|
102
|
+
export interface WebhookServiceDeps {
|
|
103
|
+
db: Db;
|
|
104
|
+
logger: Logger;
|
|
105
|
+
authSecret: string;
|
|
106
|
+
identity: ToolIdentity;
|
|
107
|
+
version: string;
|
|
108
|
+
policy: SafeFetchPolicy;
|
|
109
|
+
email: EmailDriver;
|
|
110
|
+
brand: MailBrand;
|
|
111
|
+
publicBaseUrl: string;
|
|
112
|
+
catalog: WebhookCatalogIndex;
|
|
113
|
+
dials?: Partial<WebhookDials> | undefined;
|
|
114
|
+
/** Test seams of the guarded fetch (a lookup, a transport). */
|
|
115
|
+
fetchOptions?: SafeFetchOptions | undefined;
|
|
116
|
+
now?: () => Date;
|
|
117
|
+
}
|
|
118
|
+
export declare class WebhookRefusedError extends Error {
|
|
119
|
+
readonly code: 'url_refused' | 'not_found' | 'unknown_event' | 'unknown_subject';
|
|
120
|
+
constructor(code: 'url_refused' | 'not_found' | 'unknown_event' | 'unknown_subject', message: string);
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Thrown by an attempt that did not run because the endpoint already holds
|
|
124
|
+
* its share of this replica's pollers (`MAX_IN_FLIGHT_PER_ENDPOINT`): the
|
|
125
|
+
* worker puts the job back in the queue a moment later, its retry budget
|
|
126
|
+
* untouched, so one dead receiver can never take every slot (verifier
|
|
127
|
+
* round 2, MJ-R2-1).
|
|
128
|
+
*/
|
|
129
|
+
export declare class WebhookDeliveryDeferred extends Error {
|
|
130
|
+
readonly deliveryId: string;
|
|
131
|
+
constructor(deliveryId: string);
|
|
132
|
+
}
|
|
133
|
+
/** How many attempts one endpoint may have in flight on one replica at once. */
|
|
134
|
+
export declare const MAX_IN_FLIGHT_PER_ENDPOINT = 2;
|
|
135
|
+
/** Thrown by a retryable attempt that failed, so pg-boss retries the job. */
|
|
136
|
+
export declare class WebhookDeliveryFailed extends Error {
|
|
137
|
+
readonly deliveryId: string;
|
|
138
|
+
constructor(deliveryId: string, message: string);
|
|
139
|
+
}
|
|
140
|
+
export declare class WebhookService implements WebhookEmitter {
|
|
141
|
+
private readonly deps;
|
|
142
|
+
readonly dials: WebhookDials;
|
|
143
|
+
readonly promMetrics: Counter<string>[];
|
|
144
|
+
private readonly box;
|
|
145
|
+
private readonly listeners;
|
|
146
|
+
private readonly deliveries;
|
|
147
|
+
private readonly held;
|
|
148
|
+
private readonly disabled;
|
|
149
|
+
private readonly now;
|
|
150
|
+
/** Filled by boot once pg-boss exists (`attach`); the jobs are declared before it. */
|
|
151
|
+
private boss;
|
|
152
|
+
/** Attempts in flight per endpoint on this replica (the per-endpoint bound). */
|
|
153
|
+
private readonly inFlight;
|
|
154
|
+
constructor(deps: WebhookServiceDeps);
|
|
155
|
+
attach(boss: PgBoss): void;
|
|
156
|
+
get catalog(): WebhookCatalogIndex;
|
|
157
|
+
emit(conn: DbConn, event: WebhookEmit): Promise<string | null>;
|
|
158
|
+
/** Does any enabled endpoint of the workspace subscribe to this type? Cached per replica. */
|
|
159
|
+
wants(workspaceId: string, type: string): Promise<boolean>;
|
|
160
|
+
private loadListeners;
|
|
161
|
+
/** Forget the cached answer of a workspace (every write of its endpoints calls it). */
|
|
162
|
+
invalidateListeners(workspaceId: string): void;
|
|
163
|
+
/**
|
|
164
|
+
* The chassis's own events, bridged from the in-process bus: fired after
|
|
165
|
+
* the write committed, so never for a rolled-back one, and at most once
|
|
166
|
+
* per replica that saw it. The events emitted inside a transaction the
|
|
167
|
+
* chassis owns (projects) do not pass here.
|
|
168
|
+
*/
|
|
169
|
+
bindBus(events: EventBus): void;
|
|
170
|
+
/** One delivery row and one delivery job per endpoint that wants the event; answers how many. */
|
|
171
|
+
fanOut(job: WebhookEventJob): Promise<number>;
|
|
172
|
+
/** The envelope as the receiver reads it (without `attempt`, stamped at each send). */
|
|
173
|
+
private envelopeOf;
|
|
174
|
+
private queueDelivery;
|
|
175
|
+
/**
|
|
176
|
+
* One attempt of a delivery. `retryable` (the queue's worker): a failure
|
|
177
|
+
* throws `WebhookDeliveryFailed` so pg-boss retries. Not retryable (the
|
|
178
|
+
* test ping, run inline): a failure marks the row `failed` and answers it.
|
|
179
|
+
* Answers the row as it is after the attempt.
|
|
180
|
+
*/
|
|
181
|
+
attempt(deliveryId: string, opts: {
|
|
182
|
+
retryable: boolean;
|
|
183
|
+
}): Promise<WebhookDeliveryRow | null>;
|
|
184
|
+
private finish;
|
|
185
|
+
/** Put a delivery back in the queue after `inSeconds` (a deferred attempt). */
|
|
186
|
+
requeue(deliveryId: string, inSeconds: number): Promise<void>;
|
|
187
|
+
/**
|
|
188
|
+
* The held queue's work: mark the row held and queue it again after the
|
|
189
|
+
* hold, in ONE transaction, so a re-send that fails leaves the row pending
|
|
190
|
+
* and the held job's own retry finds it again (the chassis suite's round 1:
|
|
191
|
+
* a row marked held outside the transaction was never re-driven once its
|
|
192
|
+
* send had failed). A row already delivered, failed or gone is left alone.
|
|
193
|
+
*/
|
|
194
|
+
hold(deliveryId: string): Promise<void>;
|
|
195
|
+
private mailDisabled;
|
|
196
|
+
/** The nightly purge: deliveries older than `retentionDays`, in bounded batches. */
|
|
197
|
+
purge(retentionDays: number): Promise<number>;
|
|
198
|
+
/** The URL rule at registration (the address itself is judged at every delivery). */
|
|
199
|
+
checkUrl(url: string): void;
|
|
200
|
+
/**
|
|
201
|
+
* The subscription is checked against the catalog at registration
|
|
202
|
+
* (verifier round 1, MN1): an exact type must be declared, a family must
|
|
203
|
+
* have at least one declared type, `*` is always right; every filter key
|
|
204
|
+
* must be a declared subject. A typo would otherwise subscribe to nothing,
|
|
205
|
+
* silently.
|
|
206
|
+
*/
|
|
207
|
+
checkSubscription(events: readonly string[] | undefined, filters: Record<string, string[]> | undefined): void;
|
|
208
|
+
create(workspaceId: string, createdBy: string, input: WebhookEndpointCreate): Promise<{
|
|
209
|
+
endpoint: WebhookEndpoint;
|
|
210
|
+
secret: string;
|
|
211
|
+
}>;
|
|
212
|
+
get(workspaceId: string, id: string): Promise<WebhookEndpoint | null>;
|
|
213
|
+
list(workspaceId: string, cursor: string | undefined, limit: number): Promise<{
|
|
214
|
+
webhooks: WebhookEndpoint[];
|
|
215
|
+
nextCursor: string | null;
|
|
216
|
+
}>;
|
|
217
|
+
/** Endpoints of the workspace, for the plan limit's count hook. */
|
|
218
|
+
count(workspaceId: string): Promise<number>;
|
|
219
|
+
update(workspaceId: string, id: string, patch: WebhookEndpointUpdate): Promise<WebhookEndpoint | null>;
|
|
220
|
+
remove(workspaceId: string, id: string): Promise<WebhookEndpoint | null>;
|
|
221
|
+
rotateSecret(workspaceId: string, id: string): Promise<{
|
|
222
|
+
endpoint: WebhookEndpoint;
|
|
223
|
+
secret: string;
|
|
224
|
+
previousSecretExpiresAt: string;
|
|
225
|
+
} | null>;
|
|
226
|
+
/** A signed `webhook.ping`, attempted inline, once; the row says how it went. */
|
|
227
|
+
test(workspaceId: string, id: string, sentBy: string | null): Promise<WebhookDelivery | null>;
|
|
228
|
+
listDeliveries(workspaceId: string, id: string, query: {
|
|
229
|
+
cursor?: string | undefined;
|
|
230
|
+
limit: number;
|
|
231
|
+
status?: WebhookDeliveryRow['status'] | undefined;
|
|
232
|
+
}): Promise<{
|
|
233
|
+
deliveries: WebhookDelivery[];
|
|
234
|
+
nextCursor: string | null;
|
|
235
|
+
} | null>;
|
|
236
|
+
getDelivery(workspaceId: string, id: string, deliveryId: string): Promise<WebhookDelivery | null>;
|
|
237
|
+
/** The same event queued again for the endpoint, as a new row (the event id unchanged: the receiver's dedupe key). */
|
|
238
|
+
redeliver(workspaceId: string, id: string, deliveryId: string): Promise<WebhookDelivery | null>;
|
|
239
|
+
private row;
|
|
240
|
+
private deliveryRow;
|
|
241
|
+
private requireBoss;
|
|
242
|
+
}
|
|
243
|
+
export declare function endpointToWire(row: WebhookEndpointRow): WebhookEndpoint;
|
|
244
|
+
export declare function deliveryToWire(row: WebhookDeliveryRow): WebhookDelivery;
|
|
245
|
+
//# sourceMappingURL=service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../src/webhooks/service.ts"],"names":[],"mappings":"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"}
|