@secondlayer/shared 3.0.0-beta.1 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Standard Webhooks signing helpers — https://standardwebhooks.com
3
+ *
4
+ * Produces the three headers that every Standard Webhooks receiver expects:
5
+ * webhook-id — UUID identifying the delivery (used for dedup)
6
+ * webhook-timestamp — unix seconds, receiver rejects if skew > tolerance
7
+ * webhook-signature — space-separated list of `vN,<base64-hmac>` tuples
8
+ *
9
+ * The signed content is `{id}.{timestamp}.{body}`. The HMAC key is the raw
10
+ * bytes of the secret. If the secret is a `whsec_`-prefixed base64 string
11
+ * (the Svix convention) we base64-decode after stripping the prefix;
12
+ * otherwise we use the UTF-8 bytes directly.
13
+ */
14
+ interface StandardWebhooksHeaders {
15
+ "webhook-id": string;
16
+ "webhook-timestamp": string;
17
+ "webhook-signature": string;
18
+ }
19
+ interface SignOptions {
20
+ /** Override the delivery id. Defaults to a random UUID v4. */
21
+ id?: string;
22
+ /** Override the timestamp (unix seconds). Defaults to `Date.now()`. */
23
+ timestampSeconds?: number;
24
+ }
25
+ declare function sign(body: string, secret: string, opts?: SignOptions): StandardWebhooksHeaders;
26
+ interface VerifyOptions {
27
+ /** Max clock skew in seconds. Default 5 minutes per spec. */
28
+ toleranceSeconds?: number;
29
+ /** Current time in unix seconds. Injectable for testing. */
30
+ nowSeconds?: number;
31
+ }
32
+ declare function verify(body: string, headers: StandardWebhooksHeaders | Record<string, string | string[] | undefined>, secret: string, opts?: VerifyOptions): boolean;
33
+ export { verify, sign, VerifyOptions, StandardWebhooksHeaders, SignOptions };
@@ -0,0 +1,77 @@
1
+ import { createRequire } from "node:module";
2
+ var __defProp = Object.defineProperty;
3
+ var __returnValue = (v) => v;
4
+ function __exportSetter(name, newValue) {
5
+ this[name] = __returnValue.bind(null, newValue);
6
+ }
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, {
10
+ get: all[name],
11
+ enumerable: true,
12
+ configurable: true,
13
+ set: __exportSetter.bind(all, name)
14
+ });
15
+ };
16
+
17
+ // src/crypto/standard-webhooks.ts
18
+ import { createHmac, randomUUID } from "node:crypto";
19
+ function secretToKey(secret) {
20
+ if (secret.startsWith("whsec_")) {
21
+ return Buffer.from(secret.slice("whsec_".length), "base64");
22
+ }
23
+ return Buffer.from(secret, "utf8");
24
+ }
25
+ function sign(body, secret, opts = {}) {
26
+ const id = opts.id ?? randomUUID();
27
+ const timestamp = String(opts.timestampSeconds ?? Math.floor(Date.now() / 1000));
28
+ const toSign = `${id}.${timestamp}.${body}`;
29
+ const key = secretToKey(secret);
30
+ const signature = createHmac("sha256", key).update(toSign).digest("base64");
31
+ return {
32
+ "webhook-id": id,
33
+ "webhook-timestamp": timestamp,
34
+ "webhook-signature": `v1,${signature}`
35
+ };
36
+ }
37
+ function verify(body, headers, secret, opts = {}) {
38
+ const pick = (k) => {
39
+ const v = headers[k];
40
+ return typeof v === "string" ? v : undefined;
41
+ };
42
+ const id = pick("webhook-id");
43
+ const timestamp = pick("webhook-timestamp");
44
+ const sigHeader = pick("webhook-signature");
45
+ if (!id || !timestamp || !sigHeader)
46
+ return false;
47
+ const ts = Number.parseInt(timestamp, 10);
48
+ if (!Number.isFinite(ts))
49
+ return false;
50
+ const tolerance = opts.toleranceSeconds ?? 5 * 60;
51
+ const now = opts.nowSeconds ?? Math.floor(Date.now() / 1000);
52
+ if (Math.abs(now - ts) > tolerance)
53
+ return false;
54
+ const key = secretToKey(secret);
55
+ const expected = createHmac("sha256", key).update(`${id}.${timestamp}.${body}`).digest("base64");
56
+ for (const part of sigHeader.split(" ")) {
57
+ const [version, sig] = part.split(",", 2);
58
+ if (version !== "v1" || !sig)
59
+ continue;
60
+ if (sig.length !== expected.length)
61
+ continue;
62
+ let diff = 0;
63
+ for (let i = 0;i < sig.length; i++) {
64
+ diff |= sig.charCodeAt(i) ^ expected.charCodeAt(i);
65
+ }
66
+ if (diff === 0)
67
+ return true;
68
+ }
69
+ return false;
70
+ }
71
+ export {
72
+ verify,
73
+ sign
74
+ };
75
+
76
+ //# debugId=AE46F7E38D913C0D64756E2164756E21
77
+ //# sourceMappingURL=standard-webhooks.js.map
@@ -0,0 +1,10 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/crypto/standard-webhooks.ts"],
4
+ "sourcesContent": [
5
+ "import { createHmac, randomUUID } from \"node:crypto\";\n\n/**\n * Standard Webhooks signing helpers — https://standardwebhooks.com\n *\n * Produces the three headers that every Standard Webhooks receiver expects:\n * webhook-id — UUID identifying the delivery (used for dedup)\n * webhook-timestamp — unix seconds, receiver rejects if skew > tolerance\n * webhook-signature — space-separated list of `vN,<base64-hmac>` tuples\n *\n * The signed content is `{id}.{timestamp}.{body}`. The HMAC key is the raw\n * bytes of the secret. If the secret is a `whsec_`-prefixed base64 string\n * (the Svix convention) we base64-decode after stripping the prefix;\n * otherwise we use the UTF-8 bytes directly.\n */\n\nexport interface StandardWebhooksHeaders {\n\t\"webhook-id\": string;\n\t\"webhook-timestamp\": string;\n\t\"webhook-signature\": string;\n}\n\nexport interface SignOptions {\n\t/** Override the delivery id. Defaults to a random UUID v4. */\n\tid?: string;\n\t/** Override the timestamp (unix seconds). Defaults to `Date.now()`. */\n\ttimestampSeconds?: number;\n}\n\nfunction secretToKey(secret: string): Buffer {\n\tif (secret.startsWith(\"whsec_\")) {\n\t\treturn Buffer.from(secret.slice(\"whsec_\".length), \"base64\");\n\t}\n\treturn Buffer.from(secret, \"utf8\");\n}\n\nexport function sign(\n\tbody: string,\n\tsecret: string,\n\topts: SignOptions = {},\n): StandardWebhooksHeaders {\n\tconst id = opts.id ?? randomUUID();\n\tconst timestamp = String(\n\t\topts.timestampSeconds ?? Math.floor(Date.now() / 1000),\n\t);\n\tconst toSign = `${id}.${timestamp}.${body}`;\n\tconst key = secretToKey(secret);\n\tconst signature = createHmac(\"sha256\", key).update(toSign).digest(\"base64\");\n\treturn {\n\t\t\"webhook-id\": id,\n\t\t\"webhook-timestamp\": timestamp,\n\t\t\"webhook-signature\": `v1,${signature}`,\n\t};\n}\n\nexport interface VerifyOptions {\n\t/** Max clock skew in seconds. Default 5 minutes per spec. */\n\ttoleranceSeconds?: number;\n\t/** Current time in unix seconds. Injectable for testing. */\n\tnowSeconds?: number;\n}\n\nexport function verify(\n\tbody: string,\n\theaders:\n\t\t| StandardWebhooksHeaders\n\t\t| Record<string, string | string[] | undefined>,\n\tsecret: string,\n\topts: VerifyOptions = {},\n): boolean {\n\tconst pick = (k: string) => {\n\t\tconst v = (headers as Record<string, unknown>)[k];\n\t\treturn typeof v === \"string\" ? v : undefined;\n\t};\n\tconst id = pick(\"webhook-id\");\n\tconst timestamp = pick(\"webhook-timestamp\");\n\tconst sigHeader = pick(\"webhook-signature\");\n\tif (!id || !timestamp || !sigHeader) return false;\n\n\tconst ts = Number.parseInt(timestamp, 10);\n\tif (!Number.isFinite(ts)) return false;\n\tconst tolerance = opts.toleranceSeconds ?? 5 * 60;\n\tconst now = opts.nowSeconds ?? Math.floor(Date.now() / 1000);\n\tif (Math.abs(now - ts) > tolerance) return false;\n\n\tconst key = secretToKey(secret);\n\tconst expected = createHmac(\"sha256\", key)\n\t\t.update(`${id}.${timestamp}.${body}`)\n\t\t.digest(\"base64\");\n\n\t// webhook-signature can carry multiple versions: \"v1,abc v1a,def\"\n\tfor (const part of sigHeader.split(\" \")) {\n\t\tconst [version, sig] = part.split(\",\", 2);\n\t\tif (version !== \"v1\" || !sig) continue;\n\t\tif (sig.length !== expected.length) continue;\n\t\tlet diff = 0;\n\t\tfor (let i = 0; i < sig.length; i++) {\n\t\t\tdiff |= sig.charCodeAt(i) ^ expected.charCodeAt(i);\n\t\t}\n\t\tif (diff === 0) return true;\n\t}\n\treturn false;\n}\n"
6
+ ],
7
+ "mappings": ";;;;;;;;;;;;;;;;;AAAA;AA6BA,SAAS,WAAW,CAAC,QAAwB;AAAA,EAC5C,IAAI,OAAO,WAAW,QAAQ,GAAG;AAAA,IAChC,OAAO,OAAO,KAAK,OAAO,MAAM,SAAS,MAAM,GAAG,QAAQ;AAAA,EAC3D;AAAA,EACA,OAAO,OAAO,KAAK,QAAQ,MAAM;AAAA;AAG3B,SAAS,IAAI,CACnB,MACA,QACA,OAAoB,CAAC,GACK;AAAA,EAC1B,MAAM,KAAK,KAAK,MAAM,WAAW;AAAA,EACjC,MAAM,YAAY,OACjB,KAAK,oBAAoB,KAAK,MAAM,KAAK,IAAI,IAAI,IAAI,CACtD;AAAA,EACA,MAAM,SAAS,GAAG,MAAM,aAAa;AAAA,EACrC,MAAM,MAAM,YAAY,MAAM;AAAA,EAC9B,MAAM,YAAY,WAAW,UAAU,GAAG,EAAE,OAAO,MAAM,EAAE,OAAO,QAAQ;AAAA,EAC1E,OAAO;AAAA,IACN,cAAc;AAAA,IACd,qBAAqB;AAAA,IACrB,qBAAqB,MAAM;AAAA,EAC5B;AAAA;AAUM,SAAS,MAAM,CACrB,MACA,SAGA,QACA,OAAsB,CAAC,GACb;AAAA,EACV,MAAM,OAAO,CAAC,MAAc;AAAA,IAC3B,MAAM,IAAK,QAAoC;AAAA,IAC/C,OAAO,OAAO,MAAM,WAAW,IAAI;AAAA;AAAA,EAEpC,MAAM,KAAK,KAAK,YAAY;AAAA,EAC5B,MAAM,YAAY,KAAK,mBAAmB;AAAA,EAC1C,MAAM,YAAY,KAAK,mBAAmB;AAAA,EAC1C,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC;AAAA,IAAW,OAAO;AAAA,EAE5C,MAAM,KAAK,OAAO,SAAS,WAAW,EAAE;AAAA,EACxC,IAAI,CAAC,OAAO,SAAS,EAAE;AAAA,IAAG,OAAO;AAAA,EACjC,MAAM,YAAY,KAAK,oBAAoB,IAAI;AAAA,EAC/C,MAAM,MAAM,KAAK,cAAc,KAAK,MAAM,KAAK,IAAI,IAAI,IAAI;AAAA,EAC3D,IAAI,KAAK,IAAI,MAAM,EAAE,IAAI;AAAA,IAAW,OAAO;AAAA,EAE3C,MAAM,MAAM,YAAY,MAAM;AAAA,EAC9B,MAAM,WAAW,WAAW,UAAU,GAAG,EACvC,OAAO,GAAG,MAAM,aAAa,MAAM,EACnC,OAAO,QAAQ;AAAA,EAGjB,WAAW,QAAQ,UAAU,MAAM,GAAG,GAAG;AAAA,IACxC,OAAO,SAAS,OAAO,KAAK,MAAM,KAAK,CAAC;AAAA,IACxC,IAAI,YAAY,QAAQ,CAAC;AAAA,MAAK;AAAA,IAC9B,IAAI,IAAI,WAAW,SAAS;AAAA,MAAQ;AAAA,IACpC,IAAI,OAAO;AAAA,IACX,SAAS,IAAI,EAAG,IAAI,IAAI,QAAQ,KAAK;AAAA,MACpC,QAAQ,IAAI,WAAW,CAAC,IAAI,SAAS,WAAW,CAAC;AAAA,IAClD;AAAA,IACA,IAAI,SAAS;AAAA,MAAG,OAAO;AAAA,EACxB;AAAA,EACA,OAAO;AAAA;",
8
+ "debugId": "AE46F7E38D913C0D64756E2164756E21",
9
+ "names": []
10
+ }
@@ -0,0 +1,475 @@
1
+ import { Kysely } from "kysely";
2
+ import { ColumnType, Generated, Selectable } from "kysely";
3
+ interface BlocksTable {
4
+ height: number;
5
+ hash: string;
6
+ parent_hash: string;
7
+ burn_block_height: number;
8
+ timestamp: number;
9
+ canonical: Generated<boolean>;
10
+ created_at: Generated<Date>;
11
+ }
12
+ interface TransactionsTable {
13
+ tx_id: string;
14
+ block_height: number;
15
+ tx_index: Generated<number>;
16
+ type: string;
17
+ sender: string;
18
+ status: string;
19
+ contract_id: string | null;
20
+ function_name: string | null;
21
+ function_args: Generated<unknown | null>;
22
+ raw_result: Generated<string | null>;
23
+ raw_tx: string;
24
+ created_at: Generated<Date>;
25
+ }
26
+ interface EventsTable {
27
+ id: Generated<string>;
28
+ tx_id: string;
29
+ block_height: number;
30
+ event_index: number;
31
+ type: string;
32
+ data: unknown;
33
+ created_at: Generated<Date>;
34
+ }
35
+ interface IndexProgressTable {
36
+ network: string;
37
+ last_indexed_block: Generated<number>;
38
+ last_contiguous_block: Generated<number>;
39
+ highest_seen_block: Generated<number>;
40
+ updated_at: Generated<Date>;
41
+ }
42
+ interface SubgraphsTable {
43
+ id: Generated<string>;
44
+ name: string;
45
+ version: Generated<string>;
46
+ status: Generated<string>;
47
+ definition: Record<string, unknown>;
48
+ schema_hash: string;
49
+ handler_path: string;
50
+ schema_name: string | null;
51
+ start_block: Generated<number>;
52
+ last_processed_block: Generated<number>;
53
+ reindex_from_block: number | null;
54
+ reindex_to_block: number | null;
55
+ last_error: string | null;
56
+ last_error_at: Date | null;
57
+ total_processed: Generated<number>;
58
+ total_errors: Generated<number>;
59
+ account_id: string;
60
+ handler_code: string | null;
61
+ source_code: string | null;
62
+ project_id: string | null;
63
+ created_at: Generated<Date>;
64
+ updated_at: Generated<Date>;
65
+ }
66
+ interface SubgraphGapsTable {
67
+ id: Generated<string>;
68
+ subgraph_id: string;
69
+ subgraph_name: string;
70
+ gap_start: number;
71
+ gap_end: number;
72
+ reason: string;
73
+ detected_at: Generated<Date>;
74
+ resolved_at: Date | null;
75
+ }
76
+ interface ApiKeysTable {
77
+ id: Generated<string>;
78
+ key_hash: string;
79
+ key_prefix: string;
80
+ name: string | null;
81
+ status: Generated<string>;
82
+ rate_limit: Generated<number>;
83
+ ip_address: string;
84
+ account_id: string;
85
+ last_used_at: Date | null;
86
+ revoked_at: Date | null;
87
+ created_at: Generated<Date>;
88
+ }
89
+ interface AccountsTable {
90
+ id: Generated<string>;
91
+ email: string;
92
+ plan: Generated<string>;
93
+ display_name: string | null;
94
+ bio: string | null;
95
+ avatar_url: string | null;
96
+ slug: string | null;
97
+ stripe_customer_id: string | null;
98
+ created_at: Generated<Date>;
99
+ }
100
+ interface SessionsTable {
101
+ id: Generated<string>;
102
+ token_hash: string;
103
+ token_prefix: string;
104
+ account_id: string;
105
+ ip_address: string;
106
+ expires_at: Generated<Date>;
107
+ revoked_at: Date | null;
108
+ last_used_at: Date | null;
109
+ created_at: Generated<Date>;
110
+ }
111
+ interface MagicLinksTable {
112
+ id: Generated<string>;
113
+ email: string;
114
+ token: string;
115
+ code: string | null;
116
+ expires_at: Date;
117
+ used_at: Date | null;
118
+ failed_attempts: Generated<number>;
119
+ created_at: Generated<Date>;
120
+ }
121
+ interface UsageDailyTable {
122
+ account_id: string;
123
+ tenant_id: string | null;
124
+ date: string;
125
+ api_requests: Generated<number>;
126
+ deliveries: Generated<number>;
127
+ }
128
+ interface UsageSnapshotsTable {
129
+ id: Generated<string>;
130
+ account_id: string;
131
+ measured_at: Generated<Date>;
132
+ storage_bytes: Generated<number>;
133
+ }
134
+ interface WaitlistTable {
135
+ id: Generated<string>;
136
+ email: string;
137
+ source: Generated<string>;
138
+ status: Generated<string>;
139
+ created_at: Generated<Date>;
140
+ }
141
+ interface AccountInsightsTable {
142
+ id: Generated<string>;
143
+ account_id: string;
144
+ category: string;
145
+ insight_type: string;
146
+ resource_id: string | null;
147
+ severity: string;
148
+ title: string;
149
+ body: string;
150
+ data: unknown;
151
+ dismissed_at: Date | null;
152
+ expires_at: Date | null;
153
+ created_at: Generated<Date>;
154
+ }
155
+ interface AccountAgentRunsTable {
156
+ id: Generated<string>;
157
+ account_id: string;
158
+ started_at: Generated<Date>;
159
+ completed_at: Date | null;
160
+ status: Generated<string>;
161
+ input_tokens: Generated<number>;
162
+ output_tokens: Generated<number>;
163
+ cost_usd: Generated<number>;
164
+ insights_created: Generated<number>;
165
+ error: string | null;
166
+ }
167
+ interface SubgraphProcessingStatsTable {
168
+ id: Generated<string>;
169
+ subgraph_name: string;
170
+ api_key_id: string | null;
171
+ bucket_start: Date | null;
172
+ bucket_end: Date | null;
173
+ blocks_processed: number | null;
174
+ total_time_ms: number | null;
175
+ handler_time_ms: number | null;
176
+ flush_time_ms: number | null;
177
+ max_block_time_ms: number | null;
178
+ max_handler_time_ms: number | null;
179
+ avg_ops_per_block: number | null;
180
+ is_catchup: Generated<boolean>;
181
+ created_at: Generated<Date>;
182
+ }
183
+ interface SubgraphTableSnapshotsTable {
184
+ id: Generated<string>;
185
+ subgraph_name: string;
186
+ api_key_id: string | null;
187
+ table_name: string;
188
+ row_count: number | null;
189
+ created_at: Generated<Date>;
190
+ }
191
+ interface SubgraphHealthSnapshotsTable {
192
+ id: Generated<string>;
193
+ subgraph_id: string;
194
+ total_processed: number;
195
+ total_errors: number;
196
+ last_processed_block: number | null;
197
+ captured_at: Generated<Date>;
198
+ }
199
+ interface SubgraphUsageDailyTable {
200
+ subgraph_id: string;
201
+ date: string;
202
+ query_count: Generated<number>;
203
+ }
204
+ interface ProjectsTable {
205
+ id: Generated<string>;
206
+ name: string;
207
+ slug: string;
208
+ account_id: string;
209
+ settings: Generated<Record<string, unknown>>;
210
+ network: Generated<string>;
211
+ node_rpc: string | null;
212
+ created_at: Generated<Date>;
213
+ updated_at: Generated<Date>;
214
+ }
215
+ interface TeamMembersTable {
216
+ id: Generated<string>;
217
+ project_id: string;
218
+ account_id: string;
219
+ role: Generated<string>;
220
+ invited_by: string | null;
221
+ created_at: Generated<Date>;
222
+ }
223
+ interface TeamInvitationsTable {
224
+ id: Generated<string>;
225
+ project_id: string;
226
+ email: string;
227
+ role: Generated<string>;
228
+ token: string;
229
+ invited_by: string | null;
230
+ expires_at: Date;
231
+ accepted_at: Date | null;
232
+ created_at: Generated<Date>;
233
+ }
234
+ interface ChatSessionsTable {
235
+ id: Generated<string>;
236
+ account_id: string;
237
+ title: string | null;
238
+ summary: unknown | null;
239
+ created_at: Generated<Date>;
240
+ updated_at: Generated<Date>;
241
+ }
242
+ interface ChatMessagesTable {
243
+ id: Generated<string>;
244
+ chat_session_id: string;
245
+ role: string;
246
+ parts: unknown;
247
+ metadata: unknown | null;
248
+ created_at: Generated<Date>;
249
+ }
250
+ interface Database {
251
+ blocks: BlocksTable;
252
+ transactions: TransactionsTable;
253
+ events: EventsTable;
254
+ index_progress: IndexProgressTable;
255
+ subgraphs: SubgraphsTable;
256
+ api_keys: ApiKeysTable;
257
+ accounts: AccountsTable;
258
+ sessions: SessionsTable;
259
+ magic_links: MagicLinksTable;
260
+ usage_daily: UsageDailyTable;
261
+ usage_snapshots: UsageSnapshotsTable;
262
+ waitlist: WaitlistTable;
263
+ account_insights: AccountInsightsTable;
264
+ account_agent_runs: AccountAgentRunsTable;
265
+ subgraph_health_snapshots: SubgraphHealthSnapshotsTable;
266
+ subgraph_processing_stats: SubgraphProcessingStatsTable;
267
+ subgraph_table_snapshots: SubgraphTableSnapshotsTable;
268
+ subgraph_gaps: SubgraphGapsTable;
269
+ subgraph_usage_daily: SubgraphUsageDailyTable;
270
+ projects: ProjectsTable;
271
+ team_members: TeamMembersTable;
272
+ team_invitations: TeamInvitationsTable;
273
+ chat_sessions: ChatSessionsTable;
274
+ chat_messages: ChatMessagesTable;
275
+ tenants: TenantsTable;
276
+ tenant_usage_monthly: TenantUsageMonthlyTable;
277
+ tenant_compute_addons: TenantComputeAddonsTable;
278
+ account_spend_caps: AccountSpendCapsTable;
279
+ provisioning_audit_log: ProvisioningAuditLogTable;
280
+ subscriptions: SubscriptionsTable;
281
+ subscription_outbox: SubscriptionOutboxTable;
282
+ subscription_deliveries: SubscriptionDeliveriesTable;
283
+ }
284
+ type TenantStatus = "provisioning" | "active" | "suspended" | "error" | "deleted";
285
+ interface TenantsTable {
286
+ id: Generated<string>;
287
+ account_id: string;
288
+ slug: string;
289
+ status: ColumnType<TenantStatus, TenantStatus | undefined, TenantStatus>;
290
+ plan: string;
291
+ cpus: ColumnType<number, number | string, number | string>;
292
+ memory_mb: number;
293
+ storage_limit_mb: number;
294
+ storage_used_mb: number | null;
295
+ pg_container_id: string | null;
296
+ api_container_id: string | null;
297
+ processor_container_id: string | null;
298
+ target_database_url_enc: Buffer;
299
+ tenant_jwt_secret_enc: Buffer;
300
+ anon_key_enc: Buffer;
301
+ service_key_enc: Buffer;
302
+ api_url_internal: string;
303
+ api_url_public: string;
304
+ suspended_at: Date | null;
305
+ last_health_check_at: Date | null;
306
+ last_active_at: Generated<Date>;
307
+ service_gen: Generated<number>;
308
+ anon_gen: Generated<number>;
309
+ project_id: string | null;
310
+ created_at: Generated<Date>;
311
+ updated_at: Generated<Date>;
312
+ }
313
+ interface TenantUsageMonthlyTable {
314
+ id: Generated<string>;
315
+ tenant_id: string;
316
+ period_month: Date;
317
+ storage_peak_mb: Generated<number>;
318
+ storage_avg_mb: Generated<number>;
319
+ storage_last_mb: Generated<number>;
320
+ measurements: Generated<number>;
321
+ first_at: Generated<Date>;
322
+ last_at: Generated<Date>;
323
+ }
324
+ interface TenantComputeAddonsTable {
325
+ id: Generated<string>;
326
+ tenant_id: string;
327
+ memory_mb_delta: Generated<number>;
328
+ cpu_delta: Generated<number | string>;
329
+ storage_mb_delta: Generated<number>;
330
+ effective_from: Generated<Date>;
331
+ effective_until: Date | null;
332
+ stripe_subscription_item_id: string | null;
333
+ created_at: Generated<Date>;
334
+ }
335
+ interface AccountSpendCapsTable {
336
+ account_id: string;
337
+ monthly_cap_cents: number | null;
338
+ compute_cap_cents: number | null;
339
+ storage_cap_cents: number | null;
340
+ ai_cap_cents: number | null;
341
+ alert_threshold_pct: Generated<number>;
342
+ alert_sent_at: Date | null;
343
+ frozen_at: Date | null;
344
+ updated_at: Generated<Date>;
345
+ }
346
+ type ProvisioningAuditEvent = "provision.start" | "provision.success" | "provision.failure" | "suspend" | "resume" | "resize" | "keys.rotate" | "bastion.key.upload" | "bastion.key.revoke" | "teardown";
347
+ type ProvisioningAuditStatus = "ok" | "error";
348
+ interface ProvisioningAuditLogTable {
349
+ id: Generated<string>;
350
+ tenant_id: string | null;
351
+ tenant_slug: string | null;
352
+ account_id: string | null;
353
+ actor: string;
354
+ event: ProvisioningAuditEvent;
355
+ status: ProvisioningAuditStatus;
356
+ detail: unknown | null;
357
+ error: string | null;
358
+ created_at: Generated<Date>;
359
+ }
360
+ type SubscriptionStatus = "active" | "paused" | "error";
361
+ type SubscriptionFormat = "standard-webhooks" | "inngest" | "trigger" | "cloudflare" | "cloudevents" | "raw";
362
+ type SubscriptionRuntime = "inngest" | "trigger" | "cloudflare" | "node";
363
+ interface SubscriptionsTable {
364
+ id: Generated<string>;
365
+ account_id: string;
366
+ project_id: string | null;
367
+ name: string;
368
+ status: ColumnType<SubscriptionStatus, SubscriptionStatus | undefined, SubscriptionStatus>;
369
+ subgraph_name: string;
370
+ table_name: string;
371
+ filter: Generated<unknown>;
372
+ format: ColumnType<SubscriptionFormat, SubscriptionFormat | undefined, SubscriptionFormat>;
373
+ runtime: SubscriptionRuntime | null;
374
+ url: string;
375
+ signing_secret_enc: Buffer;
376
+ auth_config: Generated<unknown>;
377
+ max_retries: Generated<number>;
378
+ timeout_ms: Generated<number>;
379
+ concurrency: Generated<number>;
380
+ circuit_failures: Generated<number>;
381
+ circuit_opened_at: Date | null;
382
+ last_delivery_at: Date | null;
383
+ last_success_at: Date | null;
384
+ last_error: string | null;
385
+ created_at: Generated<Date>;
386
+ updated_at: Generated<Date>;
387
+ }
388
+ type Subscription = Selectable<SubscriptionsTable>;
389
+ type OutboxStatus = "pending" | "delivered" | "dead";
390
+ interface SubscriptionOutboxTable {
391
+ id: Generated<string>;
392
+ subscription_id: string;
393
+ subgraph_name: string;
394
+ table_name: string;
395
+ block_height: number | bigint;
396
+ tx_id: string | null;
397
+ row_pk: unknown;
398
+ event_type: string;
399
+ payload: unknown;
400
+ dedup_key: string;
401
+ attempt: Generated<number>;
402
+ next_attempt_at: Generated<Date>;
403
+ status: ColumnType<OutboxStatus, OutboxStatus | undefined, OutboxStatus>;
404
+ is_replay: Generated<boolean>;
405
+ delivered_at: Date | null;
406
+ failed_at: Date | null;
407
+ locked_by: string | null;
408
+ locked_until: Date | null;
409
+ created_at: Generated<Date>;
410
+ }
411
+ interface SubscriptionDeliveriesTable {
412
+ id: Generated<string>;
413
+ outbox_id: string;
414
+ subscription_id: string;
415
+ attempt: number;
416
+ status_code: number | null;
417
+ response_headers: unknown | null;
418
+ response_body: string | null;
419
+ error_message: string | null;
420
+ duration_ms: number | null;
421
+ dispatched_at: Generated<Date>;
422
+ }
423
+ /**
424
+ * Subscription CRUD. `signing_secret_enc` is transparently encrypted via
425
+ * `encryptSecret`/`decryptSecret`. Plaintext secrets only leave via the
426
+ * return value of `create` (one-time display) and `rotateSecret`.
427
+ */
428
+ interface CreateSubscriptionInput {
429
+ accountId: string;
430
+ projectId?: string | null;
431
+ name: string;
432
+ subgraphName: string;
433
+ tableName: string;
434
+ filter?: unknown;
435
+ format?: SubscriptionFormat;
436
+ runtime?: SubscriptionRuntime | null;
437
+ url: string;
438
+ authConfig?: unknown;
439
+ maxRetries?: number;
440
+ timeoutMs?: number;
441
+ concurrency?: number;
442
+ }
443
+ interface CreateSubscriptionResult {
444
+ subscription: Subscription;
445
+ /** Plaintext signing secret — surfaced once, never stored decrypted. */
446
+ signingSecret: string;
447
+ }
448
+ declare function createSubscription(db: Kysely<Database>, input: CreateSubscriptionInput): Promise<CreateSubscriptionResult>;
449
+ declare function listSubscriptions(db: Kysely<Database>, accountId: string): Promise<Subscription[]>;
450
+ declare function getSubscription(db: Kysely<Database>, accountId: string, id: string): Promise<Subscription | null>;
451
+ declare function getSubscriptionByName(db: Kysely<Database>, accountId: string, name: string): Promise<Subscription | null>;
452
+ interface UpdateSubscriptionInput {
453
+ name?: string;
454
+ filter?: unknown;
455
+ format?: SubscriptionFormat;
456
+ runtime?: SubscriptionRuntime | null;
457
+ url?: string;
458
+ authConfig?: unknown;
459
+ maxRetries?: number;
460
+ timeoutMs?: number;
461
+ concurrency?: number;
462
+ }
463
+ declare function updateSubscription(db: Kysely<Database>, accountId: string, id: string, patch: UpdateSubscriptionInput): Promise<Subscription | null>;
464
+ declare function toggleSubscriptionStatus(db: Kysely<Database>, accountId: string, id: string, status: SubscriptionStatus): Promise<Subscription | null>;
465
+ declare function deleteSubscription(db: Kysely<Database>, accountId: string, id: string): Promise<boolean>;
466
+ interface RotateSecretResult {
467
+ subscription: Subscription;
468
+ signingSecret: string;
469
+ }
470
+ declare function rotateSubscriptionSecret(db: Kysely<Database>, accountId: string, id: string): Promise<RotateSecretResult | null>;
471
+ /** Decrypt a subscription's signing secret for HMAC signing at emit time. */
472
+ declare function getSubscriptionSigningSecret(sub: Subscription): string;
473
+ /** Fire `subscriptions:changed` notify so the emitter hot-reloads its cache. */
474
+ declare function notifySubscriptionsChanged(db: Kysely<Database>, accountId: string): Promise<void>;
475
+ export { updateSubscription, toggleSubscriptionStatus, rotateSubscriptionSecret, notifySubscriptionsChanged, listSubscriptions, getSubscriptionSigningSecret, getSubscriptionByName, getSubscription, deleteSubscription, createSubscription, UpdateSubscriptionInput, RotateSecretResult, CreateSubscriptionResult, CreateSubscriptionInput };
@@ -0,0 +1,264 @@
1
+ import { createRequire } from "node:module";
2
+ var __defProp = Object.defineProperty;
3
+ var __returnValue = (v) => v;
4
+ function __exportSetter(name, newValue) {
5
+ this[name] = __returnValue.bind(null, newValue);
6
+ }
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, {
10
+ get: all[name],
11
+ enumerable: true,
12
+ configurable: true,
13
+ set: __exportSetter.bind(all, name)
14
+ });
15
+ };
16
+
17
+ // src/crypto/hmac.ts
18
+ var exports_hmac = {};
19
+ __export(exports_hmac, {
20
+ verifySignatureHeader: () => verifySignatureHeader,
21
+ verifySignature: () => verifySignature,
22
+ signPayload: () => signPayload,
23
+ generateSecret: () => generateSecret,
24
+ createSignatureHeader: () => createSignatureHeader
25
+ });
26
+ import { createHmac, randomBytes } from "crypto";
27
+ function generateSecret() {
28
+ return randomBytes(32).toString("hex");
29
+ }
30
+ function signPayload(payload, secret) {
31
+ const hmac = createHmac("sha256", secret);
32
+ hmac.update(payload);
33
+ return hmac.digest("hex");
34
+ }
35
+ function verifySignature(payload, signature, secret) {
36
+ const expectedSignature = signPayload(payload, secret);
37
+ if (signature.length !== expectedSignature.length) {
38
+ return false;
39
+ }
40
+ let result = 0;
41
+ for (let i = 0;i < signature.length; i++) {
42
+ result |= signature.charCodeAt(i) ^ expectedSignature.charCodeAt(i);
43
+ }
44
+ return result === 0;
45
+ }
46
+ function createSignatureHeader(payload, secret, timestamp) {
47
+ const ts = timestamp ?? Math.floor(Date.now() / 1000);
48
+ const signedPayload = `${ts}.${payload}`;
49
+ const signature = signPayload(signedPayload, secret);
50
+ return `t=${ts},v1=${signature}`;
51
+ }
52
+ function verifySignatureHeader(payload, header, secret, toleranceSeconds = 300) {
53
+ const parts = header.split(",");
54
+ const timestamp = parts.find((p) => p.startsWith("t="))?.slice(2);
55
+ const signature = parts.find((p) => p.startsWith("v1="))?.slice(3);
56
+ if (!timestamp || !signature) {
57
+ return false;
58
+ }
59
+ const ts = Number.parseInt(timestamp, 10);
60
+ if (isNaN(ts)) {
61
+ return false;
62
+ }
63
+ const now = Math.floor(Date.now() / 1000);
64
+ if (Math.abs(now - ts) > toleranceSeconds) {
65
+ return false;
66
+ }
67
+ const signedPayload = `${ts}.${payload}`;
68
+ return verifySignature(signedPayload, signature, secret);
69
+ }
70
+
71
+ // src/mode.ts
72
+ var VALID_MODES = ["oss", "dedicated", "platform"];
73
+ function getInstanceMode() {
74
+ const raw = process.env.INSTANCE_MODE?.trim().toLowerCase();
75
+ if (raw && VALID_MODES.includes(raw)) {
76
+ return raw;
77
+ }
78
+ return "oss";
79
+ }
80
+ function isPlatformMode() {
81
+ return getInstanceMode() === "platform";
82
+ }
83
+ function isOssMode() {
84
+ return getInstanceMode() === "oss";
85
+ }
86
+ function isDedicatedMode() {
87
+ return getInstanceMode() === "dedicated";
88
+ }
89
+
90
+ // src/crypto/secrets.ts
91
+ import { createCipheriv, createDecipheriv, randomBytes as randomBytes2 } from "node:crypto";
92
+ import { appendFileSync, existsSync, readFileSync } from "node:fs";
93
+ import { resolve } from "node:path";
94
+ var KEY_ENV = "SECONDLAYER_SECRETS_KEY";
95
+ var IV_LEN = 12;
96
+ var TAG_LEN = 16;
97
+ function bootstrapOssKey() {
98
+ const envPath = resolve(process.cwd(), ".env.local");
99
+ if (existsSync(envPath)) {
100
+ const contents = readFileSync(envPath, "utf8");
101
+ const match = contents.match(/^SECONDLAYER_SECRETS_KEY=([a-fA-F0-9]{64})/m);
102
+ if (match) {
103
+ process.env[KEY_ENV] = match[1];
104
+ return match[1];
105
+ }
106
+ }
107
+ const hex = randomBytes2(32).toString("hex");
108
+ const line = `${existsSync(envPath) ? `
109
+ ` : ""}${KEY_ENV}=${hex}
110
+ `;
111
+ appendFileSync(envPath, line, { mode: 384 });
112
+ process.env[KEY_ENV] = hex;
113
+ console.log(`[secondlayer] generated ${KEY_ENV}; saved to ${envPath} (mode 0600)`);
114
+ return hex;
115
+ }
116
+ function loadKey() {
117
+ let hex = process.env[KEY_ENV];
118
+ if (!hex) {
119
+ if (getInstanceMode() === "oss") {
120
+ hex = bootstrapOssKey();
121
+ } else {
122
+ throw new Error(`${KEY_ENV} not set. Generate one with: openssl rand -hex 32`);
123
+ }
124
+ }
125
+ const key = Buffer.from(hex, "hex");
126
+ if (key.length !== 32) {
127
+ throw new Error(`${KEY_ENV} must be 32 bytes hex (got ${key.length})`);
128
+ }
129
+ return key;
130
+ }
131
+ var _cachedKey = null;
132
+ function getKey() {
133
+ if (!_cachedKey)
134
+ _cachedKey = loadKey();
135
+ return _cachedKey;
136
+ }
137
+ function encryptSecret(plaintext) {
138
+ const key = getKey();
139
+ const iv = randomBytes2(IV_LEN);
140
+ const cipher = createCipheriv("aes-256-gcm", key, iv);
141
+ const ciphertext = Buffer.concat([
142
+ cipher.update(plaintext, "utf8"),
143
+ cipher.final()
144
+ ]);
145
+ const tag = cipher.getAuthTag();
146
+ return Buffer.concat([iv, tag, ciphertext]);
147
+ }
148
+ function decryptSecret(envelope) {
149
+ const key = getKey();
150
+ const iv = envelope.subarray(0, IV_LEN);
151
+ const tag = envelope.subarray(IV_LEN, IV_LEN + TAG_LEN);
152
+ const ciphertext = envelope.subarray(IV_LEN + TAG_LEN);
153
+ const decipher = createDecipheriv("aes-256-gcm", key, iv);
154
+ decipher.setAuthTag(tag);
155
+ return decipher.update(ciphertext).toString("utf8") + decipher.final("utf8");
156
+ }
157
+ function generateSecretsKey() {
158
+ return randomBytes2(32).toString("hex");
159
+ }
160
+
161
+ // src/db/queries/subscriptions.ts
162
+ import { sql } from "kysely";
163
+ async function createSubscription(db, input) {
164
+ const signingSecret = generateSecret();
165
+ const row = {
166
+ account_id: input.accountId,
167
+ project_id: input.projectId ?? null,
168
+ name: input.name,
169
+ status: "active",
170
+ subgraph_name: input.subgraphName,
171
+ table_name: input.tableName,
172
+ filter: input.filter ?? {},
173
+ format: input.format ?? "standard-webhooks",
174
+ runtime: input.runtime ?? null,
175
+ url: input.url,
176
+ signing_secret_enc: encryptSecret(signingSecret),
177
+ auth_config: input.authConfig ?? {},
178
+ ...input.maxRetries !== undefined ? { max_retries: input.maxRetries } : {},
179
+ ...input.timeoutMs !== undefined ? { timeout_ms: input.timeoutMs } : {},
180
+ ...input.concurrency !== undefined ? { concurrency: input.concurrency } : {}
181
+ };
182
+ const subscription = await db.insertInto("subscriptions").values(row).returningAll().executeTakeFirstOrThrow();
183
+ return { subscription, signingSecret };
184
+ }
185
+ async function listSubscriptions(db, accountId) {
186
+ return db.selectFrom("subscriptions").selectAll().where("account_id", "=", accountId).orderBy("created_at", "desc").execute();
187
+ }
188
+ async function getSubscription(db, accountId, id) {
189
+ const row = await db.selectFrom("subscriptions").selectAll().where("account_id", "=", accountId).where("id", "=", id).executeTakeFirst();
190
+ return row ?? null;
191
+ }
192
+ async function getSubscriptionByName(db, accountId, name) {
193
+ const row = await db.selectFrom("subscriptions").selectAll().where("account_id", "=", accountId).where("name", "=", name).executeTakeFirst();
194
+ return row ?? null;
195
+ }
196
+ async function updateSubscription(db, accountId, id, patch) {
197
+ const update = { updated_at: new Date };
198
+ if (patch.name !== undefined)
199
+ update.name = patch.name;
200
+ if (patch.filter !== undefined)
201
+ update.filter = patch.filter;
202
+ if (patch.format !== undefined)
203
+ update.format = patch.format;
204
+ if (patch.runtime !== undefined)
205
+ update.runtime = patch.runtime;
206
+ if (patch.url !== undefined)
207
+ update.url = patch.url;
208
+ if (patch.authConfig !== undefined)
209
+ update.auth_config = patch.authConfig;
210
+ if (patch.maxRetries !== undefined)
211
+ update.max_retries = patch.maxRetries;
212
+ if (patch.timeoutMs !== undefined)
213
+ update.timeout_ms = patch.timeoutMs;
214
+ if (patch.concurrency !== undefined)
215
+ update.concurrency = patch.concurrency;
216
+ const row = await db.updateTable("subscriptions").set(update).where("account_id", "=", accountId).where("id", "=", id).returningAll().executeTakeFirst();
217
+ return row ?? null;
218
+ }
219
+ async function toggleSubscriptionStatus(db, accountId, id, status) {
220
+ const row = await db.updateTable("subscriptions").set({
221
+ status,
222
+ updated_at: new Date,
223
+ ...status === "active" ? {
224
+ circuit_failures: 0,
225
+ circuit_opened_at: null
226
+ } : {}
227
+ }).where("account_id", "=", accountId).where("id", "=", id).returningAll().executeTakeFirst();
228
+ return row ?? null;
229
+ }
230
+ async function deleteSubscription(db, accountId, id) {
231
+ const res = await db.deleteFrom("subscriptions").where("account_id", "=", accountId).where("id", "=", id).executeTakeFirst();
232
+ return Number(res.numDeletedRows ?? 0) > 0;
233
+ }
234
+ async function rotateSubscriptionSecret(db, accountId, id) {
235
+ const signingSecret = generateSecret();
236
+ const row = await db.updateTable("subscriptions").set({
237
+ signing_secret_enc: encryptSecret(signingSecret),
238
+ updated_at: new Date
239
+ }).where("account_id", "=", accountId).where("id", "=", id).returningAll().executeTakeFirst();
240
+ if (!row)
241
+ return null;
242
+ return { subscription: row, signingSecret };
243
+ }
244
+ function getSubscriptionSigningSecret(sub) {
245
+ return decryptSecret(sub.signing_secret_enc);
246
+ }
247
+ async function notifySubscriptionsChanged(db, accountId) {
248
+ await sql`SELECT pg_notify('subscriptions:changed', ${accountId})`.execute(db);
249
+ }
250
+ export {
251
+ updateSubscription,
252
+ toggleSubscriptionStatus,
253
+ rotateSubscriptionSecret,
254
+ notifySubscriptionsChanged,
255
+ listSubscriptions,
256
+ getSubscriptionSigningSecret,
257
+ getSubscriptionByName,
258
+ getSubscription,
259
+ deleteSubscription,
260
+ createSubscription
261
+ };
262
+
263
+ //# debugId=6C6C97A09D12DAB864756E2164756E21
264
+ //# sourceMappingURL=subscriptions.js.map
@@ -0,0 +1,13 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/crypto/hmac.ts", "../src/mode.ts", "../src/crypto/secrets.ts", "../src/db/queries/subscriptions.ts"],
4
+ "sourcesContent": [
5
+ "import { createHmac, randomBytes } from \"crypto\";\n\n/**\n * Generate a random secret for delivery signing\n * Returns 32 bytes as a 64-character hex string\n */\nexport function generateSecret(): string {\n\treturn randomBytes(32).toString(\"hex\");\n}\n\n/**\n * Sign a payload with HMAC-SHA256\n * Returns the signature as a hex string\n */\nexport function signPayload(payload: string, secret: string): string {\n\tconst hmac = createHmac(\"sha256\", secret);\n\thmac.update(payload);\n\treturn hmac.digest(\"hex\");\n}\n\n/**\n * Verify an HMAC signature\n * Uses constant-time comparison to prevent timing attacks\n */\nexport function verifySignature(\n\tpayload: string,\n\tsignature: string,\n\tsecret: string,\n): boolean {\n\tconst expectedSignature = signPayload(payload, secret);\n\n\t// Constant-time comparison\n\tif (signature.length !== expectedSignature.length) {\n\t\treturn false;\n\t}\n\n\tlet result = 0;\n\tfor (let i = 0; i < signature.length; i++) {\n\t\tresult |= signature.charCodeAt(i) ^ expectedSignature.charCodeAt(i);\n\t}\n\n\treturn result === 0;\n}\n\n/**\n * Create a Stripe-style signature header\n * Format: t=timestamp,v1=signature\n */\nexport function createSignatureHeader(\n\tpayload: string,\n\tsecret: string,\n\ttimestamp?: number,\n): string {\n\tconst ts = timestamp ?? Math.floor(Date.now() / 1000);\n\tconst signedPayload = `${ts}.${payload}`;\n\tconst signature = signPayload(signedPayload, secret);\n\n\treturn `t=${ts},v1=${signature}`;\n}\n\n/**\n * Parse and verify a Stripe-style signature header\n * Returns true if valid, false otherwise\n */\nexport function verifySignatureHeader(\n\tpayload: string,\n\theader: string,\n\tsecret: string,\n\ttoleranceSeconds = 300, // 5 minutes\n): boolean {\n\t// Parse header\n\tconst parts = header.split(\",\");\n\tconst timestamp = parts.find((p) => p.startsWith(\"t=\"))?.slice(2);\n\tconst signature = parts.find((p) => p.startsWith(\"v1=\"))?.slice(3);\n\n\tif (!timestamp || !signature) {\n\t\treturn false;\n\t}\n\n\tconst ts = Number.parseInt(timestamp, 10);\n\tif (isNaN(ts)) {\n\t\treturn false;\n\t}\n\n\t// Check timestamp is within tolerance\n\tconst now = Math.floor(Date.now() / 1000);\n\tif (Math.abs(now - ts) > toleranceSeconds) {\n\t\treturn false;\n\t}\n\n\t// Verify signature\n\tconst signedPayload = `${ts}.${payload}`;\n\treturn verifySignature(signedPayload, signature, secret);\n}\n",
6
+ "/**\n * Instance modes for the Secondlayer platform.\n *\n * - `oss`: self-hosted, single-tenant. No auth middleware, no platform routes\n * (projects, admin, workflows). Everything runs against a single\n * `DATABASE_URL`. Intended for `docker compose up`.\n *\n * - `dedicated`: per-customer managed instance. JWT-based auth (anon =\n * read-only, service = full). Dual-DB mode — shared source indexer DB for\n * block reads, per-tenant target DB for subgraph data. No platform-wide\n * routes mounted (no cross-tenant accounts).\n *\n * - `platform`: control-plane mode. Magic-link auth, API keys, projects,\n * tenants, admin. Serves the dashboard + CLI against a single shared DB.\n */\n\nexport type InstanceMode = \"oss\" | \"dedicated\" | \"platform\";\n\nconst VALID_MODES: readonly InstanceMode[] = [\"oss\", \"dedicated\", \"platform\"];\n\n/**\n * Resolve the active instance mode from `process.env.INSTANCE_MODE`.\n * Defaults to `\"oss\"` — the safest default for self-hosters who deploy\n * without setting the variable.\n */\nexport function getInstanceMode(): InstanceMode {\n\tconst raw = process.env.INSTANCE_MODE?.trim().toLowerCase();\n\tif (raw && (VALID_MODES as readonly string[]).includes(raw)) {\n\t\treturn raw as InstanceMode;\n\t}\n\treturn \"oss\";\n}\n\n/** True when the active mode is `\"platform\"` (shared multi-tenant). */\nexport function isPlatformMode(): boolean {\n\treturn getInstanceMode() === \"platform\";\n}\n\n/** True when the active mode is `\"oss\"` (self-hosted). */\nexport function isOssMode(): boolean {\n\treturn getInstanceMode() === \"oss\";\n}\n\n/** True when the active mode is `\"dedicated\"` (per-tenant managed). */\nexport function isDedicatedMode(): boolean {\n\treturn getInstanceMode() === \"dedicated\";\n}\n",
7
+ "import { createCipheriv, createDecipheriv, randomBytes } from \"node:crypto\";\nimport { appendFileSync, existsSync, readFileSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport { getInstanceMode } from \"../mode.ts\";\n\n/**\n * AES-256-GCM symmetric envelope for encrypted secrets at rest (tenant keys,\n * subscription signing secrets, etc.).\n *\n * Ciphertext layout: `iv (12 bytes) || authTag (16 bytes) || ciphertext`\n *\n * The key comes from `SECONDLAYER_SECRETS_KEY` — 32 bytes hex. In OSS mode,\n * if the env var is unset on first use we autogenerate a key and persist it\n * to `.env.local` in the current working directory so subsequent restarts\n * pick it up without user intervention. Dedicated/platform modes throw —\n * those runtimes must provision the key explicitly.\n *\n * Rotation strategy: re-encrypt all rows with the new key and swap the env\n * var. Not zero-downtime, but acceptable at v2 scale. For real KMS (AWS\n * KMS, Vault, GCP KMS), wrap the same byte layout behind an\n * `EncryptSecret`/`DecryptSecret` interface and swap at startup.\n */\n\nconst KEY_ENV = \"SECONDLAYER_SECRETS_KEY\";\nconst IV_LEN = 12;\nconst TAG_LEN = 16;\n\nfunction bootstrapOssKey(): string {\n\tconst envPath = resolve(process.cwd(), \".env.local\");\n\n\t// Check existing .env.local first — prior run may have written it.\n\tif (existsSync(envPath)) {\n\t\tconst contents = readFileSync(envPath, \"utf8\");\n\t\tconst match = contents.match(/^SECONDLAYER_SECRETS_KEY=([a-fA-F0-9]{64})/m);\n\t\tif (match) {\n\t\t\tprocess.env[KEY_ENV] = match[1];\n\t\t\treturn match[1];\n\t\t}\n\t}\n\n\tconst hex = randomBytes(32).toString(\"hex\");\n\tconst line = `${existsSync(envPath) ? \"\\n\" : \"\"}${KEY_ENV}=${hex}\\n`;\n\tappendFileSync(envPath, line, { mode: 0o600 });\n\tprocess.env[KEY_ENV] = hex;\n\tconsole.log(\n\t\t`[secondlayer] generated ${KEY_ENV}; saved to ${envPath} (mode 0600)`,\n\t);\n\treturn hex;\n}\n\nfunction loadKey(): Buffer {\n\tlet hex = process.env[KEY_ENV];\n\tif (!hex) {\n\t\tif (getInstanceMode() === \"oss\") {\n\t\t\thex = bootstrapOssKey();\n\t\t} else {\n\t\t\tthrow new Error(\n\t\t\t\t`${KEY_ENV} not set. Generate one with: openssl rand -hex 32`,\n\t\t\t);\n\t\t}\n\t}\n\tconst key = Buffer.from(hex, \"hex\");\n\tif (key.length !== 32) {\n\t\tthrow new Error(`${KEY_ENV} must be 32 bytes hex (got ${key.length})`);\n\t}\n\treturn key;\n}\n\nlet _cachedKey: Buffer | null = null;\nfunction getKey(): Buffer {\n\tif (!_cachedKey) _cachedKey = loadKey();\n\treturn _cachedKey;\n}\n\nexport function encryptSecret(plaintext: string): Buffer {\n\tconst key = getKey();\n\tconst iv = randomBytes(IV_LEN);\n\tconst cipher = createCipheriv(\"aes-256-gcm\", key, iv);\n\tconst ciphertext = Buffer.concat([\n\t\tcipher.update(plaintext, \"utf8\"),\n\t\tcipher.final(),\n\t]);\n\tconst tag = cipher.getAuthTag();\n\treturn Buffer.concat([iv, tag, ciphertext]);\n}\n\nexport function decryptSecret(envelope: Buffer): string {\n\tconst key = getKey();\n\tconst iv = envelope.subarray(0, IV_LEN);\n\tconst tag = envelope.subarray(IV_LEN, IV_LEN + TAG_LEN);\n\tconst ciphertext = envelope.subarray(IV_LEN + TAG_LEN);\n\tconst decipher = createDecipheriv(\"aes-256-gcm\", key, iv);\n\tdecipher.setAuthTag(tag);\n\treturn decipher.update(ciphertext).toString(\"utf8\") + decipher.final(\"utf8\");\n}\n\n/** Generate a fresh 32-byte hex key suitable for `SECONDLAYER_SECRETS_KEY`. */\nexport function generateSecretsKey(): string {\n\treturn randomBytes(32).toString(\"hex\");\n}\n",
8
+ "import { type Kysely, sql } from \"kysely\";\nimport { generateSecret } from \"../../crypto/hmac.ts\";\nimport { decryptSecret, encryptSecret } from \"../../crypto/secrets.ts\";\nimport type {\n\tDatabase,\n\tInsertSubscription,\n\tSubscription,\n\tSubscriptionFormat,\n\tSubscriptionRuntime,\n\tSubscriptionStatus,\n\tUpdateSubscription,\n} from \"../types.ts\";\n\n/**\n * Subscription CRUD. `signing_secret_enc` is transparently encrypted via\n * `encryptSecret`/`decryptSecret`. Plaintext secrets only leave via the\n * return value of `create` (one-time display) and `rotateSecret`.\n */\n\nexport interface CreateSubscriptionInput {\n\taccountId: string;\n\tprojectId?: string | null;\n\tname: string;\n\tsubgraphName: string;\n\ttableName: string;\n\tfilter?: unknown;\n\tformat?: SubscriptionFormat;\n\truntime?: SubscriptionRuntime | null;\n\turl: string;\n\tauthConfig?: unknown;\n\tmaxRetries?: number;\n\ttimeoutMs?: number;\n\tconcurrency?: number;\n}\n\nexport interface CreateSubscriptionResult {\n\tsubscription: Subscription;\n\t/** Plaintext signing secret — surfaced once, never stored decrypted. */\n\tsigningSecret: string;\n}\n\nexport async function createSubscription(\n\tdb: Kysely<Database>,\n\tinput: CreateSubscriptionInput,\n): Promise<CreateSubscriptionResult> {\n\tconst signingSecret = generateSecret();\n\tconst row: InsertSubscription = {\n\t\taccount_id: input.accountId,\n\t\tproject_id: input.projectId ?? null,\n\t\tname: input.name,\n\t\tstatus: \"active\",\n\t\tsubgraph_name: input.subgraphName,\n\t\ttable_name: input.tableName,\n\t\tfilter: input.filter ?? {},\n\t\tformat: input.format ?? \"standard-webhooks\",\n\t\truntime: input.runtime ?? null,\n\t\turl: input.url,\n\t\tsigning_secret_enc: encryptSecret(signingSecret),\n\t\tauth_config: input.authConfig ?? {},\n\t\t...(input.maxRetries !== undefined ? { max_retries: input.maxRetries } : {}),\n\t\t...(input.timeoutMs !== undefined ? { timeout_ms: input.timeoutMs } : {}),\n\t\t...(input.concurrency !== undefined\n\t\t\t? { concurrency: input.concurrency }\n\t\t\t: {}),\n\t};\n\tconst subscription = await db\n\t\t.insertInto(\"subscriptions\")\n\t\t.values(row)\n\t\t.returningAll()\n\t\t.executeTakeFirstOrThrow();\n\treturn { subscription, signingSecret };\n}\n\nexport async function listSubscriptions(\n\tdb: Kysely<Database>,\n\taccountId: string,\n): Promise<Subscription[]> {\n\treturn db\n\t\t.selectFrom(\"subscriptions\")\n\t\t.selectAll()\n\t\t.where(\"account_id\", \"=\", accountId)\n\t\t.orderBy(\"created_at\", \"desc\")\n\t\t.execute();\n}\n\nexport async function getSubscription(\n\tdb: Kysely<Database>,\n\taccountId: string,\n\tid: string,\n): Promise<Subscription | null> {\n\tconst row = await db\n\t\t.selectFrom(\"subscriptions\")\n\t\t.selectAll()\n\t\t.where(\"account_id\", \"=\", accountId)\n\t\t.where(\"id\", \"=\", id)\n\t\t.executeTakeFirst();\n\treturn row ?? null;\n}\n\nexport async function getSubscriptionByName(\n\tdb: Kysely<Database>,\n\taccountId: string,\n\tname: string,\n): Promise<Subscription | null> {\n\tconst row = await db\n\t\t.selectFrom(\"subscriptions\")\n\t\t.selectAll()\n\t\t.where(\"account_id\", \"=\", accountId)\n\t\t.where(\"name\", \"=\", name)\n\t\t.executeTakeFirst();\n\treturn row ?? null;\n}\n\nexport interface UpdateSubscriptionInput {\n\tname?: string;\n\tfilter?: unknown;\n\tformat?: SubscriptionFormat;\n\truntime?: SubscriptionRuntime | null;\n\turl?: string;\n\tauthConfig?: unknown;\n\tmaxRetries?: number;\n\ttimeoutMs?: number;\n\tconcurrency?: number;\n}\n\nexport async function updateSubscription(\n\tdb: Kysely<Database>,\n\taccountId: string,\n\tid: string,\n\tpatch: UpdateSubscriptionInput,\n): Promise<Subscription | null> {\n\tconst update: UpdateSubscription = { updated_at: new Date() };\n\tif (patch.name !== undefined) update.name = patch.name;\n\tif (patch.filter !== undefined) update.filter = patch.filter;\n\tif (patch.format !== undefined) update.format = patch.format;\n\tif (patch.runtime !== undefined) update.runtime = patch.runtime;\n\tif (patch.url !== undefined) update.url = patch.url;\n\tif (patch.authConfig !== undefined) update.auth_config = patch.authConfig;\n\tif (patch.maxRetries !== undefined) update.max_retries = patch.maxRetries;\n\tif (patch.timeoutMs !== undefined) update.timeout_ms = patch.timeoutMs;\n\tif (patch.concurrency !== undefined) update.concurrency = patch.concurrency;\n\n\tconst row = await db\n\t\t.updateTable(\"subscriptions\")\n\t\t.set(update)\n\t\t.where(\"account_id\", \"=\", accountId)\n\t\t.where(\"id\", \"=\", id)\n\t\t.returningAll()\n\t\t.executeTakeFirst();\n\treturn row ?? null;\n}\n\nexport async function toggleSubscriptionStatus(\n\tdb: Kysely<Database>,\n\taccountId: string,\n\tid: string,\n\tstatus: SubscriptionStatus,\n): Promise<Subscription | null> {\n\tconst row = await db\n\t\t.updateTable(\"subscriptions\")\n\t\t.set({\n\t\t\tstatus,\n\t\t\tupdated_at: new Date(),\n\t\t\t...(status === \"active\"\n\t\t\t\t? {\n\t\t\t\t\t\tcircuit_failures: 0,\n\t\t\t\t\t\tcircuit_opened_at: null,\n\t\t\t\t\t}\n\t\t\t\t: {}),\n\t\t})\n\t\t.where(\"account_id\", \"=\", accountId)\n\t\t.where(\"id\", \"=\", id)\n\t\t.returningAll()\n\t\t.executeTakeFirst();\n\treturn row ?? null;\n}\n\nexport async function deleteSubscription(\n\tdb: Kysely<Database>,\n\taccountId: string,\n\tid: string,\n): Promise<boolean> {\n\tconst res = await db\n\t\t.deleteFrom(\"subscriptions\")\n\t\t.where(\"account_id\", \"=\", accountId)\n\t\t.where(\"id\", \"=\", id)\n\t\t.executeTakeFirst();\n\treturn Number(res.numDeletedRows ?? 0) > 0;\n}\n\nexport interface RotateSecretResult {\n\tsubscription: Subscription;\n\tsigningSecret: string;\n}\n\nexport async function rotateSubscriptionSecret(\n\tdb: Kysely<Database>,\n\taccountId: string,\n\tid: string,\n): Promise<RotateSecretResult | null> {\n\tconst signingSecret = generateSecret();\n\tconst row = await db\n\t\t.updateTable(\"subscriptions\")\n\t\t.set({\n\t\t\tsigning_secret_enc: encryptSecret(signingSecret),\n\t\t\tupdated_at: new Date(),\n\t\t})\n\t\t.where(\"account_id\", \"=\", accountId)\n\t\t.where(\"id\", \"=\", id)\n\t\t.returningAll()\n\t\t.executeTakeFirst();\n\tif (!row) return null;\n\treturn { subscription: row, signingSecret };\n}\n\n/** Decrypt a subscription's signing secret for HMAC signing at emit time. */\nexport function getSubscriptionSigningSecret(sub: Subscription): string {\n\treturn decryptSecret(sub.signing_secret_enc);\n}\n\n/** Fire `subscriptions:changed` notify so the emitter hot-reloads its cache. */\nexport async function notifySubscriptionsChanged(\n\tdb: Kysely<Database>,\n\taccountId: string,\n): Promise<void> {\n\tawait sql`SELECT pg_notify('subscriptions:changed', ${accountId})`.execute(db);\n}\n"
9
+ ],
10
+ "mappings": "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",
11
+ "debugId": "6C6C97A09D12DAB864756E2164756E21",
12
+ "names": []
13
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@secondlayer/shared",
3
- "version": "3.0.0-beta.1",
3
+ "version": "3.0.0",
4
4
  "type": "module",
5
5
  "main": "./dist/src/index.js",
6
6
  "types": "./dist/src/index.d.ts",
@@ -81,21 +81,17 @@
81
81
  "types": "./dist/src/db/queries/account-spend-caps.d.ts",
82
82
  "import": "./dist/src/db/queries/account-spend-caps.js"
83
83
  },
84
- "./db/queries/sentries": {
85
- "types": "./dist/src/db/queries/sentries.d.ts",
86
- "import": "./dist/src/db/queries/sentries.js"
87
- },
88
84
  "./db/queries/account-usage": {
89
85
  "types": "./dist/src/db/queries/account-usage.d.ts",
90
86
  "import": "./dist/src/db/queries/account-usage.js"
91
87
  },
92
- "./db/queries/workflow-runs": {
93
- "types": "./dist/src/db/queries/workflow-runs.d.ts",
94
- "import": "./dist/src/db/queries/workflow-runs.js"
88
+ "./db/queries/subscriptions": {
89
+ "types": "./dist/src/db/queries/subscriptions.d.ts",
90
+ "import": "./dist/src/db/queries/subscriptions.js"
95
91
  },
96
- "./schemas/sentries": {
97
- "types": "./dist/src/schemas/sentries.d.ts",
98
- "import": "./dist/src/schemas/sentries.js"
92
+ "./crypto/standard-webhooks": {
93
+ "types": "./dist/src/crypto/standard-webhooks.d.ts",
94
+ "import": "./dist/src/crypto/standard-webhooks.js"
99
95
  },
100
96
  "./types": {
101
97
  "types": "./dist/src/types.d.ts",
@@ -176,7 +172,7 @@
176
172
  "prepublishOnly": "bun run build"
177
173
  },
178
174
  "dependencies": {
179
- "@secondlayer/stacks": "^1.0.0-alpha.0",
175
+ "@secondlayer/stacks": "^1.0.0",
180
176
  "kysely": "0.28.15",
181
177
  "kysely-postgres-js": "3.0.0",
182
178
  "postgres": "^3.4.6",