@toon-protocol/relay 1.3.4 → 2.0.1
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 +51 -93
- package/dist/chunk-FXQSNOCG.js +939 -0
- package/dist/chunk-FXQSNOCG.js.map +1 -0
- package/dist/cli.js +38 -216
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +121 -1005
- package/dist/index.js +72 -424
- package/dist/index.js.map +1 -1
- package/package.json +3 -8
- package/dist/chunk-NYKVCNJL.js +0 -2103
- package/dist/chunk-NYKVCNJL.js.map +0 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
import { NostrEvent } from 'nostr-tools/pure';
|
|
2
2
|
import { Filter } from 'nostr-tools/filter';
|
|
3
3
|
import { WebSocket } from 'ws';
|
|
4
|
-
import { EmbeddableConnectorLike, SkillDescriptor, BootstrapPhase, ChainPreset, IlpClient } from '@toon-protocol/core';
|
|
5
|
-
export { ToonDecodeError, ToonEncodeError, decodeEventFromToon, encodeEventToToon } from '@toon-protocol/core';
|
|
6
|
-
import { Hono, Context } from 'hono';
|
|
7
4
|
import { SimplePool } from 'nostr-tools/pool';
|
|
8
|
-
import {
|
|
9
|
-
|
|
10
|
-
|
|
5
|
+
import { Context } from 'hono';
|
|
6
|
+
|
|
7
|
+
/** Package version, surfaced on `GET /health`. */
|
|
8
|
+
declare const VERSION = "0.1.0";
|
|
11
9
|
|
|
12
10
|
/**
|
|
13
11
|
* Configuration options for the Nostr relay.
|
|
@@ -182,6 +180,15 @@ declare class ConnectionHandler {
|
|
|
182
180
|
* Get the number of active subscriptions.
|
|
183
181
|
*/
|
|
184
182
|
getSubscriptionCount(): number;
|
|
183
|
+
/**
|
|
184
|
+
* Emit an outbound NIP-01 EVENT frame.
|
|
185
|
+
*
|
|
186
|
+
* The event MUST go on the wire as canonical NIP-01 JSON —
|
|
187
|
+
* `["EVENT", <subId>, {id, pubkey, created_at, kind, tags, content, sig}]`
|
|
188
|
+
* with the event as a plain JSON object — so any standard nostr client can
|
|
189
|
+
* parse it and verify `id`/`sig` from the wire bytes (#46). Never re-encode
|
|
190
|
+
* the event (TOON text, double-JSON-stringify, etc.) at this boundary.
|
|
191
|
+
*/
|
|
185
192
|
private sendEvent;
|
|
186
193
|
private sendEose;
|
|
187
194
|
private sendOk;
|
|
@@ -226,189 +233,37 @@ declare class NostrRelayServer {
|
|
|
226
233
|
}
|
|
227
234
|
|
|
228
235
|
/**
|
|
229
|
-
*
|
|
230
|
-
*/
|
|
231
|
-
interface PricingConfig {
|
|
232
|
-
/** Base price per byte for event storage */
|
|
233
|
-
basePricePerByte: bigint;
|
|
234
|
-
/** Optional price overrides by event kind */
|
|
235
|
-
kindOverrides?: Map<number, bigint>;
|
|
236
|
-
}
|
|
237
|
-
/**
|
|
238
|
-
* Error class for pricing-specific errors.
|
|
239
|
-
*/
|
|
240
|
-
declare class PricingError extends RelayError {
|
|
241
|
-
constructor(message: string, code?: string);
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
/**
|
|
245
|
-
* Service for calculating event storage prices with kind-based overrides.
|
|
246
|
-
*/
|
|
247
|
-
declare class PricingService {
|
|
248
|
-
private readonly basePricePerByte;
|
|
249
|
-
private readonly kindOverrides;
|
|
250
|
-
constructor(config: PricingConfig);
|
|
251
|
-
/**
|
|
252
|
-
* Calculate price for a Nostr event.
|
|
253
|
-
*
|
|
254
|
-
* @param event - The Nostr event to price
|
|
255
|
-
* @returns The calculated price as bigint
|
|
256
|
-
*/
|
|
257
|
-
calculatePrice(event: NostrEvent): bigint;
|
|
258
|
-
/**
|
|
259
|
-
* Calculate price from raw TOON bytes and event kind.
|
|
260
|
-
*
|
|
261
|
-
* @param bytes - The TOON-encoded event bytes
|
|
262
|
-
* @param kind - The event kind number
|
|
263
|
-
* @returns The calculated price as bigint
|
|
264
|
-
*/
|
|
265
|
-
calculatePriceFromBytes(bytes: Uint8Array, kind: number): bigint;
|
|
266
|
-
/**
|
|
267
|
-
* Get the effective price per byte for a given kind.
|
|
268
|
-
*
|
|
269
|
-
* @param kind - The event kind number
|
|
270
|
-
* @returns The price per byte (kind override if exists, otherwise base price)
|
|
271
|
-
*/
|
|
272
|
-
getPricePerByte(kind: number): bigint;
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
/**
|
|
276
|
-
* Load pricing configuration from environment variables.
|
|
277
|
-
*
|
|
278
|
-
* Environment variables:
|
|
279
|
-
* - RELAY_BASE_PRICE_PER_BYTE: Base price per byte (default: "1")
|
|
280
|
-
* - RELAY_KIND_OVERRIDES: JSON object mapping kind to price (optional)
|
|
281
|
-
* Format: {"1":"5","30023":"100"}
|
|
236
|
+
* TOON event codec.
|
|
282
237
|
*
|
|
283
|
-
*
|
|
284
|
-
*
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
* Load pricing configuration from a JSON file.
|
|
238
|
+
* Encodes/decodes Nostr events to and from the TOON text format. Vendored from
|
|
239
|
+
* `@toon-protocol/core` so the relay depends only on the lightweight
|
|
240
|
+
* `@toon-format/toon` encoder rather than core's full transitive tree (which
|
|
241
|
+
* pulls Arweave / web3 wallet stacks the relay does not use). Same MIT
|
|
242
|
+
* license / org.
|
|
289
243
|
*
|
|
290
|
-
*
|
|
291
|
-
*
|
|
292
|
-
*
|
|
293
|
-
*
|
|
294
|
-
*
|
|
295
|
-
* "1": "5",
|
|
296
|
-
* "30023": "100"
|
|
297
|
-
* }
|
|
298
|
-
* }
|
|
244
|
+
* NOTE: this codec is NOT used on the relay's NIP-01 read surface. Outbound
|
|
245
|
+
* EVENT frames are canonical NIP-01 JSON (see
|
|
246
|
+
* `websocket/ConnectionHandler.sendEvent`, #46) so that standard nostr clients
|
|
247
|
+
* can parse events and verify signatures from the wire. The codec remains
|
|
248
|
+
* exported for library consumers that exchange TOON-text events elsewhere.
|
|
299
249
|
*
|
|
300
|
-
* @
|
|
301
|
-
* @returns PricingConfig loaded from file
|
|
302
|
-
* @throws PricingError if file cannot be read or contains invalid values
|
|
303
|
-
*/
|
|
304
|
-
declare function loadPricingConfigFromFile(path: string): PricingConfig;
|
|
305
|
-
|
|
306
|
-
/**
|
|
307
|
-
* Validate that a string is a valid Nostr pubkey format.
|
|
308
|
-
* @param pubkey - The pubkey to validate
|
|
309
|
-
* @returns true if valid 64-character lowercase hex string
|
|
310
|
-
*/
|
|
311
|
-
declare function isValidPubkey(pubkey: string): boolean;
|
|
312
|
-
/**
|
|
313
|
-
* Configuration for the Business Logic Server.
|
|
314
|
-
*/
|
|
315
|
-
interface BlsConfig {
|
|
316
|
-
/** Base price per byte for event storage (used for simple pricing) */
|
|
317
|
-
basePricePerByte: bigint;
|
|
318
|
-
/** Optional PricingService for kind-based pricing overrides */
|
|
319
|
-
pricingService?: PricingService;
|
|
320
|
-
/** Optional owner pubkey - events from this pubkey bypass payment */
|
|
321
|
-
ownerPubkey?: string;
|
|
322
|
-
}
|
|
323
|
-
/**
|
|
324
|
-
* Incoming packet request from ILP connector.
|
|
325
|
-
*/
|
|
326
|
-
interface HandlePacketRequest {
|
|
327
|
-
/** Payment amount as string (parsed to bigint) */
|
|
328
|
-
amount: string;
|
|
329
|
-
/** ILP destination address */
|
|
330
|
-
destination: string;
|
|
331
|
-
/** Base64-encoded TOON Nostr event */
|
|
332
|
-
data: string;
|
|
333
|
-
/** Source ILP address */
|
|
334
|
-
sourceAccount?: string;
|
|
335
|
-
}
|
|
336
|
-
/**
|
|
337
|
-
* Response for accepted packet.
|
|
338
|
-
*/
|
|
339
|
-
interface HandlePacketAcceptResponse {
|
|
340
|
-
accept: true;
|
|
341
|
-
metadata?: {
|
|
342
|
-
eventId: string;
|
|
343
|
-
storedAt: number;
|
|
344
|
-
};
|
|
345
|
-
}
|
|
346
|
-
/**
|
|
347
|
-
* Response for rejected packet.
|
|
348
|
-
*/
|
|
349
|
-
interface HandlePacketRejectResponse {
|
|
350
|
-
accept: false;
|
|
351
|
-
/** ILP error code (F00, F06, etc.) */
|
|
352
|
-
code: string;
|
|
353
|
-
/** Human-readable error message */
|
|
354
|
-
message: string;
|
|
355
|
-
metadata?: {
|
|
356
|
-
required?: string;
|
|
357
|
-
received?: string;
|
|
358
|
-
};
|
|
359
|
-
}
|
|
360
|
-
/**
|
|
361
|
-
* Union type for packet response.
|
|
362
|
-
*/
|
|
363
|
-
type HandlePacketResponse = HandlePacketAcceptResponse | HandlePacketRejectResponse;
|
|
364
|
-
/**
|
|
365
|
-
* ILP error code constants.
|
|
366
|
-
*/
|
|
367
|
-
declare const ILP_ERROR_CODES: {
|
|
368
|
-
readonly BAD_REQUEST: "F00";
|
|
369
|
-
readonly INSUFFICIENT_AMOUNT: "F06";
|
|
370
|
-
readonly INTERNAL_ERROR: "T00";
|
|
371
|
-
};
|
|
372
|
-
/**
|
|
373
|
-
* BLS-specific error class.
|
|
250
|
+
* @module
|
|
374
251
|
*/
|
|
375
|
-
declare class BlsError extends RelayError {
|
|
376
|
-
constructor(message: string, code?: string);
|
|
377
|
-
}
|
|
378
252
|
|
|
379
|
-
/**
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
*/
|
|
394
|
-
private setupRoutes;
|
|
395
|
-
/**
|
|
396
|
-
* Process a packet request.
|
|
397
|
-
*
|
|
398
|
-
* This method is public to support direct connector integration in embedded mode,
|
|
399
|
-
* where the connector calls this method directly via setPacketHandler() instead
|
|
400
|
-
* of making HTTP requests.
|
|
401
|
-
*/
|
|
402
|
-
handlePacket(request: HandlePacketRequest): HandlePacketAcceptResponse | HandlePacketRejectResponse;
|
|
403
|
-
/**
|
|
404
|
-
* Get the Hono app instance for testing or composition.
|
|
405
|
-
*/
|
|
406
|
-
getApp(): Hono;
|
|
407
|
-
/**
|
|
408
|
-
* Start the HTTP server on the specified port.
|
|
409
|
-
*/
|
|
410
|
-
start(port: number): void;
|
|
411
|
-
}
|
|
253
|
+
/** Thrown when a Nostr event cannot be encoded to TOON. */
|
|
254
|
+
declare class ToonEncodeError extends Error {
|
|
255
|
+
readonly code = "TOON_ENCODE_ERROR";
|
|
256
|
+
constructor(message: string, cause?: Error);
|
|
257
|
+
}
|
|
258
|
+
/** Thrown when TOON data cannot be decoded into a valid Nostr event. */
|
|
259
|
+
declare class ToonDecodeError extends Error {
|
|
260
|
+
readonly code = "TOON_DECODE_ERROR";
|
|
261
|
+
constructor(message: string, cause?: Error);
|
|
262
|
+
}
|
|
263
|
+
/** Encode a Nostr event to TOON bytes (UTF-8). */
|
|
264
|
+
declare function encodeEventToToon(event: NostrEvent): Uint8Array;
|
|
265
|
+
/** Decode TOON bytes into a validated Nostr event. */
|
|
266
|
+
declare function decodeEventFromToon(data: Uint8Array): NostrEvent;
|
|
412
267
|
|
|
413
268
|
/**
|
|
414
269
|
* Subscribe to upstream relays and propagate events into the local EventStore.
|
|
@@ -459,268 +314,70 @@ declare class RelaySubscriber {
|
|
|
459
314
|
/**
|
|
460
315
|
* startRelay() -- Programmatic API for starting a TOON relay node.
|
|
461
316
|
*
|
|
462
|
-
*
|
|
463
|
-
*
|
|
464
|
-
*
|
|
317
|
+
* The relay is a plain HTTP/WebSocket app. It does NOT speak ILP and contains
|
|
318
|
+
* no payment, connector, settlement, or pricing logic: payment is enforced
|
|
319
|
+
* entirely upstream by an external terminator (see the connector repo). By the
|
|
320
|
+
* time a write reaches this process it is already proven paid, so the relay
|
|
321
|
+
* simply stores the event and serves reads.
|
|
465
322
|
*
|
|
466
|
-
*
|
|
323
|
+
* Two surfaces:
|
|
467
324
|
*
|
|
468
|
-
*
|
|
469
|
-
*
|
|
470
|
-
*
|
|
325
|
+
* - `POST /write` (TOON_BLS_PORT, default 3100): accepts `{ event }` as JSON,
|
|
326
|
+
* trusts the injected `X-TOON-Payer`/`-Amount`/`-Chain` headers WITHOUT
|
|
327
|
+
* re-validating payment, verifies only the event's own signature for
|
|
328
|
+
* integrity, and stores it. `GET /health` lives on the same port.
|
|
329
|
+
* - Free NIP-01 WebSocket reads (TOON_RELAY_PORT, default 7100).
|
|
471
330
|
*
|
|
472
|
-
*
|
|
331
|
+
* `startRelay()` returns a `RelayInstance` with an explicit `.stop()` for
|
|
332
|
+
* lifecycle control (the CLI wraps this with process-signal handling).
|
|
473
333
|
*
|
|
474
|
-
*
|
|
475
|
-
* destined for its own ILP address can be routed locally (the connector and
|
|
476
|
-
* the BLS handler must share a process for the round-trip to work).
|
|
477
|
-
*
|
|
478
|
-
* - **Standalone embedded** (no `connectorUrl`): A self-routing embedded
|
|
479
|
-
* connector with no upstream peers. Useful for genesis nodes and tests.
|
|
480
|
-
* - **Embedded with parent** (`connectorUrl` set): The embedded connector
|
|
481
|
-
* is configured with `connectorUrl` as a parent BTP peer, plus a self-route
|
|
482
|
-
* for local delivery and a default-route to the parent for everything else.
|
|
483
|
-
* - **Pre-built embedded** (`connector`): Pass a fully constructed
|
|
484
|
-
* `EmbeddableConnectorLike`. The town does not modify it.
|
|
485
|
-
*
|
|
486
|
-
* `connector` and `connectorUrl` are mutually exclusive — provide at most one.
|
|
487
|
-
*
|
|
488
|
-
* - **Oblivious** (`obliviousMode: true`, default `false`): the relay runs as a
|
|
489
|
-
* payment-oblivious app behind an external terminator. No embedded connector
|
|
490
|
-
* is created; no x402/EIP-3009/ILP-settlement code runs. The node exposes
|
|
491
|
-
* `POST /write` (event-as-JSON), trusting injected `X-TOON-Payer`/`-Amount`/
|
|
492
|
-
* `-Chain` headers without re-validating payment. Free NIP-01 WS reads are
|
|
493
|
-
* unchanged. Mutually exclusive with `connector`/`connectorUrl`. The embedded
|
|
494
|
-
* modes above remain the DEFAULT and are unchanged when `obliviousMode` is
|
|
495
|
-
* false.
|
|
334
|
+
* @module
|
|
496
335
|
*/
|
|
497
336
|
|
|
498
337
|
/**
|
|
499
338
|
* Configuration for starting a TOON relay node via `startRelay()`.
|
|
500
339
|
*
|
|
501
|
-
* Exactly one of `mnemonic` or `secretKey` must be provided
|
|
502
|
-
*
|
|
503
|
-
*
|
|
504
|
-
* - When neither is provided, a standalone embedded `ConnectorNode` is built
|
|
505
|
-
* with only a self-route (no upstream peers).
|
|
506
|
-
* - When `connectorUrl` is set, the embedded connector is configured with
|
|
507
|
-
* that URL as a parent BTP peer plus a default-route to it. `ilpAddress`
|
|
508
|
-
* becomes REQUIRED in this mode and must fall under the parent's prefix
|
|
509
|
-
* (e.g. `g.townhouse.<self>`).
|
|
510
|
-
* - When `connector` is set, the caller-supplied `EmbeddableConnectorLike`
|
|
511
|
-
* is used as-is; town does not configure peers, routes, or settlement on it.
|
|
340
|
+
* Exactly one of `mnemonic` or `secretKey` must be provided -- it is the node's
|
|
341
|
+
* Nostr identity (surfaced on `/health`).
|
|
512
342
|
*/
|
|
513
343
|
interface RelayConfig {
|
|
514
|
-
/** 12-word or 24-word BIP-39 mnemonic phrase. */
|
|
344
|
+
/** 12-word or 24-word BIP-39 mnemonic phrase (NIP-06 derivation). */
|
|
515
345
|
mnemonic?: string;
|
|
516
346
|
/** 32-byte secp256k1 secret key. */
|
|
517
347
|
secretKey?: Uint8Array;
|
|
518
|
-
/**
|
|
519
|
-
* Pre-built embedded connector. Mutually exclusive with `connectorUrl`.
|
|
520
|
-
* When provided, town does not modify the connector — peers, routes, and
|
|
521
|
-
* settlement are the caller's responsibility.
|
|
522
|
-
*/
|
|
523
|
-
connector?: EmbeddableConnectorLike;
|
|
524
|
-
/**
|
|
525
|
-
* Pre-built EventStore. When provided, town uses it instead of constructing
|
|
526
|
-
* the default file-backed `SqliteEventStore` under `dataDir`. Useful for
|
|
527
|
-
* tests (inject an `InMemoryEventStore`) and for embedding the relay with a
|
|
528
|
-
* shared store. The caller owns its lifecycle when supplied.
|
|
529
|
-
*/
|
|
530
|
-
eventStore?: EventStore;
|
|
531
|
-
/**
|
|
532
|
-
* Parent connector BTP URL (e.g. `ws://apex.example:3001`). When set, the
|
|
533
|
-
* embedded connector is built with this URL as a parent peer and a default
|
|
534
|
-
* `g.` route to that peer; `ilpAddress` MUST also be set and fall under the
|
|
535
|
-
* parent's prefix. Mutually exclusive with `connector`.
|
|
536
|
-
*/
|
|
537
|
-
connectorUrl?: string;
|
|
538
|
-
/** BTP peer id to use for the parent connector (default: `'apex'`). */
|
|
539
|
-
parentPeerId?: string;
|
|
540
|
-
/** BTP auth token for the parent peer (default: empty string -- no-auth). */
|
|
541
|
-
parentAuthToken?: string;
|
|
542
|
-
/** Stable nodeId for the embedded connector (default: `toon-<pubkeyShort>`). */
|
|
543
|
-
nodeId?: string;
|
|
544
|
-
/**
|
|
545
|
-
* Run as a payment-oblivious relay (default `false`). When `true`, the relay
|
|
546
|
-
* runs as a payment-oblivious app behind an external terminator: no embedded
|
|
547
|
-
* connector is created and no x402/EIP-3009/ILP-settlement code runs. The
|
|
548
|
-
* node exposes `POST /write` (event-as-JSON), trusting injected
|
|
549
|
-
* `X-TOON-Payer`/`X-TOON-Amount`/`X-TOON-Chain` headers without re-validating
|
|
550
|
-
* payment. Free NIP-01 WS reads are unchanged. Mutually exclusive with
|
|
551
|
-
* `connector`/`connectorUrl`. Embedded modes remain the default and unchanged
|
|
552
|
-
* when this is `false`. Overridable via the `TOON_OBLIVIOUS_MODE` env var.
|
|
553
|
-
*/
|
|
554
|
-
obliviousMode?: boolean;
|
|
555
|
-
/** BTP server port for the embedded connector (default: 3000). */
|
|
556
|
-
btpServerPort?: number;
|
|
557
|
-
/**
|
|
558
|
-
* EVM private key for settlement infrastructure on the embedded connector.
|
|
559
|
-
* If not set, the identity's secp256k1 key is reused.
|
|
560
|
-
*/
|
|
561
|
-
settlementPrivateKey?: string;
|
|
562
|
-
/**
|
|
563
|
-
* EVM treasury address advertised to the parent connector for the
|
|
564
|
-
* embedded-with-parent peer entry. The apex's PerPacketClaimService uses
|
|
565
|
-
* this as the `peerAddress` when the apex opens a payment channel toward
|
|
566
|
-
* this child. Only meaningful when `connectorUrl` is set. When omitted,
|
|
567
|
-
* the parent peer entry has no `evmAddress` and the apex's channel-open
|
|
568
|
-
* call must supply `peerAddress` explicitly.
|
|
569
|
-
*/
|
|
570
|
-
parentEvmAddress?: string;
|
|
571
|
-
/** WebSocket relay port (default: 7100). */
|
|
348
|
+
/** WebSocket relay (read) port (default: 7100). */
|
|
572
349
|
relayPort?: number;
|
|
573
|
-
/**
|
|
350
|
+
/** HTTP write/health port (default: 3100). */
|
|
574
351
|
blsPort?: number;
|
|
575
352
|
/**
|
|
576
|
-
*
|
|
577
|
-
*
|
|
578
|
-
* field is REQUIRED and must fall under the parent's address prefix.
|
|
353
|
+
* WebSocket bind host (default: 0.0.0.0). Set to `127.0.0.1` to bind the read
|
|
354
|
+
* port to localhost only (e.g. when an upstream proxy handles inbound).
|
|
579
355
|
*/
|
|
580
|
-
|
|
581
|
-
/**
|
|
582
|
-
btpEndpoint?: string;
|
|
583
|
-
/** Base price per byte in ILP units (default: 10n). */
|
|
584
|
-
basePricePerByte?: bigint;
|
|
585
|
-
/** Routing buffer percentage for x402 multi-hop overhead (default: 10). */
|
|
586
|
-
routingBufferPercent?: number;
|
|
587
|
-
/** Enable x402 /publish endpoint (default: false). */
|
|
588
|
-
x402Enabled?: boolean;
|
|
589
|
-
/** Facilitator EVM address for x402 payments. Defaults to the node's EVM address. */
|
|
590
|
-
facilitatorAddress?: string;
|
|
591
|
-
/** Known peers to bootstrap with. */
|
|
592
|
-
knownPeers?: {
|
|
593
|
-
pubkey: string;
|
|
594
|
-
relayUrl: string;
|
|
595
|
-
btpEndpoint: string;
|
|
596
|
-
}[];
|
|
597
|
-
/** Chain preset name (default: 'anvil'). See resolveChainConfig(). */
|
|
598
|
-
chain?: string;
|
|
599
|
-
/** Chain ID -> RPC URL mapping (e.g., { 'evm:base:31337': 'http://localhost:8545' }). */
|
|
600
|
-
chainRpcUrls?: Record<string, string>;
|
|
601
|
-
/** Chain ID -> TokenNetwork contract address. */
|
|
602
|
-
tokenNetworks?: Record<string, string>;
|
|
603
|
-
/** Chain ID -> preferred token address. */
|
|
604
|
-
preferredTokens?: Record<string, string>;
|
|
605
|
-
/**
|
|
606
|
-
* Chain ID -> settlement (recipient) address advertised in kind:10032.
|
|
607
|
-
*
|
|
608
|
-
* By default every supported chain advertises the identity's EVM address.
|
|
609
|
-
* That is wrong for non-EVM chains (e.g. `solana:devnet`), whose settlement
|
|
610
|
-
* recipient must be a chain-native address (a base58 Solana pubkey). Provide
|
|
611
|
-
* a per-chain override here to advertise a chain-native recipient; chains
|
|
612
|
-
* absent from this map keep the EVM-address default.
|
|
613
|
-
*
|
|
614
|
-
* NOTE (Phase-2 Stage 2 gate): advertising a Solana recipient is necessary
|
|
615
|
-
* but NOT sufficient for a settleable Solana loop — the client must also open
|
|
616
|
-
* a real on-chain Solana payment-channel PDA and sign over that PDA. See the
|
|
617
|
-
* Stage-2 PR description / gate report.
|
|
618
|
-
*/
|
|
619
|
-
settlementAddresses?: Record<string, string>;
|
|
620
|
-
/** Data directory path (default: ./data). */
|
|
356
|
+
host?: string;
|
|
357
|
+
/** Data directory for the file-backed SQLite store (default: ./data). */
|
|
621
358
|
dataDir?: string;
|
|
622
|
-
/** Enable dev mode (skip verification). Default: false. */
|
|
623
|
-
devMode?: boolean;
|
|
624
|
-
/** Discovery mode: 'seed-list' for production, 'genesis' for dev (default: 'genesis'). */
|
|
625
|
-
discovery?: 'seed-list' | 'genesis';
|
|
626
|
-
/** Public Nostr relay URLs for seed relay discovery (used when discovery: 'seed-list'). */
|
|
627
|
-
seedRelays?: string[];
|
|
628
|
-
/** Whether to publish this node as a seed relay entry (default: false). */
|
|
629
|
-
publishSeedEntry?: boolean;
|
|
630
|
-
/** External WebSocket URL of this relay (required if publishSeedEntry is true). */
|
|
631
|
-
externalRelayUrl?: string;
|
|
632
|
-
/**
|
|
633
|
-
* Ator hidden service configuration for the relay.
|
|
634
|
-
*
|
|
635
|
-
* When enabled, the relay binds to localhost only (ator handles inbound routing)
|
|
636
|
-
* and publishes the `.anon` address in seed relay discovery events.
|
|
637
|
-
*
|
|
638
|
-
* - `enabled: false` (default): Relay binds to `0.0.0.0`, no privacy overlay.
|
|
639
|
-
* - `enabled: true`: Relay binds to `127.0.0.1`, publishes `anonAddress` for discovery.
|
|
640
|
-
*/
|
|
641
|
-
ator?: {
|
|
642
|
-
enabled: boolean;
|
|
643
|
-
/** The `.anon` hidden service address for this relay (e.g., "wss://abc123.anon:443"). */
|
|
644
|
-
anonAddress?: string;
|
|
645
|
-
/** SOCKS5 proxy URL for outbound connections (default: "socks5h://127.0.0.1:9050"). */
|
|
646
|
-
socksProxy?: string;
|
|
647
|
-
};
|
|
648
|
-
/**
|
|
649
|
-
* Optional DVM skill descriptor to include in service discovery events.
|
|
650
|
-
* When provided, the service discovery event will include the `skill` field.
|
|
651
|
-
* Typically computed by `node.getSkillDescriptor()` from the SDK.
|
|
652
|
-
*/
|
|
653
|
-
skill?: SkillDescriptor;
|
|
654
|
-
/** Enable ArDrive peer lookup (default: false). */
|
|
655
|
-
ardriveEnabled?: boolean;
|
|
656
|
-
/** Public Nostr relay URLs for social discovery. */
|
|
657
|
-
relayUrls?: string[];
|
|
658
|
-
/** Asset code for ILP (default: 'USD'). */
|
|
659
|
-
assetCode?: string;
|
|
660
|
-
/** Asset scale for ILP (default: 6). */
|
|
661
|
-
assetScale?: number;
|
|
662
359
|
/**
|
|
663
|
-
*
|
|
664
|
-
*
|
|
665
|
-
*
|
|
360
|
+
* Pre-built EventStore. When provided, the relay uses it instead of building
|
|
361
|
+
* the default file-backed `SqliteEventStore` under `dataDir` (useful for
|
|
362
|
+
* tests via `InMemoryEventStore`, or to share a store when embedding). The
|
|
363
|
+
* caller owns its lifecycle when supplied.
|
|
666
364
|
*/
|
|
667
|
-
|
|
668
|
-
/**
|
|
669
|
-
|
|
670
|
-
* (default 3600). The node re-publishes its announcement at half this
|
|
671
|
-
* interval so a live apex stays fresh while an offline one expires, letting
|
|
672
|
-
* clients skip its unreachable BTP endpoint (issue #261). Set to 0 to disable
|
|
673
|
-
* the expiration tag and the heartbeat (non-expiring announcement). Override
|
|
674
|
-
* via the `TOON_ANNOUNCEMENT_TTL_SECONDS` env var.
|
|
675
|
-
*/
|
|
676
|
-
announcementTtlSeconds?: number;
|
|
365
|
+
eventStore?: EventStore;
|
|
366
|
+
/** Skip event-signature verification on `POST /write` (default: false). */
|
|
367
|
+
devMode?: boolean;
|
|
677
368
|
}
|
|
678
369
|
/**
|
|
679
|
-
* Resolved configuration with all defaults applied.
|
|
680
|
-
* (ports, pricing, paths have been filled in).
|
|
370
|
+
* Resolved configuration with all defaults applied.
|
|
681
371
|
*/
|
|
682
372
|
interface ResolvedRelayConfig {
|
|
683
373
|
relayPort: number;
|
|
684
374
|
blsPort: number;
|
|
685
|
-
|
|
686
|
-
btpEndpoint: string;
|
|
687
|
-
/** Stable nodeId of the embedded connector. */
|
|
688
|
-
nodeId: string;
|
|
689
|
-
/** Parent connector URL when peering with one (omitted otherwise). */
|
|
690
|
-
connectorUrl?: string;
|
|
691
|
-
/** Parent BTP peer id (only meaningful when connectorUrl is set). */
|
|
692
|
-
parentPeerId?: string;
|
|
693
|
-
basePricePerByte: bigint;
|
|
694
|
-
routingBufferPercent: number;
|
|
695
|
-
x402Enabled: boolean;
|
|
696
|
-
knownPeers: {
|
|
697
|
-
pubkey: string;
|
|
698
|
-
relayUrl: string;
|
|
699
|
-
btpEndpoint: string;
|
|
700
|
-
}[];
|
|
375
|
+
host: string;
|
|
701
376
|
dataDir: string;
|
|
702
377
|
devMode: boolean;
|
|
703
|
-
ardriveEnabled: boolean;
|
|
704
|
-
relayUrls: string[];
|
|
705
|
-
assetCode: string;
|
|
706
|
-
assetScale: number;
|
|
707
|
-
/** Discovery mode: 'seed-list' for production, 'genesis' for dev. */
|
|
708
|
-
discovery: 'seed-list' | 'genesis';
|
|
709
|
-
/** Public Nostr relay URLs for seed relay discovery. */
|
|
710
|
-
seedRelays: string[];
|
|
711
|
-
/** Whether to publish this node as a seed relay entry. */
|
|
712
|
-
publishSeedEntry: boolean;
|
|
713
|
-
/** External WebSocket URL of this relay (for seed entry publishing). */
|
|
714
|
-
externalRelayUrl?: string;
|
|
715
|
-
/** Chain preset name (e.g., 'anvil', 'arbitrum-one'). */
|
|
716
|
-
chain: string;
|
|
717
|
-
/** Whether the relay is running in payment-oblivious mode (no connector). */
|
|
718
|
-
obliviousMode: boolean;
|
|
719
378
|
}
|
|
720
379
|
/**
|
|
721
380
|
* A running TOON relay node instance returned by `startRelay()`.
|
|
722
|
-
*
|
|
723
|
-
* Provides lifecycle control (stop), identity info, and bootstrap results.
|
|
724
381
|
*/
|
|
725
382
|
interface RelayInstance {
|
|
726
383
|
/** Whether the relay is currently running. */
|
|
@@ -728,28 +385,19 @@ interface RelayInstance {
|
|
|
728
385
|
/** Gracefully stop the relay and release all resources. */
|
|
729
386
|
stop(): Promise<void>;
|
|
730
387
|
/**
|
|
731
|
-
* Subscribe to a remote Nostr relay. Received events are stored in
|
|
732
|
-
*
|
|
388
|
+
* Subscribe to a remote Nostr relay. Received events are stored in this
|
|
389
|
+
* node's EventStore. Returns a handle for lifecycle management.
|
|
733
390
|
*
|
|
734
391
|
* @param relayUrl - WebSocket URL of the relay to subscribe to.
|
|
735
392
|
* @param filter - Nostr filter (kinds, authors, etc.).
|
|
736
393
|
* @returns A RelaySubscription handle.
|
|
737
|
-
* @throws If the
|
|
394
|
+
* @throws If the relay is not running.
|
|
738
395
|
*/
|
|
739
396
|
subscribe(relayUrl: string, filter: Filter): RelaySubscription;
|
|
740
397
|
/** The node's Nostr x-only public key (64-char hex). */
|
|
741
398
|
pubkey: string;
|
|
742
|
-
/** The node's EVM address (0x-prefixed). */
|
|
743
|
-
evmAddress: string;
|
|
744
399
|
/** The resolved configuration with all defaults applied. */
|
|
745
400
|
config: ResolvedRelayConfig;
|
|
746
|
-
/** Bootstrap results from the startup phase. */
|
|
747
|
-
bootstrapResult: {
|
|
748
|
-
peerCount: number;
|
|
749
|
-
channelCount: number;
|
|
750
|
-
};
|
|
751
|
-
/** Discovery mode used by this instance. */
|
|
752
|
-
discoveryMode: 'seed-list' | 'genesis';
|
|
753
401
|
}
|
|
754
402
|
/**
|
|
755
403
|
* Handle for managing an outbound subscription to a remote Nostr relay.
|
|
@@ -766,636 +414,104 @@ interface RelaySubscription {
|
|
|
766
414
|
/**
|
|
767
415
|
* Start a TOON relay node with the given configuration.
|
|
768
416
|
*
|
|
769
|
-
*
|
|
770
|
-
*
|
|
771
|
-
* and relay monitor. Returns a `RelayInstance` for lifecycle management.
|
|
417
|
+
* Wires the event store, the HTTP write/health server, and the NIP-01
|
|
418
|
+
* WebSocket read server, then returns a `RelayInstance` for lifecycle control.
|
|
772
419
|
*
|
|
773
|
-
*
|
|
774
|
-
* are supported:
|
|
775
|
-
* - No connector args: standalone embedded connector with self-route only.
|
|
776
|
-
* - `connectorUrl`: embedded connector configured with that URL as a parent
|
|
777
|
-
* BTP peer plus a default `g.` route to it. `ilpAddress` is REQUIRED here.
|
|
778
|
-
* - `connector`: pass a pre-built `EmbeddableConnectorLike`; town does not
|
|
779
|
-
* modify it.
|
|
780
|
-
*
|
|
781
|
-
* @param config - Node configuration. One of `mnemonic`/`secretKey` is required;
|
|
782
|
-
* `connector` and `connectorUrl` are mutually exclusive.
|
|
420
|
+
* @param config - Node configuration. One of `mnemonic`/`secretKey` is required.
|
|
783
421
|
* @returns A running RelayInstance.
|
|
784
422
|
* @throws If both or neither of mnemonic/secretKey are provided.
|
|
785
|
-
* @throws If both connector and connectorUrl are provided.
|
|
786
|
-
* @throws If connectorUrl is set without an explicit ilpAddress.
|
|
787
423
|
*
|
|
788
424
|
* @example
|
|
789
425
|
* ```typescript
|
|
790
|
-
*
|
|
791
|
-
*
|
|
792
|
-
*
|
|
793
|
-
* // Embedded with parent
|
|
794
|
-
* const town = await startRelay({
|
|
795
|
-
* mnemonic: 'abandon ...',
|
|
796
|
-
* connectorUrl: 'ws://apex.example:3001',
|
|
797
|
-
* parentPeerId: 'apex',
|
|
798
|
-
* parentAuthToken: '',
|
|
799
|
-
* ilpAddress: 'g.townhouse.alice',
|
|
800
|
-
* });
|
|
426
|
+
* const relay = await startRelay({ secretKey });
|
|
427
|
+
* // ... POST /write on 3100, read NIP-01 on 7100 ...
|
|
428
|
+
* await relay.stop();
|
|
801
429
|
* ```
|
|
802
430
|
*/
|
|
803
431
|
declare function startRelay(config: RelayConfig): Promise<RelayInstance>;
|
|
804
|
-
/**
|
|
805
|
-
* @deprecated Use {@link startRelay} instead. Retained for backwards
|
|
806
|
-
* compatibility after the town → relay package merge.
|
|
807
|
-
*/
|
|
808
|
-
declare const startTown: typeof startRelay;
|
|
809
|
-
/**
|
|
810
|
-
* @deprecated Use {@link RelayConfig} instead.
|
|
811
|
-
*/
|
|
812
|
-
type TownConfig = RelayConfig;
|
|
813
|
-
/**
|
|
814
|
-
* @deprecated Use {@link RelayInstance} instead.
|
|
815
|
-
*/
|
|
816
|
-
type TownInstance = RelayInstance;
|
|
817
|
-
/**
|
|
818
|
-
* @deprecated Use {@link ResolvedRelayConfig} instead.
|
|
819
|
-
*/
|
|
820
|
-
type ResolvedTownConfig = ResolvedRelayConfig;
|
|
821
|
-
/**
|
|
822
|
-
* @deprecated Use {@link RelaySubscription} instead.
|
|
823
|
-
*/
|
|
824
|
-
type TownSubscription = RelaySubscription;
|
|
825
432
|
|
|
826
433
|
/**
|
|
827
|
-
*
|
|
434
|
+
* Health response for the relay's HTTP server.
|
|
828
435
|
*
|
|
829
|
-
*
|
|
830
|
-
* health
|
|
831
|
-
*
|
|
832
|
-
*
|
|
833
|
-
* The response mirrors kind:10035 service discovery event fields but adds
|
|
834
|
-
* runtime-only fields that cannot be known at event publish time.
|
|
436
|
+
* The relay is a plain read/write app (no payment, connector, or settlement
|
|
437
|
+
* layer), so the health response is deliberately minimal: liveness plus the
|
|
438
|
+
* node's identity and version. It is served from `GET /health` on the write
|
|
439
|
+
* port and is the target of the container healthcheck.
|
|
835
440
|
*
|
|
836
441
|
* @module
|
|
837
442
|
*/
|
|
838
|
-
|
|
839
|
-
/** TEE attestation state for the health response (enforcement guideline 12). */
|
|
840
|
-
interface TeeHealthInfo {
|
|
841
|
-
/** Whether a valid attestation has been published. */
|
|
842
|
-
attested: boolean;
|
|
843
|
-
/** Enclave type identifier (e.g., 'aws-nitro', 'marlin-oyster'). */
|
|
844
|
-
enclaveType: string;
|
|
845
|
-
/** Unix timestamp of the last attestation event. */
|
|
846
|
-
lastAttestation: number;
|
|
847
|
-
/** Platform Configuration Register 0 (SHA-384 hex, 96 chars). */
|
|
848
|
-
pcr0: string;
|
|
849
|
-
/** Attestation validity state. */
|
|
850
|
-
state: 'valid' | 'stale' | 'unattested';
|
|
851
|
-
}
|
|
852
443
|
/** Configuration for building a health response. */
|
|
853
444
|
interface HealthConfig {
|
|
854
|
-
/** Current bootstrap phase. */
|
|
855
|
-
phase: BootstrapPhase;
|
|
856
445
|
/** Node's Nostr pubkey (64-char hex). */
|
|
857
446
|
pubkey: string;
|
|
858
|
-
/** Node's ILP address. */
|
|
859
|
-
ilpAddress: string;
|
|
860
|
-
/** Number of registered peers. */
|
|
861
|
-
peerCount: number;
|
|
862
|
-
/** Number of discovered (not yet registered) peers. */
|
|
863
|
-
discoveredPeerCount: number;
|
|
864
|
-
/** Number of open payment channels. */
|
|
865
|
-
channelCount: number;
|
|
866
|
-
/**
|
|
867
|
-
* Base price per byte (bigint from config, converted to number via Number()).
|
|
868
|
-
* Values exceeding Number.MAX_SAFE_INTEGER (2^53 - 1) will lose precision.
|
|
869
|
-
*/
|
|
870
|
-
basePricePerByte: bigint;
|
|
871
|
-
/** Whether x402 is enabled. */
|
|
872
|
-
x402Enabled: boolean;
|
|
873
|
-
/** Chain preset name. */
|
|
874
|
-
chain: string;
|
|
875
|
-
/**
|
|
876
|
-
* TEE attestation info.
|
|
877
|
-
* Omit entirely when not running in a TEE (enforcement guideline 12).
|
|
878
|
-
*/
|
|
879
|
-
tee?: TeeHealthInfo;
|
|
880
447
|
}
|
|
881
|
-
/** The
|
|
448
|
+
/** The health response shape. */
|
|
882
449
|
interface HealthResponse {
|
|
883
450
|
status: 'healthy';
|
|
884
|
-
phase: BootstrapPhase;
|
|
885
451
|
pubkey: string;
|
|
886
|
-
ilpAddress: string;
|
|
887
|
-
peerCount: number;
|
|
888
|
-
discoveredPeerCount: number;
|
|
889
|
-
channelCount: number;
|
|
890
|
-
pricing: {
|
|
891
|
-
basePricePerByte: number;
|
|
892
|
-
currency: 'USDC';
|
|
893
|
-
};
|
|
894
|
-
x402?: {
|
|
895
|
-
enabled: true;
|
|
896
|
-
endpoint: string;
|
|
897
|
-
};
|
|
898
|
-
/**
|
|
899
|
-
* TEE attestation info. Only present when running in a TEE enclave.
|
|
900
|
-
* Entirely absent when not in TEE (enforcement guideline 12 --
|
|
901
|
-
* never `{ attested: false }`, simply omit the field).
|
|
902
|
-
*/
|
|
903
|
-
tee?: TeeHealthInfo;
|
|
904
452
|
capabilities: string[];
|
|
905
|
-
chain: string;
|
|
906
453
|
version: string;
|
|
907
|
-
sdk: true;
|
|
908
454
|
timestamp: number;
|
|
909
455
|
}
|
|
910
456
|
/**
|
|
911
|
-
* Build
|
|
457
|
+
* Build a health response for the relay.
|
|
912
458
|
*
|
|
913
|
-
*
|
|
914
|
-
*
|
|
915
|
-
* test and reuse across entrypoints.
|
|
459
|
+
* Pure function: takes a config and returns the response object, so it is
|
|
460
|
+
* trivially unit-testable and reusable.
|
|
916
461
|
*
|
|
917
|
-
*
|
|
918
|
-
*
|
|
919
|
-
*
|
|
920
|
-
* @param config - Health configuration with runtime state and static config.
|
|
921
|
-
* @returns The enriched health response object.
|
|
462
|
+
* @param config - Health configuration (the node's pubkey).
|
|
463
|
+
* @returns The health response object.
|
|
922
464
|
*/
|
|
923
465
|
declare function createHealthResponse(config: HealthConfig): HealthResponse;
|
|
924
466
|
|
|
925
467
|
/**
|
|
926
|
-
*
|
|
927
|
-
*
|
|
928
|
-
* Stores incoming Nostr events in the EventStore after decoding from TOON.
|
|
929
|
-
* This is the "default" handler for the relay -- it processes all event kinds
|
|
930
|
-
* except those handled by kind-specific handlers.
|
|
931
|
-
*
|
|
932
|
-
* The handler is intentionally simple (~15 lines of logic). The SDK pipeline
|
|
933
|
-
* handles signature verification, pricing validation, and self-write bypass
|
|
934
|
-
* before the handler is invoked. The handler only needs to:
|
|
935
|
-
* 1. ctx.decode() -- lazy-decode the TOON payload into a NostrEvent
|
|
936
|
-
* 2. eventStore.store(event) -- persist the event
|
|
937
|
-
* 3. ctx.accept({ eventId, storedAt }) -- accept the ILP packet
|
|
938
|
-
*/
|
|
939
|
-
|
|
940
|
-
/**
|
|
941
|
-
* Configuration for the event storage handler.
|
|
942
|
-
*
|
|
943
|
-
* Minimal by design -- the handler's only job is decode + store + accept.
|
|
944
|
-
* Pricing, verification, and self-write bypass are SDK pipeline concerns.
|
|
945
|
-
*/
|
|
946
|
-
interface EventStorageHandlerConfig {
|
|
947
|
-
/** Event store backend (e.g., SqliteEventStore from @toon-protocol/relay). */
|
|
948
|
-
eventStore: EventStore;
|
|
949
|
-
}
|
|
950
|
-
/**
|
|
951
|
-
* Creates an event storage handler that decodes TOON payloads and stores
|
|
952
|
-
* Nostr events in the configured EventStore.
|
|
953
|
-
*
|
|
954
|
-
* Errors from `ctx.decode()` or `eventStore.store()` are not caught here --
|
|
955
|
-
* they propagate to the SDK's dispatch error boundary, which converts
|
|
956
|
-
* unhandled exceptions to `{ accept: false, code: 'T00', message: 'Internal error' }`.
|
|
957
|
-
*
|
|
958
|
-
* @param config - Handler configuration with the event store backend.
|
|
959
|
-
* @returns A handler function compatible with `node.onDefault(handler)`.
|
|
960
|
-
*/
|
|
961
|
-
declare function createEventStorageHandler(config: EventStorageHandlerConfig): Handler;
|
|
962
|
-
|
|
963
|
-
/**
|
|
964
|
-
* EIP-3009 types and constants for the x402 publish endpoint.
|
|
965
|
-
*
|
|
966
|
-
* EIP-3009 (`transferWithAuthorization`) allows gasless USDC transfers:
|
|
967
|
-
* the user signs an off-chain authorization, and the facilitator (node
|
|
968
|
-
* operator) submits it on-chain, paying gas. The user pays only the
|
|
969
|
-
* USDC transfer amount.
|
|
970
|
-
*
|
|
971
|
-
* @module
|
|
972
|
-
*/
|
|
973
|
-
|
|
974
|
-
/**
|
|
975
|
-
* EIP-3009 `transferWithAuthorization` signed authorization.
|
|
976
|
-
*
|
|
977
|
-
* The user signs this off-chain (EIP-712 typed data). The facilitator
|
|
978
|
-
* submits the signature on-chain to execute the USDC transfer.
|
|
979
|
-
*/
|
|
980
|
-
interface Eip3009Authorization {
|
|
981
|
-
/** Sender's EVM address ('0x...'). */
|
|
982
|
-
from: string;
|
|
983
|
-
/** Recipient's EVM address ('0x...' -- facilitator). */
|
|
984
|
-
to: string;
|
|
985
|
-
/** USDC amount in micro-units (bigint). */
|
|
986
|
-
value: bigint;
|
|
987
|
-
/** Unix timestamp: authorization valid after this time. */
|
|
988
|
-
validAfter: number;
|
|
989
|
-
/** Unix timestamp: authorization expires at this time. */
|
|
990
|
-
validBefore: number;
|
|
991
|
-
/** 32-byte nonce ('0x...' hex string). */
|
|
992
|
-
nonce: string;
|
|
993
|
-
/** ECDSA recovery id (27 or 28). */
|
|
994
|
-
v: number;
|
|
995
|
-
/** ECDSA r component ('0x...' 32 bytes). */
|
|
996
|
-
r: string;
|
|
997
|
-
/** ECDSA s component ('0x...' 32 bytes). */
|
|
998
|
-
s: string;
|
|
999
|
-
}
|
|
1000
|
-
/**
|
|
1001
|
-
* EIP-712 typed data structure for `transferWithAuthorization`.
|
|
1002
|
-
*
|
|
1003
|
-
* This is the type definition used for off-chain signature verification
|
|
1004
|
-
* and on-chain contract calls.
|
|
1005
|
-
*
|
|
1006
|
-
* NOTE: The EIP-712 domain for USDC's `transferWithAuthorization` is
|
|
1007
|
-
* different from the EIP-712 domain for TokenNetwork's balance proofs.
|
|
1008
|
-
* The x402 handler must use the USDC contract's domain.
|
|
1009
|
-
*/
|
|
1010
|
-
declare const EIP_3009_TYPES: {
|
|
1011
|
-
readonly TransferWithAuthorization: readonly [{
|
|
1012
|
-
readonly name: "from";
|
|
1013
|
-
readonly type: "address";
|
|
1014
|
-
}, {
|
|
1015
|
-
readonly name: "to";
|
|
1016
|
-
readonly type: "address";
|
|
1017
|
-
}, {
|
|
1018
|
-
readonly name: "value";
|
|
1019
|
-
readonly type: "uint256";
|
|
1020
|
-
}, {
|
|
1021
|
-
readonly name: "validAfter";
|
|
1022
|
-
readonly type: "uint256";
|
|
1023
|
-
}, {
|
|
1024
|
-
readonly name: "validBefore";
|
|
1025
|
-
readonly type: "uint256";
|
|
1026
|
-
}, {
|
|
1027
|
-
readonly name: "nonce";
|
|
1028
|
-
readonly type: "bytes32";
|
|
1029
|
-
}];
|
|
1030
|
-
};
|
|
1031
|
-
/**
|
|
1032
|
-
* EIP-712 domain separator for USDC's `transferWithAuthorization`.
|
|
1033
|
-
*
|
|
1034
|
-
* Uses the USDC contract's name and version, NOT the TokenNetwork's.
|
|
1035
|
-
*/
|
|
1036
|
-
declare const USDC_EIP712_DOMAIN: {
|
|
1037
|
-
readonly name: "USD Coin";
|
|
1038
|
-
readonly version: "2";
|
|
1039
|
-
};
|
|
1040
|
-
/**
|
|
1041
|
-
* Minimal EventStore interface for destination reachability checks.
|
|
1042
|
-
* Uses structural typing to avoid importing @toon-protocol/relay directly.
|
|
1043
|
-
* The query method accepts Filter[] (array) per the relay's EventStore interface.
|
|
1044
|
-
*/
|
|
1045
|
-
interface EventStoreLike {
|
|
1046
|
-
query(filters: {
|
|
1047
|
-
kinds?: number[];
|
|
1048
|
-
authors?: string[];
|
|
1049
|
-
}[]): unknown[];
|
|
1050
|
-
}
|
|
1051
|
-
/**
|
|
1052
|
-
* Request body for the x402 `/publish` endpoint.
|
|
1053
|
-
*
|
|
1054
|
-
* The client sends a signed Nostr event and a destination ILP address.
|
|
1055
|
-
* The handler TOON-encodes the event before routing.
|
|
1056
|
-
*/
|
|
1057
|
-
interface X402PublishRequest {
|
|
1058
|
-
/** Signed Nostr event. */
|
|
1059
|
-
event: NostrEvent;
|
|
1060
|
-
/** Target ILP address (e.g., "g.toon.target-relay"). */
|
|
1061
|
-
destination: string;
|
|
1062
|
-
}
|
|
1063
|
-
/**
|
|
1064
|
-
* Response body for a successful x402 `/publish` request (HTTP 200).
|
|
1065
|
-
*/
|
|
1066
|
-
interface X402PublishResponse {
|
|
1067
|
-
/** Nostr event ID (64-char hex). */
|
|
1068
|
-
eventId: string;
|
|
1069
|
-
/** On-chain settlement transaction hash. */
|
|
1070
|
-
settlementTxHash: string;
|
|
1071
|
-
/** Whether the ILP PREPARE was fulfilled or rejected by the destination. */
|
|
1072
|
-
deliveryStatus: 'fulfilled' | 'rejected';
|
|
1073
|
-
/** Always false -- no refunds on REJECT per protocol design. */
|
|
1074
|
-
refundInitiated: false;
|
|
1075
|
-
}
|
|
1076
|
-
/**
|
|
1077
|
-
* Response body for the 402 pricing negotiation.
|
|
1078
|
-
*/
|
|
1079
|
-
interface X402PricingResponse {
|
|
1080
|
-
/** Price in USDC micro-units (as string for BigInt serialization). */
|
|
1081
|
-
amount: string;
|
|
1082
|
-
/** Node operator's EVM address that will receive the USDC. */
|
|
1083
|
-
facilitatorAddress: string;
|
|
1084
|
-
/** Payment network identifier. */
|
|
1085
|
-
paymentNetwork: 'eip-3009';
|
|
1086
|
-
/** EVM chain ID. */
|
|
1087
|
-
chainId: number;
|
|
1088
|
-
/** USDC contract address on this chain. */
|
|
1089
|
-
usdcAddress: string;
|
|
1090
|
-
}
|
|
1091
|
-
/**
|
|
1092
|
-
* Minimal USDC ABI for EIP-3009 operations.
|
|
1093
|
-
*
|
|
1094
|
-
* Includes only the functions needed by the x402 handler:
|
|
1095
|
-
* - `balanceOf`: Read sender's USDC balance (pre-flight check #2)
|
|
1096
|
-
* - `authorizationState`: Check nonce freshness (pre-flight check #3)
|
|
1097
|
-
* - `transferWithAuthorization`: Execute gasless USDC transfer (settlement)
|
|
1098
|
-
*/
|
|
1099
|
-
declare const USDC_ABI: readonly [{
|
|
1100
|
-
readonly name: "balanceOf";
|
|
1101
|
-
readonly type: "function";
|
|
1102
|
-
readonly stateMutability: "view";
|
|
1103
|
-
readonly inputs: readonly [{
|
|
1104
|
-
readonly name: "account";
|
|
1105
|
-
readonly type: "address";
|
|
1106
|
-
}];
|
|
1107
|
-
readonly outputs: readonly [{
|
|
1108
|
-
readonly name: "";
|
|
1109
|
-
readonly type: "uint256";
|
|
1110
|
-
}];
|
|
1111
|
-
}, {
|
|
1112
|
-
readonly name: "authorizationState";
|
|
1113
|
-
readonly type: "function";
|
|
1114
|
-
readonly stateMutability: "view";
|
|
1115
|
-
readonly inputs: readonly [{
|
|
1116
|
-
readonly name: "authorizer";
|
|
1117
|
-
readonly type: "address";
|
|
1118
|
-
}, {
|
|
1119
|
-
readonly name: "nonce";
|
|
1120
|
-
readonly type: "bytes32";
|
|
1121
|
-
}];
|
|
1122
|
-
readonly outputs: readonly [{
|
|
1123
|
-
readonly name: "";
|
|
1124
|
-
readonly type: "bool";
|
|
1125
|
-
}];
|
|
1126
|
-
}, {
|
|
1127
|
-
readonly name: "transferWithAuthorization";
|
|
1128
|
-
readonly type: "function";
|
|
1129
|
-
readonly stateMutability: "nonpayable";
|
|
1130
|
-
readonly inputs: readonly [{
|
|
1131
|
-
readonly name: "from";
|
|
1132
|
-
readonly type: "address";
|
|
1133
|
-
}, {
|
|
1134
|
-
readonly name: "to";
|
|
1135
|
-
readonly type: "address";
|
|
1136
|
-
}, {
|
|
1137
|
-
readonly name: "value";
|
|
1138
|
-
readonly type: "uint256";
|
|
1139
|
-
}, {
|
|
1140
|
-
readonly name: "validAfter";
|
|
1141
|
-
readonly type: "uint256";
|
|
1142
|
-
}, {
|
|
1143
|
-
readonly name: "validBefore";
|
|
1144
|
-
readonly type: "uint256";
|
|
1145
|
-
}, {
|
|
1146
|
-
readonly name: "nonce";
|
|
1147
|
-
readonly type: "bytes32";
|
|
1148
|
-
}, {
|
|
1149
|
-
readonly name: "v";
|
|
1150
|
-
readonly type: "uint8";
|
|
1151
|
-
}, {
|
|
1152
|
-
readonly name: "r";
|
|
1153
|
-
readonly type: "bytes32";
|
|
1154
|
-
}, {
|
|
1155
|
-
readonly name: "s";
|
|
1156
|
-
readonly type: "bytes32";
|
|
1157
|
-
}];
|
|
1158
|
-
readonly outputs: readonly [];
|
|
1159
|
-
}];
|
|
1160
|
-
|
|
1161
|
-
/**
|
|
1162
|
-
* Pre-flight validation pipeline for the x402 publish endpoint.
|
|
1163
|
-
*
|
|
1164
|
-
* Implements 6 free checks that run before any on-chain transaction,
|
|
1165
|
-
* preventing gas griefing (E3-R008). All checks are either pure
|
|
1166
|
-
* cryptography or read-only RPC calls (no gas cost).
|
|
1167
|
-
*
|
|
1168
|
-
* Check order (cheapest to most expensive):
|
|
1169
|
-
* 1. EIP-3009 signature verification (off-chain, ~1ms)
|
|
1170
|
-
* 2. USDC balance check (eth_call, ~50ms)
|
|
1171
|
-
* 3. Nonce freshness check (eth_call, ~50ms)
|
|
1172
|
-
* 4. TOON shallow parse (pure computation, ~0.1ms)
|
|
1173
|
-
* 5. Schnorr signature verification (pure crypto, ~2ms)
|
|
1174
|
-
* 6. Destination reachability check (local lookup, ~0.1ms)
|
|
1175
|
-
*
|
|
1176
|
-
* @module
|
|
1177
|
-
*/
|
|
1178
|
-
|
|
1179
|
-
/**
|
|
1180
|
-
* Result of running the pre-flight validation pipeline.
|
|
1181
|
-
*/
|
|
1182
|
-
interface PreflightResult {
|
|
1183
|
-
/** Whether all checks passed. */
|
|
1184
|
-
passed: boolean;
|
|
1185
|
-
/** Which check failed (only set if passed is false). */
|
|
1186
|
-
failedCheck?: string;
|
|
1187
|
-
/** List of check names that were executed. */
|
|
1188
|
-
checksPerformed: string[];
|
|
1189
|
-
}
|
|
1190
|
-
/**
|
|
1191
|
-
* Callback for Schnorr signature verification.
|
|
1192
|
-
* Returns true if the signature is valid.
|
|
1193
|
-
*/
|
|
1194
|
-
type SchnorrVerifyFn = (meta: ToonRoutingMeta) => Promise<boolean>;
|
|
1195
|
-
/**
|
|
1196
|
-
* Configuration for the pre-flight validation pipeline.
|
|
1197
|
-
*/
|
|
1198
|
-
interface PreflightConfig {
|
|
1199
|
-
/** Resolved chain configuration. */
|
|
1200
|
-
chainConfig: ChainPreset;
|
|
1201
|
-
/** Base price per byte for pricing validation. */
|
|
1202
|
-
basePricePerByte: bigint;
|
|
1203
|
-
/** This node's Nostr public key. */
|
|
1204
|
-
ownPubkey: string;
|
|
1205
|
-
/** Whether dev mode is enabled (skips Schnorr verification). */
|
|
1206
|
-
devMode: boolean;
|
|
1207
|
-
/** viem public client for read-only contract calls (optional, for testing). */
|
|
1208
|
-
publicClient?: PublicClient;
|
|
1209
|
-
/** EventStore for destination reachability check (optional). */
|
|
1210
|
-
eventStore?: EventStoreLike;
|
|
1211
|
-
/** Schnorr verification callback (optional, uses SDK verification pipeline). */
|
|
1212
|
-
schnorrVerify?: SchnorrVerifyFn;
|
|
1213
|
-
}
|
|
1214
|
-
/**
|
|
1215
|
-
* Run the 6-stage pre-flight validation pipeline.
|
|
468
|
+
* Write handler for @toon-protocol/relay.
|
|
1216
469
|
*
|
|
1217
|
-
*
|
|
1218
|
-
*
|
|
470
|
+
* Exposes a plain-HTTP write surface that accepts an event-as-JSON, trusts
|
|
471
|
+
* (but does NOT validate) injected payment headers, verifies ONLY the event
|
|
472
|
+
* signature for integrity, and stores the event.
|
|
1219
473
|
*
|
|
1220
|
-
*
|
|
1221
|
-
*
|
|
1222
|
-
*
|
|
1223
|
-
*
|
|
1224
|
-
*
|
|
1225
|
-
*/
|
|
1226
|
-
declare function runPreflight(authorization: Eip3009Authorization, toonData: string, destination: string, config: PreflightConfig): Promise<PreflightResult>;
|
|
1227
|
-
|
|
1228
|
-
/**
|
|
1229
|
-
* EIP-3009 on-chain settlement module for the x402 publish endpoint.
|
|
1230
|
-
*
|
|
1231
|
-
* Executes `transferWithAuthorization` on the USDC contract to settle
|
|
1232
|
-
* the gasless USDC transfer from the client to the facilitator (node
|
|
1233
|
-
* operator). The facilitator pays gas; the client pays only USDC.
|
|
1234
|
-
*
|
|
1235
|
-
* Settlement atomicity (E3-R006):
|
|
1236
|
-
* - If settlement fails (revert), no ILP PREPARE is constructed.
|
|
1237
|
-
* - If settlement succeeds but ILP PREPARE is rejected, no refund.
|
|
1238
|
-
*
|
|
1239
|
-
* @module
|
|
1240
|
-
*/
|
|
1241
|
-
|
|
1242
|
-
/**
|
|
1243
|
-
* Result of an EIP-3009 settlement attempt.
|
|
1244
|
-
*/
|
|
1245
|
-
interface X402SettlementResult {
|
|
1246
|
-
/** Whether the on-chain transaction succeeded. */
|
|
1247
|
-
success: boolean;
|
|
1248
|
-
/** Transaction hash (only set on success). */
|
|
1249
|
-
txHash?: string;
|
|
1250
|
-
/** Error message (only set on failure). */
|
|
1251
|
-
error?: string;
|
|
1252
|
-
}
|
|
1253
|
-
/**
|
|
1254
|
-
* @deprecated Use X402SettlementResult instead.
|
|
1255
|
-
*/
|
|
1256
|
-
type SettlementResult = X402SettlementResult;
|
|
1257
|
-
/**
|
|
1258
|
-
* Configuration for the settlement module.
|
|
1259
|
-
*
|
|
1260
|
-
* Named `X402SettlementConfig` to avoid collision with
|
|
1261
|
-
* `SettlementConfig` from `@toon-protocol/core` (bootstrap).
|
|
1262
|
-
*/
|
|
1263
|
-
interface X402SettlementConfig {
|
|
1264
|
-
/** Resolved chain configuration. */
|
|
1265
|
-
chainConfig: ChainPreset;
|
|
1266
|
-
/** viem wallet client for the facilitator (submits the tx, pays gas). */
|
|
1267
|
-
walletClient: WalletClient;
|
|
1268
|
-
/** viem public client for waiting on transaction receipts. */
|
|
1269
|
-
publicClient?: PublicClient;
|
|
1270
|
-
}
|
|
1271
|
-
/**
|
|
1272
|
-
* Settle an EIP-3009 `transferWithAuthorization` on-chain.
|
|
1273
|
-
*
|
|
1274
|
-
* Submits the client's signed authorization to the USDC contract.
|
|
1275
|
-
* The facilitator (node operator) pays gas for the transaction.
|
|
1276
|
-
*
|
|
1277
|
-
* @param authorization - Signed EIP-3009 authorization from the client.
|
|
1278
|
-
* @param config - Settlement configuration with wallet client.
|
|
1279
|
-
* @returns SettlementResult indicating success/failure.
|
|
1280
|
-
*/
|
|
1281
|
-
/**
|
|
1282
|
-
* @deprecated Use X402SettlementConfig instead.
|
|
1283
|
-
*/
|
|
1284
|
-
type SettlementConfig = X402SettlementConfig;
|
|
1285
|
-
declare function settleEip3009(authorization: Eip3009Authorization, config: X402SettlementConfig): Promise<X402SettlementResult>;
|
|
1286
|
-
|
|
1287
|
-
/**
|
|
1288
|
-
* x402 publish handler for the TOON protocol.
|
|
1289
|
-
*
|
|
1290
|
-
* Implements the HTTP-native payment on-ramp via the x402 protocol pattern.
|
|
1291
|
-
* Allows any HTTP client (AI agents, browsers, CLI tools) to publish Nostr
|
|
1292
|
-
* events to the network by paying USDC, without understanding ILP or
|
|
1293
|
-
* running an ILP client.
|
|
474
|
+
* This handler is intentionally decoupled from any payment layer: it contains
|
|
475
|
+
* no claim/settlement/ILP logic and imports none of it. Payment validation is
|
|
476
|
+
* the upstream terminator's concern; by the time a request reaches this surface
|
|
477
|
+
* the trusted `X-TOON-*` headers are assumed already proven. The handler
|
|
478
|
+
* captures them purely for the response echo and a log line.
|
|
1294
479
|
*
|
|
1295
480
|
* Flow:
|
|
1296
|
-
* 1.
|
|
1297
|
-
* 2.
|
|
1298
|
-
* 3.
|
|
1299
|
-
* 4.
|
|
1300
|
-
* 5.
|
|
1301
|
-
* 6.
|
|
1302
|
-
* 7. Handler returns 200 with event ID and tx hash
|
|
481
|
+
* 1. Parse JSON body `{ event }` -> 400 on malformed/missing event
|
|
482
|
+
* 2. Capture trusted X-TOON-Payer / X-TOON-Amount / X-TOON-Chain headers
|
|
483
|
+
* 3. Verify the event signature (skipped in devMode) -> 422 on invalid sig
|
|
484
|
+
* 4. Store the event in the EventStore
|
|
485
|
+
* 5. Fire the optional onStored callback
|
|
486
|
+
* 6. Respond 200 with the event id, storedAt timestamp, and echoed headers
|
|
1303
487
|
*
|
|
1304
488
|
* @module
|
|
1305
489
|
*/
|
|
1306
490
|
|
|
1307
491
|
/**
|
|
1308
|
-
* Configuration for the
|
|
492
|
+
* Configuration for the write handler.
|
|
1309
493
|
*/
|
|
1310
|
-
interface
|
|
1311
|
-
/**
|
|
1312
|
-
|
|
1313
|
-
/**
|
|
1314
|
-
chainConfig: ChainPreset;
|
|
1315
|
-
/** Base price per byte in USDC micro-units. */
|
|
1316
|
-
basePricePerByte: bigint;
|
|
1317
|
-
/** Routing buffer percentage for multi-hop overhead (default: 10). */
|
|
1318
|
-
routingBufferPercent: number;
|
|
1319
|
-
/** Facilitator's EVM address (receives USDC payments). */
|
|
1320
|
-
facilitatorAddress: string;
|
|
1321
|
-
/** This node's Nostr public key. */
|
|
1322
|
-
ownPubkey: string;
|
|
1323
|
-
/** Whether dev mode is enabled (skips Schnorr verification). */
|
|
494
|
+
interface WriteHandlerConfig {
|
|
495
|
+
/** Event store backend used to persist accepted events. */
|
|
496
|
+
eventStore: EventStore;
|
|
497
|
+
/** Whether dev mode is enabled (skips Schnorr signature verification). */
|
|
1324
498
|
devMode: boolean;
|
|
1325
|
-
/**
|
|
1326
|
-
|
|
1327
|
-
/** Event store for destination reachability check. */
|
|
1328
|
-
eventStore?: EventStoreLike;
|
|
1329
|
-
/** TOON encoder function (defaults to core's encodeEventToToon). */
|
|
1330
|
-
toonEncoder?: (event: NostrEvent) => Uint8Array;
|
|
1331
|
-
/** viem wallet client for on-chain settlement (facilitator pays gas). */
|
|
1332
|
-
walletClient?: WalletClient;
|
|
1333
|
-
/** viem public client for read-only contract calls. */
|
|
1334
|
-
publicClient?: PublicClient;
|
|
1335
|
-
/** Override settle function (for testing). */
|
|
1336
|
-
settle?: (auth: Eip3009Authorization, config: X402SettlementConfig) => Promise<X402SettlementResult>;
|
|
1337
|
-
/** Override pre-flight function (for testing). */
|
|
1338
|
-
runPreflightFn?: typeof runPreflight;
|
|
499
|
+
/** Optional callback fired after an event is successfully stored. */
|
|
500
|
+
onStored?: (event: NostrEvent) => void;
|
|
1339
501
|
}
|
|
1340
502
|
/**
|
|
1341
|
-
*
|
|
503
|
+
* Write handler instance.
|
|
1342
504
|
*/
|
|
1343
|
-
interface
|
|
1344
|
-
/** Handle a
|
|
1345
|
-
|
|
505
|
+
interface WriteHandler {
|
|
506
|
+
/** Handle a plain-HTTP write request. */
|
|
507
|
+
handleWrite(c: Context): Promise<Response>;
|
|
1346
508
|
}
|
|
1347
509
|
/**
|
|
1348
|
-
* Create
|
|
1349
|
-
*
|
|
1350
|
-
* Returns a handler that processes both the 402 pricing negotiation
|
|
1351
|
-
* (no X-PAYMENT header) and the paid publish flow (with X-PAYMENT header).
|
|
510
|
+
* Create a write handler.
|
|
1352
511
|
*
|
|
1353
512
|
* @param config - Handler configuration.
|
|
1354
|
-
* @returns
|
|
1355
|
-
*/
|
|
1356
|
-
declare function createX402Handler(config: X402HandlerConfig): X402Handler;
|
|
1357
|
-
|
|
1358
|
-
/**
|
|
1359
|
-
* x402 pricing calculator with multi-hop routing buffer.
|
|
1360
|
-
*
|
|
1361
|
-
* Computes the all-in USDC price for publishing a Nostr event via the
|
|
1362
|
-
* x402 HTTP on-ramp. The price includes a configurable routing buffer
|
|
1363
|
-
* (default 10%) to cover multi-hop overhead -- intermediate relays charge
|
|
1364
|
-
* their own per-byte fees.
|
|
1365
|
-
*
|
|
1366
|
-
* @module
|
|
1367
|
-
*/
|
|
1368
|
-
/**
|
|
1369
|
-
* Configuration for the x402 pricing calculator.
|
|
513
|
+
* @returns A WriteHandler with a handleWrite method.
|
|
1370
514
|
*/
|
|
1371
|
-
|
|
1372
|
-
/** Base price per byte in ILP/USDC micro-units (e.g., 10n). */
|
|
1373
|
-
basePricePerByte: bigint;
|
|
1374
|
-
/** Routing buffer percentage (default: 10, meaning 10%). */
|
|
1375
|
-
routingBufferPercent: number;
|
|
1376
|
-
}
|
|
1377
|
-
/**
|
|
1378
|
-
* Calculate the all-in x402 price for a TOON payload.
|
|
1379
|
-
*
|
|
1380
|
-
* Formula:
|
|
1381
|
-
* basePrice = basePricePerByte * toonLength
|
|
1382
|
-
* buffer = basePrice * routingBufferPercent / 100
|
|
1383
|
-
* total = basePrice + buffer
|
|
1384
|
-
*
|
|
1385
|
-
* The routing buffer covers multi-hop overhead. 10% default is a
|
|
1386
|
-
* conservative estimate per Party Mode Decision 8.
|
|
1387
|
-
*
|
|
1388
|
-
* @param config - Pricing configuration with base price and buffer percent.
|
|
1389
|
-
* @param toonLength - Length of the TOON-encoded payload in bytes.
|
|
1390
|
-
* @returns Total price in USDC micro-units.
|
|
1391
|
-
*/
|
|
1392
|
-
declare function calculateX402Price(config: X402PricingConfig, toonLength: number): bigint;
|
|
1393
|
-
|
|
1394
|
-
/**
|
|
1395
|
-
* @toon-protocol/relay
|
|
1396
|
-
*
|
|
1397
|
-
* ILP-gated Nostr relay with Business Logic Server.
|
|
1398
|
-
*/
|
|
1399
|
-
declare const VERSION = "0.1.0";
|
|
515
|
+
declare function createWriteHandler(config: WriteHandlerConfig): WriteHandler;
|
|
1400
516
|
|
|
1401
|
-
export {
|
|
517
|
+
export { ConnectionHandler, DEFAULT_RELAY_CONFIG, type EventStore, type HealthConfig, type HealthResponse, InMemoryEventStore, NostrRelayServer, type RelayConfig, RelayError, type RelayInstance, type RelayServerConfig, RelaySubscriber, type RelaySubscriberConfig, type RelaySubscription, type ResolvedRelayConfig, SqliteEventStore, type Subscription, ToonDecodeError, ToonEncodeError, VERSION, type WriteHandler, type WriteHandlerConfig, createHealthResponse, createWriteHandler, decodeEventFromToon, encodeEventToToon, matchFilter, startRelay };
|