@bsv/overlay-express
Version:
BSV Blockchain Overlay Express
420 lines • 16.6 kB
TypeScript
import express from 'express';
import { Engine, LookupService, TopicManager, Advertiser } from '@bsv/overlay';
import { ChainTracker, Broadcaster, OverlayBroadcastFacilitator, WalletInterface } from '@bsv/sdk';
import Knex from 'knex';
import { MongoClient, Db } from 'mongodb';
import { type UIConfig } from './makeUserInterface.js';
import { BanService } from './BanService.js';
/**
* Knex database migration.
*/
interface Migration {
name?: string;
up: (knex: Knex.Knex) => Promise<void>;
down?: (knex: Knex.Knex) => Promise<void>;
}
/**
* Configuration options that map to Engine constructor parameters.
*/
export interface EngineConfig {
chainTracker?: ChainTracker | 'scripts only';
shipTrackers?: string[];
slapTrackers?: string[];
broadcaster?: Broadcaster;
advertiser?: Advertiser;
syncConfiguration?: Record<string, string[] | 'SHIP' | false>;
logTime?: boolean;
logPrefix?: string;
throwOnBroadcastFailure?: boolean;
overlayBroadcastFacilitator?: OverlayBroadcastFacilitator;
suppressDefaultSyncAdvertisements?: boolean;
topicAnchorHeaderResolver?: TopicAnchorHeaderResolver;
enableBASMSync?: boolean;
unprovenEvictionBlocks?: number;
reorgStreamUrl?: string;
reorgScanDepth?: number;
unprovenMaintenanceIntervalMs?: number;
}
export type HealthStatus = 'ok' | 'degraded' | 'error';
export interface HealthCheckResult {
name: string;
scope: 'live' | 'ready';
status: HealthStatus;
critical: boolean;
message?: string;
details?: Record<string, any>;
durationMs: number;
}
export type HealthCheckHandler = () => Promise<Omit<HealthCheckResult, 'name' | 'scope' | 'critical' | 'durationMs'> | void> | Omit<HealthCheckResult, 'name' | 'scope' | 'critical' | 'durationMs'> | void;
export interface HealthCheckDefinition {
name: string;
scope?: 'live' | 'ready';
critical?: boolean;
handler: HealthCheckHandler;
}
export interface HealthConfig {
includeDetails: boolean;
timeoutMs: number;
contextProvider?: () => Promise<Record<string, any> | undefined> | Record<string, any> | undefined;
}
export interface HealthReport {
status: HealthStatus;
live: boolean;
ready: boolean;
service: {
name: string;
advertisableFQDN: string;
port: number;
network: 'main' | 'test';
startedAt?: string;
uptimeMs: number;
topicManagerCount: number;
lookupServiceCount: number;
};
checks: HealthCheckResult[];
context?: Record<string, any>;
}
export type TopicAnchorHeaderResolver = (blockHeight: number) => Promise<{
blockHeight: number;
blockHash: string;
merkleRoot?: string;
} | undefined>;
/**
* OverlayExpress class provides an Express-based server for hosting Overlay Services.
* It allows configuration of various components like databases, topic managers, and lookup services.
* It encapsulates an Express application and provides methods to start the server.
*/
export default class OverlayExpress {
name: string;
privateKey: string;
advertisableFQDN: string;
app: express.Application;
port: number;
logger: typeof console;
knex?: Knex.Knex;
migrationsToRun: Migration[];
mongoDb?: Db;
mongoClient?: MongoClient;
network: 'main' | 'test';
chainTracker: ChainTracker | 'scripts only';
engine?: Engine;
managers: Record<string, TopicManager>;
services: Record<string, LookupService>;
enableGASPSync: boolean;
enableBASMSync: boolean;
unprovenEvictionBlocks: number;
basmBlockPollIntervalMs: number;
private basmBlockPollTimer?;
unprovenMaintenanceIntervalMs: number;
private unprovenMaintenanceTimer?;
reorgStreamUrl?: string;
reorgScanDepth: number;
private reorgAdapter?;
topicAnchorHeaderResolver?: TopicAnchorHeaderResolver;
arcApiKey: string | undefined;
arcCallbackToken: string | undefined;
arcadeUrl: string | undefined;
arcadeApiKey: string | undefined;
arcadeDeploymentId: string | undefined;
arcadeChaintracksApiPrefix: string;
private arcadeProvider?;
verboseRequestLogging: boolean;
webUIConfig: UIConfig;
engineConfig: EngineConfig;
private readonly adminToken;
janitorConfig: {
requestTimeoutMs: number;
hostDownRevokeScore: number;
autoBanOnRemoval: boolean;
};
banService?: BanService;
adminIdentityKey?: string;
serverWallet?: WalletInterface;
private startTime?;
healthConfig: HealthConfig;
healthChecks: HealthCheckDefinition[];
isListening: boolean;
/**
* Constructs an instance of OverlayExpress.
* @param name - The name of the service
* @param privateKey - Private key used for signing advertisements
* @param advertisableFQDN - The fully qualified domain name where this service is available. Does not include "https://".
* @param adminToken - Optional. An administrative Bearer token used to protect admin routes.
* If not provided, a random token will be generated at runtime.
*/
constructor(name: string, privateKey: string, advertisableFQDN: string, adminToken?: string);
/**
* Returns the current admin token in case you need to programmatically retrieve or display it.
*/
getAdminToken(): string;
/**
* Configures the port on which the server will listen.
* @param port - The port number
*/
configurePort(port: number): void;
/**
* Configures the web user interface
* @param config - Web UI configuration options
*/
configureWebUI(config: UIConfig): void;
/**
* Configures the janitor service parameters
* @param config - Janitor configuration options
* - requestTimeoutMs: Timeout for health check requests (default: 10000ms)
* - hostDownRevokeScore: Number of consecutive failures before deleting output (default: 3)
* - autoBanOnRemoval: Whether to auto-ban domains when removed by janitor (default: true)
*/
configureJanitor(config: Partial<typeof this.janitorConfig>): void;
/**
* Configures health-report behavior.
*/
configureHealth(config: Partial<HealthConfig>): void;
/**
* Registers an application-specific health check.
*/
registerHealthCheck(definition: HealthCheckDefinition): void;
/**
* Configures the admin identity key for wallet-based admin detection.
* When set, the frontend can compare the user's wallet identity key against this
* to determine whether to show the admin dashboard.
*
* @param identityKey - The hex-encoded public key of the admin
*/
configureAdminIdentityKey(identityKey: string): void;
/**
* Configures the logger to be used by the server.
* @param logger - A logger object (e.g., console)
*/
configureLogger(logger: typeof console): void;
/**
* Configures the BSV Blockchain network to be used ('main' or 'test').
* By default, it re-initializes chainTracker as a WhatsOnChain for that network.
* @param network - The network ('main' or 'test')
*/
configureNetwork(network: 'main' | 'test'): void;
/**
* Configures the ChainTracker to be used.
* If 'scripts only' is used, it implies no full SPV chain tracking in the Engine.
* @param chainTracker - An instance of ChainTracker or 'scripts only'
*/
configureChainTracker(chainTracker?: ChainTracker | 'scripts only'): void;
/**
* Configures the ARC API key.
* @param apiKey - The ARC API key
*/
configureArcApiKey(apiKey: string): void;
/**
* Configures the ARC callback token expected by /arc-ingest.
* @param token - The token ARC should present when posting callback notifications.
*/
configureArcCallbackToken(token: string): void;
/**
* Configures Arcade for first-choice transaction propagation and proof lookup.
*/
configureArcade(url: string, config?: {
apiKey?: string;
deploymentId?: string;
chaintracksApiPrefix?: string;
}): void;
/**
* Configures a go-chaintracks compatible service for header validation and
* BASM reorg streaming. Arcade exposes this at `/chaintracks/v2`.
*/
configureChaintracks(url: string, config?: {
apiPrefix?: string;
reorgStream?: boolean;
scanDepth?: number;
}): void;
/**
* Enables or disables GASP synchronization (high-level setting).
* This is a broad toggle that can be overridden or customized through syncConfiguration.
* @param enable - true to enable, false to disable
*/
configureEnableGASPSync(enable: boolean): void;
/**
* Enables or disables BRC-136 BASM synchronization.
* BASM is opt-in because it requires direct proofs and block hash resolution.
*/
configureEnableBASMSync(enable: boolean): void;
/**
* Configures the block header resolver used to derive BASM block hashes.
*/
configureTopicAnchorHeaderResolver(resolver: TopicAnchorHeaderResolver): void;
/**
* Configures the go-chaintracks (Arcade) reorg SSE stream used to reconcile
* BASM anchors with blockchain reorganizations in real time.
* @param url - The reorg stream URL, e.g. `https://arcade.example/v2/reorg/stream`.
* @param scanDepth - Optional revalidation-sweep depth in blocks (default 3).
*/
configureReorgStream(url: string, scanDepth?: number): void;
/**
* Configures the opt-in unproven state eviction threshold.
*/
configureUnprovenEviction(config: {
thresholdBlocks?: number;
}): void;
/**
* Configures periodic unproven maintenance. Each run first tries configured
* proof providers, then evicts rows that are still unproven past the threshold.
*/
configureUnprovenMaintenance(config: {
intervalMs?: number;
thresholdBlocks?: number;
}): void;
/**
* Configures how often the BASM anchor chain is extended with empty anchors to
* follow the chain tip. Set to 0 to disable periodic polling.
*/
configureBASMBlockPollInterval(intervalMs: number): void;
/**
* Enables or disables verbose request logging.
* @param enable - true to enable, false to disable
*/
configureVerboseRequestLogging(enable: boolean): void;
/**
* Configure Knex (SQL) database connection.
* @param config - Knex configuration object, or a MySQL connection string loaded from configuration.
*/
configureKnex(config: Knex.Knex.Config | string): Promise<void>;
/**
* Configures the MongoDB database connection.
* Also initializes the BanService for persistent ban tracking.
* @param connectionString - MongoDB connection string
*/
configureMongo(connectionString: string): Promise<void>;
/**
* Configures a Topic Manager.
* @param name - The name of the Topic Manager
* @param manager - An instance of TopicManager
*/
configureTopicManager(name: string, manager: TopicManager): void;
/**
* Configures a Lookup Service.
* @param name - The name of the Lookup Service
* @param service - An instance of LookupService
*/
configureLookupService(name: string, service: LookupService): void;
/**
* Configures a Lookup Service using Knex (SQL) database.
* @param name - The name of the Lookup Service
* @param serviceFactory - A factory function that creates a LookupService instance using Knex
*/
configureLookupServiceWithKnex(name: string, serviceFactory: (knex: Knex.Knex) => {
service: LookupService;
migrations: Migration[];
}): void;
/**
* Configures a Lookup Service using MongoDB.
* @param name - The name of the Lookup Service
* @param serviceFactory - A factory function that creates a LookupService instance using MongoDB
*/
configureLookupServiceWithMongo(name: string, serviceFactory: (mongoDb: Db) => LookupService): void;
/**
* Advanced configuration method for setting or overriding any
* Engine constructor parameters via an EngineConfig object.
*
* Example usage:
* configureEngineParams({
* logTime: true,
* throwOnBroadcastFailure: true,
* overlayBroadcastFacilitator: new MyCustomFacilitator()
* })
*
* These fields will be respected when we finally build/configure the Engine
* in the `configureEngine()` method below.
*/
configureEngineParams(params: EngineConfig): void;
/**
* Configures the Overlay Engine itself.
* By default, auto-configures SHIP and SLAP unless autoConfigureShipSlap = false
* Then it merges in any advanced engine config from `this.engineConfig`.
*
* When a BanService is available (from configureMongo), auto-configured SHIP
* and SLAP managers, discovery storage, and lookup services are wrapped so
* banned outputs are not admitted or indexed.
*
* @param autoConfigureShipSlap - Whether to auto-configure SHIP and SLAP services (default: true)
*/
configureEngine(autoConfigureShipSlap?: boolean): Promise<void>;
/** Wrap SHIP/SLAP managers and services with ban-aware filters if BanService is configured. */
private wrapBanAwareServices;
/** Build the sync config based on enableGASPSync and engineConfig. */
private buildSyncConfig;
/** Build the configured transaction propagation provider chain. */
private buildBroadcaster;
private ensureArcadeProvider;
private fetchArcadeProof;
private fetchConfiguredMerkleProof;
/** Build the BASM block header resolver. */
private buildTopicAnchorHeaderResolver;
/** Resolve the SLAP trackers from config or network defaults. */
private resolveSlapTrackers;
/** Build the WalletAdvertiser (or use user-provided one). */
private buildAdvertiser;
/** Initialize the server wallet for BSV mutual authentication. */
private initServerWallet;
/**
* Ensures that Knex is configured and returns it.
* @throws Error if Knex is not configured
*/
private ensureKnex;
/**
* Ensures that MongoDB is configured and returns it.
* @throws Error if MongoDB is not configured
*/
private ensureMongo;
/**
* Ensures that the Overlay Engine is configured and returns it.
* @throws Error if the Engine is not configured
*/
private ensureEngine;
/**
* Creates a JanitorService instance with current configuration.
*/
private createJanitor;
/** Ban a domain and remove all its SHIP/SLAP records from MongoDB. */
private handleBanDomain;
/** Parse outpoint string, ban it, and evict it from all lookup services. */
private handleBanOutpoint;
/** Evict an output from a specific service or all services (silent per-service errors). */
private evictFromServices;
/** Look up the domain of an outpoint from SHIP or SLAP records. */
private lookupDomainForOutpoint;
/** Ban a domain and delete all SHIP/SLAP records for it. */
private banDomainAndRemoveRecords;
private runHealthCheck;
private collectHealthReport;
/**
* Renders a request or response body for verbose logging, truncating overly long payloads.
*/
private formatBodyForLog;
/**
* Installs middleware that verbosely logs incoming requests and outgoing responses.
*/
private setupVerboseRequestLogging;
/**
* Starts the Express server.
* Sets up routes and begins listening on the configured port.
*/
start(): Promise<void>;
/**
* Runs the post-listen startup work: advertiser init, advertisement sync,
* and the optional GASP/BASM background syncs.
*/
private runStartupSync;
/** Attempt a GASP sync at startup when enabled. */
private runGaspStartupSync;
/** Attempt a BASM sync at startup when enabled, then begin tip-following. */
private runBasmStartupSync;
/** Poll for new blocks to advance anchor chains and detect reorgs. */
private startBASMBlockPolling;
/** Real-time reorg reconciliation via the go-chaintracks (Arcade) reorg SSE. */
private startBASMReorgStream;
/** Extend every topic's BASM anchor chain to the current chain tip. */
private advanceBASMAnchorChains;
/** Revalidate recent BASM anchors against the chain tracker, reconciling any reorg. */
private revalidateBASMAnchors;
private startUnprovenMaintenance;
}
export {};
//# sourceMappingURL=OverlayExpress.d.ts.map