@secondlayer/shared 13.4.0 → 13.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/dist/src/archive/range-digest.d.ts +45 -0
  2. package/dist/src/archive/semantic-digest-builder.d.ts +45 -0
  3. package/dist/src/coverage/index.d.ts +45 -0
  4. package/dist/src/db/index.d.ts +50 -1
  5. package/dist/src/db/index.js +4 -1
  6. package/dist/src/db/index.js.map +3 -3
  7. package/dist/src/db/queries/chain-reorgs.d.ts +45 -0
  8. package/dist/src/db/queries/chain-reorgs.js +4 -1
  9. package/dist/src/db/queries/chain-reorgs.js.map +3 -3
  10. package/dist/src/db/queries/contracts.d.ts +45 -0
  11. package/dist/src/db/queries/instance.d.ts +45 -0
  12. package/dist/src/db/queries/integrity.d.ts +45 -0
  13. package/dist/src/db/queries/subgraph-gaps.d.ts +45 -0
  14. package/dist/src/db/queries/subgraph-operations.d.ts +45 -0
  15. package/dist/src/db/queries/subgraphs.d.ts +45 -0
  16. package/dist/src/db/queries/sync-scope.d.ts +45 -0
  17. package/dist/src/db/queries/webhooks.d.ts +45 -0
  18. package/dist/src/db/schema.d.ts +47 -1
  19. package/dist/src/index.d.ts +144 -7
  20. package/dist/src/index.js +183 -87
  21. package/dist/src/index.js.map +5 -4
  22. package/dist/src/insights/index.d.ts +1554 -0
  23. package/dist/src/insights/index.js +2284 -0
  24. package/dist/src/insights/index.js.map +29 -0
  25. package/dist/src/insights/thresholds.d.ts +56 -0
  26. package/dist/src/insights/thresholds.js +86 -0
  27. package/dist/src/insights/thresholds.js.map +10 -0
  28. package/dist/src/leader.d.ts +3 -1
  29. package/dist/src/leader.js +3 -1
  30. package/dist/src/leader.js.map +3 -3
  31. package/dist/src/node/local-client.d.ts +45 -0
  32. package/dist/src/queue/listener.js +4 -1
  33. package/dist/src/queue/listener.js.map +3 -3
  34. package/dist/src/schemas/index.d.ts +95 -7
  35. package/dist/src/schemas/index.js +180 -87
  36. package/dist/src/schemas/index.js.map +4 -3
  37. package/dist/src/schemas/insights.d.ts +89 -0
  38. package/dist/src/schemas/insights.js +98 -0
  39. package/dist/src/schemas/insights.js.map +10 -0
  40. package/migrations/0170_insights.ts +63 -0
  41. package/migrations/0171_webhook_incident_alerts.ts +45 -0
  42. package/package.json +13 -1
@@ -0,0 +1,1554 @@
1
+ /**
2
+ * The insight contract. The tenant (`/api/insights`) and platform
3
+ * (`/api/accounts/insights`) routes serve `Insight` verbatim. The web app keeps
4
+ * a field-for-field copy in `apps/web/src/lib/insights.ts`; change both
5
+ * together.
6
+ *
7
+ * The types are written out and the schemas annotated with them, rather than
8
+ * inferred: the bundled `.d.ts` loses inferred zod types (they come out as
9
+ * `unknown`), the same reason `schemas/webhooks.ts` annotates its schemas.
10
+ */
11
+ declare const INSIGHT_SEVERITIES: readonly ["losing", "will_fail", "costs", "recovered"];
12
+ type InsightSeverity = (typeof INSIGHT_SEVERITIES)[number];
13
+ type InsightResourceKind = "webhook" | "subgraph" | "account";
14
+ type InsightEvidence = {
15
+ kind: "series"
16
+ title: string
17
+ series: {
18
+ name: string
19
+ role: "a" | "context" | "mark"
20
+ points: number[]
21
+ }[]
22
+ labels: [number, string][]
23
+ unit?: string
24
+ } | {
25
+ kind: "table"
26
+ title: string
27
+ columns: string[]
28
+ rows: (string | number)[][]
29
+ markRow?: number
30
+ } | {
31
+ kind: "shapes"
32
+ title: string
33
+ shapes: {
34
+ code: string
35
+ body: string
36
+ count: number
37
+ isNew: boolean
38
+ }[]
39
+ } | {
40
+ kind: "timeline"
41
+ title: string
42
+ steps: {
43
+ what: string
44
+ at: string
45
+ note?: string
46
+ failed: boolean
47
+ }[]
48
+ };
49
+ interface Insight {
50
+ id: string;
51
+ rule: string;
52
+ severity: InsightSeverity;
53
+ impact: number;
54
+ resource: {
55
+ kind: InsightResourceKind
56
+ id: string
57
+ name: string
58
+ };
59
+ line: string;
60
+ meta?: string;
61
+ lead: string;
62
+ body: string;
63
+ evidence: InsightEvidence[];
64
+ handoff: {
65
+ prompt: string
66
+ commands: string[]
67
+ };
68
+ how: {
69
+ detector: string
70
+ facts: [string, string][]
71
+ label?: {
72
+ cause: string
73
+ confidence: number
74
+ }
75
+ };
76
+ createdAt: string;
77
+ resolvedAt?: string | null;
78
+ }
79
+ /** Severity, then impact (desc), then createdAt (desc). Returns a new array. */
80
+ declare function rankInsights(insights: readonly Insight[]): Insight[];
81
+ declare const RATE_LIMITED_MIN_ATTEMPTS = 10;
82
+ declare const RATE_LIMITED_MIN_RATIO = .5;
83
+ declare const DOWN_MIN_CONSECUTIVE = 5;
84
+ declare const REJECTS_MIN_CONSECUTIVE = 5;
85
+ declare const SLOW_WINDOW = 20;
86
+ declare const SLOW_MIN_SAMPLES = 5;
87
+ declare const SLOW_TIMEOUT_RATIO = .5;
88
+ declare const LAG_WINDOW = 20;
89
+ declare const LAG_MIN_SAMPLES = 5;
90
+ declare const LAG_THRESHOLD_MS = 6e4;
91
+ /** Consecutive newest 2xx HTML responses that mean a login wall. */
92
+ declare const LOGIN_WALL_MIN_CONSECUTIVE = 20;
93
+ /** Share of prior-week 2xx responses that must have been JSON. */
94
+ declare const LOGIN_WALL_PRIOR_JSON_RATIO = .8;
95
+ /** Prior-week successful responses needed before a baseline counts. */
96
+ declare const LOGIN_WALL_MIN_PRIOR = 10;
97
+ declare const SLOWING_DAYS = 14;
98
+ declare const SLOWING_MIN_RISING_DAYS = 10;
99
+ declare const SLOWING_MIN_R2 = .8;
100
+ declare const TLS_WARN_DAYS = 14;
101
+ /** Dead outbox rows are deleted at 90 days; warn at 83. */
102
+ declare const DEAD_LETTER_WARN_AGE_DAYS = 83;
103
+ declare const DEAD_LETTER_DELETE_AGE_DAYS = 90;
104
+ declare const TEST_ENDPOINT_MIN_AGE_DAYS = 7;
105
+ declare const HANDLER_ERRORS_MIN_24H = 10;
106
+ /** Blocks behind tip, for 3 consecutive snapshots. */
107
+ declare const SUBGRAPH_LAG_BLOCKS = 20;
108
+ declare const SUBGRAPH_LAG_SNAPSHOTS = 3;
109
+ declare const SUBGRAPH_LAG_LOSING_BLOCKS = 500;
110
+ /**
111
+ * Index and Streams cap one page at 1000 rows
112
+ * (`packages/api/src/index/_shared.ts` `Math.min(1000, parsed)`,
113
+ * `packages/api/src/routes/streams.ts` `limit: Math.min(1000, limit)`).
114
+ */
115
+ declare const PAGE_CAP_ROWS = 1e3;
116
+ declare const DUPLICATE_READS_MIN_PAGES = 3;
117
+ declare const DUPLICATE_READS_WINDOW_MS = 36e5;
118
+ /** Only repeats that started within this long ago count as current. */
119
+ declare const DUPLICATE_READS_LOOKBACK_MS: number;
120
+ declare const POLLING_MIN_DURATION_MS = 36e5;
121
+ /** Requests needed in the window before an interval means anything. */
122
+ declare const POLLING_MIN_REQUESTS = 5;
123
+ declare const POLLING_LOOKBACK_MS: number;
124
+ declare const POLLING_MAX_STDDEV_RATIO = .2;
125
+ declare const POLLING_MIN_EMPTY_RATIO = .8;
126
+ declare const SERVER_ERRORS_MIN_5XX = 3;
127
+ declare const BACKFILL_DAY_SHARE = .25;
128
+ declare const BACKFILL_FULL_PAGE_RATIO = .9;
129
+ /** A day under this many rows is never called a backfill, however full its pages. */
130
+ declare const BACKFILL_MIN_DAY_ROWS = 1e5;
131
+ declare const CREDITS_RUNWAY_WARN_DAYS = 7;
132
+ /** A resolved insight stays visible as "recovered" for this long. */
133
+ declare const RECOVERED_VISIBLE_MS: number;
134
+ /** Consecutive clean evaluations before an open insight resolves. */
135
+ declare const CLEAR_STREAK_TO_RESOLVE = 2;
136
+ /**
137
+ * Hand-off prompt helpers. A prompt tells the developer's coding agent what we
138
+ * saw (names, times, counts, error text) and what to look at, in plain words.
139
+ * It never contains code and never asks the agent to read the user's source.
140
+ */
141
+ declare function handoffPrompt(parts: {
142
+ /** `My Secondlayer webhook "mint-alerts"` */
143
+ subject: string
144
+ /** Plain facts with numbers and UTC times. The first one continues the
145
+ * subject ("has been failing since 16:06 UTC"); the rest are sentences. */
146
+ observed: string[]
147
+ /** What to check, in words. */
148
+ lookAt: string
149
+ /** Optional follow-up naming an `sl` command. */
150
+ followUp?: string
151
+ }): string;
152
+ /**
153
+ * Receiver and handler text is untrusted and often JSON or HTML. Collapse it
154
+ * to one plain line with no braces, backticks or arrows so it can sit inside
155
+ * a prompt or a body sentence.
156
+ */
157
+ declare function plainSnippet(text: string | null | undefined, max?: number): string;
158
+ /** "16:06 UTC" */
159
+ declare function utcTime(date: Date): string;
160
+ /** "Oct 15 16:06 UTC" */
161
+ declare function utcDateTime(date: Date): string;
162
+ /** "Oct 15" */
163
+ declare function utcDay(date: Date): string;
164
+ declare function plural(n: number, one: string, many?: string): string;
165
+ /** The host of a URL, or the input when it does not parse. */
166
+ declare function hostOf(url: string): string;
167
+ import { Kysely } from "kysely";
168
+ import { ColumnType, Generated } from "kysely";
169
+ /**
170
+ * Coverage-kernel constraints. SQL CHECKs in migration 0116 must stay
171
+ * identical — property tests treat this file as the spec.
172
+ */
173
+ declare const STAGE_KINDS: readonly ["raw", "decode", "subgraph", "queue"];
174
+ type StageKind = (typeof STAGE_KINDS)[number];
175
+ declare const NATIVE_CLOCKS: readonly ["block", "cursor", "queue"];
176
+ type NativeClock = (typeof NATIVE_CLOCKS)[number];
177
+ declare const REPAIR_MODES: readonly ["range_safe", "full_reindex", "archive_replay", "none"];
178
+ type RepairMode = (typeof REPAIR_MODES)[number];
179
+ declare const RUN_STATUSES: readonly ["pending", "running", "complete", "syncing", "lagging", "gap", "stale", "failed", "unverified_import", "unanchored", "source_unavailable", "out_of_scope", "disabled", "halted"];
180
+ type RunStatus = (typeof RUN_STATUSES)[number];
181
+ declare const FAILURE_UNITS: readonly ["block", "range", "cursor", "queue"];
182
+ type FailureUnit = (typeof FAILURE_UNITS)[number];
183
+ declare const FAILURE_CLASSES: readonly ["omission", "version", "digest_mismatch", "crash", "reorg", "source_gap", "handler", "timeout", "unknown"];
184
+ type FailureClass = (typeof FAILURE_CLASSES)[number];
185
+ declare const RETRY_STATES: readonly ["open", "retrying", "halted", "resolved"];
186
+ type RetryState = (typeof RETRY_STATES)[number];
187
+ type BootstrapSource = "archive" | "genesis" | "import";
188
+ /**
189
+ * How far a subgraph's rows can be checked without trusting the server,
190
+ * derived from its sources and a scan of its handler. Never configured.
191
+ *
192
+ * - `state`: every input is a named state write, provable from block headers.
193
+ * Handlers run in the deterministic realm. A deployed `state` subgraph is
194
+ * verifiable: one with any reason is refused at deploy.
195
+ * - `events`: an input (events, prints, tx results, `ctx.client`) needs
196
+ * re-execution proofs, which are not served yet.
197
+ * - `none`: a source can never be proven as written (trait scope, tip-first
198
+ * backfill).
199
+ */
200
+ interface SubgraphVerification {
201
+ level: "state" | "events" | "none";
202
+ /** What stands between the subgraph and checkable rows, e.g.
203
+ * `print_event source "swaps" needs event proofs` or
204
+ * `subgraph.ts:41:12 Date: wall-clock time differs per run`. */
205
+ reasons: string[];
206
+ /** What stays unproven even at this level, e.g. tx attribution of writes. */
207
+ unproven: string[];
208
+ }
209
+ interface BlocksTable {
210
+ height: number;
211
+ hash: string;
212
+ parent_hash: string;
213
+ burn_block_height: number;
214
+ burn_block_hash: ColumnType<string | null, string | null | undefined, string | null>;
215
+ /** Nakamoto StacksBlockId. Null on rows ingested before it was persisted. */
216
+ index_block_hash: ColumnType<string | null, string | null | undefined, string | null>;
217
+ timestamp: number;
218
+ canonical: Generated<boolean>;
219
+ /** Tx count `persistBlock` was handed for this height. Null on rows
220
+ * ingested before this column existed. */
221
+ tx_count: ColumnType<number | null, number | null | undefined, number | null>;
222
+ created_at: Generated<Date>;
223
+ }
224
+ interface TransactionsTable {
225
+ tx_id: string;
226
+ block_height: number;
227
+ tx_index: Generated<number>;
228
+ type: string;
229
+ sender: string;
230
+ status: string;
231
+ contract_id: string | null;
232
+ function_name: string | null;
233
+ function_args: Generated<unknown | null>;
234
+ raw_result: Generated<string | null>;
235
+ raw_tx: string;
236
+ created_at: Generated<Date>;
237
+ }
238
+ interface EventsTable {
239
+ id: Generated<string>;
240
+ tx_id: string;
241
+ block_height: number;
242
+ event_index: number;
243
+ type: string;
244
+ data: unknown;
245
+ created_at: Generated<Date>;
246
+ }
247
+ interface TransactionsArchiveTable {
248
+ archive_id: Generated<string>;
249
+ tx_id: string;
250
+ block_height: number;
251
+ tx_index: number;
252
+ type: string;
253
+ sender: string;
254
+ status: string;
255
+ contract_id: string | null;
256
+ function_name: string | null;
257
+ function_args: unknown | null;
258
+ raw_result: string | null;
259
+ raw_tx: string;
260
+ created_at: Date;
261
+ orphaned_block_hash: string | null;
262
+ archived_at: Generated<Date>;
263
+ }
264
+ interface EventsArchiveTable {
265
+ archive_id: Generated<string>;
266
+ id: string;
267
+ tx_id: string;
268
+ block_height: number;
269
+ event_index: number;
270
+ type: string;
271
+ data: unknown;
272
+ created_at: Date;
273
+ orphaned_block_hash: string | null;
274
+ archived_at: Generated<Date>;
275
+ }
276
+ interface VmEventsTable {
277
+ tx_id: string;
278
+ block_height: number;
279
+ ordinal: number;
280
+ type: string;
281
+ data: unknown;
282
+ created_at: Generated<Date>;
283
+ }
284
+ interface VmEventsArchiveTable {
285
+ archive_id: Generated<string>;
286
+ /** `block_height:ordinal`; rows archived before migration 0153 hold the old uuid. */
287
+ id: string;
288
+ tx_id: string;
289
+ block_height: number;
290
+ ordinal: number;
291
+ type: string;
292
+ data: unknown;
293
+ created_at: Date;
294
+ orphaned_block_hash: string | null;
295
+ archived_at: Generated<Date>;
296
+ }
297
+ interface StateWritesTable {
298
+ block_height: number;
299
+ ordinal: number;
300
+ tx_index: number | null;
301
+ key: string;
302
+ value_hex: string;
303
+ }
304
+ interface StateWritesArchiveTable {
305
+ archive_id: Generated<string>;
306
+ /** `block_height:ordinal`. */
307
+ id: string;
308
+ block_height: number;
309
+ ordinal: number;
310
+ tx_index: number | null;
311
+ key: string;
312
+ value_hex: string;
313
+ orphaned_block_hash: string | null;
314
+ archived_at: Generated<Date>;
315
+ }
316
+ interface DeadLetterEventsTable {
317
+ id: Generated<string>;
318
+ block_height: number;
319
+ tx_id: string;
320
+ event_index: number;
321
+ event_type: string;
322
+ data: unknown;
323
+ reason: string;
324
+ created_at: Generated<Date>;
325
+ }
326
+ interface MempoolTransactionsTable {
327
+ seq: Generated<string>;
328
+ tx_id: string;
329
+ raw_tx: string;
330
+ type: string;
331
+ sender: string;
332
+ contract_id: string | null;
333
+ function_name: string | null;
334
+ function_args: unknown | null;
335
+ received_at: Generated<Date>;
336
+ }
337
+ interface IndexProgressTable {
338
+ network: string;
339
+ last_indexed_block: Generated<number>;
340
+ last_contiguous_block: Generated<number>;
341
+ highest_seen_block: Generated<number>;
342
+ updated_at: Generated<Date>;
343
+ }
344
+ interface SubgraphsTable {
345
+ id: Generated<string>;
346
+ name: string;
347
+ version: Generated<string>;
348
+ status: Generated<string>;
349
+ definition: Record<string, unknown>;
350
+ schema_hash: string;
351
+ handler_path: string;
352
+ schema_name: string | null;
353
+ start_block: Generated<number>;
354
+ last_processed_block: Generated<number>;
355
+ reindex_from_block: number | null;
356
+ reindex_to_block: number | null;
357
+ last_error: string | null;
358
+ last_error_at: Date | null;
359
+ total_processed: Generated<number>;
360
+ total_errors: Generated<number>;
361
+ account_id: string;
362
+ handler_code: string | null;
363
+ source_code: string | null;
364
+ project_id: string | null;
365
+ /** Paid (wallet-ghost) deploys expire unless renewed or claimed; NULL = no expiry. */
366
+ expires_at: Date | null;
367
+ /** (event type, contract) probe pairs persisted at deploy for weight classification. */
368
+ sparse_probe_targets: unknown | null;
369
+ /** sha256 over everything that shapes rows (schema, handler bundle,
370
+ * startBlock, network, runtime). NULL for unbundled or older deploys. */
371
+ pin: string | null;
372
+ /** The canonical JSON `pin` hashes, so a client can recompute it. NULL
373
+ * wherever `pin` is, and for deploys before migration 0158. */
374
+ pin_preimage: string | null;
375
+ /** Level derived at deploy. NULL (older deploys) = not verifiable. */
376
+ verification: SubgraphVerification | null;
377
+ created_at: Generated<Date>;
378
+ updated_at: Generated<Date>;
379
+ }
380
+ interface ContractsTable {
381
+ contract_id: string;
382
+ deployer: string;
383
+ block_height: number;
384
+ canonical: Generated<boolean>;
385
+ abi: unknown | null;
386
+ declared_traits: Generated<string[]>;
387
+ inferred_standards: Generated<string[]>;
388
+ abi_status: Generated<string>;
389
+ abi_fetched_at: Date | null;
390
+ created_at: Generated<Date>;
391
+ }
392
+ interface SubgraphGapsTable {
393
+ id: Generated<string>;
394
+ subgraph_id: string;
395
+ subgraph_name: string;
396
+ gap_start: number;
397
+ gap_end: number;
398
+ reason: string;
399
+ detected_at: Generated<Date>;
400
+ resolved_at: Date | null;
401
+ }
402
+ /** Print-validate skips persisted for the authed violations GET. Cap 100/subgraph. */
403
+ interface SubgraphViolationsTable {
404
+ id: Generated<string>;
405
+ subgraph_name: string;
406
+ source_name: string;
407
+ block_height: number;
408
+ tx_id: string;
409
+ reason: string;
410
+ sample_payload: Record<string, unknown>;
411
+ seen_at: Generated<Date>;
412
+ }
413
+ type SubgraphOperationKind = "reindex" | "backfill";
414
+ type SubgraphOperationStatus = "queued" | "running" | "completed" | "failed" | "cancelled";
415
+ interface SubgraphOperationsTable {
416
+ id: Generated<string>;
417
+ subgraph_id: string;
418
+ subgraph_name: string;
419
+ account_id: string | null;
420
+ kind: ColumnType<SubgraphOperationKind, SubgraphOperationKind, SubgraphOperationKind>;
421
+ status: ColumnType<SubgraphOperationStatus, SubgraphOperationStatus | undefined, SubgraphOperationStatus>;
422
+ from_block: number | null;
423
+ to_block: number | null;
424
+ cancel_requested: Generated<boolean>;
425
+ locked_by: string | null;
426
+ locked_until: Date | null;
427
+ started_at: Date | null;
428
+ finished_at: Date | null;
429
+ processed_blocks: number | null;
430
+ error: string | null;
431
+ created_at: Generated<Date>;
432
+ updated_at: Generated<Date>;
433
+ /** 'light' (contract-scoped sparse) | 'heavy' (broad). Claim budgets heavy. */
434
+ weight: Generated<string>;
435
+ /** Candidate-event denominator computed at enqueue (sparse ops only). */
436
+ estimated_events: string | number | bigint | null;
437
+ /** Events processed so far — written by the progress flush. */
438
+ processed_events: string | number | bigint | null;
439
+ /** Backfill ops' own crash checkpoint — advanced conditionally in-tx with
440
+ * each written block; replays skip at/below it. NULL for reindex ops
441
+ * (those checkpoint on subgraphs.last_processed_block). */
442
+ cursor_block: string | number | bigint | null;
443
+ }
444
+ interface ApiKeysTable {
445
+ id: Generated<string>;
446
+ key_hash: string;
447
+ key_prefix: string;
448
+ name: string | null;
449
+ status: Generated<string>;
450
+ rate_limit: Generated<number>;
451
+ ip_address: string;
452
+ account_id: string;
453
+ product: Generated<"account" | "streams" | "index" | "sentinel" | "docs" | "tenant">;
454
+ /** Sentinel permission areas; set only on `product = 'sentinel'` keys. */
455
+ areas: unknown | null;
456
+ tier: "free" | "build" | "scale" | "enterprise" | "internal" | null;
457
+ last_used_at: Date | null;
458
+ revoked_at: Date | null;
459
+ /** Set only on `docs` keys (24h). A key past this instant never authenticates. */
460
+ expires_at: Date | null;
461
+ created_at: Generated<Date>;
462
+ }
463
+ /** Singleton self-host / publisher identity. Network is immutable. */
464
+ interface InstancesTable {
465
+ id: Generated<string>;
466
+ network: string;
467
+ created_at: Generated<Date>;
468
+ }
469
+ interface AccountsTable {
470
+ id: Generated<string>;
471
+ /** NULL for unclaimed ghost accounts (anonymous self-serve keys). */
472
+ email: string | null;
473
+ /** True for anonymous self-serve accounts until claimed via magic link. */
474
+ ghost: Generated<boolean>;
475
+ /** Stacks principal owning a wallet-ghost account. */
476
+ wallet_principal: string | null;
477
+ display_name: string | null;
478
+ bio: string | null;
479
+ avatar_url: string | null;
480
+ stripe_customer_id: string | null;
481
+ /** Opt-out toggle for the subgraph reindex-completion email. Defaults true. */
482
+ notify_reindex_complete: Generated<boolean>;
483
+ created_at: Generated<Date>;
484
+ }
485
+ interface SessionsTable {
486
+ id: Generated<string>;
487
+ token_hash: string;
488
+ token_prefix: string;
489
+ account_id: string;
490
+ ip_address: string;
491
+ expires_at: Generated<Date>;
492
+ revoked_at: Date | null;
493
+ last_used_at: Date | null;
494
+ created_at: Generated<Date>;
495
+ }
496
+ interface MagicLinksTable {
497
+ id: Generated<string>;
498
+ email: string;
499
+ token: string;
500
+ code: string | null;
501
+ expires_at: Date;
502
+ used_at: Date | null;
503
+ failed_attempts: Generated<number>;
504
+ created_at: Generated<Date>;
505
+ }
506
+ interface SubgraphProcessingStatsTable {
507
+ id: Generated<string>;
508
+ subgraph_name: string;
509
+ api_key_id: string | null;
510
+ bucket_start: Date | null;
511
+ bucket_end: Date | null;
512
+ blocks_processed: number | null;
513
+ total_time_ms: number | null;
514
+ handler_time_ms: number | null;
515
+ flush_time_ms: number | null;
516
+ max_block_time_ms: number | null;
517
+ max_handler_time_ms: number | null;
518
+ avg_ops_per_block: number | null;
519
+ is_catchup: Generated<boolean>;
520
+ created_at: Generated<Date>;
521
+ }
522
+ interface SubgraphHealthSnapshotsTable {
523
+ id: Generated<string>;
524
+ subgraph_id: string;
525
+ total_processed: number;
526
+ total_errors: number;
527
+ last_processed_block: number | null;
528
+ chain_tip: string | number | null;
529
+ rows_written_total: string | number | null;
530
+ storage_bytes: string | number | null;
531
+ captured_at: Generated<Date>;
532
+ }
533
+ /** Handler errors grouped by fingerprint. Cap 50/subgraph. */
534
+ interface SubgraphErrorGroupsTable {
535
+ id: Generated<string>;
536
+ subgraph_name: string;
537
+ fingerprint: string;
538
+ source_name: string;
539
+ event_type: string | null;
540
+ message: string;
541
+ frame: string | null;
542
+ count: Generated<string | number>;
543
+ sample_tx_id: string | null;
544
+ sample_block_height: string | number | null;
545
+ first_seen: Generated<Date>;
546
+ last_seen: Generated<Date>;
547
+ }
548
+ /**
549
+ * One ranked dashboard insight. `account_id` is NULL on tenant and self-host
550
+ * databases. `severity` is never "recovered": that is derived on read from
551
+ * `resolved_at`.
552
+ */
553
+ interface InsightsTable {
554
+ id: Generated<string>;
555
+ account_id: string | null;
556
+ resource_kind: string;
557
+ resource_id: string;
558
+ rule: string;
559
+ severity: string;
560
+ impact: Generated<number>;
561
+ /** resource, line, meta, lead, body, evidence, handoff, how. */
562
+ payload: unknown;
563
+ first_seen_at: Generated<Date>;
564
+ last_seen_at: Generated<Date>;
565
+ clear_streak: Generated<number>;
566
+ resolved_at: Date | null;
567
+ dismissed_at: Date | null;
568
+ }
569
+ /** "Expected": mutes one rule for one resource until removed. */
570
+ interface InsightMutesTable {
571
+ account_id: string | null;
572
+ resource_kind: string;
573
+ resource_id: string;
574
+ rule: string;
575
+ created_at: Generated<Date>;
576
+ }
577
+ interface ProcessedStripeEventsTable {
578
+ event_id: string;
579
+ event_type: string;
580
+ processed_at: Generated<Date>;
581
+ }
582
+ interface DecodedEventsTable {
583
+ cursor: string;
584
+ block_height: number;
585
+ tx_id: string;
586
+ tx_index: number;
587
+ event_index: number;
588
+ event_type: string;
589
+ microblock_hash: string | null;
590
+ canonical: Generated<boolean>;
591
+ contract_id: string | null;
592
+ sender: string | null;
593
+ recipient: string | null;
594
+ amount: string | null;
595
+ asset_identifier: string | null;
596
+ value: string | null;
597
+ memo: string | null;
598
+ /** Decoded payload for event types that don't fit the flat columns
599
+ * (e.g. `print`: { topic, value, raw_value }). Null for transfer types. */
600
+ payload: unknown | null;
601
+ source_cursor: string;
602
+ created_at: Generated<Date>;
603
+ }
604
+ interface DecoderCheckpointsTable {
605
+ decoder_name: string;
606
+ last_cursor: string | null;
607
+ updated_at: Generated<Date>;
608
+ }
609
+ interface ChainReorgsTable {
610
+ id: Generated<string>;
611
+ detected_at: Generated<Date>;
612
+ fork_point_height: number;
613
+ old_index_block_hash: string | null;
614
+ new_index_block_hash: string | null;
615
+ orphaned_from_height: number;
616
+ orphaned_from_event_index: number;
617
+ orphaned_to_height: number;
618
+ orphaned_to_event_index: number;
619
+ new_canonical_height: number;
620
+ new_canonical_event_index: number;
621
+ created_at: Generated<Date>;
622
+ }
623
+ type Pox4FunctionName = "stack-stx" | "delegate-stx" | "stack-extend" | "stack-increase" | "revoke-delegate-stx" | "delegate-stack-stx" | "delegate-stack-extend" | "delegate-stack-increase" | "stack-aggregation-commit" | "stack-aggregation-commit-indexed" | "stack-aggregation-increase" | "set-signer-key-authorization";
624
+ interface Pox4CallsTable {
625
+ cursor: string;
626
+ block_height: number;
627
+ block_time: Date;
628
+ burn_block_height: number;
629
+ tx_id: string;
630
+ tx_index: number;
631
+ function_name: Pox4FunctionName;
632
+ caller: string;
633
+ stacker: string | null;
634
+ delegate_to: string | null;
635
+ amount_ustx: string | null;
636
+ lock_period: number | null;
637
+ pox_addr_version: number | null;
638
+ pox_addr_hashbytes: string | null;
639
+ pox_addr_btc: string | null;
640
+ start_cycle: number | null;
641
+ end_cycle: number | null;
642
+ signer_key: string | null;
643
+ signer_signature: string | null;
644
+ auth_id: string | null;
645
+ max_amount: string | null;
646
+ reward_cycle: number | null;
647
+ aggregated_amount_ustx: string | null;
648
+ aggregated_signer_index: number | null;
649
+ auth_period: number | null;
650
+ auth_topic: string | null;
651
+ auth_allowed: boolean | null;
652
+ result_ok: boolean;
653
+ result_raw: string;
654
+ canonical: Generated<boolean>;
655
+ source_cursor: string;
656
+ created_at: Generated<Date>;
657
+ }
658
+ type Pox5EventTopic = "set-bond-admin" | "set-pause-admin" | "pause-rewards" | "setup-bond" | "add-to-allowlist" | "register-for-bond" | "update-bond-registration" | "register-signer" | "stake" | "stake-update" | "announce-l1-early-exit" | "unstake-sbtc" | "unstake" | "calculate-rewards" | "bond-distribution" | "claim-rewards" | "claim-staker-rewards-for-signer" | "grant-signer-key" | "revoke-signer-grant";
659
+ interface Pox5EventsTable {
660
+ cursor: string;
661
+ block_height: number;
662
+ block_time: Date;
663
+ tx_id: string;
664
+ tx_index: number;
665
+ event_index: number;
666
+ topic: Pox5EventTopic;
667
+ staker: string | null;
668
+ signer: string | null;
669
+ signer_manager: string | null;
670
+ bond_index: number | null;
671
+ amount_ustx: string | null;
672
+ amount_sats: string | null;
673
+ reward_cycle: number | null;
674
+ first_reward_cycle: number | null;
675
+ unlock_cycle: number | null;
676
+ unlock_burn_height: number | null;
677
+ is_l1_lock: boolean | null;
678
+ signer_key: string | null;
679
+ data: unknown;
680
+ canonical: Generated<boolean>;
681
+ source_cursor: string;
682
+ created_at: Generated<Date>;
683
+ }
684
+ interface Pox5CyclesTable {
685
+ reward_cycle: number;
686
+ start_burn_height: number;
687
+ prepare_start_burn_height: number;
688
+ end_burn_height: number;
689
+ total_stacked_ustx: string;
690
+ reward_eligible_ustx: string;
691
+ stakers: number;
692
+ signers_in_set: number;
693
+ /** bond index (as string) -> sats staked for this cycle. */
694
+ bond_sats: unknown;
695
+ bond_total_sats: string;
696
+ sbtc_custodied_sats: string;
697
+ rewards_allocated_stx: string;
698
+ rewards_allocated_bond: string;
699
+ reserve_deposit: string;
700
+ rewards_per_token_stx: string | null;
701
+ /** bond index (as string) -> cumulative rewards-per-sat for this cycle. */
702
+ rewards_per_token_bond: unknown;
703
+ distributions: number;
704
+ rewards_claimed: string;
705
+ computed_through_height: number;
706
+ updated_at: Generated<Date>;
707
+ }
708
+ interface Pox5CycleSignersTable {
709
+ reward_cycle: number;
710
+ signer: string;
711
+ delegated_ustx: string;
712
+ stx_only_ustx: string;
713
+ reward_shares_ustx: string;
714
+ in_set: boolean;
715
+ rewards_claimed: string;
716
+ }
717
+ interface BurnBlockRewardsTable {
718
+ cursor: string;
719
+ burn_block_height: number;
720
+ burn_block_hash: string;
721
+ reward_index: number;
722
+ recipient_btc: string;
723
+ amount_sats: string;
724
+ burn_amount: Generated<string>;
725
+ created_at: Generated<Date>;
726
+ }
727
+ interface BurnBlockRewardSlotsTable {
728
+ cursor: string;
729
+ burn_block_height: number;
730
+ burn_block_hash: string;
731
+ slot_index: number;
732
+ holder_btc: string;
733
+ created_at: Generated<Date>;
734
+ }
735
+ type SbtcEventTopic = "completed-deposit" | "withdrawal-create" | "withdrawal-accept" | "withdrawal-reject" | "key-rotation" | "update-protocol-contract";
736
+ interface SbtcEventsTable {
737
+ cursor: string;
738
+ block_height: number;
739
+ block_time: Date;
740
+ tx_id: string;
741
+ tx_index: number;
742
+ event_index: number;
743
+ topic: SbtcEventTopic;
744
+ request_id: number | null;
745
+ amount: string | null;
746
+ sender: string | null;
747
+ recipient_btc_version: number | null;
748
+ recipient_btc_hashbytes: string | null;
749
+ bitcoin_txid: string | null;
750
+ output_index: number | null;
751
+ sweep_txid: string | null;
752
+ burn_hash: string | null;
753
+ burn_height: number | null;
754
+ signer_bitmap: string | null;
755
+ max_fee: string | null;
756
+ fee: string | null;
757
+ block_height_at_request: number | null;
758
+ governance_contract_type: number | null;
759
+ governance_new_contract: string | null;
760
+ signer_aggregate_pubkey: string | null;
761
+ signer_threshold: number | null;
762
+ signer_address: string | null;
763
+ signer_keys_count: number | null;
764
+ canonical: Generated<boolean>;
765
+ source_cursor: string;
766
+ created_at: Generated<Date>;
767
+ }
768
+ type SbtcTokenEventType = "transfer" | "mint" | "burn";
769
+ interface SbtcTokenEventsTable {
770
+ cursor: string;
771
+ block_height: number;
772
+ block_time: Date;
773
+ tx_id: string;
774
+ tx_index: number;
775
+ event_index: number;
776
+ event_type: SbtcTokenEventType;
777
+ sender: string | null;
778
+ recipient: string | null;
779
+ amount: string;
780
+ memo: string | null;
781
+ canonical: Generated<boolean>;
782
+ source_cursor: string;
783
+ created_at: Generated<Date>;
784
+ }
785
+ /**
786
+ * BTC L1 settlement status for sBTC withdrawal sweeps. Filled by the indexer's
787
+ * settlement-confirmer worker, which polls our own bitcoind for confirmations of
788
+ * each `withdrawal-accept`'s `sweep_txid` and surfaces real settlement on the
789
+ * withdrawal lifecycle (the API's `settlement.{btc_confirmations,
790
+ * settlement_confirmed}` placeholder). Keyed on `sweep_txid` (not `request_id`):
791
+ * a request can produce more than one sweep over its life (RBF/rebroadcast), so
792
+ * each broadcast keeps its own confirmation history.
793
+ */
794
+ interface SbtcSettlementsTable {
795
+ sweep_txid: string;
796
+ request_id: number;
797
+ btc_confirmations: Generated<number>;
798
+ settlement_confirmed: Generated<boolean>;
799
+ /** Confirming Bitcoin block — null while the sweep is unconfirmed. */
800
+ block_hash: string | null;
801
+ block_height: number | null;
802
+ first_seen_at: Generated<Date>;
803
+ last_checked_at: Generated<Date>;
804
+ /** Set once, when `settlement_confirmed` first flips true. */
805
+ confirmed_at: Date | null;
806
+ updated_at: Generated<Date>;
807
+ }
808
+ type BnsNameEventTopic = "new-name" | "transfer-name" | "renew-name" | "burn-name" | "new-airdrop";
809
+ interface BnsNameEventsTable {
810
+ cursor: string;
811
+ block_height: number;
812
+ block_time: Date;
813
+ tx_id: string;
814
+ tx_index: number;
815
+ event_index: number;
816
+ topic: BnsNameEventTopic;
817
+ namespace: string;
818
+ name: string;
819
+ fqn: string;
820
+ owner: string | null;
821
+ bns_id: string;
822
+ registered_at: number | null;
823
+ imported_at: number | null;
824
+ renewal_height: number | null;
825
+ stx_burn: string | null;
826
+ preordered_by: string | null;
827
+ hashed_salted_fqn_preorder: string | null;
828
+ canonical: Generated<boolean>;
829
+ source_cursor: string;
830
+ created_at: Generated<Date>;
831
+ }
832
+ type BnsNamespaceEventStatus = "launch" | "transfer-manager" | "freeze-manager" | "update-price-manager" | "freeze-price-manager" | "turn-off-manager-transfers";
833
+ interface BnsNamespaceEventsTable {
834
+ cursor: string;
835
+ block_height: number;
836
+ block_time: Date;
837
+ tx_id: string;
838
+ tx_index: number;
839
+ event_index: number;
840
+ status: BnsNamespaceEventStatus;
841
+ namespace: string;
842
+ manager: string | null;
843
+ manager_frozen: boolean | null;
844
+ manager_transfers_disabled: boolean | null;
845
+ price_function: string | null;
846
+ price_frozen: boolean | null;
847
+ lifetime: number | null;
848
+ revealed_at: number | null;
849
+ launched_at: number | null;
850
+ canonical: Generated<boolean>;
851
+ source_cursor: string;
852
+ created_at: Generated<Date>;
853
+ }
854
+ type BnsMarketplaceAction = "list-in-ustx" | "unlist-in-ustx" | "buy-in-ustx";
855
+ interface BnsMarketplaceEventsTable {
856
+ cursor: string;
857
+ block_height: number;
858
+ block_time: Date;
859
+ tx_id: string;
860
+ tx_index: number;
861
+ event_index: number;
862
+ action: BnsMarketplaceAction;
863
+ bns_id: string;
864
+ price_ustx: string | null;
865
+ commission: string | null;
866
+ canonical: Generated<boolean>;
867
+ source_cursor: string;
868
+ created_at: Generated<Date>;
869
+ }
870
+ interface BnsNamesTable {
871
+ fqn: string;
872
+ namespace: string;
873
+ name: string;
874
+ owner: string;
875
+ bns_id: string;
876
+ registered_at: number | null;
877
+ renewal_height: number | null;
878
+ last_event_cursor: string;
879
+ last_event_at: Date;
880
+ updated_at: Generated<Date>;
881
+ }
882
+ interface BnsNamespacesTable {
883
+ namespace: string;
884
+ manager: string | null;
885
+ manager_frozen: Generated<boolean>;
886
+ price_frozen: Generated<boolean>;
887
+ lifetime: number | null;
888
+ launched_at: number | null;
889
+ last_event_cursor: string;
890
+ last_event_at: Date;
891
+ name_count: Generated<number>;
892
+ updated_at: Generated<Date>;
893
+ }
894
+ /**
895
+ * Memoized `ctx.client.readOnly()` results. Keyed on `index_block_hash` so a
896
+ * reorg-replaced block never inherits the orphaned fork's answer; a NULL
897
+ * `index_block_hash` marks a caller-declared contract-constant value, cached
898
+ * once per contract+args instead of once per block.
899
+ */
900
+ interface ChainReadCacheTable {
901
+ id: Generated<number>;
902
+ contract_id: string;
903
+ function_name: string;
904
+ args_hash: string;
905
+ index_block_hash: string | null;
906
+ block_height: ColumnType<number | null, number | null | undefined, number | null>;
907
+ result_hex: string;
908
+ created_at: Generated<Date>;
909
+ }
910
+ /**
911
+ * A block that arrived for a height we already hold, staged until a later
912
+ * block's `parent_hash` says which of the two the chain kept. See migration
913
+ * 0112 — adopting on first sight picked the losing fork twice.
914
+ */
915
+ interface PendingForkBlocksTable {
916
+ height: number;
917
+ block_hash: string;
918
+ parent_hash: string;
919
+ /** The canonical hash we held at this height when the contender arrived. */
920
+ incumbent_hash: string;
921
+ /** The raw observer payload, replayed verbatim if this fork wins. */
922
+ payload: ColumnType<unknown, unknown, unknown>;
923
+ received_at: Generated<Date>;
924
+ }
925
+ type ObserverJournalStatus = "received" | "processed" | "failed" | "retried";
926
+ /**
927
+ * Exact observer callback receipt. `raw_body` is immutable evidence; the
928
+ * processing columns are updated only after the current derived-state write
929
+ * succeeds or fails.
930
+ */
931
+ interface ObserverJournalTable {
932
+ sequence: Generated<string>;
933
+ network: string;
934
+ path: string;
935
+ source: string | null;
936
+ received_at: Generated<Date>;
937
+ raw_body: ColumnType<Buffer, Buffer, Buffer>;
938
+ raw_body_sha256: string;
939
+ status: ColumnType<ObserverJournalStatus, ObserverJournalStatus | undefined, ObserverJournalStatus>;
940
+ semantic_sha256: string | null;
941
+ block_height: number | null;
942
+ block_hash: string | null;
943
+ burn_block_height: number | null;
944
+ burn_block_hash: string | null;
945
+ result: unknown | null;
946
+ error: string | null;
947
+ processed_at: Date | null;
948
+ }
949
+ /** Declared coverage stage. Identity is the `id` string (`raw`, `decode:stx`, …). */
950
+ interface StageRegistryTable {
951
+ id: string;
952
+ kind: StageKind;
953
+ depends_on: string | null;
954
+ native_clock: NativeClock;
955
+ producer_version: string;
956
+ repair_mode: RepairMode;
957
+ enabled: Generated<boolean>;
958
+ created_at: Generated<Date>;
959
+ }
960
+ /** One versioned sweep of a stage. */
961
+ interface StageRunsTable {
962
+ id: Generated<string>;
963
+ stage_id: string;
964
+ code_hash: string;
965
+ config_hash: string;
966
+ handler_hash: string | null;
967
+ target_height: number | null;
968
+ target_cursor: string | null;
969
+ status: RunStatus;
970
+ complete_through: number | null;
971
+ started_at: Generated<Date>;
972
+ finished_at: Date | null;
973
+ }
974
+ /** Per-block acknowledgement. Unique on (stage, height, hash). */
975
+ interface StageBlockReceiptsTable {
976
+ id: Generated<string>;
977
+ stage_id: string;
978
+ run_id: string | null;
979
+ block_height: number;
980
+ block_hash: string;
981
+ input_count: number;
982
+ input_digest: string;
983
+ effect_digest: string;
984
+ finalized: Generated<boolean>;
985
+ compacted_at: Date | null;
986
+ created_at: Generated<Date>;
987
+ }
988
+ /** Compacted finalized range. Only sealed after receipts in the range finalize. */
989
+ interface CoverageSegmentsTable {
990
+ id: Generated<string>;
991
+ stage_id: string;
992
+ from_height: number;
993
+ to_height: number;
994
+ chain_digest: string;
995
+ input_digest: string;
996
+ output_digest: string;
997
+ sealed_at: Generated<Date>;
998
+ }
999
+ /** Exact unit or range that failed, plus retry/retention state. */
1000
+ interface StageFailuresTable {
1001
+ id: Generated<string>;
1002
+ stage_id: string;
1003
+ run_id: string | null;
1004
+ unit_kind: FailureUnit;
1005
+ from_height: number | null;
1006
+ to_height: number | null;
1007
+ class: FailureClass;
1008
+ retry_state: RetryState;
1009
+ retry_count: Generated<number>;
1010
+ last_error: string | null;
1011
+ created_at: Generated<Date>;
1012
+ resolved_at: Date | null;
1013
+ retain_until: Generated<Date>;
1014
+ }
1015
+ /**
1016
+ * The instance's declared scope — one row per network. `start_height` is what
1017
+ * makes missing history below it deliberate rather than a gap.
1018
+ */
1019
+ interface SyncScopesTable {
1020
+ network: string;
1021
+ start_height: number;
1022
+ target_height: number | null;
1023
+ bootstrap_source: BootstrapSource;
1024
+ bootstrap_manifest_digest: string | null;
1025
+ genesis_hash: string | null;
1026
+ created_at: Generated<Date>;
1027
+ updated_at: Generated<Date>;
1028
+ }
1029
+ interface Database {
1030
+ blocks: BlocksTable;
1031
+ transactions: TransactionsTable;
1032
+ events: EventsTable;
1033
+ vm_events: VmEventsTable;
1034
+ state_writes: StateWritesTable;
1035
+ transactions_archive: TransactionsArchiveTable;
1036
+ events_archive: EventsArchiveTable;
1037
+ vm_events_archive: VmEventsArchiveTable;
1038
+ state_writes_archive: StateWritesArchiveTable;
1039
+ dead_letter_events: DeadLetterEventsTable;
1040
+ mempool_transactions: MempoolTransactionsTable;
1041
+ index_progress: IndexProgressTable;
1042
+ contracts: ContractsTable;
1043
+ subgraphs: SubgraphsTable;
1044
+ api_keys: ApiKeysTable;
1045
+ instances: InstancesTable;
1046
+ accounts: AccountsTable;
1047
+ sessions: SessionsTable;
1048
+ magic_links: MagicLinksTable;
1049
+ subgraph_health_snapshots: SubgraphHealthSnapshotsTable;
1050
+ subgraph_processing_stats: SubgraphProcessingStatsTable;
1051
+ subgraph_gaps: SubgraphGapsTable;
1052
+ subgraph_violations: SubgraphViolationsTable;
1053
+ subgraph_error_groups: SubgraphErrorGroupsTable;
1054
+ subgraph_operations: SubgraphOperationsTable;
1055
+ processed_stripe_events: ProcessedStripeEventsTable;
1056
+ account_spend_caps: AccountSpendCapsTable;
1057
+ account_credits: AccountCreditsTable;
1058
+ webhooks: WebhooksTable;
1059
+ webhook_outbox: WebhookOutboxTable;
1060
+ webhook_deliveries: WebhookDeliveriesTable;
1061
+ trigger_evaluator_state: TriggerEvaluatorStateTable;
1062
+ decoded_events: DecodedEventsTable;
1063
+ decoder_checkpoints: DecoderCheckpointsTable;
1064
+ chain_reorgs: ChainReorgsTable;
1065
+ pox4_calls: Pox4CallsTable;
1066
+ pox5_events: Pox5EventsTable;
1067
+ pox5_cycles: Pox5CyclesTable;
1068
+ pox5_cycle_signers: Pox5CycleSignersTable;
1069
+ burn_block_rewards: BurnBlockRewardsTable;
1070
+ burn_block_reward_slots: BurnBlockRewardSlotsTable;
1071
+ sbtc_events: SbtcEventsTable;
1072
+ sbtc_token_events: SbtcTokenEventsTable;
1073
+ sbtc_settlements: SbtcSettlementsTable;
1074
+ bns_name_events: BnsNameEventsTable;
1075
+ bns_namespace_events: BnsNamespaceEventsTable;
1076
+ bns_marketplace_events: BnsMarketplaceEventsTable;
1077
+ bns_names: BnsNamesTable;
1078
+ bns_namespaces: BnsNamespacesTable;
1079
+ service_heartbeats: ServiceHeartbeatsTable;
1080
+ chain_read_cache: ChainReadCacheTable;
1081
+ pending_fork_blocks: PendingForkBlocksTable;
1082
+ observer_journal: ObserverJournalTable;
1083
+ sync_scopes: SyncScopesTable;
1084
+ stage_registry: StageRegistryTable;
1085
+ stage_runs: StageRunsTable;
1086
+ stage_block_receipts: StageBlockReceiptsTable;
1087
+ coverage_segments: CoverageSegmentsTable;
1088
+ stage_failures: StageFailuresTable;
1089
+ archive_fetches: ArchiveFetchesTable;
1090
+ usage_ledger: UsageLedgerTable;
1091
+ account_balance_alerts: AccountBalanceAlertsTable;
1092
+ webhook_incident_alerts: WebhookIncidentAlertsTable;
1093
+ sentinel_accounts: SentinelAccountsTable;
1094
+ waitlists: WaitlistsTable;
1095
+ waitlist_signups: WaitlistSignupsTable;
1096
+ contact_requests: ContactRequestsTable;
1097
+ api_failed_requests: ApiFailedRequestsTable;
1098
+ api_request_log: ApiRequestLogTable;
1099
+ insights: InsightsTable;
1100
+ insight_mutes: InsightMutesTable;
1101
+ feedback_tickets: FeedbackTicketsTable;
1102
+ }
1103
+ interface ServiceHeartbeatsTable {
1104
+ name: string;
1105
+ updated_at: Generated<Date>;
1106
+ }
1107
+ interface AccountSpendCapsTable {
1108
+ account_id: string;
1109
+ monthly_cap_cents: number | null;
1110
+ compute_cap_cents: number | null;
1111
+ storage_cap_cents: number | null;
1112
+ alert_threshold_pct: Generated<number>;
1113
+ alert_sent_at: Date | null;
1114
+ frozen_at: Date | null;
1115
+ updated_at: Generated<Date>;
1116
+ }
1117
+ /** Prepaid dev credits — card-funded balance per account. */
1118
+ interface AccountCreditsTable {
1119
+ account_id: string;
1120
+ balance_usd_micros: Generated<string | number | bigint>;
1121
+ /** Month bucket ("YYYY-MM") the spend counter applies to. */
1122
+ spent_month: string | null;
1123
+ spent_month_usd_micros: Generated<string | number | bigint>;
1124
+ /** Null = auto-refill off. */
1125
+ refill_below_usd_micros: string | number | bigint | null;
1126
+ refill_pack_usd: number | null;
1127
+ refill_last_at: Date | null;
1128
+ updated_at: Generated<Date>;
1129
+ }
1130
+ /**
1131
+ * Archive fetch charge log. Append-only: one row per
1132
+ * priced attempt to fetch a partition object. `usd_micros` is 0 for
1133
+ * free rows (24h re-issue, monthly repair allowance).
1134
+ */
1135
+ interface ArchiveFetchesTable {
1136
+ id: Generated<string>;
1137
+ account_id: string;
1138
+ path: string;
1139
+ dataset: string;
1140
+ usd_micros: string | number | bigint;
1141
+ via_allowance: Generated<boolean>;
1142
+ charged_at: Generated<Date>;
1143
+ }
1144
+ /**
1145
+ * One metered ledger for every billable unit (archive partitions, hosted
1146
+ * Index/Streams rows, and future hosted-stack meters). Append-only, written
1147
+ * only by `meter()`. `idempotency_key` is UNIQUE so a retried submission
1148
+ * no-ops instead of double-charging. `debited` is false when the balance
1149
+ * (or a spend cap) refused the charge — the row still exists so the gap is
1150
+ * visible, never silently dropped. `usd_micros` is negative for a Stripe
1151
+ * top-up (`unit: "topup"`).
1152
+ */
1153
+ interface UsageLedgerTable {
1154
+ id: Generated<string>;
1155
+ account_id: string;
1156
+ unit: string;
1157
+ quantity: string | number | bigint;
1158
+ /** Raw sampled quantity before any floor (e.g. `memory.gb_hour`'s actual
1159
+ * RAM, before the 0.5 GB minimum). NULL for every unit that has no
1160
+ * floor to observe past. */
1161
+ observed_quantity: string | number | null;
1162
+ usd_micros: string | number | bigint;
1163
+ debited: Generated<boolean>;
1164
+ source: string;
1165
+ /** API key behind a metered read; null for non-read units and older rows. */
1166
+ api_key_id: string | null;
1167
+ idempotency_key: string;
1168
+ occurred_at: Generated<Date>;
1169
+ recorded_at: Generated<Date>;
1170
+ }
1171
+ /** Preferences + debounce state for the balance-runway email alerts. One
1172
+ * row per account; a missing row means both alerts are on (defaults). */
1173
+ interface AccountBalanceAlertsTable {
1174
+ account_id: string;
1175
+ notify_7d: Generated<boolean>;
1176
+ notify_2d: Generated<boolean>;
1177
+ sent_7d_at: Date | null;
1178
+ sent_2d_at: Date | null;
1179
+ sent_stopped_at: Date | null;
1180
+ }
1181
+ /** Debounce state for the webhook-down email: one row per incident, open
1182
+ * while `resolved_at` is null. No preference columns by design. */
1183
+ interface WebhookIncidentAlertsTable {
1184
+ id: Generated<string>;
1185
+ account_id: string;
1186
+ webhook_id: string;
1187
+ webhook_name: string;
1188
+ failing_since: Date;
1189
+ opened_at: Generated<Date>;
1190
+ emailed_at: Date | null;
1191
+ email_attempts: Generated<number>;
1192
+ resolved_at: Date | null;
1193
+ }
1194
+ /** Accounts Sentinel's service key may touch: created by Sentinel, or the
1195
+ * owner opted in. */
1196
+ interface SentinelAccountsTable {
1197
+ account_id: string;
1198
+ linked_at: Generated<Date>;
1199
+ via: "created" | "consent";
1200
+ }
1201
+ type WebhookStatus = "active" | "paused" | "error";
1202
+ /** Webhooks react to raw chain events matched directly off the Index/Streams
1203
+ * clock. `chain` is the only kind since migration 0159 removed subgraph-row
1204
+ * webhooks; the column stays as the discriminator. */
1205
+ type WebhookKind = "chain";
1206
+ type WebhookFormat = "standard-webhooks" | "inngest" | "trigger" | "cloudflare" | "cloudevents" | "raw";
1207
+ interface WebhooksTable {
1208
+ id: Generated<string>;
1209
+ account_id: string;
1210
+ project_id: string | null;
1211
+ name: string;
1212
+ status: ColumnType<WebhookStatus, WebhookStatus | undefined, WebhookStatus>;
1213
+ kind: ColumnType<WebhookKind, WebhookKind | undefined, WebhookKind>;
1214
+ /** Chain-trigger filter array (the `SubgraphFilter` shape, JSON). Typed
1215
+ * loosely here to avoid a shared→subgraphs import cycle; the Zod schema in
1216
+ * schemas/webhooks.ts owns the shape. */
1217
+ triggers: unknown;
1218
+ format: ColumnType<WebhookFormat, WebhookFormat | undefined, WebhookFormat>;
1219
+ url: string;
1220
+ signing_secret_enc: Buffer;
1221
+ auth_config: Generated<unknown>;
1222
+ max_retries: Generated<number>;
1223
+ timeout_ms: Generated<number>;
1224
+ concurrency: Generated<number>;
1225
+ circuit_failures: Generated<number>;
1226
+ circuit_opened_at: Date | null;
1227
+ last_delivery_at: Date | null;
1228
+ last_success_at: Date | null;
1229
+ last_error: string | null;
1230
+ /** Receiver TLS leaf certificate expiry, from the emitter's periodic probe. */
1231
+ receiver_cert_expires_at: Date | null;
1232
+ receiver_cert_checked_at: Date | null;
1233
+ created_at: Generated<Date>;
1234
+ updated_at: Generated<Date>;
1235
+ }
1236
+ type OutboxStatus = "pending" | "delivered" | "dead";
1237
+ interface WebhookOutboxTable {
1238
+ id: Generated<string>;
1239
+ webhook_id: string;
1240
+ kind: ColumnType<WebhookKind, WebhookKind | undefined, WebhookKind>;
1241
+ block_height: number | bigint;
1242
+ tx_id: string | null;
1243
+ row_pk: unknown;
1244
+ event_type: string;
1245
+ payload: unknown;
1246
+ dedup_key: string;
1247
+ attempt: Generated<number>;
1248
+ next_attempt_at: Generated<Date>;
1249
+ status: ColumnType<OutboxStatus, OutboxStatus | undefined, OutboxStatus>;
1250
+ is_replay: Generated<boolean>;
1251
+ /** The chain block_time of the matched event/tx. Null for rows written
1252
+ * before this column existed, and for the settlement scan path (not yet
1253
+ * wired). Lets `delivered_at - block_time` measure end-to-end webhook
1254
+ * latency without a join to SOURCE-plane `blocks`. */
1255
+ block_time: Date | null;
1256
+ delivered_at: Date | null;
1257
+ failed_at: Date | null;
1258
+ locked_by: string | null;
1259
+ locked_until: Date | null;
1260
+ /** Set when a reorg marks an in-flight row `dead` instead of retrying it
1261
+ * into an orphaned fork. Null otherwise — delivery failures track their
1262
+ * history on `webhooks.last_error`, not per-row. */
1263
+ last_error: string | null;
1264
+ created_at: Generated<Date>;
1265
+ }
1266
+ interface WebhookDeliveriesTable {
1267
+ id: Generated<string>;
1268
+ /** Nullable after migration 0077 — outbox row may be cleaned up while
1269
+ * delivery telemetry is retained. */
1270
+ outbox_id: string | null;
1271
+ webhook_id: string;
1272
+ attempt: number;
1273
+ status_code: number | null;
1274
+ response_headers: unknown | null;
1275
+ response_body: string | null;
1276
+ error_message: string | null;
1277
+ duration_ms: number | null;
1278
+ dispatched_at: Generated<Date>;
1279
+ }
1280
+ /** Single-row (id always TRUE) high-water mark for the chain-trigger evaluator.
1281
+ * One loop serves all chain webhooks, so the cursor is global. */
1282
+ interface TriggerEvaluatorStateTable {
1283
+ id: Generated<boolean>;
1284
+ last_processed_block: ColumnType<bigint, bigint | number | undefined, bigint | number>;
1285
+ /** Watermark for the async sBTC settlement-confirmed webhook scan; null until
1286
+ * the evaluator fast-forwards it on first run. */
1287
+ last_settlement_scan_at: ColumnType<Date | null, Date | null | undefined, Date | null>;
1288
+ /** Streams cursor (`<block_height>:<event_index>`) for the Bitcoin (Runes)
1289
+ * chain-trigger evaluator (plan 060) — a separate clock from
1290
+ * `last_processed_block` (Stacks). Null until the evaluator fast-forwards
1291
+ * it to the Bitcoin Streams tip on first run. */
1292
+ bitcoin_last_cursor: ColumnType<string | null, string | null | undefined, string | null>;
1293
+ updated_at: Generated<Date>;
1294
+ }
1295
+ /** A public waitlist page: what it is for and whether it takes signups. */
1296
+ interface WaitlistsTable {
1297
+ slug: string;
1298
+ title: string;
1299
+ description: string;
1300
+ url: string;
1301
+ opened_at: Generated<Date>;
1302
+ /** Null while the list is open. */
1303
+ closed_at: Date | null;
1304
+ }
1305
+ /**
1306
+ * One signup per (list, contact, token). `answers` holds the list's own
1307
+ * questions, validated per list by the API before insert.
1308
+ */
1309
+ interface WaitlistSignupsTable {
1310
+ id: Generated<string>;
1311
+ list: string;
1312
+ contact: string;
1313
+ answers: Record<string, unknown>;
1314
+ created_at: Generated<Date>;
1315
+ }
1316
+ /**
1317
+ * A "Talk to us" message. Stored before the operator is emailed;
1318
+ * `notified_at` stays null until the email goes out.
1319
+ */
1320
+ interface ContactRequestsTable {
1321
+ id: Generated<string>;
1322
+ name: string;
1323
+ email: string;
1324
+ company: string | null;
1325
+ volume: "lt10m" | "10m-100m" | "100m-1b" | "gt1b" | "unsure" | null;
1326
+ message: string;
1327
+ source_page: string | null;
1328
+ account_id: string | null;
1329
+ /** Hex sha256 of the client IP, never the address. */
1330
+ ip_hash: string | null;
1331
+ notified_at: Date | null;
1332
+ created_at: Generated<Date>;
1333
+ }
1334
+ /**
1335
+ * 24h hosted record of a failed API request, keyed by `request_id` and
1336
+ * scoped to the account that made it. `query` is redacted before insert.
1337
+ */
1338
+ interface ApiFailedRequestsTable {
1339
+ request_id: string;
1340
+ account_id: string;
1341
+ method: string;
1342
+ path: string;
1343
+ status: number;
1344
+ code: string;
1345
+ message: Generated<string>;
1346
+ query: Record<string, unknown>;
1347
+ origin: string | null;
1348
+ created_at: Generated<Date>;
1349
+ }
1350
+ /**
1351
+ * 7-day hosted log of account-attributed Index/Streams reads. Query values
1352
+ * are stored only as hashes; never bodies, headers or tokens.
1353
+ */
1354
+ interface ApiRequestLogTable {
1355
+ id: Generated<string>;
1356
+ request_id: string | null;
1357
+ account_id: string;
1358
+ api_key_id: string | null;
1359
+ method: string;
1360
+ path: string;
1361
+ path_template: string;
1362
+ params_hash: string | null;
1363
+ cursor_hash: string | null;
1364
+ page_size: number | null;
1365
+ rows: number | null;
1366
+ status: number;
1367
+ duration_ms: number;
1368
+ created_at: Generated<Date>;
1369
+ }
1370
+ /**
1371
+ * Hosted problem report. `attempted` is the server's own copy of the failed
1372
+ * request (never caller-supplied); `status` starts `new` until classified.
1373
+ */
1374
+ interface FeedbackTicketsTable {
1375
+ id: Generated<string>;
1376
+ account_id: string;
1377
+ idempotency_key: string | null;
1378
+ request_id: string | null;
1379
+ intent: string;
1380
+ expected: Record<string, unknown> | null;
1381
+ kind_hint: string | null;
1382
+ evidence: Record<string, unknown> | null;
1383
+ attempted: Record<string, unknown> | null;
1384
+ origin: string;
1385
+ status: Generated<"new" | "classified">;
1386
+ route: string | null;
1387
+ classification: Record<string, unknown> | null;
1388
+ classified_at: Date | null;
1389
+ created_at: Generated<Date>;
1390
+ }
1391
+ /** What a rule returns: an `Insight` before the store assigns identity and time. */
1392
+ type DetectedInsight = Omit<Insight, "id" | "createdAt" | "resolvedAt">;
1393
+ /** One (resource, rule) pair a pass actually looked at. */
1394
+ interface EvaluatedKey {
1395
+ kind: Insight["resource"]["kind"];
1396
+ id: string;
1397
+ rule: string;
1398
+ }
1399
+ /** `null` on tenant and self-host databases, the account id on the platform. */
1400
+ interface InsightScope {
1401
+ accountId: string | null;
1402
+ }
1403
+ /**
1404
+ * A pure rule over plain inputs. Returns null when it does not fire.
1405
+ * `skip` returns true when the input lacks the data the rule needs (a failed
1406
+ * ABI fetch, no health snapshots yet): a skipped rule fires nothing and does
1407
+ * not count toward clearing an open insight.
1408
+ */
1409
+ interface InsightRule<Input> {
1410
+ id: string;
1411
+ skip?(input: Input): boolean;
1412
+ evaluate(input: Input): DetectedInsight | null;
1413
+ }
1414
+ /**
1415
+ * Persist one evaluation pass.
1416
+ *
1417
+ * Fired insights upsert on the open (resource, rule) row. Every key in
1418
+ * `evaluatedKeys` that has an open row and did not fire gains a clear streak,
1419
+ * and resolves once the streak reaches `CLEAR_STREAK_TO_RESOLVE`. A rule that
1420
+ * was skipped (missing data) must stay out of `evaluatedKeys` so it never
1421
+ * counts toward clearing.
1422
+ */
1423
+ declare function recordEvaluation(db: Kysely<Database>, scope: InsightScope, fired: DetectedInsight[], evaluatedKeys: EvaluatedKey[], now?: Date): Promise<void>;
1424
+ /** (kind, id) pairs that have an open insight in this scope. */
1425
+ declare function listOpenResources(db: Kysely<Database>, scope: InsightScope): Promise<{
1426
+ kind: Insight["resource"]["kind"]
1427
+ id: string
1428
+ rule: string
1429
+ }[]>;
1430
+ /**
1431
+ * Open rows plus rows resolved within `RECOVERED_VISIBLE_MS`, minus dismissed
1432
+ * and muted ones. Resolved rows come back as `recovered`. Ranked.
1433
+ */
1434
+ declare function listInsights(db: Kysely<Database>, scope: InsightScope, now?: Date, filter?: {
1435
+ kind?: string
1436
+ id?: string
1437
+ }): Promise<Insight[]>;
1438
+ /** Hide the current incident. Returns false when the id is not in scope. */
1439
+ declare function dismissInsight(db: Kysely<Database>, scope: InsightScope, id: string, now?: Date): Promise<boolean>;
1440
+ /** Mute the insight's rule for its resource until un-muted. */
1441
+ declare function muteRule(db: Kysely<Database>, scope: InsightScope, id: string): Promise<boolean>;
1442
+ declare function unmuteRule(db: Kysely<Database>, scope: InsightScope, id: string): Promise<boolean>;
1443
+ /** Delete resolved or dismissed-and-resolved rows older than `olderThanDays`. */
1444
+ declare function purgeResolved(db: Kysely<Database>, scope: InsightScope, olderThanDays?: number, now?: Date): Promise<void>;
1445
+ type ClassifierName = "jev" | "kev" | "clef" | "rules";
1446
+ type Question = {
1447
+ type: "choice"
1448
+ instructions: string
1449
+ criteria: Record<string, string>
1450
+ } | {
1451
+ type: "score"
1452
+ instructions: string
1453
+ criteria: readonly string[]
1454
+ } | {
1455
+ type: "boolean"
1456
+ instructions: string
1457
+ criteria: {
1458
+ true: string
1459
+ false: string
1460
+ }
1461
+ };
1462
+ type Answer = {
1463
+ type: "choice"
1464
+ choice: string
1465
+ probabilities: Record<string, number>
1466
+ confidence: number
1467
+ } | {
1468
+ type: "score"
1469
+ score: number
1470
+ probabilities: Record<string, number>
1471
+ confidence: number
1472
+ } | {
1473
+ type: "boolean"
1474
+ probability: number
1475
+ confidence: number
1476
+ } | {
1477
+ type: "refusal"
1478
+ };
1479
+ type Answers<Q extends Record<string, Question>> = { [K in keyof Q] : Answer };
1480
+ type ClassifyResult<Q extends Record<string, Question>> = {
1481
+ provider: ClassifierName
1482
+ /** response.modelId when the backend reports one. */
1483
+ modelId: string
1484
+ answers: Answers<Q>
1485
+ };
1486
+ /** Deterministic answers for the offline provider. Return a value per question
1487
+ * it can decide; omitted questions come back as `{ type: "refusal" }`. */
1488
+ type RuleValue = string | number | boolean;
1489
+ type RulesFn<Q extends Record<string, Question>> = (state: string | Record<string, unknown>) => { [K in keyof Q]? : RuleValue } | null;
1490
+ /**
1491
+ * One classifier call over jev, kev, clef or offline rules.
1492
+ * Returns null on any provider failure (fail-open: callers decide what null
1493
+ * means). Only ClassifierInputError, a bad question definition, escapes.
1494
+ * Never logs state or response bodies.
1495
+ */
1496
+ declare function classify<const Q extends Record<string, Question>>(input: {
1497
+ state: string | Record<string, unknown>
1498
+ questions: Q
1499
+ rules?: RulesFn<Q>
1500
+ /** Overrides resolveProvider(); used by classifier-compare. */
1501
+ provider?: ClassifierName
1502
+ /** Overrides CLASSIFIER_MODEL / the provider default. */
1503
+ modelId?: string
1504
+ /** Default 5000. */
1505
+ timeoutMs?: number
1506
+ env?: Record<string, string | undefined>
1507
+ /** Tests only (kev/clef). */
1508
+ fetchImpl?: typeof fetch
1509
+ }): Promise<ClassifyResult<Q> | null>;
1510
+ type LabelOutcome = {
1511
+ cause: string
1512
+ confidence: number
1513
+ } | null;
1514
+ type LabelCache = Map<string, {
1515
+ at: number
1516
+ outcome: LabelOutcome
1517
+ }>;
1518
+ interface LabelDeps {
1519
+ classifyFn?: typeof classify;
1520
+ env?: Record<string, string | undefined>;
1521
+ now?: number;
1522
+ cache?: LabelCache;
1523
+ }
1524
+ declare function maybeLabel(insight: DetectedInsight, deps?: LabelDeps): Promise<DetectedInsight>;
1525
+ import { Kysely as Kysely3 } from "kysely";
1526
+ interface AbiCache {
1527
+ get(contractId: string, now: number): string[] | undefined;
1528
+ set(contractId: string, names: string[], now: number): void;
1529
+ }
1530
+ /**
1531
+ * Run one resource's rules. A rule that throws or reports `skip` is left out
1532
+ * of `evaluated`, so missing data can never clear an open insight.
1533
+ */
1534
+ declare function runRules<Input>(rules: InsightRule<Input>[], input: Input, resource: {
1535
+ kind: EvaluatedKey["kind"]
1536
+ id: string
1537
+ }, opts?: {
1538
+ active?: boolean
1539
+ }): {
1540
+ fired: DetectedInsight[]
1541
+ evaluated: EvaluatedKey[]
1542
+ };
1543
+ interface TenantEvaluateDeps extends Omit<LabelDeps, "now"> {
1544
+ now?: Date;
1545
+ fetchImpl?: typeof fetch;
1546
+ abiCache?: AbiCache;
1547
+ }
1548
+ /**
1549
+ * One tenant (or self-host) pass: webhook rules, subgraph rules, optional
1550
+ * cause labels, then persist. Open insights for resources that no longer
1551
+ * exist resolve through the normal two-pass clear.
1552
+ */
1553
+ declare function evaluateTenantInsights(db: Kysely3<Database>, deps?: TenantEvaluateDeps): Promise<void>;
1554
+ export { BACKFILL_DAY_SHARE, BACKFILL_FULL_PAGE_RATIO, BACKFILL_MIN_DAY_ROWS, CLEAR_STREAK_TO_RESOLVE, CREDITS_RUNWAY_WARN_DAYS, DEAD_LETTER_DELETE_AGE_DAYS, DEAD_LETTER_WARN_AGE_DAYS, DOWN_MIN_CONSECUTIVE, DUPLICATE_READS_LOOKBACK_MS, DUPLICATE_READS_MIN_PAGES, DUPLICATE_READS_WINDOW_MS, DetectedInsight, EvaluatedKey, HANDLER_ERRORS_MIN_24H, InsightRule, InsightScope, LAG_MIN_SAMPLES, LAG_THRESHOLD_MS, LAG_WINDOW, LOGIN_WALL_MIN_CONSECUTIVE, LOGIN_WALL_MIN_PRIOR, LOGIN_WALL_PRIOR_JSON_RATIO, PAGE_CAP_ROWS, POLLING_LOOKBACK_MS, POLLING_MAX_STDDEV_RATIO, POLLING_MIN_DURATION_MS, POLLING_MIN_EMPTY_RATIO, POLLING_MIN_REQUESTS, RATE_LIMITED_MIN_ATTEMPTS, RATE_LIMITED_MIN_RATIO, RECOVERED_VISIBLE_MS, REJECTS_MIN_CONSECUTIVE, SERVER_ERRORS_MIN_5XX, SLOWING_DAYS, SLOWING_MIN_R2, SLOWING_MIN_RISING_DAYS, SLOW_MIN_SAMPLES, SLOW_TIMEOUT_RATIO, SLOW_WINDOW, SUBGRAPH_LAG_BLOCKS, SUBGRAPH_LAG_LOSING_BLOCKS, SUBGRAPH_LAG_SNAPSHOTS, TEST_ENDPOINT_MIN_AGE_DAYS, TLS_WARN_DAYS, TenantEvaluateDeps, dismissInsight, evaluateTenantInsights, handoffPrompt, hostOf, listInsights, listOpenResources, maybeLabel, muteRule, plainSnippet, plural, purgeResolved, rankInsights, recordEvaluation, runRules, unmuteRule, utcDateTime, utcDay, utcTime };