@toon-protocol/connector 3.33.1 → 3.34.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 (91) hide show
  1. package/dist/config/child-expander.d.ts +11 -0
  2. package/dist/config/child-expander.d.ts.map +1 -0
  3. package/dist/config/child-expander.js +131 -0
  4. package/dist/config/child-expander.js.map +1 -0
  5. package/dist/config/config-loader.d.ts +7 -0
  6. package/dist/config/config-loader.d.ts.map +1 -1
  7. package/dist/config/config-loader.js +280 -4
  8. package/dist/config/config-loader.js.map +1 -1
  9. package/dist/config/types.d.ts +39 -0
  10. package/dist/config/types.d.ts.map +1 -1
  11. package/dist/config/types.js.map +1 -1
  12. package/dist/core/connector-node.d.ts +14 -0
  13. package/dist/core/connector-node.d.ts.map +1 -1
  14. package/dist/core/connector-node.js +173 -3
  15. package/dist/core/connector-node.js.map +1 -1
  16. package/dist/discovery/bootstrap-cache.d.ts +20 -0
  17. package/dist/discovery/bootstrap-cache.d.ts.map +1 -0
  18. package/dist/discovery/bootstrap-cache.js +69 -0
  19. package/dist/discovery/bootstrap-cache.js.map +1 -0
  20. package/dist/discovery/bootstrap-manifest.d.ts +23 -0
  21. package/dist/discovery/bootstrap-manifest.d.ts.map +1 -0
  22. package/dist/discovery/bootstrap-manifest.js +119 -0
  23. package/dist/discovery/bootstrap-manifest.js.map +1 -0
  24. package/dist/discovery/bootstrap-seeds.d.ts +7 -0
  25. package/dist/discovery/bootstrap-seeds.d.ts.map +1 -0
  26. package/dist/discovery/bootstrap-seeds.js +10 -0
  27. package/dist/discovery/bootstrap-seeds.js.map +1 -0
  28. package/dist/discovery/bootstrap-service.d.ts +64 -0
  29. package/dist/discovery/bootstrap-service.d.ts.map +1 -0
  30. package/dist/discovery/bootstrap-service.js +320 -0
  31. package/dist/discovery/bootstrap-service.js.map +1 -0
  32. package/dist/discovery/discovered-node-registry.d.ts +46 -0
  33. package/dist/discovery/discovered-node-registry.d.ts.map +1 -0
  34. package/dist/discovery/discovered-node-registry.js +185 -0
  35. package/dist/discovery/discovered-node-registry.js.map +1 -0
  36. package/dist/discovery/ilp-peer-info-event.d.ts +20 -0
  37. package/dist/discovery/ilp-peer-info-event.d.ts.map +1 -1
  38. package/dist/discovery/ilp-peer-info-event.js +76 -1
  39. package/dist/discovery/ilp-peer-info-event.js.map +1 -1
  40. package/dist/discovery/index.d.ts +18 -3
  41. package/dist/discovery/index.d.ts.map +1 -1
  42. package/dist/discovery/index.js +39 -1
  43. package/dist/discovery/index.js.map +1 -1
  44. package/dist/discovery/nostr-relay-client.d.ts +13 -0
  45. package/dist/discovery/nostr-relay-client.d.ts.map +1 -0
  46. package/dist/discovery/nostr-relay-client.js +17 -0
  47. package/dist/discovery/nostr-relay-client.js.map +1 -0
  48. package/dist/discovery/relay-probe.d.ts +5 -0
  49. package/dist/discovery/relay-probe.d.ts.map +1 -0
  50. package/dist/discovery/relay-probe.js +85 -0
  51. package/dist/discovery/relay-probe.js.map +1 -0
  52. package/dist/discovery/route-learning-service.d.ts +55 -0
  53. package/dist/discovery/route-learning-service.d.ts.map +1 -0
  54. package/dist/discovery/route-learning-service.js +238 -0
  55. package/dist/discovery/route-learning-service.js.map +1 -0
  56. package/dist/discovery/self-announce-builder.d.ts +8 -3
  57. package/dist/discovery/self-announce-builder.d.ts.map +1 -1
  58. package/dist/discovery/self-announce-builder.js +124 -5
  59. package/dist/discovery/self-announce-builder.js.map +1 -1
  60. package/dist/http/admin-api-inventory.d.ts.map +1 -1
  61. package/dist/http/admin-api-inventory.js +22 -0
  62. package/dist/http/admin-api-inventory.js.map +1 -1
  63. package/dist/http/admin-api.d.ts +3 -0
  64. package/dist/http/admin-api.d.ts.map +1 -1
  65. package/dist/http/admin-api.js +24 -1
  66. package/dist/http/admin-api.js.map +1 -1
  67. package/dist/http/admin-server.d.ts +2 -0
  68. package/dist/http/admin-server.d.ts.map +1 -1
  69. package/dist/http/admin-server.js +4 -1
  70. package/dist/http/admin-server.js.map +1 -1
  71. package/dist/lib.d.ts +25 -1
  72. package/dist/lib.d.ts.map +1 -1
  73. package/dist/lib.js +46 -2
  74. package/dist/lib.js.map +1 -1
  75. package/dist/observability/metrics-registry.d.ts +7 -0
  76. package/dist/observability/metrics-registry.d.ts.map +1 -1
  77. package/dist/observability/metrics-registry.js +24 -0
  78. package/dist/observability/metrics-registry.js.map +1 -1
  79. package/dist/routing/link-state-db.d.ts +27 -0
  80. package/dist/routing/link-state-db.d.ts.map +1 -0
  81. package/dist/routing/link-state-db.js +81 -0
  82. package/dist/routing/link-state-db.js.map +1 -0
  83. package/dist/routing/path-computation.d.ts +13 -0
  84. package/dist/routing/path-computation.d.ts.map +1 -0
  85. package/dist/routing/path-computation.js +97 -0
  86. package/dist/routing/path-computation.js.map +1 -0
  87. package/dist/routing/routing-table.d.ts +5 -0
  88. package/dist/routing/routing-table.d.ts.map +1 -1
  89. package/dist/routing/routing-table.js +35 -0
  90. package/dist/routing/routing-table.js.map +1 -1
  91. package/package.json +1 -1
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FALLBACK_CURATOR_PUBKEY = exports.FALLBACK_RELAY_SEEDS = void 0;
4
+ exports.FALLBACK_RELAY_SEEDS = [
5
+ { relayUrl: 'wss://relay-ws.devnet.toonprotocol.dev' },
6
+ { relayUrl: 'wss://relay-ws-2.devnet.toonprotocol.dev' },
7
+ { relayUrl: 'wss://relay-ws.toonprotocol.dev' },
8
+ ];
9
+ exports.FALLBACK_CURATOR_PUBKEY = 'c07a4e778ad24f0c8b48cd8ee63c2d7d1ab6a6f6f52c2f5f7a90d5f0ffb0000f';
10
+ //# sourceMappingURL=bootstrap-seeds.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bootstrap-seeds.js","sourceRoot":"","sources":["../../src/discovery/bootstrap-seeds.ts"],"names":[],"mappings":";;;AA0Ca,QAAA,oBAAoB,GAAyB;IACxD,EAAE,QAAQ,EAAE,wCAAwC,EAAE;IACtD,EAAE,QAAQ,EAAE,0CAA0C,EAAE;IACxD,EAAE,QAAQ,EAAE,iCAAiC,EAAE;CAChD,CAAC;AAUW,QAAA,uBAAuB,GAClC,kEAAkE,CAAC"}
@@ -0,0 +1,64 @@
1
+ import type { Logger } from 'pino';
2
+ import type { BootstrapConfig } from '../config/types';
3
+ import { type RelaySeed } from './bootstrap-seeds';
4
+ import type { BootstrapCacheStore, RelaySeedSource } from './bootstrap-cache';
5
+ export declare const DEFAULT_SAMPLE_SIZE = 3;
6
+ export declare const DEFAULT_BOOTSTRAP_REFRESH_INTERVAL_SECS = 3600;
7
+ export declare const DEFAULT_PROBE_TIMEOUT_MS = 5000;
8
+ export declare const DEFAULT_CACHE_MAX_AGE_MS: number;
9
+ export interface ResolvedRelaySeed extends RelaySeed {
10
+ source: RelaySeedSource;
11
+ }
12
+ export interface RelayProbeResult {
13
+ ok: boolean;
14
+ detail?: string;
15
+ }
16
+ export type RelayProbeFn = (relayUrl: string, timeoutMs: number) => Promise<RelayProbeResult>;
17
+ export interface FetchResponseLike {
18
+ ok: boolean;
19
+ status: number;
20
+ text(): Promise<string>;
21
+ }
22
+ export type FetchFn = (url: string) => Promise<FetchResponseLike>;
23
+ export type RelaysResolvedListener = (relayUrls: string[]) => void;
24
+ export interface BootstrapServiceDeps {
25
+ config: BootstrapConfig;
26
+ fetchFn?: FetchFn;
27
+ relayProbe: RelayProbeFn;
28
+ cacheStore: BootstrapCacheStore;
29
+ logger: Logger;
30
+ now?: () => number;
31
+ probeTimeoutMs?: number;
32
+ cacheMaxAgeMs?: number;
33
+ }
34
+ export declare class BootstrapService {
35
+ private readonly _config;
36
+ private readonly _fetchFn;
37
+ private readonly _relayProbe;
38
+ private readonly _cacheStore;
39
+ private readonly _logger;
40
+ private readonly _now;
41
+ private readonly _sampleSize;
42
+ private readonly _refreshIntervalSecs;
43
+ private readonly _probeTimeoutMs;
44
+ private readonly _cacheMaxAgeMs;
45
+ private readonly _listeners;
46
+ private _timer;
47
+ private _running;
48
+ private _refreshing;
49
+ private _verified;
50
+ constructor(deps: BootstrapServiceDeps);
51
+ get running(): boolean;
52
+ getRelayUrls(): string[];
53
+ onRelaysResolved(listener: RelaysResolvedListener): () => void;
54
+ start(): void;
55
+ stop(): void;
56
+ refresh(): Promise<void>;
57
+ private _refreshOnce;
58
+ private _resolveCandidates;
59
+ private _fetchRegistrySeeds;
60
+ private _loadFreshCacheSeeds;
61
+ private _sampleAndVerify;
62
+ private _persistVerified;
63
+ }
64
+ //# sourceMappingURL=bootstrap-service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bootstrap-service.d.ts","sourceRoot":"","sources":["../../src/discovery/bootstrap-service.ts"],"names":[],"mappings":"AA8BA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AACnC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AACvD,OAAO,EAAiD,KAAK,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAElG,OAAO,KAAK,EAAE,mBAAmB,EAAmB,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAG/F,eAAO,MAAM,mBAAmB,IAAI,CAAC;AAErC,eAAO,MAAM,uCAAuC,OAAO,CAAC;AAE5D,eAAO,MAAM,wBAAwB,OAAO,CAAC;AAE7C,eAAO,MAAM,wBAAwB,QAA0B,CAAC;AAGhE,MAAM,WAAW,iBAAkB,SAAQ,SAAS;IAClD,MAAM,EAAE,eAAe,CAAC;CACzB;AAGD,MAAM,WAAW,gBAAgB;IAE/B,EAAE,EAAE,OAAO,CAAC;IAEZ,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAQD,MAAM,MAAM,YAAY,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,gBAAgB,CAAC,CAAC;AAG9F,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;CACzB;AAGD,MAAM,MAAM,OAAO,GAAG,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,iBAAiB,CAAC,CAAC;AAGlE,MAAM,MAAM,sBAAsB,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;AAEnE,MAAM,WAAW,oBAAoB;IAEnC,MAAM,EAAE,eAAe,CAAC;IAExB,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB,UAAU,EAAE,YAAY,CAAC;IAEzB,UAAU,EAAE,mBAAmB,CAAC;IAEhC,MAAM,EAAE,MAAM,CAAC;IAEf,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IAEnB,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAMD,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAkB;IAC1C,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAU;IACnC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAe;IAC3C,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAsB;IAClD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAe;IACpC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAAS;IAC9C,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAS;IACzC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;IACxC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAqC;IAEhE,OAAO,CAAC,MAAM,CAA+C;IAC7D,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,WAAW,CAA8B;IAEjD,OAAO,CAAC,SAAS,CAA2B;gBAEhC,IAAI,EAAE,oBAAoB;IAoBtC,IAAI,OAAO,IAAI,OAAO,CAErB;IAOD,YAAY,IAAI,MAAM,EAAE;IAQxB,gBAAgB,CAAC,QAAQ,EAAE,sBAAsB,GAAG,MAAM,IAAI;IAY9D,KAAK,IAAI,IAAI;IAiCb,IAAI,IAAI,IAAI;IAgBZ,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;YAUV,YAAY;YAsDZ,kBAAkB;YAuClB,mBAAmB;YA2EnB,oBAAoB;YAoCpB,gBAAgB;YA4ChB,gBAAgB;CAqC/B"}
@@ -0,0 +1,320 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BootstrapService = exports.DEFAULT_CACHE_MAX_AGE_MS = exports.DEFAULT_PROBE_TIMEOUT_MS = exports.DEFAULT_BOOTSTRAP_REFRESH_INTERVAL_SECS = exports.DEFAULT_SAMPLE_SIZE = void 0;
4
+ const bootstrap_seeds_1 = require("./bootstrap-seeds");
5
+ const bootstrap_manifest_1 = require("./bootstrap-manifest");
6
+ exports.DEFAULT_SAMPLE_SIZE = 3;
7
+ exports.DEFAULT_BOOTSTRAP_REFRESH_INTERVAL_SECS = 3600;
8
+ exports.DEFAULT_PROBE_TIMEOUT_MS = 5000;
9
+ exports.DEFAULT_CACHE_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1000;
10
+ class BootstrapService {
11
+ _config;
12
+ _fetchFn;
13
+ _relayProbe;
14
+ _cacheStore;
15
+ _logger;
16
+ _now;
17
+ _sampleSize;
18
+ _refreshIntervalSecs;
19
+ _probeTimeoutMs;
20
+ _cacheMaxAgeMs;
21
+ _listeners = new Set();
22
+ _timer = null;
23
+ _running = false;
24
+ _refreshing = null;
25
+ _verified = [];
26
+ constructor(deps) {
27
+ this._config = deps.config;
28
+ this._fetchFn = deps.fetchFn ?? defaultFetch;
29
+ this._relayProbe = deps.relayProbe;
30
+ this._cacheStore = deps.cacheStore;
31
+ this._logger = deps.logger.child({ component: 'BootstrapService' });
32
+ this._now = deps.now ?? Date.now;
33
+ this._sampleSize =
34
+ deps.config.sampleSize && deps.config.sampleSize > 0
35
+ ? Math.floor(deps.config.sampleSize)
36
+ : exports.DEFAULT_SAMPLE_SIZE;
37
+ this._refreshIntervalSecs =
38
+ deps.config.refreshIntervalSecs && deps.config.refreshIntervalSecs > 0
39
+ ? Math.floor(deps.config.refreshIntervalSecs)
40
+ : exports.DEFAULT_BOOTSTRAP_REFRESH_INTERVAL_SECS;
41
+ this._probeTimeoutMs = deps.probeTimeoutMs ?? exports.DEFAULT_PROBE_TIMEOUT_MS;
42
+ this._cacheMaxAgeMs = deps.cacheMaxAgeMs ?? exports.DEFAULT_CACHE_MAX_AGE_MS;
43
+ }
44
+ get running() {
45
+ return this._running;
46
+ }
47
+ getRelayUrls() {
48
+ return this._verified.map((seed) => seed.relayUrl);
49
+ }
50
+ onRelaysResolved(listener) {
51
+ this._listeners.add(listener);
52
+ return () => {
53
+ this._listeners.delete(listener);
54
+ };
55
+ }
56
+ start() {
57
+ if (this._running) {
58
+ this._logger.warn('Bootstrap service already running');
59
+ return;
60
+ }
61
+ if (!this._config.enabled) {
62
+ this._logger.info({ event: 'bootstrap_disabled' }, 'Cold-start bootstrap disabled');
63
+ return;
64
+ }
65
+ this._running = true;
66
+ this._logger.info({
67
+ event: 'bootstrap_started',
68
+ registryUrl: this._config.registryUrl,
69
+ configSeeds: this._config.seeds?.length ?? 0,
70
+ sampleSize: this._sampleSize,
71
+ refreshIntervalSecs: this._refreshIntervalSecs,
72
+ }, 'Cold-start bootstrap service started');
73
+ void this.refresh();
74
+ this._timer = setInterval(() => {
75
+ void this.refresh();
76
+ }, this._refreshIntervalSecs * 1000);
77
+ this._timer.unref?.();
78
+ }
79
+ stop() {
80
+ if (this._timer) {
81
+ clearInterval(this._timer);
82
+ this._timer = null;
83
+ }
84
+ if (this._running) {
85
+ this._logger.info({ event: 'bootstrap_stopped' }, 'Cold-start bootstrap service stopped');
86
+ }
87
+ this._running = false;
88
+ }
89
+ refresh() {
90
+ if (this._refreshing) {
91
+ return this._refreshing;
92
+ }
93
+ this._refreshing = this._refreshOnce().finally(() => {
94
+ this._refreshing = null;
95
+ });
96
+ return this._refreshing;
97
+ }
98
+ async _refreshOnce() {
99
+ const candidates = await this._resolveCandidates();
100
+ if (candidates.length === 0) {
101
+ this._logger.warn({ event: 'bootstrap_no_candidates' }, 'Bootstrap produced no relay seed candidates (registry, cache, config, and fallback all empty)');
102
+ return;
103
+ }
104
+ const verified = await this._sampleAndVerify(candidates);
105
+ if (verified.length === 0) {
106
+ this._logger.warn({
107
+ event: 'bootstrap_no_relays_verified',
108
+ candidates: candidates.map((seed) => seed.relayUrl),
109
+ lastKnownGood: this.getRelayUrls(),
110
+ }, 'No relay candidate passed sample-and-verify; keeping last known-good relays (will retry on next refresh)');
111
+ return;
112
+ }
113
+ this._verified = verified;
114
+ this._logger.info({
115
+ event: 'bootstrap_seed_resolved',
116
+ relays: verified.map((seed) => ({ relayUrl: seed.relayUrl, source: seed.source })),
117
+ candidateCount: candidates.length,
118
+ verifiedCount: verified.length,
119
+ }, 'Bootstrap relay seeds resolved and verified');
120
+ await this._persistVerified(verified);
121
+ const relayUrls = this.getRelayUrls();
122
+ for (const listener of this._listeners) {
123
+ try {
124
+ listener(relayUrls);
125
+ }
126
+ catch (err) {
127
+ this._logger.warn({ event: 'bootstrap_listener_failed', err: errMsg(err) }, 'A bootstrap relays-resolved listener threw; continuing');
128
+ }
129
+ }
130
+ }
131
+ async _resolveCandidates() {
132
+ const merged = new Map();
133
+ const add = (seed, source) => {
134
+ const key = normalizeRelayUrl(seed.relayUrl);
135
+ if (key === null || merged.has(key)) {
136
+ return;
137
+ }
138
+ const resolved = { relayUrl: seed.relayUrl, source };
139
+ if (seed.pubkey !== undefined) {
140
+ resolved.pubkey = seed.pubkey;
141
+ }
142
+ merged.set(key, resolved);
143
+ };
144
+ for (const seed of await this._fetchRegistrySeeds()) {
145
+ add(seed, 'registry');
146
+ }
147
+ for (const seed of await this._loadFreshCacheSeeds()) {
148
+ add(seed, 'cache');
149
+ }
150
+ for (const seed of this._config.seeds ?? []) {
151
+ add(seed, 'config');
152
+ }
153
+ for (const seed of bootstrap_seeds_1.FALLBACK_RELAY_SEEDS) {
154
+ add(seed, 'fallback');
155
+ }
156
+ return [...merged.values()];
157
+ }
158
+ async _fetchRegistrySeeds() {
159
+ const registryUrl = this._config.registryUrl;
160
+ if (!registryUrl) {
161
+ return [];
162
+ }
163
+ let body;
164
+ try {
165
+ const response = await this._fetchFn(registryUrl);
166
+ if (!response.ok) {
167
+ this._logger.warn({ event: 'bootstrap_registry_fetch_failed', registryUrl, status: response.status }, 'Bootstrap seed registry returned a non-OK status; falling back');
168
+ return [];
169
+ }
170
+ body = await response.text();
171
+ }
172
+ catch (err) {
173
+ this._logger.warn({ event: 'bootstrap_registry_fetch_failed', registryUrl, err: errMsg(err) }, 'Failed to fetch bootstrap seed registry; falling back');
174
+ return [];
175
+ }
176
+ let raw;
177
+ try {
178
+ raw = JSON.parse(body);
179
+ }
180
+ catch (err) {
181
+ this._logger.warn({ event: 'bootstrap_registry_invalid', registryUrl, err: errMsg(err) }, 'Bootstrap seed registry is not valid JSON; falling back');
182
+ return [];
183
+ }
184
+ const parsed = (0, bootstrap_manifest_1.parseSeedManifest)(raw);
185
+ if (!parsed.ok) {
186
+ this._logger.warn({ event: 'bootstrap_registry_invalid', registryUrl, error: parsed.error }, 'Bootstrap seed registry manifest is malformed; falling back');
187
+ return [];
188
+ }
189
+ const pinnedKey = this._config.curatorPubkey ?? bootstrap_seeds_1.FALLBACK_CURATOR_PUBKEY;
190
+ if (!(0, bootstrap_manifest_1.verifySeedManifest)(parsed.manifest, pinnedKey)) {
191
+ this._logger.warn({
192
+ event: 'bootstrap_registry_signature_invalid',
193
+ registryUrl,
194
+ manifestVersion: parsed.manifest.version,
195
+ updatedAt: parsed.manifest.updatedAt,
196
+ }, 'Bootstrap seed registry signature does not verify against the pinned curator key; REJECTING manifest and falling back');
197
+ return [];
198
+ }
199
+ this._logger.info({
200
+ event: 'bootstrap_registry_verified',
201
+ registryUrl,
202
+ manifestVersion: parsed.manifest.version,
203
+ updatedAt: parsed.manifest.updatedAt,
204
+ entryCount: parsed.manifest.entries.length,
205
+ }, 'Bootstrap seed registry fetched and signature-verified');
206
+ return parsed.manifest.entries;
207
+ }
208
+ async _loadFreshCacheSeeds() {
209
+ let cached;
210
+ try {
211
+ cached = await this._cacheStore.load();
212
+ }
213
+ catch (err) {
214
+ this._logger.warn({ event: 'bootstrap_cache_load_failed', err: errMsg(err) }, 'Failed to load bootstrap relay cache; skipping cache tier');
215
+ return [];
216
+ }
217
+ const cutoff = this._now() - this._cacheMaxAgeMs;
218
+ const fresh = cached.filter((entry) => {
219
+ const verifiedAt = Date.parse(entry.verifiedAt);
220
+ return !Number.isNaN(verifiedAt) && verifiedAt >= cutoff;
221
+ });
222
+ if (fresh.length < cached.length) {
223
+ this._logger.debug({
224
+ event: 'bootstrap_cache_stale_dropped',
225
+ dropped: cached.length - fresh.length,
226
+ kept: fresh.length,
227
+ }, 'Dropped stale bootstrap cache entries');
228
+ }
229
+ return fresh;
230
+ }
231
+ async _sampleAndVerify(candidates) {
232
+ const verified = [];
233
+ for (let i = 0; i < candidates.length && verified.length < this._sampleSize; i += this._sampleSize) {
234
+ const wave = candidates.slice(i, i + this._sampleSize);
235
+ const results = await Promise.all(wave.map(async (seed) => {
236
+ try {
237
+ return await this._relayProbe(seed.relayUrl, this._probeTimeoutMs);
238
+ }
239
+ catch (err) {
240
+ return { ok: false, detail: errMsg(err) };
241
+ }
242
+ }));
243
+ for (const [index, seed] of wave.entries()) {
244
+ const result = results[index];
245
+ if (result?.ok) {
246
+ if (verified.length < this._sampleSize) {
247
+ verified.push(seed);
248
+ }
249
+ }
250
+ else {
251
+ this._logger.warn({
252
+ event: 'bootstrap_relay_probe_failed',
253
+ relayUrl: seed.relayUrl,
254
+ source: seed.source,
255
+ detail: result?.detail,
256
+ }, 'Bootstrap relay candidate failed sample-and-verify; skipping');
257
+ }
258
+ }
259
+ }
260
+ return verified;
261
+ }
262
+ async _persistVerified(verified) {
263
+ try {
264
+ const nowIso = new Date(this._now()).toISOString();
265
+ const merged = new Map();
266
+ for (const entry of await this._loadFreshCacheSeeds()) {
267
+ const key = normalizeRelayUrl(entry.relayUrl);
268
+ if (key !== null) {
269
+ merged.set(key, entry);
270
+ }
271
+ }
272
+ for (const seed of verified) {
273
+ const key = normalizeRelayUrl(seed.relayUrl);
274
+ if (key === null) {
275
+ continue;
276
+ }
277
+ const cachedSeed = {
278
+ relayUrl: seed.relayUrl,
279
+ verifiedAt: nowIso,
280
+ source: seed.source,
281
+ };
282
+ if (seed.pubkey !== undefined) {
283
+ cachedSeed.pubkey = seed.pubkey;
284
+ }
285
+ merged.set(key, cachedSeed);
286
+ }
287
+ await this._cacheStore.save([...merged.values()]);
288
+ this._logger.debug({ event: 'bootstrap_cache_saved', entries: merged.size }, 'Persisted verified bootstrap relays to the learned-peer cache');
289
+ }
290
+ catch (err) {
291
+ this._logger.warn({ event: 'bootstrap_cache_save_failed', err: errMsg(err) }, 'Failed to persist verified bootstrap relays; continuing');
292
+ }
293
+ }
294
+ }
295
+ exports.BootstrapService = BootstrapService;
296
+ function normalizeRelayUrl(relayUrl) {
297
+ if (typeof relayUrl !== 'string' || !/^wss?:\/\/.+/i.test(relayUrl)) {
298
+ return null;
299
+ }
300
+ try {
301
+ const url = new URL(relayUrl);
302
+ const pathname = url.pathname === '/' ? '' : url.pathname;
303
+ return `${url.protocol}//${url.host}${pathname}${url.search}`;
304
+ }
305
+ catch {
306
+ return null;
307
+ }
308
+ }
309
+ const defaultFetch = async (url) => {
310
+ const response = await fetch(url);
311
+ return {
312
+ ok: response.ok,
313
+ status: response.status,
314
+ text: () => response.text(),
315
+ };
316
+ };
317
+ function errMsg(err) {
318
+ return err instanceof Error ? err.message : String(err);
319
+ }
320
+ //# sourceMappingURL=bootstrap-service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bootstrap-service.js","sourceRoot":"","sources":["../../src/discovery/bootstrap-service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,46 @@
1
+ import type { Logger } from 'pino';
2
+ import { type LinkStateEventInput } from '../routing/link-state-db';
3
+ export interface DiscoveredNode {
4
+ pubkey: string;
5
+ ilpAddress: string;
6
+ ilpAddresses: string[];
7
+ btpEndpoint: string;
8
+ httpEndpoint?: string;
9
+ relayUrl?: string;
10
+ assetCode: string;
11
+ assetScale: number;
12
+ supportedChains?: string[];
13
+ settlementAddresses?: Record<string, string>;
14
+ firstSeenAt: number;
15
+ lastSeenAt: number;
16
+ expiresAt?: number;
17
+ funded: boolean;
18
+ }
19
+ export interface FundedPeerRef {
20
+ peerId: string;
21
+ nostrPubkey?: string;
22
+ btpUrl?: string;
23
+ }
24
+ export type DiscoveredNodeIngestResult = 'discovered' | 'updated' | 'stale' | 'expired' | 'self' | 'invalid';
25
+ export interface DiscoveredNodeRegistryDeps {
26
+ getFundedPeers: () => FundedPeerRef[];
27
+ ownPubkey?: string;
28
+ logger: Logger;
29
+ }
30
+ export declare class DiscoveredNodeRegistry {
31
+ private readonly _getFundedPeers;
32
+ private readonly _ownPubkey;
33
+ private readonly _logger;
34
+ private readonly _nodes;
35
+ constructor(deps: DiscoveredNodeRegistryDeps);
36
+ ingest(event: LinkStateEventInput, nowSecs?: number): DiscoveredNodeIngestResult;
37
+ sweepExpired(nowSecs?: number): string[];
38
+ list(): DiscoveredNode[];
39
+ size(): number;
40
+ counts(): {
41
+ discovered: number;
42
+ funded: number;
43
+ };
44
+ clear(): void;
45
+ }
46
+ //# sourceMappingURL=discovered-node-registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"discovered-node-registry.d.ts","sourceRoot":"","sources":["../../src/discovery/discovered-node-registry.ts"],"names":[],"mappings":"AAqCA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAEnC,OAAO,EAAsB,KAAK,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAUxF,MAAM,WAAW,cAAc;IAE7B,MAAM,EAAE,MAAM,CAAC;IAEf,UAAU,EAAE,MAAM,CAAC;IAEnB,YAAY,EAAE,MAAM,EAAE,CAAC;IAEvB,WAAW,EAAE,MAAM,CAAC;IAEpB,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB,SAAS,EAAE,MAAM,CAAC;IAElB,UAAU,EAAE,MAAM,CAAC;IAEnB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAE3B,mBAAmB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAE7C,WAAW,EAAE,MAAM,CAAC;IAEpB,UAAU,EAAE,MAAM,CAAC;IAEnB,SAAS,CAAC,EAAE,MAAM,CAAC;IAMnB,MAAM,EAAE,OAAO,CAAC;CACjB;AAOD,MAAM,WAAW,aAAa;IAE5B,MAAM,EAAE,MAAM,CAAC;IAEf,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAGD,MAAM,MAAM,0BAA0B,GAElC,YAAY,GAEZ,SAAS,GAET,OAAO,GAEP,SAAS,GAET,MAAM,GAEN,SAAS,CAAC;AAEd,MAAM,WAAW,0BAA0B;IAMzC,cAAc,EAAE,MAAM,aAAa,EAAE,CAAC;IAKtC,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB,MAAM,EAAE,MAAM,CAAC;CAChB;AAwBD,qBAAa,sBAAsB;IACjC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAwB;IACxD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAqB;IAChD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAiC;gBAE5C,IAAI,EAAE,0BAA0B;IAa5C,MAAM,CACJ,KAAK,EAAE,mBAAmB,EAC1B,OAAO,GAAE,MAAsC,GAC9C,0BAA0B;IA2D7B,YAAY,CAAC,OAAO,GAAE,MAAsC,GAAG,MAAM,EAAE;IAwBvE,IAAI,IAAI,cAAc,EAAE;IA2BxB,IAAI,IAAI,MAAM;IAKd,MAAM,IAAI;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE;IAUhD,KAAK,IAAI,IAAI;CAGd"}
@@ -0,0 +1,185 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DiscoveredNodeRegistry = void 0;
4
+ const ilp_peer_info_event_1 = require("./ilp-peer-info-event");
5
+ const link_state_db_1 = require("../routing/link-state-db");
6
+ const NOSTR_PUBKEY_RE = /^[0-9a-f]{64}$/;
7
+ class DiscoveredNodeRegistry {
8
+ _getFundedPeers;
9
+ _ownPubkey;
10
+ _logger;
11
+ _nodes = new Map();
12
+ constructor(deps) {
13
+ this._getFundedPeers = deps.getFundedPeers;
14
+ this._ownPubkey = deps.ownPubkey;
15
+ this._logger = deps.logger.child({ component: 'DiscoveredNodeRegistry' });
16
+ }
17
+ ingest(event, nowSecs = Math.floor(Date.now() / 1000)) {
18
+ if (event.kind !== ilp_peer_info_event_1.ILP_PEER_INFO_KIND)
19
+ return 'invalid';
20
+ if (!NOSTR_PUBKEY_RE.test(event.pubkey))
21
+ return 'invalid';
22
+ if (!Number.isFinite(event.created_at))
23
+ return 'invalid';
24
+ if (this._ownPubkey !== undefined && event.pubkey === this._ownPubkey)
25
+ return 'self';
26
+ const expiresAt = (0, link_state_db_1.parseExpirationTag)(event.tags);
27
+ if (expiresAt !== null && expiresAt <= nowSecs)
28
+ return 'expired';
29
+ const existing = this._nodes.get(event.pubkey);
30
+ if (existing && event.created_at <= existing.createdAt)
31
+ return 'stale';
32
+ const parsed = parseDiscoveredPeerInfo(event.content);
33
+ if (parsed === null)
34
+ return 'invalid';
35
+ const stored = {
36
+ ...parsed,
37
+ pubkey: event.pubkey,
38
+ firstSeenAt: existing ? existing.firstSeenAt : nowSecs,
39
+ lastSeenAt: nowSecs,
40
+ createdAt: event.created_at,
41
+ ...(expiresAt !== null ? { expiresAt } : {}),
42
+ };
43
+ this._nodes.set(event.pubkey, stored);
44
+ if (existing) {
45
+ this._logger.debug({
46
+ event: 'node_updated',
47
+ pubkey: event.pubkey.slice(0, 16),
48
+ ilpAddress: stored.ilpAddress,
49
+ expiresAt: stored.expiresAt ?? null,
50
+ }, 'Discovered node updated by a fresher announcement');
51
+ return 'updated';
52
+ }
53
+ this._logger.info({
54
+ event: 'node_discovered',
55
+ pubkey: event.pubkey.slice(0, 16),
56
+ ilpAddress: stored.ilpAddress,
57
+ btpEndpoint: stored.btpEndpoint,
58
+ expiresAt: stored.expiresAt ?? null,
59
+ discovered: this._nodes.size,
60
+ }, 'Discovered node from kind:10032 announcement');
61
+ return 'discovered';
62
+ }
63
+ sweepExpired(nowSecs = Math.floor(Date.now() / 1000)) {
64
+ const removed = [];
65
+ for (const [pubkey, node] of this._nodes) {
66
+ if (node.expiresAt !== undefined && node.expiresAt <= nowSecs) {
67
+ this._nodes.delete(pubkey);
68
+ removed.push(pubkey);
69
+ this._logger.info({
70
+ event: 'node_expired',
71
+ pubkey: pubkey.slice(0, 16),
72
+ ilpAddress: node.ilpAddress,
73
+ discovered: this._nodes.size,
74
+ }, 'Discovered node expired with its announcement');
75
+ }
76
+ }
77
+ return removed;
78
+ }
79
+ list() {
80
+ const fundedPeers = this._getFundedPeers();
81
+ return Array.from(this._nodes.values())
82
+ .map((node) => ({
83
+ pubkey: node.pubkey,
84
+ ilpAddress: node.ilpAddress,
85
+ ilpAddresses: [...node.ilpAddresses],
86
+ btpEndpoint: node.btpEndpoint,
87
+ ...(node.httpEndpoint !== undefined ? { httpEndpoint: node.httpEndpoint } : {}),
88
+ ...(node.relayUrl !== undefined ? { relayUrl: node.relayUrl } : {}),
89
+ assetCode: node.assetCode,
90
+ assetScale: node.assetScale,
91
+ ...(node.supportedChains !== undefined
92
+ ? { supportedChains: [...node.supportedChains] }
93
+ : {}),
94
+ ...(node.settlementAddresses !== undefined
95
+ ? { settlementAddresses: { ...node.settlementAddresses } }
96
+ : {}),
97
+ firstSeenAt: node.firstSeenAt,
98
+ lastSeenAt: node.lastSeenAt,
99
+ ...(node.expiresAt !== undefined ? { expiresAt: node.expiresAt } : {}),
100
+ funded: isFundedBy(node, fundedPeers),
101
+ }))
102
+ .sort((a, b) => (a.ilpAddress < b.ilpAddress ? -1 : a.ilpAddress > b.ilpAddress ? 1 : 0));
103
+ }
104
+ size() {
105
+ return this._nodes.size;
106
+ }
107
+ counts() {
108
+ const fundedPeers = this._getFundedPeers();
109
+ let funded = 0;
110
+ for (const node of this._nodes.values()) {
111
+ if (isFundedBy(node, fundedPeers))
112
+ funded++;
113
+ }
114
+ return { discovered: this._nodes.size, funded };
115
+ }
116
+ clear() {
117
+ this._nodes.clear();
118
+ }
119
+ }
120
+ exports.DiscoveredNodeRegistry = DiscoveredNodeRegistry;
121
+ function isFundedBy(node, fundedPeers) {
122
+ for (const peer of fundedPeers) {
123
+ if (peer.nostrPubkey !== undefined && peer.nostrPubkey === node.pubkey)
124
+ return true;
125
+ if (peer.btpUrl !== undefined && peer.btpUrl === node.btpEndpoint)
126
+ return true;
127
+ }
128
+ return false;
129
+ }
130
+ function parseDiscoveredPeerInfo(content) {
131
+ let parsed;
132
+ try {
133
+ parsed = JSON.parse(content);
134
+ }
135
+ catch {
136
+ return null;
137
+ }
138
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed))
139
+ return null;
140
+ const raw = parsed;
141
+ const { ilpAddress, btpEndpoint, assetCode, assetScale } = raw;
142
+ if (typeof ilpAddress !== 'string' || ilpAddress.length === 0)
143
+ return null;
144
+ if (typeof btpEndpoint !== 'string' || btpEndpoint.length === 0)
145
+ return null;
146
+ if (typeof assetCode !== 'string' || assetCode.length === 0)
147
+ return null;
148
+ if (typeof assetScale !== 'number' || !Number.isFinite(assetScale))
149
+ return null;
150
+ const ilpAddresses = Array.isArray(raw.ilpAddresses)
151
+ ? raw.ilpAddresses.filter((a) => typeof a === 'string' && a.length > 0)
152
+ : [];
153
+ const supportedChains = Array.isArray(raw.supportedChains)
154
+ ? raw.supportedChains.filter((c) => typeof c === 'string' && c.length > 0)
155
+ : undefined;
156
+ let settlementAddresses;
157
+ if (raw.settlementAddresses &&
158
+ typeof raw.settlementAddresses === 'object' &&
159
+ !Array.isArray(raw.settlementAddresses)) {
160
+ settlementAddresses = {};
161
+ for (const [chain, address] of Object.entries(raw.settlementAddresses)) {
162
+ if (typeof address === 'string' && address.length > 0) {
163
+ settlementAddresses[chain] = address;
164
+ }
165
+ }
166
+ }
167
+ return {
168
+ ilpAddress,
169
+ ilpAddresses: ilpAddresses.length > 0 ? ilpAddresses : [ilpAddress],
170
+ btpEndpoint,
171
+ ...(typeof raw.httpEndpoint === 'string' && raw.httpEndpoint.length > 0
172
+ ? { httpEndpoint: raw.httpEndpoint }
173
+ : {}),
174
+ ...(typeof raw.relayUrl === 'string' && raw.relayUrl.length > 0
175
+ ? { relayUrl: raw.relayUrl }
176
+ : {}),
177
+ assetCode,
178
+ assetScale,
179
+ ...(supportedChains && supportedChains.length > 0 ? { supportedChains } : {}),
180
+ ...(settlementAddresses && Object.keys(settlementAddresses).length > 0
181
+ ? { settlementAddresses }
182
+ : {}),
183
+ };
184
+ }
185
+ //# sourceMappingURL=discovered-node-registry.js.map