@fiber-pay/runtime 0.1.0-rc.2 → 0.1.0-rc.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @fiber-pay/runtime
2
2
 
3
- Runtime monitor + job orchestration for Fiber (`fnn v0.6.1`).
3
+ Runtime monitor + job orchestration for Fiber (`fnn v0.7.1`).
4
4
 
5
5
  ## Quick start
6
6
 
@@ -104,8 +104,8 @@ curl -fsS "$PROXY_A/jobs/$PAYMENT_JOB_ID/events" | jq
104
104
  ## One-command regression (recommended)
105
105
 
106
106
  ```bash
107
- pnpm e2e:runtime-jobs
108
- JOB_TIMEOUT_SEC=420 CHANNEL_CLEANUP_TIMEOUT_SEC=120 pnpm e2e:runtime-jobs -- --json
107
+ pnpm e2e
108
+ CHANNEL_READY_TIMEOUT_SEC=420 CHANNEL_CLOSE_TIMEOUT_SEC=180 pnpm e2e
109
109
  ```
110
110
 
111
111
  ## Handy job commands
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { ChannelId, Channel, PaymentHash, PeerInfo, OpenChannelParams, ShutdownChannelParams, AcceptChannelParams, AbandonChannelParams, UpdateChannelParams, OpenChannelResult, AcceptChannelResult, NewInvoiceParams, CancelInvoiceParams, SettleInvoiceParams, GetInvoiceResult, SendPaymentParams, FiberRpcClient } from '@fiber-pay/sdk';
1
+ import { ChannelId, Channel, PaymentHash, PeerInfo, OpenChannelParams, ShutdownChannelParams, AcceptChannelParams, AbandonChannelParams, UpdateChannelParams, OpenChannelResult, AcceptChannelResult, NewInvoiceParams, CancelInvoiceParams, SettleInvoiceParams, GetInvoiceResult, SendPaymentParams, FiberRpcClient, PaymentStatus, HexString } from '@fiber-pay/sdk';
2
2
  import { EventEmitter } from 'node:events';
3
3
 
4
4
  type AlertPriority = 'critical' | 'high' | 'medium' | 'low';
@@ -97,6 +97,12 @@ interface AlertBackend {
97
97
  stop?(): Promise<void>;
98
98
  }
99
99
 
100
+ interface FormatRuntimeAlertOptions {
101
+ color?: 'auto' | 'always' | 'never';
102
+ prefix?: string;
103
+ }
104
+ declare function formatRuntimeAlert(alert: Alert, options?: FormatRuntimeAlertOptions): string;
105
+
100
106
  type JobType = 'payment' | 'invoice' | 'channel';
101
107
  type JobState = 'queued' | 'executing' | 'inflight' | 'waiting_retry' | 'invoice_created' | 'invoice_active' | 'invoice_received' | 'invoice_settled' | 'invoice_expired' | 'invoice_cancelled' | 'channel_opening' | 'channel_accepting' | 'channel_abandoning' | 'channel_updating' | 'channel_awaiting_ready' | 'channel_ready' | 'channel_closing' | 'channel_closed' | 'succeeded' | 'failed' | 'cancelled';
102
108
  declare const TERMINAL_JOB_STATES: Set<JobState>;
@@ -309,6 +315,108 @@ interface RuntimeBootstrap {
309
315
  }
310
316
  declare function startRuntimeService(configInput?: RuntimeConfigInput): Promise<RuntimeBootstrap>;
311
317
 
318
+ /**
319
+ * Classify an RPC / job failure into a structured error.
320
+ *
321
+ * `failedError` is the raw `failed_error` string from Fiber's `get_payment` response.
322
+ * `error` is any caught JS exception.
323
+ */
324
+ declare function classifyRpcError(error: unknown, failedError?: string): ClassifiedError;
325
+
326
+ declare class SqliteJobStore {
327
+ private readonly db;
328
+ constructor(dbPath: string);
329
+ private runMigrations;
330
+ createJob<P, R>(job: Omit<Job<P, R>, 'id' | 'createdAt' | 'updatedAt'>): Job<P, R>;
331
+ updateJob<P, R>(id: string, updates: Partial<Job<P, R>>): Job<P, R>;
332
+ getJob<P = unknown, R = unknown>(id: string): Job<P, R> | undefined;
333
+ getJobByIdempotencyKey<P = unknown, R = unknown>(key: string): Job<P, R> | undefined;
334
+ listJobs<P = unknown, R = unknown>(filter?: JobFilter): Job<P, R>[];
335
+ deleteJob(id: string): void;
336
+ /** Return jobs that are ready to be retried right now. */
337
+ getRetryableJobs<P = unknown, R = unknown>(now?: number): Job<P, R>[];
338
+ /** Return jobs in non-terminal states (for recovery after daemon restart). */
339
+ getInProgressJobs<P = unknown, R = unknown>(): Job<P, R>[];
340
+ addJobEvent(jobId: string, eventType: JobEventType, fromState?: JobState, toState?: JobState, data?: Record<string, unknown>): JobEvent;
341
+ listJobEvents(jobId: string): JobEvent[];
342
+ close(): void;
343
+ private rowToJob;
344
+ }
345
+
346
+ interface JobManagerEvents {
347
+ 'job:created': [job: RuntimeJob];
348
+ 'job:state_changed': [job: RuntimeJob, from: JobState];
349
+ 'job:succeeded': [job: RuntimeJob];
350
+ 'job:failed': [job: RuntimeJob];
351
+ 'job:cancelled': [job: RuntimeJob];
352
+ }
353
+ interface JobManagerConfig {
354
+ schedulerIntervalMs?: number;
355
+ maxConcurrentJobs?: number;
356
+ retryPolicy?: RetryPolicy;
357
+ }
358
+ declare class JobManager extends EventEmitter<JobManagerEvents> {
359
+ private readonly rpc;
360
+ private readonly store;
361
+ private readonly retryPolicy;
362
+ private readonly schedulerIntervalMs;
363
+ private readonly maxConcurrentJobs;
364
+ private running;
365
+ private schedulerTimer;
366
+ private readonly active;
367
+ constructor(rpc: FiberRpcClient, store: SqliteJobStore, config?: JobManagerConfig);
368
+ ensurePayment(params: PaymentJobParams, options?: {
369
+ idempotencyKey?: string;
370
+ maxRetries?: number;
371
+ }): Promise<PaymentJob>;
372
+ manageInvoice(params: InvoiceJobParams, options?: {
373
+ idempotencyKey?: string;
374
+ maxRetries?: number;
375
+ reuseTerminal?: boolean;
376
+ }): Promise<InvoiceJob>;
377
+ manageChannel(params: ChannelJobParams, options?: {
378
+ idempotencyKey?: string;
379
+ maxRetries?: number;
380
+ reuseTerminal?: boolean;
381
+ }): Promise<ChannelJob>;
382
+ getJob(id: string): RuntimeJob | undefined;
383
+ listJobs(filter?: JobFilter): RuntimeJob[];
384
+ cancelJob(id: string): void;
385
+ start(): void;
386
+ stop(): Promise<void>;
387
+ private createOrReuseJob;
388
+ private tick;
389
+ private schedule;
390
+ private recover;
391
+ private execute;
392
+ private buildEventData;
393
+ }
394
+
395
+ declare const defaultPaymentRetryPolicy: RetryPolicy;
396
+ /**
397
+ * Decide whether a job should be retried given its error and current retry count.
398
+ */
399
+ declare function shouldRetry(error: ClassifiedError, retryCount: number, policy: RetryPolicy): boolean;
400
+ /**
401
+ * Compute the next retry delay using exponential backoff with random jitter.
402
+ * retryCount is the number of retries already attempted (0-based before this retry).
403
+ */
404
+ declare function computeRetryDelay(retryCount: number, policy: RetryPolicy): number;
405
+
406
+ type MachineEvent = 'send_issued' | 'payment_inflight' | 'payment_success' | 'payment_failed_retryable' | 'payment_failed_permanent' | 'invoice_created' | 'invoice_received' | 'invoice_settled' | 'invoice_expired' | 'invoice_cancelled' | 'channel_opening' | 'channel_accepting' | 'channel_abandoning' | 'channel_updating' | 'channel_closing' | 'channel_ready' | 'channel_closed' | 'channel_failed' | 'retry_delay_elapsed' | 'cancel';
407
+ interface Transition {
408
+ from: JobState | JobState[];
409
+ event: MachineEvent;
410
+ to: JobState;
411
+ }
412
+ declare class JobStateMachine {
413
+ private readonly table;
414
+ constructor(transitions: Transition[]);
415
+ transition(current: JobState, event: MachineEvent): JobState | null;
416
+ isTerminal(state: JobState): boolean;
417
+ }
418
+ declare const paymentStateMachine: JobStateMachine;
419
+
312
420
  interface RpcMonitorProxyConfig {
313
421
  listen: string;
314
422
  targetUrl: string;
@@ -412,112 +520,142 @@ declare class MemoryStore implements Store {
412
520
  listAlerts(filters?: AlertFilter): Alert[];
413
521
  }
414
522
 
415
- declare class SqliteJobStore {
416
- private readonly db;
417
- constructor(dbPath: string);
418
- private runMigrations;
419
- createJob<P, R>(job: Omit<Job<P, R>, 'id' | 'createdAt' | 'updatedAt'>): Job<P, R>;
420
- updateJob<P, R>(id: string, updates: Partial<Job<P, R>>): Job<P, R>;
421
- getJob<P = unknown, R = unknown>(id: string): Job<P, R> | undefined;
422
- getJobByIdempotencyKey<P = unknown, R = unknown>(key: string): Job<P, R> | undefined;
423
- listJobs<P = unknown, R = unknown>(filter?: JobFilter): Job<P, R>[];
424
- deleteJob(id: string): void;
425
- /** Return jobs that are ready to be retried right now. */
426
- getRetryableJobs<P = unknown, R = unknown>(now?: number): Job<P, R>[];
427
- /** Return jobs in non-terminal states (for recovery after daemon restart). */
428
- getInProgressJobs<P = unknown, R = unknown>(): Job<P, R>[];
429
- addJobEvent(jobId: string, eventType: JobEventType, fromState?: JobState, toState?: JobState, data?: Record<string, unknown>): JobEvent;
430
- listJobEvents(jobId: string): JobEvent[];
431
- close(): void;
432
- private rowToJob;
433
- }
434
-
435
- interface JobManagerEvents {
436
- 'job:created': [job: RuntimeJob];
437
- 'job:state_changed': [job: RuntimeJob, from: JobState];
438
- 'job:succeeded': [job: RuntimeJob];
439
- 'job:failed': [job: RuntimeJob];
440
- 'job:cancelled': [job: RuntimeJob];
441
- }
442
- interface JobManagerConfig {
443
- schedulerIntervalMs?: number;
444
- maxConcurrentJobs?: number;
445
- retryPolicy?: RetryPolicy;
446
- }
447
- declare class JobManager extends EventEmitter<JobManagerEvents> {
448
- private readonly rpc;
449
- private readonly store;
450
- private readonly retryPolicy;
451
- private readonly schedulerIntervalMs;
452
- private readonly maxConcurrentJobs;
453
- private running;
454
- private schedulerTimer;
455
- private readonly active;
456
- constructor(rpc: FiberRpcClient, store: SqliteJobStore, config?: JobManagerConfig);
457
- ensurePayment(params: PaymentJobParams, options?: {
458
- idempotencyKey?: string;
459
- maxRetries?: number;
460
- }): Promise<PaymentJob>;
461
- manageInvoice(params: InvoiceJobParams, options?: {
462
- idempotencyKey?: string;
463
- maxRetries?: number;
464
- reuseTerminal?: boolean;
465
- }): Promise<InvoiceJob>;
466
- manageChannel(params: ChannelJobParams, options?: {
467
- idempotencyKey?: string;
468
- maxRetries?: number;
469
- reuseTerminal?: boolean;
470
- }): Promise<ChannelJob>;
471
- getJob(id: string): RuntimeJob | undefined;
472
- listJobs(filter?: JobFilter): RuntimeJob[];
473
- cancelJob(id: string): void;
474
- start(): void;
475
- stop(): Promise<void>;
476
- private createOrReuseJob;
477
- private tick;
478
- private schedule;
479
- private recover;
480
- private execute;
481
- private buildEventData;
482
- }
483
-
484
- type MachineEvent = 'send_issued' | 'payment_inflight' | 'payment_success' | 'payment_failed_retryable' | 'payment_failed_permanent' | 'invoice_created' | 'invoice_received' | 'invoice_settled' | 'invoice_expired' | 'invoice_cancelled' | 'channel_opening' | 'channel_accepting' | 'channel_abandoning' | 'channel_updating' | 'channel_closing' | 'channel_ready' | 'channel_closed' | 'channel_failed' | 'retry_delay_elapsed' | 'cancel';
485
- interface Transition {
486
- from: JobState | JobState[];
487
- event: MachineEvent;
488
- to: JobState;
489
- }
490
- declare class JobStateMachine {
491
- private readonly table;
492
- constructor(transitions: Transition[]);
493
- transition(current: JobState, event: MachineEvent): JobState | null;
494
- isTerminal(state: JobState): boolean;
495
- }
496
- declare const paymentStateMachine: JobStateMachine;
497
-
498
523
  /**
499
- * Classify an RPC / job failure into a structured error.
524
+ * Payment Proof & Tracking System
525
+ * Records, stores, and verifies payment evidence for audit trail and reconciliation
500
526
  *
501
- * `failedError` is the raw `failed_error` string from Fiber's `get_payment` response.
502
- * `error` is any caught JS exception.
527
+ * IMPORTANT LIMITATION:
528
+ * Fiber Network RPC currently does NOT return the payment preimage to the sender
529
+ * after a successful payment. In standard Lightning protocol, the preimage serves
530
+ * as cryptographic proof of payment (SHA256(preimage) === payment_hash).
531
+ *
532
+ * Until Fiber exposes the preimage in send_payment/get_payment responses:
533
+ * - Payment proofs are based on RPC status (Success/Failed) rather than preimage
534
+ * - For invoices YOU create (as receiver), preimage IS available
535
+ * - This limitation affects sender-side proof verification only
536
+ *
537
+ * Tracking issue: Preimage not exposed in Fiber RPC send_payment result
503
538
  */
504
- declare function classifyRpcError(error: unknown, failedError?: string): ClassifiedError;
505
539
 
506
- declare const defaultPaymentRetryPolicy: RetryPolicy;
507
- /**
508
- * Decide whether a job should be retried given its error and current retry count.
509
- */
510
- declare function shouldRetry(error: ClassifiedError, retryCount: number, policy: RetryPolicy): boolean;
511
540
  /**
512
- * Compute the next retry delay using exponential backoff with random jitter.
513
- * retryCount is the number of retries already attempted (0-based before this retry).
541
+ * Complete payment proof record
514
542
  */
515
- declare function computeRetryDelay(retryCount: number, policy: RetryPolicy): number;
516
-
517
- interface FormatRuntimeAlertOptions {
518
- color?: 'auto' | 'always' | 'never';
519
- prefix?: string;
543
+ interface PaymentProof {
544
+ /** Unique identifier (payment hash) */
545
+ id: string;
546
+ /** Status at time of recording */
547
+ status: PaymentStatus;
548
+ /** Original invoice string */
549
+ invoice: string;
550
+ /** Invoice details extracted */
551
+ invoiceDetails: {
552
+ paymentHash: string;
553
+ amountCkb: number;
554
+ description?: string;
555
+ };
556
+ /** Payment execution details */
557
+ execution: {
558
+ amountCkb: number;
559
+ feeCkb: number;
560
+ actualTimestamp: number;
561
+ requestTimestamp: number;
562
+ };
563
+ /** Proof of payment */
564
+ proof: {
565
+ preimage?: HexString;
566
+ peerAddress?: string;
567
+ channelUsed?: string;
568
+ routePath?: string[];
569
+ };
570
+ /** Verification status */
571
+ verified: boolean;
572
+ verifiedAt?: number;
573
+ verificationMethod?: 'preimage_hash' | 'rpc_status' | 'manual';
574
+ /** Metadata for audit trail */
575
+ metadata: {
576
+ createdAt: number;
577
+ updatedAt: number;
578
+ notes?: string;
579
+ };
580
+ }
581
+ interface PaymentProofSummary {
582
+ totalProofs: number;
583
+ verifiedCount: number;
584
+ pendingCount: number;
585
+ failedCount: number;
586
+ totalAmountCkb: number;
587
+ totalFeesCkb: number;
588
+ timeRange: {
589
+ earliest?: number;
590
+ latest?: number;
591
+ };
592
+ }
593
+ declare class PaymentProofManager {
594
+ private proofs;
595
+ private proofFilePath;
596
+ private maxStoredProofs;
597
+ constructor(dataDir: string);
598
+ /**
599
+ * Load proofs from disk
600
+ */
601
+ load(): Promise<void>;
602
+ /**
603
+ * Save proofs to disk
604
+ */
605
+ save(): Promise<void>;
606
+ /**
607
+ * Record a payment proof after successful execution
608
+ */
609
+ recordPaymentProof(paymentHash: string, invoice: string, invoiceDetails: {
610
+ paymentHash: string;
611
+ amountCkb: number;
612
+ description?: string;
613
+ }, execution: {
614
+ amountCkb: number;
615
+ feeCkb: number;
616
+ actualTimestamp: number;
617
+ requestTimestamp: number;
618
+ }, status: PaymentStatus, proof?: {
619
+ preimage?: HexString;
620
+ peerAddress?: string;
621
+ channelUsed?: string;
622
+ routePath?: string[];
623
+ }): PaymentProof;
624
+ /**
625
+ * Get proof by payment hash
626
+ */
627
+ getProof(paymentHash: string): PaymentProof | undefined;
628
+ /**
629
+ * Verify a proof's authenticity
630
+ */
631
+ verifyProof(proof: PaymentProof): {
632
+ valid: boolean;
633
+ reason: string;
634
+ };
635
+ /**
636
+ * Get payment timeline between two timestamps
637
+ */
638
+ getPaymentChain(startTime: number, endTime: number): PaymentProof[];
639
+ /**
640
+ * Get summary statistics
641
+ */
642
+ getSummary(): PaymentProofSummary;
643
+ /**
644
+ * Export proofs as audit report
645
+ */
646
+ exportAuditReport(startTime?: number, endTime?: number): string;
647
+ /**
648
+ * Verify preimage hash (SHA256 of preimage = payment_hash)
649
+ */
650
+ private verifyPreimageHash;
651
+ /**
652
+ * Remove oldest proofs to keep storage bounded
653
+ */
654
+ private pruneOldestProofs;
655
+ /**
656
+ * Ensure directory exists
657
+ */
658
+ private ensureDirectory;
520
659
  }
521
- declare function formatRuntimeAlert(alert: Alert, options?: FormatRuntimeAlertOptions): string;
522
660
 
523
- export { type Alert, type AlertBackend, type AlertBackendConfig, type AlertFilter, type AlertInput, type AlertPriority, type AlertType, type ChannelBalanceChangedData, type ChannelJob, type ChannelJobAction, type ChannelJobAlertData, type ChannelJobParams, type ChannelJobResult, type ChannelStateChangedData, type ClassifiedError, type ErrorCategory, FiberMonitorService, type FileAlertConfig, type InvoiceJob, type InvoiceJobAction, type InvoiceJobAlertData, type InvoiceJobParams, type InvoiceJobResult, type Job, type JobEvent, type JobEventType, type JobFilter, JobManager, type JobState, type JobType, MemoryStore, type PaymentJob, type PaymentJobAlertData, type PaymentJobParams, type PaymentJobResult, type PeerEventData, type PendingTlcChangedData, type RetryPolicy, type RpcHealthData, RpcMonitorProxy, type RuntimeConfig, type RuntimeConfigInput, type RuntimeJob, SqliteJobStore, type StdoutAlertConfig, TERMINAL_JOB_STATES, type TrackedInvoiceState, type TrackedPaymentState, type WebhookAlertConfig, type WebsocketAlertConfig, alertPriorityOrder, alertTypeValues, classifyRpcError, computeRetryDelay, createRuntimeConfig, defaultPaymentRetryPolicy, defaultRuntimeConfig, formatRuntimeAlert, isAlertPriority, isAlertType, parseListenAddress, paymentStateMachine, shouldRetry, startRuntimeService };
661
+ export { type Alert, type AlertBackend, type AlertBackendConfig, type AlertFilter, type AlertInput, type AlertPriority, type AlertType, type ChannelBalanceChangedData, type ChannelJob, type ChannelJobAction, type ChannelJobAlertData, type ChannelJobParams, type ChannelJobResult, type ChannelStateChangedData, type ClassifiedError, type ErrorCategory, FiberMonitorService, type FileAlertConfig, type InvoiceJob, type InvoiceJobAction, type InvoiceJobAlertData, type InvoiceJobParams, type InvoiceJobResult, type Job, type JobEvent, type JobEventType, type JobFilter, JobManager, type JobState, type JobType, MemoryStore, type PaymentJob, type PaymentJobAlertData, type PaymentJobParams, type PaymentJobResult, type PaymentProof, PaymentProofManager, type PaymentProofSummary, type PeerEventData, type PendingTlcChangedData, type RetryPolicy, type RpcHealthData, RpcMonitorProxy, type RuntimeConfig, type RuntimeConfigInput, type RuntimeJob, SqliteJobStore, type StdoutAlertConfig, TERMINAL_JOB_STATES, type TrackedInvoiceState, type TrackedPaymentState, type WebhookAlertConfig, type WebsocketAlertConfig, alertPriorityOrder, alertTypeValues, classifyRpcError, computeRetryDelay, createRuntimeConfig, defaultPaymentRetryPolicy, defaultRuntimeConfig, formatRuntimeAlert, isAlertPriority, isAlertType, parseListenAddress, paymentStateMachine, shouldRetry, startRuntimeService };