@toon-protocol/relay 1.3.2 → 1.3.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/launcher/cli.ts"],"sourcesContent":["#!/usr/bin/env node\n\n/**\n * CLI entrypoint for @toon-protocol/relay.\n *\n * Thin wrapper around startRelay() that parses CLI flags and environment\n * variables, then delegates all logic to town.ts.\n *\n * Usage:\n * npx @toon-protocol/relay --mnemonic \"abandon abandon ...\" \\\n * --connector-url \"ws://apex.example:3001\" \\\n * --ilp-address \"g.townhouse.alice\"\n *\n * Environment variables override defaults; CLI flags override environment variables.\n */\n\nimport { parseArgs } from 'node:util';\nimport { startRelay } from './town.js';\nimport type { RelayConfig, RelayInstance } from './town.js';\n\n// ---------- CLI Parsing ----------\n\nfunction printHelp(): void {\n console.log(\n `\nUsage: toon-town [options]\n\nOptions:\n --mnemonic <words> BIP-39 mnemonic (12 or 24 words)\n --secret-key <hex> 32-byte secret key in hex\n --relay-port <port> WebSocket relay port (default: 7100)\n --bls-port <port> BLS HTTP port (default: 3100)\n --data-dir <path> Data directory (default: ./data)\n --connector-url <url> Parent connector BTP URL (e.g. ws://apex:3001).\n When set, the embedded connector peers with this\n URL and routes everything outside the local prefix\n through it. --ilp-address becomes REQUIRED and must\n fall under the parent's prefix.\n --parent-peer-id <id> BTP peer id to register the parent under (default: apex)\n --parent-auth-token <t> Auth token for the parent peer (default: empty / no-auth)\n --ilp-address <addr> ILP address for this node (default: g.toon.<pubkey>;\n REQUIRED when --connector-url is set, e.g. g.townhouse.<self>)\n --node-id <id> Stable nodeId for the embedded connector (default: toon-<pubkey>)\n --known-peers <json> Known peers as JSON array\n --dev-mode Enable dev mode (skip verification)\n --x402-enabled Enable x402 /publish endpoint (default: false)\n --discovery <mode> Discovery mode: 'seed-list' or 'genesis' (default: 'genesis')\n --seed-relays <urls> Comma-separated public Nostr relay URLs for seed discovery\n --publish-seed-entry Publish this node as a seed relay entry (default: false)\n --external-relay-url <url> External WebSocket URL of this relay\n --help Show this help message\n\nEnvironment Variables:\n TOON_MNEMONIC Same as --mnemonic\n TOON_SECRET_KEY Same as --secret-key\n TOON_RELAY_PORT Same as --relay-port\n TOON_BLS_PORT Same as --bls-port\n TOON_DATA_DIR Same as --data-dir\n TOON_CONNECTOR_URL Same as --connector-url (parent BTP URL)\n TOON_PARENT_PEER_ID Same as --parent-peer-id\n TOON_PARENT_AUTH_TOKEN Same as --parent-auth-token\n TOON_ILP_ADDRESS Same as --ilp-address (required with TOON_CONNECTOR_URL)\n TOON_NODE_ID Same as --node-id\n TOON_KNOWN_PEERS Same as --known-peers\n TOON_DEV_MODE Same as --dev-mode (set to \"true\")\n TOON_X402_ENABLED Same as --x402-enabled (set to \"true\")\n TOON_DISCOVERY Same as --discovery\n TOON_SEED_RELAYS Same as --seed-relays\n TOON_PUBLISH_SEED_ENTRY Same as --publish-seed-entry (set to \"true\")\n TOON_EXTERNAL_RELAY_URL Same as --external-relay-url\n TOON_FEE_PER_EVENT Fee per event in ILP units (overrides basePricePerByte)\n TOON_SETTLEMENT_PRIVATE_KEY EVM private key (0x-prefixed 32-byte hex) for the\n embedded connector's ClaimReceiver / chainProviders.\n Defaults to the identity-derived secp256k1 hex.\n TOON_PARENT_EVM_ADDRESS EVM treasury address advertised to the parent\n connector as the peer entry's evmAddress (used by the\n apex's PerPacketClaimService when opening a settlement\n channel toward this child).\n\nSecurity:\n Prefer TOON_MNEMONIC or TOON_SECRET_KEY environment variables\n over --mnemonic / --secret-key CLI flags. CLI arguments are visible to\n other users on the system via process listings (e.g. ps aux). See CWE-214.\n`.trim()\n );\n}\n\nfunction parseCli(): RelayConfig {\n const { values } = parseArgs({\n options: {\n mnemonic: { type: 'string' },\n 'secret-key': { type: 'string' },\n 'relay-port': { type: 'string' },\n 'bls-port': { type: 'string' },\n 'data-dir': { type: 'string' },\n 'connector-url': { type: 'string' },\n 'parent-peer-id': { type: 'string' },\n 'parent-auth-token': { type: 'string' },\n 'ilp-address': { type: 'string' },\n 'node-id': { type: 'string' },\n 'known-peers': { type: 'string' },\n 'dev-mode': { type: 'boolean' },\n 'x402-enabled': { type: 'boolean' },\n discovery: { type: 'string' },\n 'seed-relays': { type: 'string' },\n 'publish-seed-entry': { type: 'boolean' },\n 'external-relay-url': { type: 'string' },\n help: { type: 'boolean' },\n },\n strict: true,\n allowPositionals: false,\n });\n\n if (values.help) {\n printHelp();\n process.exit(0);\n }\n\n // Resolve: CLI flags override env vars\n\n // Warn about process-listing exposure (CWE-214) when secrets are passed via CLI flags\n if (values.mnemonic) {\n console.warn(\n 'Warning: --mnemonic is visible in process listings. ' +\n 'Prefer TOON_MNEMONIC environment variable for production use.'\n );\n }\n if (values['secret-key']) {\n console.warn(\n 'Warning: --secret-key is visible in process listings. ' +\n 'Prefer TOON_SECRET_KEY environment variable for production use.'\n );\n }\n\n const mnemonic = values.mnemonic ?? process.env['TOON_MNEMONIC'] ?? undefined;\n\n const secretKeyHex =\n values['secret-key'] ?? process.env['TOON_SECRET_KEY'] ?? undefined;\n\n let secretKey: Uint8Array | undefined;\n if (secretKeyHex) {\n if (secretKeyHex.length !== 64 || !/^[0-9a-fA-F]{64}$/.test(secretKeyHex)) {\n console.error('Error: --secret-key must be a 64-character hex string');\n process.exit(1);\n }\n secretKey = Uint8Array.from(Buffer.from(secretKeyHex, 'hex'));\n }\n\n const connectorUrl =\n values['connector-url'] ?? process.env['TOON_CONNECTOR_URL'] ?? undefined;\n\n const parentPeerId =\n values['parent-peer-id'] ?? process.env['TOON_PARENT_PEER_ID'] ?? undefined;\n\n const parentAuthToken =\n values['parent-auth-token'] ??\n process.env['TOON_PARENT_AUTH_TOKEN'] ??\n undefined;\n\n const ilpAddress =\n values['ilp-address'] ?? process.env['TOON_ILP_ADDRESS'] ?? undefined;\n\n const nodeId = values['node-id'] ?? process.env['TOON_NODE_ID'] ?? undefined;\n\n if (connectorUrl && !ilpAddress) {\n console.error(\n 'Error: --ilp-address (or TOON_ILP_ADDRESS) is required when ' +\n '--connector-url is set; it must fall under the parent connector prefix ' +\n '(e.g. g.townhouse.<self>)'\n );\n process.exit(1);\n }\n\n const relayPortStr =\n values['relay-port'] ?? process.env['TOON_RELAY_PORT'] ?? undefined;\n const relayPort = relayPortStr ? parseInt(relayPortStr, 10) : undefined;\n if (\n relayPort !== undefined &&\n (Number.isNaN(relayPort) || relayPort <= 0 || relayPort > 65535)\n ) {\n console.error('Error: --relay-port must be an integer between 1 and 65535');\n process.exit(1);\n }\n\n const blsPortStr =\n values['bls-port'] ?? process.env['TOON_BLS_PORT'] ?? undefined;\n const blsPort = blsPortStr ? parseInt(blsPortStr, 10) : undefined;\n if (\n blsPort !== undefined &&\n (Number.isNaN(blsPort) || blsPort <= 0 || blsPort > 65535)\n ) {\n console.error('Error: --bls-port must be an integer between 1 and 65535');\n process.exit(1);\n }\n\n const dataDir =\n values['data-dir'] ?? process.env['TOON_DATA_DIR'] ?? undefined;\n\n const devMode =\n values['dev-mode'] ??\n (process.env['TOON_DEV_MODE'] === 'true' ? true : undefined);\n\n const x402Enabled =\n values['x402-enabled'] ??\n (process.env['TOON_X402_ENABLED'] === 'true' ? true : undefined);\n\n const knownPeersJson =\n values['known-peers'] ?? process.env['TOON_KNOWN_PEERS'] ?? undefined;\n\n let knownPeers:\n | { pubkey: string; relayUrl: string; btpEndpoint: string }[]\n | undefined;\n if (knownPeersJson) {\n try {\n const parsed: unknown = JSON.parse(knownPeersJson);\n if (Array.isArray(parsed)) {\n knownPeers = (parsed as unknown[])\n .filter(\n (p): p is Record<string, unknown> =>\n typeof p === 'object' &&\n p !== null &&\n typeof (p as Record<string, unknown>)['pubkey'] === 'string' &&\n typeof (p as Record<string, unknown>)['btpEndpoint'] === 'string'\n )\n .map((p) => ({\n pubkey: p['pubkey'] as string,\n relayUrl: (p['relayUrl'] as string) || 'ws://localhost:7100',\n btpEndpoint: p['btpEndpoint'] as string,\n }));\n }\n } catch {\n console.error('Error: --known-peers must be valid JSON');\n process.exit(1);\n }\n }\n\n if (!mnemonic && !secretKey) {\n console.error(\n 'Error: one of --mnemonic (or TOON_MNEMONIC) or --secret-key (or TOON_SECRET_KEY) is required'\n );\n process.exit(1);\n }\n\n // Discovery mode\n const discoveryStr =\n values.discovery ?? process.env['TOON_DISCOVERY'] ?? undefined;\n let discoveryMode: 'seed-list' | 'genesis' | undefined;\n if (discoveryStr) {\n if (discoveryStr !== 'seed-list' && discoveryStr !== 'genesis') {\n console.error('Error: --discovery must be \"seed-list\" or \"genesis\"');\n process.exit(1);\n }\n discoveryMode = discoveryStr;\n }\n\n // Seed relays (comma-separated list of public Nostr relay URLs)\n const seedRelaysStr =\n values['seed-relays'] ?? process.env['TOON_SEED_RELAYS'] ?? undefined;\n const seedRelaysArr = seedRelaysStr\n ? seedRelaysStr\n .split(',')\n .map((s) => s.trim())\n .filter(Boolean)\n : undefined;\n\n // Validate seed relay URLs have WebSocket scheme (CWE-20)\n if (seedRelaysArr) {\n for (const url of seedRelaysArr) {\n // nosemgrep: javascript.lang.security.detect-insecure-websocket.detect-insecure-websocket -- validation check, not a connection\n if (!url.startsWith('ws://') && !url.startsWith('wss://')) {\n console.error(\n 'Error: --seed-relays contains invalid URL -- must use WebSocket scheme (ws or wss)'\n );\n process.exit(1);\n }\n }\n }\n\n // Publish seed entry flag\n const publishSeedEntry =\n values['publish-seed-entry'] ??\n (process.env['TOON_PUBLISH_SEED_ENTRY'] === 'true' ? true : undefined);\n\n // External relay URL\n const externalRelayUrl =\n values['external-relay-url'] ??\n process.env['TOON_EXTERNAL_RELAY_URL'] ??\n undefined;\n\n // Fee per event (overrides basePricePerByte)\n const feePerEventStr = process.env['TOON_FEE_PER_EVENT'] ?? undefined;\n const feePerEvent = feePerEventStr ? parseInt(feePerEventStr, 10) : undefined;\n if (\n feePerEvent !== undefined &&\n (Number.isNaN(feePerEvent) || feePerEvent < 0)\n ) {\n console.error('Error: TOON_FEE_PER_EVENT must be a non-negative integer');\n process.exit(1);\n }\n\n // Public BTP endpoint advertised in this town's kind:10032 (so clients learn\n // how to reach the apex to route packets to g.townhouse.town). Set by the\n // Townhouse orchestrator from the apex's .anyone / direct URL.\n const btpEndpoint = process.env['TOON_BTP_ENDPOINT'] ?? undefined;\n\n // Settlement asset advertised in kind:10032.\n const assetCode = process.env['TOON_ASSET_CODE'] ?? undefined;\n const assetScaleStr = process.env['TOON_ASSET_SCALE'] ?? undefined;\n const assetScale = assetScaleStr ? parseInt(assetScaleStr, 10) : undefined;\n if (\n assetScale !== undefined &&\n (Number.isNaN(assetScale) || assetScale < 0 || assetScale > 18)\n ) {\n console.error('Error: TOON_ASSET_SCALE must be an integer in 0..18');\n process.exit(1);\n }\n\n // Settlement private key — controls the embedded connector's ClaimReceiver\n // signer. CLI flag intentionally omitted (process listings would expose the\n // key via `ps`, CWE-214). Env-only.\n const settlementPrivateKey =\n process.env['TOON_SETTLEMENT_PRIVATE_KEY'] ?? undefined;\n if (\n settlementPrivateKey !== undefined &&\n !/^0x[0-9a-fA-F]{64}$/.test(settlementPrivateKey)\n ) {\n console.error(\n 'Error: TOON_SETTLEMENT_PRIVATE_KEY must be a 0x-prefixed 32-byte hex string'\n );\n process.exit(1);\n }\n\n // Parent EVM address advertised to the apex peer. Public address — safe to\n // ship via env. Validated as 0x + 40 hex chars (ERC-55 mixed-case allowed).\n const parentEvmAddress = process.env['TOON_PARENT_EVM_ADDRESS'] ?? undefined;\n if (\n parentEvmAddress !== undefined &&\n !/^0x[0-9a-fA-F]{40}$/.test(parentEvmAddress)\n ) {\n console.error(\n 'Error: TOON_PARENT_EVM_ADDRESS must be a 0x-prefixed 20-byte hex address'\n );\n process.exit(1);\n }\n\n // Multi-chain settlement advertisement (additive; opt-in via SUPPORTED_CHAINS).\n //\n // The default single-EVM-chain path (TOON_CHAIN/TOON_RPC_URL) is unchanged.\n // When SUPPORTED_CHAINS is set, parse per-chain env vars so the node can\n // advertise additional chains (notably `solana:devnet`) in kind:10032 with a\n // chain-native settlement recipient. Env key convention mirrors the SDK\n // entrypoint (docker/src/shared.ts): \"solana:devnet\" -> \"SOLANA_DEVNET\".\n // SETTLEMENT_ADDRESS_<KEY> recipient address advertised for the chain\n // CHAIN_RPC_URL_<KEY> RPC URL for the chain\n // TOKEN_NETWORK_<KEY> payment-channel program / token-network address\n // PREFERRED_TOKEN_<KEY> preferred token (e.g. USDC mint)\n let chainRpcUrls: Record<string, string> | undefined;\n let tokenNetworks: Record<string, string> | undefined;\n let preferredTokens: Record<string, string> | undefined;\n let settlementAddresses: Record<string, string> | undefined;\n const supportedChainsStr = process.env['SUPPORTED_CHAINS'];\n if (supportedChainsStr) {\n const chains = supportedChainsStr\n .split(',')\n .map((s) => s.trim())\n .filter(Boolean);\n for (const chain of chains) {\n const key = chain.replace(/:/g, '_').toUpperCase();\n const addr = process.env[`SETTLEMENT_ADDRESS_${key}`];\n if (addr) (settlementAddresses ??= {})[chain] = addr;\n const rpc = process.env[`CHAIN_RPC_URL_${key}`];\n if (rpc) (chainRpcUrls ??= {})[chain] = rpc;\n const tokenNet = process.env[`TOKEN_NETWORK_${key}`];\n if (tokenNet) (tokenNetworks ??= {})[chain] = tokenNet;\n const token = process.env[`PREFERRED_TOKEN_${key}`];\n if (token) (preferredTokens ??= {})[chain] = token;\n if (!addr) {\n console.warn(\n `[Town] Warning: chain \"${chain}\" listed in SUPPORTED_CHAINS but no SETTLEMENT_ADDRESS_${key} env var found`\n );\n }\n }\n }\n\n const config: RelayConfig = {\n ...(connectorUrl && { connectorUrl }),\n ...(chainRpcUrls && { chainRpcUrls }),\n ...(tokenNetworks && { tokenNetworks }),\n ...(preferredTokens && { preferredTokens }),\n ...(settlementAddresses && { settlementAddresses }),\n ...(parentPeerId && { parentPeerId }),\n ...(parentAuthToken !== undefined && { parentAuthToken }),\n ...(ilpAddress && { ilpAddress }),\n ...(nodeId && { nodeId }),\n ...(mnemonic && { mnemonic }),\n ...(secretKey && { secretKey }),\n ...(relayPort !== undefined && { relayPort }),\n ...(blsPort !== undefined && { blsPort }),\n ...(dataDir && { dataDir }),\n ...(knownPeers && { knownPeers }),\n ...(devMode !== undefined && { devMode }),\n ...(x402Enabled !== undefined && { x402Enabled }),\n ...(discoveryMode && { discovery: discoveryMode }),\n ...(seedRelaysArr && { seedRelays: seedRelaysArr }),\n ...(publishSeedEntry !== undefined && { publishSeedEntry }),\n ...(externalRelayUrl && { externalRelayUrl }),\n ...(feePerEvent !== undefined && { feePerEvent }),\n ...(btpEndpoint && { btpEndpoint }),\n ...(assetCode && { assetCode }),\n ...(assetScale !== undefined && { assetScale }),\n ...(settlementPrivateKey && { settlementPrivateKey }),\n ...(parentEvmAddress && { parentEvmAddress }),\n };\n\n return config;\n}\n\n// ---------- Main ----------\n\nasync function main(): Promise<void> {\n const config = parseCli();\n\n console.log('\\n' + '='.repeat(50));\n console.log('TOON Town Starting');\n console.log('='.repeat(50) + '\\n');\n\n const instance: RelayInstance = await startRelay(config);\n\n console.log('\\n' + '='.repeat(50));\n console.log('TOON Town Ready');\n console.log('='.repeat(50));\n console.log(` Pubkey: ${instance.pubkey}`);\n console.log(` EVM Address: ${instance.evmAddress}`);\n console.log(` Relay: ws://localhost:${instance.config.relayPort}`);\n console.log(` BLS: http://localhost:${instance.config.blsPort}`);\n console.log(` ILP Address: ${instance.config.ilpAddress}`);\n if (instance.config.connectorUrl) {\n console.log(` Parent BTP: ${instance.config.connectorUrl}`);\n console.log(` Parent Peer: ${instance.config.parentPeerId}`);\n }\n console.log(` Peers: ${instance.bootstrapResult.peerCount}`);\n console.log(` Channels: ${instance.bootstrapResult.channelCount}`);\n console.log('='.repeat(50) + '\\n');\n\n // Wire graceful shutdown\n const shutdown = async (signal: string): Promise<void> => {\n console.log(`\\n[Shutdown] Received ${signal}`);\n await instance.stop();\n console.log('[Shutdown] Complete');\n process.exit(0);\n };\n\n process.on('SIGINT', () => {\n shutdown('SIGINT').catch(console.error);\n });\n process.on('SIGTERM', () => {\n shutdown('SIGTERM').catch(console.error);\n });\n}\n\nmain().catch((error: unknown) => {\n console.error('[Fatal] Startup error:', error);\n process.exit(1);\n});\n"],"mappings":";;;;;;AAgBA,SAAS,iBAAiB;AAM1B,SAAS,YAAkB;AACzB,UAAQ;AAAA,IACN;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA2DF,KAAK;AAAA,EACL;AACF;AAEA,SAAS,WAAwB;AAC/B,QAAM,EAAE,OAAO,IAAI,UAAU;AAAA,IAC3B,SAAS;AAAA,MACP,UAAU,EAAE,MAAM,SAAS;AAAA,MAC3B,cAAc,EAAE,MAAM,SAAS;AAAA,MAC/B,cAAc,EAAE,MAAM,SAAS;AAAA,MAC/B,YAAY,EAAE,MAAM,SAAS;AAAA,MAC7B,YAAY,EAAE,MAAM,SAAS;AAAA,MAC7B,iBAAiB,EAAE,MAAM,SAAS;AAAA,MAClC,kBAAkB,EAAE,MAAM,SAAS;AAAA,MACnC,qBAAqB,EAAE,MAAM,SAAS;AAAA,MACtC,eAAe,EAAE,MAAM,SAAS;AAAA,MAChC,WAAW,EAAE,MAAM,SAAS;AAAA,MAC5B,eAAe,EAAE,MAAM,SAAS;AAAA,MAChC,YAAY,EAAE,MAAM,UAAU;AAAA,MAC9B,gBAAgB,EAAE,MAAM,UAAU;AAAA,MAClC,WAAW,EAAE,MAAM,SAAS;AAAA,MAC5B,eAAe,EAAE,MAAM,SAAS;AAAA,MAChC,sBAAsB,EAAE,MAAM,UAAU;AAAA,MACxC,sBAAsB,EAAE,MAAM,SAAS;AAAA,MACvC,MAAM,EAAE,MAAM,UAAU;AAAA,IAC1B;AAAA,IACA,QAAQ;AAAA,IACR,kBAAkB;AAAA,EACpB,CAAC;AAED,MAAI,OAAO,MAAM;AACf,cAAU;AACV,YAAQ,KAAK,CAAC;AAAA,EAChB;AAKA,MAAI,OAAO,UAAU;AACnB,YAAQ;AAAA,MACN;AAAA,IAEF;AAAA,EACF;AACA,MAAI,OAAO,YAAY,GAAG;AACxB,YAAQ;AAAA,MACN;AAAA,IAEF;AAAA,EACF;AAEA,QAAM,WAAW,OAAO,YAAY,QAAQ,IAAI,eAAe,KAAK;AAEpE,QAAM,eACJ,OAAO,YAAY,KAAK,QAAQ,IAAI,iBAAiB,KAAK;AAE5D,MAAI;AACJ,MAAI,cAAc;AAChB,QAAI,aAAa,WAAW,MAAM,CAAC,oBAAoB,KAAK,YAAY,GAAG;AACzE,cAAQ,MAAM,uDAAuD;AACrE,cAAQ,KAAK,CAAC;AAAA,IAChB;AACA,gBAAY,WAAW,KAAK,OAAO,KAAK,cAAc,KAAK,CAAC;AAAA,EAC9D;AAEA,QAAM,eACJ,OAAO,eAAe,KAAK,QAAQ,IAAI,oBAAoB,KAAK;AAElE,QAAM,eACJ,OAAO,gBAAgB,KAAK,QAAQ,IAAI,qBAAqB,KAAK;AAEpE,QAAM,kBACJ,OAAO,mBAAmB,KAC1B,QAAQ,IAAI,wBAAwB,KACpC;AAEF,QAAM,aACJ,OAAO,aAAa,KAAK,QAAQ,IAAI,kBAAkB,KAAK;AAE9D,QAAM,SAAS,OAAO,SAAS,KAAK,QAAQ,IAAI,cAAc,KAAK;AAEnE,MAAI,gBAAgB,CAAC,YAAY;AAC/B,YAAQ;AAAA,MACN;AAAA,IAGF;AACA,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,eACJ,OAAO,YAAY,KAAK,QAAQ,IAAI,iBAAiB,KAAK;AAC5D,QAAM,YAAY,eAAe,SAAS,cAAc,EAAE,IAAI;AAC9D,MACE,cAAc,WACb,OAAO,MAAM,SAAS,KAAK,aAAa,KAAK,YAAY,QAC1D;AACA,YAAQ,MAAM,4DAA4D;AAC1E,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,aACJ,OAAO,UAAU,KAAK,QAAQ,IAAI,eAAe,KAAK;AACxD,QAAM,UAAU,aAAa,SAAS,YAAY,EAAE,IAAI;AACxD,MACE,YAAY,WACX,OAAO,MAAM,OAAO,KAAK,WAAW,KAAK,UAAU,QACpD;AACA,YAAQ,MAAM,0DAA0D;AACxE,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,UACJ,OAAO,UAAU,KAAK,QAAQ,IAAI,eAAe,KAAK;AAExD,QAAM,UACJ,OAAO,UAAU,MAChB,QAAQ,IAAI,eAAe,MAAM,SAAS,OAAO;AAEpD,QAAM,cACJ,OAAO,cAAc,MACpB,QAAQ,IAAI,mBAAmB,MAAM,SAAS,OAAO;AAExD,QAAM,iBACJ,OAAO,aAAa,KAAK,QAAQ,IAAI,kBAAkB,KAAK;AAE9D,MAAI;AAGJ,MAAI,gBAAgB;AAClB,QAAI;AACF,YAAM,SAAkB,KAAK,MAAM,cAAc;AACjD,UAAI,MAAM,QAAQ,MAAM,GAAG;AACzB,qBAAc,OACX;AAAA,UACC,CAAC,MACC,OAAO,MAAM,YACb,MAAM,QACN,OAAQ,EAA8B,QAAQ,MAAM,YACpD,OAAQ,EAA8B,aAAa,MAAM;AAAA,QAC7D,EACC,IAAI,CAAC,OAAO;AAAA,UACX,QAAQ,EAAE,QAAQ;AAAA,UAClB,UAAW,EAAE,UAAU,KAAgB;AAAA,UACvC,aAAa,EAAE,aAAa;AAAA,QAC9B,EAAE;AAAA,MACN;AAAA,IACF,QAAQ;AACN,cAAQ,MAAM,yCAAyC;AACvD,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF;AAEA,MAAI,CAAC,YAAY,CAAC,WAAW;AAC3B,YAAQ;AAAA,MACN;AAAA,IACF;AACA,YAAQ,KAAK,CAAC;AAAA,EAChB;AAGA,QAAM,eACJ,OAAO,aAAa,QAAQ,IAAI,gBAAgB,KAAK;AACvD,MAAI;AACJ,MAAI,cAAc;AAChB,QAAI,iBAAiB,eAAe,iBAAiB,WAAW;AAC9D,cAAQ,MAAM,qDAAqD;AACnE,cAAQ,KAAK,CAAC;AAAA,IAChB;AACA,oBAAgB;AAAA,EAClB;AAGA,QAAM,gBACJ,OAAO,aAAa,KAAK,QAAQ,IAAI,kBAAkB,KAAK;AAC9D,QAAM,gBAAgB,gBAClB,cACG,MAAM,GAAG,EACT,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EACnB,OAAO,OAAO,IACjB;AAGJ,MAAI,eAAe;AACjB,eAAW,OAAO,eAAe;AAE/B,UAAI,CAAC,IAAI,WAAW,OAAO,KAAK,CAAC,IAAI,WAAW,QAAQ,GAAG;AACzD,gBAAQ;AAAA,UACN;AAAA,QACF;AACA,gBAAQ,KAAK,CAAC;AAAA,MAChB;AAAA,IACF;AAAA,EACF;AAGA,QAAM,mBACJ,OAAO,oBAAoB,MAC1B,QAAQ,IAAI,yBAAyB,MAAM,SAAS,OAAO;AAG9D,QAAM,mBACJ,OAAO,oBAAoB,KAC3B,QAAQ,IAAI,yBAAyB,KACrC;AAGF,QAAM,iBAAiB,QAAQ,IAAI,oBAAoB,KAAK;AAC5D,QAAM,cAAc,iBAAiB,SAAS,gBAAgB,EAAE,IAAI;AACpE,MACE,gBAAgB,WACf,OAAO,MAAM,WAAW,KAAK,cAAc,IAC5C;AACA,YAAQ,MAAM,0DAA0D;AACxE,YAAQ,KAAK,CAAC;AAAA,EAChB;AAKA,QAAM,cAAc,QAAQ,IAAI,mBAAmB,KAAK;AAGxD,QAAM,YAAY,QAAQ,IAAI,iBAAiB,KAAK;AACpD,QAAM,gBAAgB,QAAQ,IAAI,kBAAkB,KAAK;AACzD,QAAM,aAAa,gBAAgB,SAAS,eAAe,EAAE,IAAI;AACjE,MACE,eAAe,WACd,OAAO,MAAM,UAAU,KAAK,aAAa,KAAK,aAAa,KAC5D;AACA,YAAQ,MAAM,qDAAqD;AACnE,YAAQ,KAAK,CAAC;AAAA,EAChB;AAKA,QAAM,uBACJ,QAAQ,IAAI,6BAA6B,KAAK;AAChD,MACE,yBAAyB,UACzB,CAAC,sBAAsB,KAAK,oBAAoB,GAChD;AACA,YAAQ;AAAA,MACN;AAAA,IACF;AACA,YAAQ,KAAK,CAAC;AAAA,EAChB;AAIA,QAAM,mBAAmB,QAAQ,IAAI,yBAAyB,KAAK;AACnE,MACE,qBAAqB,UACrB,CAAC,sBAAsB,KAAK,gBAAgB,GAC5C;AACA,YAAQ;AAAA,MACN;AAAA,IACF;AACA,YAAQ,KAAK,CAAC;AAAA,EAChB;AAaA,MAAI;AACJ,MAAI;AACJ,MAAI;AACJ,MAAI;AACJ,QAAM,qBAAqB,QAAQ,IAAI,kBAAkB;AACzD,MAAI,oBAAoB;AACtB,UAAM,SAAS,mBACZ,MAAM,GAAG,EACT,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EACnB,OAAO,OAAO;AACjB,eAAW,SAAS,QAAQ;AAC1B,YAAM,MAAM,MAAM,QAAQ,MAAM,GAAG,EAAE,YAAY;AACjD,YAAM,OAAO,QAAQ,IAAI,sBAAsB,GAAG,EAAE;AACpD,UAAI,KAAM,EAAC,wBAAwB,CAAC,GAAG,KAAK,IAAI;AAChD,YAAM,MAAM,QAAQ,IAAI,iBAAiB,GAAG,EAAE;AAC9C,UAAI,IAAK,EAAC,iBAAiB,CAAC,GAAG,KAAK,IAAI;AACxC,YAAM,WAAW,QAAQ,IAAI,iBAAiB,GAAG,EAAE;AACnD,UAAI,SAAU,EAAC,kBAAkB,CAAC,GAAG,KAAK,IAAI;AAC9C,YAAM,QAAQ,QAAQ,IAAI,mBAAmB,GAAG,EAAE;AAClD,UAAI,MAAO,EAAC,oBAAoB,CAAC,GAAG,KAAK,IAAI;AAC7C,UAAI,CAAC,MAAM;AACT,gBAAQ;AAAA,UACN,0BAA0B,KAAK,0DAA0D,GAAG;AAAA,QAC9F;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,QAAM,SAAsB;AAAA,IAC1B,GAAI,gBAAgB,EAAE,aAAa;AAAA,IACnC,GAAI,gBAAgB,EAAE,aAAa;AAAA,IACnC,GAAI,iBAAiB,EAAE,cAAc;AAAA,IACrC,GAAI,mBAAmB,EAAE,gBAAgB;AAAA,IACzC,GAAI,uBAAuB,EAAE,oBAAoB;AAAA,IACjD,GAAI,gBAAgB,EAAE,aAAa;AAAA,IACnC,GAAI,oBAAoB,UAAa,EAAE,gBAAgB;AAAA,IACvD,GAAI,cAAc,EAAE,WAAW;AAAA,IAC/B,GAAI,UAAU,EAAE,OAAO;AAAA,IACvB,GAAI,YAAY,EAAE,SAAS;AAAA,IAC3B,GAAI,aAAa,EAAE,UAAU;AAAA,IAC7B,GAAI,cAAc,UAAa,EAAE,UAAU;AAAA,IAC3C,GAAI,YAAY,UAAa,EAAE,QAAQ;AAAA,IACvC,GAAI,WAAW,EAAE,QAAQ;AAAA,IACzB,GAAI,cAAc,EAAE,WAAW;AAAA,IAC/B,GAAI,YAAY,UAAa,EAAE,QAAQ;AAAA,IACvC,GAAI,gBAAgB,UAAa,EAAE,YAAY;AAAA,IAC/C,GAAI,iBAAiB,EAAE,WAAW,cAAc;AAAA,IAChD,GAAI,iBAAiB,EAAE,YAAY,cAAc;AAAA,IACjD,GAAI,qBAAqB,UAAa,EAAE,iBAAiB;AAAA,IACzD,GAAI,oBAAoB,EAAE,iBAAiB;AAAA,IAC3C,GAAI,gBAAgB,UAAa,EAAE,YAAY;AAAA,IAC/C,GAAI,eAAe,EAAE,YAAY;AAAA,IACjC,GAAI,aAAa,EAAE,UAAU;AAAA,IAC7B,GAAI,eAAe,UAAa,EAAE,WAAW;AAAA,IAC7C,GAAI,wBAAwB,EAAE,qBAAqB;AAAA,IACnD,GAAI,oBAAoB,EAAE,iBAAiB;AAAA,EAC7C;AAEA,SAAO;AACT;AAIA,eAAe,OAAsB;AACnC,QAAM,SAAS,SAAS;AAExB,UAAQ,IAAI,OAAO,IAAI,OAAO,EAAE,CAAC;AACjC,UAAQ,IAAI,oBAAoB;AAChC,UAAQ,IAAI,IAAI,OAAO,EAAE,IAAI,IAAI;AAEjC,QAAM,WAA0B,MAAM,WAAW,MAAM;AAEvD,UAAQ,IAAI,OAAO,IAAI,OAAO,EAAE,CAAC;AACjC,UAAQ,IAAI,iBAAiB;AAC7B,UAAQ,IAAI,IAAI,OAAO,EAAE,CAAC;AAC1B,UAAQ,IAAI,kBAAkB,SAAS,MAAM,EAAE;AAC/C,UAAQ,IAAI,kBAAkB,SAAS,UAAU,EAAE;AACnD,UAAQ,IAAI,iCAAiC,SAAS,OAAO,SAAS,EAAE;AACxE,UAAQ,IAAI,mCAAmC,SAAS,OAAO,OAAO,EAAE;AACxE,UAAQ,IAAI,kBAAkB,SAAS,OAAO,UAAU,EAAE;AAC1D,MAAI,SAAS,OAAO,cAAc;AAChC,YAAQ,IAAI,kBAAkB,SAAS,OAAO,YAAY,EAAE;AAC5D,YAAQ,IAAI,kBAAkB,SAAS,OAAO,YAAY,EAAE;AAAA,EAC9D;AACA,UAAQ,IAAI,kBAAkB,SAAS,gBAAgB,SAAS,EAAE;AAClE,UAAQ,IAAI,kBAAkB,SAAS,gBAAgB,YAAY,EAAE;AACrE,UAAQ,IAAI,IAAI,OAAO,EAAE,IAAI,IAAI;AAGjC,QAAM,WAAW,OAAO,WAAkC;AACxD,YAAQ,IAAI;AAAA,sBAAyB,MAAM,EAAE;AAC7C,UAAM,SAAS,KAAK;AACpB,YAAQ,IAAI,qBAAqB;AACjC,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,UAAQ,GAAG,UAAU,MAAM;AACzB,aAAS,QAAQ,EAAE,MAAM,QAAQ,KAAK;AAAA,EACxC,CAAC;AACD,UAAQ,GAAG,WAAW,MAAM;AAC1B,aAAS,SAAS,EAAE,MAAM,QAAQ,KAAK;AAAA,EACzC,CAAC;AACH;AAEA,KAAK,EAAE,MAAM,CAAC,UAAmB;AAC/B,UAAQ,MAAM,0BAA0B,KAAK;AAC7C,UAAQ,KAAK,CAAC;AAChB,CAAC;","names":[]}
1
+ {"version":3,"sources":["../src/launcher/cli.ts"],"sourcesContent":["#!/usr/bin/env node\n\n/**\n * CLI entrypoint for @toon-protocol/relay.\n *\n * Thin wrapper around startRelay() that parses CLI flags and environment\n * variables, then delegates all logic to town.ts.\n *\n * Usage:\n * npx @toon-protocol/relay --mnemonic \"abandon abandon ...\" \\\n * --connector-url \"ws://apex.example:3001\" \\\n * --ilp-address \"g.townhouse.alice\"\n *\n * Environment variables override defaults; CLI flags override environment variables.\n */\n\nimport { parseArgs } from 'node:util';\nimport { startRelay } from './town.js';\nimport type { RelayConfig, RelayInstance } from './town.js';\n\n// ---------- CLI Parsing ----------\n\nfunction printHelp(): void {\n console.log(\n `\nUsage: toon-town [options]\n\nOptions:\n --mnemonic <words> BIP-39 mnemonic (12 or 24 words)\n --secret-key <hex> 32-byte secret key in hex\n --relay-port <port> WebSocket relay port (default: 7100)\n --bls-port <port> BLS HTTP port (default: 3100)\n --data-dir <path> Data directory (default: ./data)\n --connector-url <url> Parent connector BTP URL (e.g. ws://apex:3001).\n When set, the embedded connector peers with this\n URL and routes everything outside the local prefix\n through it. --ilp-address becomes REQUIRED and must\n fall under the parent's prefix.\n --parent-peer-id <id> BTP peer id to register the parent under (default: apex)\n --parent-auth-token <t> Auth token for the parent peer (default: empty / no-auth)\n --ilp-address <addr> ILP address for this node (default: g.toon.<pubkey>;\n REQUIRED when --connector-url is set, e.g. g.townhouse.<self>)\n --node-id <id> Stable nodeId for the embedded connector (default: toon-<pubkey>)\n --known-peers <json> Known peers as JSON array\n --dev-mode Enable dev mode (skip verification)\n --x402-enabled Enable x402 /publish endpoint (default: false)\n --oblivious-mode Run as a payment-oblivious relay (default: false).\n No embedded connector is created and no x402/ILP\n settlement code runs; exposes POST /write (event-as-\n JSON) trusting injected X-TOON-* headers. Free NIP-01\n WS reads are unchanged. Mutually exclusive with\n --connector-url.\n --discovery <mode> Discovery mode: 'seed-list' or 'genesis' (default: 'genesis')\n --seed-relays <urls> Comma-separated public Nostr relay URLs for seed discovery\n --publish-seed-entry Publish this node as a seed relay entry (default: false)\n --external-relay-url <url> External WebSocket URL of this relay\n --help Show this help message\n\nEnvironment Variables:\n TOON_MNEMONIC Same as --mnemonic\n TOON_SECRET_KEY Same as --secret-key\n TOON_RELAY_PORT Same as --relay-port\n TOON_BLS_PORT Same as --bls-port\n TOON_DATA_DIR Same as --data-dir\n TOON_CONNECTOR_URL Same as --connector-url (parent BTP URL)\n TOON_PARENT_PEER_ID Same as --parent-peer-id\n TOON_PARENT_AUTH_TOKEN Same as --parent-auth-token\n TOON_ILP_ADDRESS Same as --ilp-address (required with TOON_CONNECTOR_URL)\n TOON_NODE_ID Same as --node-id\n TOON_KNOWN_PEERS Same as --known-peers\n TOON_DEV_MODE Same as --dev-mode (set to \"true\")\n TOON_X402_ENABLED Same as --x402-enabled (set to \"true\")\n TOON_OBLIVIOUS_MODE Same as --oblivious-mode (set to \"true\")\n TOON_DISCOVERY Same as --discovery\n TOON_SEED_RELAYS Same as --seed-relays\n TOON_PUBLISH_SEED_ENTRY Same as --publish-seed-entry (set to \"true\")\n TOON_EXTERNAL_RELAY_URL Same as --external-relay-url\n TOON_FEE_PER_EVENT Fee per event in ILP units (overrides basePricePerByte)\n TOON_SETTLEMENT_PRIVATE_KEY EVM private key (0x-prefixed 32-byte hex) for the\n embedded connector's ClaimReceiver / chainProviders.\n Defaults to the identity-derived secp256k1 hex.\n TOON_PARENT_EVM_ADDRESS EVM treasury address advertised to the parent\n connector as the peer entry's evmAddress (used by the\n apex's PerPacketClaimService when opening a settlement\n channel toward this child).\n\nSecurity:\n Prefer TOON_MNEMONIC or TOON_SECRET_KEY environment variables\n over --mnemonic / --secret-key CLI flags. CLI arguments are visible to\n other users on the system via process listings (e.g. ps aux). See CWE-214.\n`.trim()\n );\n}\n\nfunction parseCli(): RelayConfig {\n const { values } = parseArgs({\n options: {\n mnemonic: { type: 'string' },\n 'secret-key': { type: 'string' },\n 'relay-port': { type: 'string' },\n 'bls-port': { type: 'string' },\n 'data-dir': { type: 'string' },\n 'connector-url': { type: 'string' },\n 'parent-peer-id': { type: 'string' },\n 'parent-auth-token': { type: 'string' },\n 'ilp-address': { type: 'string' },\n 'node-id': { type: 'string' },\n 'known-peers': { type: 'string' },\n 'dev-mode': { type: 'boolean' },\n 'x402-enabled': { type: 'boolean' },\n 'oblivious-mode': { type: 'boolean' },\n discovery: { type: 'string' },\n 'seed-relays': { type: 'string' },\n 'publish-seed-entry': { type: 'boolean' },\n 'external-relay-url': { type: 'string' },\n help: { type: 'boolean' },\n },\n strict: true,\n allowPositionals: false,\n });\n\n if (values.help) {\n printHelp();\n process.exit(0);\n }\n\n // Resolve: CLI flags override env vars\n\n // Warn about process-listing exposure (CWE-214) when secrets are passed via CLI flags\n if (values.mnemonic) {\n console.warn(\n 'Warning: --mnemonic is visible in process listings. ' +\n 'Prefer TOON_MNEMONIC environment variable for production use.'\n );\n }\n if (values['secret-key']) {\n console.warn(\n 'Warning: --secret-key is visible in process listings. ' +\n 'Prefer TOON_SECRET_KEY environment variable for production use.'\n );\n }\n\n const mnemonic = values.mnemonic ?? process.env['TOON_MNEMONIC'] ?? undefined;\n\n const secretKeyHex =\n values['secret-key'] ?? process.env['TOON_SECRET_KEY'] ?? undefined;\n\n let secretKey: Uint8Array | undefined;\n if (secretKeyHex) {\n if (secretKeyHex.length !== 64 || !/^[0-9a-fA-F]{64}$/.test(secretKeyHex)) {\n console.error('Error: --secret-key must be a 64-character hex string');\n process.exit(1);\n }\n secretKey = Uint8Array.from(Buffer.from(secretKeyHex, 'hex'));\n }\n\n const connectorUrl =\n values['connector-url'] ?? process.env['TOON_CONNECTOR_URL'] ?? undefined;\n\n const parentPeerId =\n values['parent-peer-id'] ?? process.env['TOON_PARENT_PEER_ID'] ?? undefined;\n\n const parentAuthToken =\n values['parent-auth-token'] ??\n process.env['TOON_PARENT_AUTH_TOKEN'] ??\n undefined;\n\n const ilpAddress =\n values['ilp-address'] ?? process.env['TOON_ILP_ADDRESS'] ?? undefined;\n\n const nodeId = values['node-id'] ?? process.env['TOON_NODE_ID'] ?? undefined;\n\n if (connectorUrl && !ilpAddress) {\n console.error(\n 'Error: --ilp-address (or TOON_ILP_ADDRESS) is required when ' +\n '--connector-url is set; it must fall under the parent connector prefix ' +\n '(e.g. g.townhouse.<self>)'\n );\n process.exit(1);\n }\n\n const relayPortStr =\n values['relay-port'] ?? process.env['TOON_RELAY_PORT'] ?? undefined;\n const relayPort = relayPortStr ? parseInt(relayPortStr, 10) : undefined;\n if (\n relayPort !== undefined &&\n (Number.isNaN(relayPort) || relayPort <= 0 || relayPort > 65535)\n ) {\n console.error('Error: --relay-port must be an integer between 1 and 65535');\n process.exit(1);\n }\n\n const blsPortStr =\n values['bls-port'] ?? process.env['TOON_BLS_PORT'] ?? undefined;\n const blsPort = blsPortStr ? parseInt(blsPortStr, 10) : undefined;\n if (\n blsPort !== undefined &&\n (Number.isNaN(blsPort) || blsPort <= 0 || blsPort > 65535)\n ) {\n console.error('Error: --bls-port must be an integer between 1 and 65535');\n process.exit(1);\n }\n\n const dataDir =\n values['data-dir'] ?? process.env['TOON_DATA_DIR'] ?? undefined;\n\n const devMode =\n values['dev-mode'] ??\n (process.env['TOON_DEV_MODE'] === 'true' ? true : undefined);\n\n const x402Enabled =\n values['x402-enabled'] ??\n (process.env['TOON_X402_ENABLED'] === 'true' ? true : undefined);\n\n const obliviousMode =\n values['oblivious-mode'] ??\n (process.env['TOON_OBLIVIOUS_MODE'] === 'true' ? true : undefined);\n\n const knownPeersJson =\n values['known-peers'] ?? process.env['TOON_KNOWN_PEERS'] ?? undefined;\n\n let knownPeers:\n | { pubkey: string; relayUrl: string; btpEndpoint: string }[]\n | undefined;\n if (knownPeersJson) {\n try {\n const parsed: unknown = JSON.parse(knownPeersJson);\n if (Array.isArray(parsed)) {\n knownPeers = (parsed as unknown[])\n .filter(\n (p): p is Record<string, unknown> =>\n typeof p === 'object' &&\n p !== null &&\n typeof (p as Record<string, unknown>)['pubkey'] === 'string' &&\n typeof (p as Record<string, unknown>)['btpEndpoint'] === 'string'\n )\n .map((p) => ({\n pubkey: p['pubkey'] as string,\n relayUrl: (p['relayUrl'] as string) || 'ws://localhost:7100',\n btpEndpoint: p['btpEndpoint'] as string,\n }));\n }\n } catch {\n console.error('Error: --known-peers must be valid JSON');\n process.exit(1);\n }\n }\n\n if (!mnemonic && !secretKey) {\n console.error(\n 'Error: one of --mnemonic (or TOON_MNEMONIC) or --secret-key (or TOON_SECRET_KEY) is required'\n );\n process.exit(1);\n }\n\n // Discovery mode\n const discoveryStr =\n values.discovery ?? process.env['TOON_DISCOVERY'] ?? undefined;\n let discoveryMode: 'seed-list' | 'genesis' | undefined;\n if (discoveryStr) {\n if (discoveryStr !== 'seed-list' && discoveryStr !== 'genesis') {\n console.error('Error: --discovery must be \"seed-list\" or \"genesis\"');\n process.exit(1);\n }\n discoveryMode = discoveryStr;\n }\n\n // Seed relays (comma-separated list of public Nostr relay URLs)\n const seedRelaysStr =\n values['seed-relays'] ?? process.env['TOON_SEED_RELAYS'] ?? undefined;\n const seedRelaysArr = seedRelaysStr\n ? seedRelaysStr\n .split(',')\n .map((s) => s.trim())\n .filter(Boolean)\n : undefined;\n\n // Validate seed relay URLs have WebSocket scheme (CWE-20)\n if (seedRelaysArr) {\n for (const url of seedRelaysArr) {\n // nosemgrep: javascript.lang.security.detect-insecure-websocket.detect-insecure-websocket -- validation check, not a connection\n if (!url.startsWith('ws://') && !url.startsWith('wss://')) {\n console.error(\n 'Error: --seed-relays contains invalid URL -- must use WebSocket scheme (ws or wss)'\n );\n process.exit(1);\n }\n }\n }\n\n // Publish seed entry flag\n const publishSeedEntry =\n values['publish-seed-entry'] ??\n (process.env['TOON_PUBLISH_SEED_ENTRY'] === 'true' ? true : undefined);\n\n // External relay URL\n const externalRelayUrl =\n values['external-relay-url'] ??\n process.env['TOON_EXTERNAL_RELAY_URL'] ??\n undefined;\n\n // Fee per event (overrides basePricePerByte)\n const feePerEventStr = process.env['TOON_FEE_PER_EVENT'] ?? undefined;\n const feePerEvent = feePerEventStr ? parseInt(feePerEventStr, 10) : undefined;\n if (\n feePerEvent !== undefined &&\n (Number.isNaN(feePerEvent) || feePerEvent < 0)\n ) {\n console.error('Error: TOON_FEE_PER_EVENT must be a non-negative integer');\n process.exit(1);\n }\n\n // Public BTP endpoint advertised in this town's kind:10032 (so clients learn\n // how to reach the apex to route packets to g.townhouse.town). Set by the\n // Townhouse orchestrator from the apex's .anyone / direct URL.\n const btpEndpoint = process.env['TOON_BTP_ENDPOINT'] ?? undefined;\n\n // Settlement asset advertised in kind:10032.\n const assetCode = process.env['TOON_ASSET_CODE'] ?? undefined;\n const assetScaleStr = process.env['TOON_ASSET_SCALE'] ?? undefined;\n const assetScale = assetScaleStr ? parseInt(assetScaleStr, 10) : undefined;\n if (\n assetScale !== undefined &&\n (Number.isNaN(assetScale) || assetScale < 0 || assetScale > 18)\n ) {\n console.error('Error: TOON_ASSET_SCALE must be an integer in 0..18');\n process.exit(1);\n }\n\n // Settlement private key — controls the embedded connector's ClaimReceiver\n // signer. CLI flag intentionally omitted (process listings would expose the\n // key via `ps`, CWE-214). Env-only.\n const settlementPrivateKey =\n process.env['TOON_SETTLEMENT_PRIVATE_KEY'] ?? undefined;\n if (\n settlementPrivateKey !== undefined &&\n !/^0x[0-9a-fA-F]{64}$/.test(settlementPrivateKey)\n ) {\n console.error(\n 'Error: TOON_SETTLEMENT_PRIVATE_KEY must be a 0x-prefixed 32-byte hex string'\n );\n process.exit(1);\n }\n\n // Parent EVM address advertised to the apex peer. Public address — safe to\n // ship via env. Validated as 0x + 40 hex chars (ERC-55 mixed-case allowed).\n const parentEvmAddress = process.env['TOON_PARENT_EVM_ADDRESS'] ?? undefined;\n if (\n parentEvmAddress !== undefined &&\n !/^0x[0-9a-fA-F]{40}$/.test(parentEvmAddress)\n ) {\n console.error(\n 'Error: TOON_PARENT_EVM_ADDRESS must be a 0x-prefixed 20-byte hex address'\n );\n process.exit(1);\n }\n\n // Multi-chain settlement advertisement (additive; opt-in via SUPPORTED_CHAINS).\n //\n // The default single-EVM-chain path (TOON_CHAIN/TOON_RPC_URL) is unchanged.\n // When SUPPORTED_CHAINS is set, parse per-chain env vars so the node can\n // advertise additional chains (notably `solana:devnet`) in kind:10032 with a\n // chain-native settlement recipient. Env key convention mirrors the SDK\n // entrypoint (docker/src/shared.ts): \"solana:devnet\" -> \"SOLANA_DEVNET\".\n // SETTLEMENT_ADDRESS_<KEY> recipient address advertised for the chain\n // CHAIN_RPC_URL_<KEY> RPC URL for the chain\n // TOKEN_NETWORK_<KEY> payment-channel program / token-network address\n // PREFERRED_TOKEN_<KEY> preferred token (e.g. USDC mint)\n let chainRpcUrls: Record<string, string> | undefined;\n let tokenNetworks: Record<string, string> | undefined;\n let preferredTokens: Record<string, string> | undefined;\n let settlementAddresses: Record<string, string> | undefined;\n const supportedChainsStr = process.env['SUPPORTED_CHAINS'];\n if (supportedChainsStr) {\n const chains = supportedChainsStr\n .split(',')\n .map((s) => s.trim())\n .filter(Boolean);\n for (const chain of chains) {\n const key = chain.replace(/:/g, '_').toUpperCase();\n const addr = process.env[`SETTLEMENT_ADDRESS_${key}`];\n if (addr) (settlementAddresses ??= {})[chain] = addr;\n const rpc = process.env[`CHAIN_RPC_URL_${key}`];\n if (rpc) (chainRpcUrls ??= {})[chain] = rpc;\n const tokenNet = process.env[`TOKEN_NETWORK_${key}`];\n if (tokenNet) (tokenNetworks ??= {})[chain] = tokenNet;\n const token = process.env[`PREFERRED_TOKEN_${key}`];\n if (token) (preferredTokens ??= {})[chain] = token;\n if (!addr) {\n console.warn(\n `[Town] Warning: chain \"${chain}\" listed in SUPPORTED_CHAINS but no SETTLEMENT_ADDRESS_${key} env var found`\n );\n }\n }\n }\n\n const config: RelayConfig = {\n ...(connectorUrl && { connectorUrl }),\n ...(chainRpcUrls && { chainRpcUrls }),\n ...(tokenNetworks && { tokenNetworks }),\n ...(preferredTokens && { preferredTokens }),\n ...(settlementAddresses && { settlementAddresses }),\n ...(parentPeerId && { parentPeerId }),\n ...(parentAuthToken !== undefined && { parentAuthToken }),\n ...(ilpAddress && { ilpAddress }),\n ...(nodeId && { nodeId }),\n ...(mnemonic && { mnemonic }),\n ...(secretKey && { secretKey }),\n ...(relayPort !== undefined && { relayPort }),\n ...(blsPort !== undefined && { blsPort }),\n ...(dataDir && { dataDir }),\n ...(knownPeers && { knownPeers }),\n ...(devMode !== undefined && { devMode }),\n ...(x402Enabled !== undefined && { x402Enabled }),\n ...(obliviousMode !== undefined && { obliviousMode }),\n ...(discoveryMode && { discovery: discoveryMode }),\n ...(seedRelaysArr && { seedRelays: seedRelaysArr }),\n ...(publishSeedEntry !== undefined && { publishSeedEntry }),\n ...(externalRelayUrl && { externalRelayUrl }),\n ...(feePerEvent !== undefined && { feePerEvent }),\n ...(btpEndpoint && { btpEndpoint }),\n ...(assetCode && { assetCode }),\n ...(assetScale !== undefined && { assetScale }),\n ...(settlementPrivateKey && { settlementPrivateKey }),\n ...(parentEvmAddress && { parentEvmAddress }),\n };\n\n return config;\n}\n\n// ---------- Main ----------\n\nasync function main(): Promise<void> {\n const config = parseCli();\n\n console.log('\\n' + '='.repeat(50));\n console.log('TOON Town Starting');\n console.log('='.repeat(50) + '\\n');\n\n const instance: RelayInstance = await startRelay(config);\n\n console.log('\\n' + '='.repeat(50));\n console.log('TOON Town Ready');\n console.log('='.repeat(50));\n console.log(` Pubkey: ${instance.pubkey}`);\n console.log(` EVM Address: ${instance.evmAddress}`);\n console.log(` Relay: ws://localhost:${instance.config.relayPort}`);\n console.log(` BLS: http://localhost:${instance.config.blsPort}`);\n console.log(` ILP Address: ${instance.config.ilpAddress}`);\n if (instance.config.connectorUrl) {\n console.log(` Parent BTP: ${instance.config.connectorUrl}`);\n console.log(` Parent Peer: ${instance.config.parentPeerId}`);\n }\n console.log(` Peers: ${instance.bootstrapResult.peerCount}`);\n console.log(` Channels: ${instance.bootstrapResult.channelCount}`);\n console.log('='.repeat(50) + '\\n');\n\n // Wire graceful shutdown\n const shutdown = async (signal: string): Promise<void> => {\n console.log(`\\n[Shutdown] Received ${signal}`);\n await instance.stop();\n console.log('[Shutdown] Complete');\n process.exit(0);\n };\n\n process.on('SIGINT', () => {\n shutdown('SIGINT').catch(console.error);\n });\n process.on('SIGTERM', () => {\n shutdown('SIGTERM').catch(console.error);\n });\n}\n\nmain().catch((error: unknown) => {\n console.error('[Fatal] Startup error:', error);\n process.exit(1);\n});\n"],"mappings":"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","names":[]}
package/dist/index.d.ts CHANGED
@@ -484,6 +484,15 @@ declare class RelaySubscriber {
484
484
  * `EmbeddableConnectorLike`. The town does not modify it.
485
485
  *
486
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.
487
496
  */
488
497
 
489
498
  /**
@@ -512,6 +521,13 @@ interface RelayConfig {
512
521
  * settlement are the caller's responsibility.
513
522
  */
514
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;
515
531
  /**
516
532
  * Parent connector BTP URL (e.g. `ws://apex.example:3001`). When set, the
517
533
  * embedded connector is built with this URL as a parent peer and a default
@@ -525,6 +541,17 @@ interface RelayConfig {
525
541
  parentAuthToken?: string;
526
542
  /** Stable nodeId for the embedded connector (default: `toon-<pubkeyShort>`). */
527
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;
528
555
  /** BTP server port for the embedded connector (default: 3000). */
529
556
  btpServerPort?: number;
530
557
  /**
@@ -687,6 +714,8 @@ interface ResolvedRelayConfig {
687
714
  externalRelayUrl?: string;
688
715
  /** Chain preset name (e.g., 'anvil', 'arbitrum-one'). */
689
716
  chain: string;
717
+ /** Whether the relay is running in payment-oblivious mode (no connector). */
718
+ obliviousMode: boolean;
690
719
  }
691
720
  /**
692
721
  * A running TOON relay node instance returned by `startRelay()`.
package/dist/index.js CHANGED
@@ -22,7 +22,7 @@ import {
22
22
  settleEip3009,
23
23
  startRelay,
24
24
  startTown
25
- } from "./chunk-4RYYKZXO.js";
25
+ } from "./chunk-NYKVCNJL.js";
26
26
 
27
27
  // src/bls/types.ts
28
28
  var PUBKEY_REGEX = /^[0-9a-f]{64}$/;
@@ -57,8 +57,6 @@ var BusinessLogicServer = class {
57
57
  this.app = new Hono();
58
58
  this.setupRoutes();
59
59
  }
60
- config;
61
- eventStore;
62
60
  app;
63
61
  /**
64
62
  * Set up HTTP routes.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/bls/types.ts","../src/bls/BusinessLogicServer.ts","../src/pricing/types.ts","../src/pricing/PricingService.ts","../src/pricing/config.ts","../src/index.ts"],"sourcesContent":["import { RelayError } from '../storage/index.js';\nimport type { PricingService } from '../pricing/index.js';\n\n/**\n * Regex for validating Nostr pubkeys (64 lowercase hex characters).\n */\nexport const PUBKEY_REGEX = /^[0-9a-f]{64}$/;\n\n/**\n * Validate that a string is a valid Nostr pubkey format.\n * @param pubkey - The pubkey to validate\n * @returns true if valid 64-character lowercase hex string\n */\nexport function isValidPubkey(pubkey: string): boolean {\n return PUBKEY_REGEX.test(pubkey);\n}\n\n/**\n * Configuration for the Business Logic Server.\n */\nexport interface BlsConfig {\n /** Base price per byte for event storage (used for simple pricing) */\n basePricePerByte: bigint;\n /** Optional PricingService for kind-based pricing overrides */\n pricingService?: PricingService;\n /** Optional owner pubkey - events from this pubkey bypass payment */\n ownerPubkey?: string;\n}\n\n/**\n * Incoming packet request from ILP connector.\n */\nexport interface HandlePacketRequest {\n /** Payment amount as string (parsed to bigint) */\n amount: string;\n /** ILP destination address */\n destination: string;\n /** Base64-encoded TOON Nostr event */\n data: string;\n /** Source ILP address */\n sourceAccount?: string;\n}\n\n/**\n * Response for accepted packet.\n */\nexport interface HandlePacketAcceptResponse {\n accept: true;\n metadata?: {\n eventId: string;\n storedAt: number;\n };\n}\n\n/**\n * Response for rejected packet.\n */\nexport interface HandlePacketRejectResponse {\n accept: false;\n /** ILP error code (F00, F06, etc.) */\n code: string;\n /** Human-readable error message */\n message: string;\n metadata?: {\n required?: string;\n received?: string;\n };\n}\n\n/**\n * Union type for packet response.\n */\nexport type HandlePacketResponse =\n | HandlePacketAcceptResponse\n | HandlePacketRejectResponse;\n\n/**\n * ILP error code constants.\n */\nexport const ILP_ERROR_CODES = {\n BAD_REQUEST: 'F00',\n INSUFFICIENT_AMOUNT: 'F06',\n INTERNAL_ERROR: 'T00',\n} as const;\n\n/**\n * BLS-specific error class.\n */\nexport class BlsError extends RelayError {\n constructor(message: string, code = 'BLS_ERROR') {\n super(message, code);\n this.name = 'BlsError';\n }\n}\n","import { Hono } from 'hono';\nimport { verifyEvent } from 'nostr-tools/pure';\nimport type { EventStore } from '../storage/index.js';\nimport { decodeEventFromToon } from '../toon/index.js';\nimport type {\n BlsConfig,\n HandlePacketRequest,\n HandlePacketAcceptResponse,\n HandlePacketRejectResponse,\n} from './types.js';\nimport { ILP_ERROR_CODES, BlsError, isValidPubkey } from './types.js';\n\n/**\n * Business Logic Server for ILP payment verification.\n *\n * Handles payment requests from an ILP connector, verifying that the\n * payment amount meets the required price for storing the included\n * Nostr event.\n */\nexport class BusinessLogicServer {\n private app: Hono;\n\n constructor(\n private config: BlsConfig,\n private eventStore: EventStore\n ) {\n // Validate ownerPubkey format if provided\n if (\n config.ownerPubkey !== undefined &&\n !isValidPubkey(config.ownerPubkey)\n ) {\n throw new BlsError(\n 'Invalid ownerPubkey format: must be 64 lowercase hex characters',\n 'INVALID_CONFIG'\n );\n }\n this.app = new Hono();\n this.setupRoutes();\n }\n\n /**\n * Set up HTTP routes.\n */\n private setupRoutes(): void {\n this.app.post('/handle-packet', async (c) => {\n try {\n const body = await c.req.json<HandlePacketRequest>();\n const response = this.handlePacket(body);\n return c.json(response, response.accept ? 200 : 400);\n } catch (error) {\n const response: HandlePacketRejectResponse = {\n accept: false,\n code: ILP_ERROR_CODES.INTERNAL_ERROR,\n message:\n error instanceof Error ? error.message : 'Internal server error',\n };\n return c.json(response, 500);\n }\n });\n\n this.app.get('/health', (c) => {\n return c.json({\n status: 'healthy',\n timestamp: Date.now(),\n });\n });\n }\n\n /**\n * Process a packet request.\n *\n * This method is public to support direct connector integration in embedded mode,\n * where the connector calls this method directly via setPacketHandler() instead\n * of making HTTP requests.\n */\n public handlePacket(\n request: HandlePacketRequest\n ): HandlePacketAcceptResponse | HandlePacketRejectResponse {\n // Validate required fields\n if (!request.amount || !request.destination || !request.data) {\n return {\n accept: false,\n code: ILP_ERROR_CODES.BAD_REQUEST,\n message: 'Missing required fields: amount, destination, data',\n };\n }\n\n // Decode base64 data\n let toonBytes: Uint8Array;\n try {\n toonBytes = Uint8Array.from(Buffer.from(request.data, 'base64'));\n } catch {\n return {\n accept: false,\n code: ILP_ERROR_CODES.BAD_REQUEST,\n message: 'Invalid base64 encoding in data field',\n };\n }\n\n // Decode TOON to Nostr event\n let event;\n try {\n event = decodeEventFromToon(toonBytes);\n } catch (error) {\n return {\n accept: false,\n code: ILP_ERROR_CODES.BAD_REQUEST,\n message: `Invalid TOON data: ${error instanceof Error ? error.message : 'Unknown error'}`,\n };\n }\n\n // Verify event signature\n if (!verifyEvent(event)) {\n return {\n accept: false,\n code: ILP_ERROR_CODES.BAD_REQUEST,\n message: 'Invalid event signature',\n };\n }\n\n // Self-write bypass: owner events skip payment verification\n if (this.config.ownerPubkey && event.pubkey === this.config.ownerPubkey) {\n try {\n this.eventStore.store(event);\n } catch (error) {\n throw new BlsError(\n `Failed to store event: ${error instanceof Error ? error.message : 'Unknown error'}`,\n 'STORAGE_ERROR'\n );\n }\n\n return {\n accept: true,\n metadata: {\n eventId: event.id,\n storedAt: Date.now(),\n },\n };\n }\n\n // Calculate price: use PricingService if provided, otherwise simple calculation\n const price = this.config.pricingService\n ? this.config.pricingService.calculatePriceFromBytes(\n toonBytes,\n event.kind\n )\n : BigInt(toonBytes.length) * this.config.basePricePerByte;\n\n // Parse and compare amounts\n let amount: bigint;\n try {\n amount = BigInt(request.amount);\n } catch {\n return {\n accept: false,\n code: ILP_ERROR_CODES.BAD_REQUEST,\n message: 'Invalid amount format',\n };\n }\n\n if (amount < price) {\n return {\n accept: false,\n code: ILP_ERROR_CODES.INSUFFICIENT_AMOUNT,\n message: 'Insufficient payment amount',\n metadata: {\n required: price.toString(),\n received: amount.toString(),\n },\n };\n }\n\n // Store event\n try {\n this.eventStore.store(event);\n } catch (error) {\n throw new BlsError(\n `Failed to store event: ${error instanceof Error ? error.message : 'Unknown error'}`,\n 'STORAGE_ERROR'\n );\n }\n\n const storedAt = Date.now();\n return {\n accept: true,\n metadata: {\n eventId: event.id,\n storedAt,\n },\n };\n }\n\n /**\n * Get the Hono app instance for testing or composition.\n */\n getApp(): Hono {\n return this.app;\n }\n\n /**\n * Start the HTTP server on the specified port.\n */\n start(port: number): void {\n // Note: In Node.js, you would typically use @hono/node-server\n // For now, this method is a placeholder for actual server startup\n // The actual server binding would be done by the consumer\n console.log(`BLS would start on port ${port}`);\n }\n}\n","import { RelayError } from '../storage/index.js';\n\n/**\n * Configuration for the Pricing Service.\n */\nexport interface PricingConfig {\n /** Base price per byte for event storage */\n basePricePerByte: bigint;\n /** Optional price overrides by event kind */\n kindOverrides?: Map<number, bigint>;\n}\n\n/**\n * Error class for pricing-specific errors.\n */\nexport class PricingError extends RelayError {\n constructor(message: string, code = 'PRICING_ERROR') {\n super(message, code);\n this.name = 'PricingError';\n }\n}\n","import type { NostrEvent } from 'nostr-tools/pure';\nimport { encodeEventToToon } from '../toon/index.js';\nimport type { PricingConfig } from './types.js';\nimport { PricingError } from './types.js';\n\n/**\n * Service for calculating event storage prices with kind-based overrides.\n */\nexport class PricingService {\n private readonly basePricePerByte: bigint;\n private readonly kindOverrides: Map<number, bigint>;\n\n constructor(config: PricingConfig) {\n // Validate basePricePerByte is non-negative\n if (config.basePricePerByte < 0n) {\n throw new PricingError(\n 'basePricePerByte must be non-negative',\n 'INVALID_CONFIG'\n );\n }\n\n // Validate all kindOverrides are non-negative\n if (config.kindOverrides) {\n for (const [kind, price] of config.kindOverrides.entries()) {\n if (price < 0n) {\n throw new PricingError(\n `kindOverride for kind ${kind} must be non-negative`,\n 'INVALID_CONFIG'\n );\n }\n }\n }\n\n this.basePricePerByte = config.basePricePerByte;\n this.kindOverrides = config.kindOverrides ?? new Map();\n }\n\n /**\n * Calculate price for a Nostr event.\n *\n * @param event - The Nostr event to price\n * @returns The calculated price as bigint\n */\n calculatePrice(event: NostrEvent): bigint {\n const toonBytes = encodeEventToToon(event);\n return this.calculatePriceFromBytes(toonBytes, event.kind);\n }\n\n /**\n * Calculate price from raw TOON bytes and event kind.\n *\n * @param bytes - The TOON-encoded event bytes\n * @param kind - The event kind number\n * @returns The calculated price as bigint\n */\n calculatePriceFromBytes(bytes: Uint8Array, kind: number): bigint {\n const pricePerByte = this.getPricePerByte(kind);\n return BigInt(bytes.length) * pricePerByte;\n }\n\n /**\n * Get the effective price per byte for a given kind.\n *\n * @param kind - The event kind number\n * @returns The price per byte (kind override if exists, otherwise base price)\n */\n getPricePerByte(kind: number): bigint {\n return this.kindOverrides.get(kind) ?? this.basePricePerByte;\n }\n}\n","import { readFileSync } from 'fs';\nimport type { PricingConfig } from './types.js';\nimport { PricingError } from './types.js';\n\n/**\n * Load pricing configuration from environment variables.\n *\n * Environment variables:\n * - RELAY_BASE_PRICE_PER_BYTE: Base price per byte (default: \"1\")\n * - RELAY_KIND_OVERRIDES: JSON object mapping kind to price (optional)\n * Format: {\"1\":\"5\",\"30023\":\"100\"}\n *\n * @returns PricingConfig loaded from environment\n * @throws PricingError if env vars contain invalid values\n */\nexport function loadPricingConfigFromEnv(): PricingConfig {\n // Parse base price\n const basePriceStr = process.env['RELAY_BASE_PRICE_PER_BYTE'] ?? '1';\n let basePricePerByte: bigint;\n try {\n basePricePerByte = BigInt(basePriceStr);\n } catch {\n throw new PricingError(\n `Invalid RELAY_BASE_PRICE_PER_BYTE: \"${basePriceStr}\" is not a valid integer`,\n 'INVALID_ENV_CONFIG'\n );\n }\n\n if (basePricePerByte < 0n) {\n throw new PricingError(\n `Invalid RELAY_BASE_PRICE_PER_BYTE: value must be non-negative`,\n 'INVALID_ENV_CONFIG'\n );\n }\n\n // Parse kind overrides if present\n const kindOverridesStr = process.env['RELAY_KIND_OVERRIDES'];\n let kindOverrides: Map<number, bigint> | undefined;\n\n if (kindOverridesStr) {\n kindOverrides = parseKindOverridesJson(kindOverridesStr);\n }\n\n return {\n basePricePerByte,\n kindOverrides,\n };\n}\n\n/**\n * Load pricing configuration from a JSON file.\n *\n * File format:\n * {\n * \"basePricePerByte\": \"10\",\n * \"kindOverrides\": {\n * \"0\": \"0\",\n * \"1\": \"5\",\n * \"30023\": \"100\"\n * }\n * }\n *\n * @param path - Path to the JSON config file\n * @returns PricingConfig loaded from file\n * @throws PricingError if file cannot be read or contains invalid values\n */\nexport function loadPricingConfigFromFile(path: string): PricingConfig {\n let fileContent: string;\n try {\n fileContent = readFileSync(path, 'utf-8');\n } catch (error) {\n throw new PricingError(\n `Failed to read config file: ${error instanceof Error ? error.message : 'Unknown error'}`,\n 'CONFIG_FILE_ERROR'\n );\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(fileContent);\n } catch {\n throw new PricingError(\n `Invalid JSON in config file: ${path}`,\n 'INVALID_FILE_CONFIG'\n );\n }\n\n if (typeof parsed !== 'object' || parsed === null) {\n throw new PricingError(\n `Config file must contain a JSON object`,\n 'INVALID_FILE_CONFIG'\n );\n }\n\n const config = parsed as Record<string, unknown>;\n\n // Parse base price\n const basePriceStr = config['basePricePerByte'];\n if (typeof basePriceStr !== 'string' && typeof basePriceStr !== 'number') {\n throw new PricingError(\n `Config file must contain \"basePricePerByte\" as string or number`,\n 'INVALID_FILE_CONFIG'\n );\n }\n\n let basePricePerByte: bigint;\n try {\n basePricePerByte = BigInt(basePriceStr);\n } catch {\n throw new PricingError(\n `Invalid basePricePerByte: \"${basePriceStr}\" is not a valid integer`,\n 'INVALID_FILE_CONFIG'\n );\n }\n\n if (basePricePerByte < 0n) {\n throw new PricingError(\n `basePricePerByte must be non-negative`,\n 'INVALID_FILE_CONFIG'\n );\n }\n\n // Parse kind overrides if present\n let kindOverrides: Map<number, bigint> | undefined;\n const kindOverridesValue = config['kindOverrides'];\n if (kindOverridesValue !== undefined) {\n if (typeof kindOverridesValue !== 'object' || kindOverridesValue === null) {\n throw new PricingError(\n `kindOverrides must be an object`,\n 'INVALID_FILE_CONFIG'\n );\n }\n\n kindOverrides = new Map();\n const overridesObj = kindOverridesValue as Record<string, unknown>;\n\n for (const [kindStr, priceValue] of Object.entries(overridesObj)) {\n const kind = parseInt(kindStr, 10);\n if (isNaN(kind)) {\n throw new PricingError(\n `Invalid kind in kindOverrides: \"${kindStr}\" is not a valid integer`,\n 'INVALID_FILE_CONFIG'\n );\n }\n\n if (typeof priceValue !== 'string' && typeof priceValue !== 'number') {\n throw new PricingError(\n `Invalid price for kind ${kind}: must be string or number`,\n 'INVALID_FILE_CONFIG'\n );\n }\n\n let price: bigint;\n try {\n price = BigInt(priceValue);\n } catch {\n throw new PricingError(\n `Invalid price for kind ${kind}: \"${priceValue}\" is not a valid integer`,\n 'INVALID_FILE_CONFIG'\n );\n }\n\n if (price < 0n) {\n throw new PricingError(\n `Price for kind ${kind} must be non-negative`,\n 'INVALID_FILE_CONFIG'\n );\n }\n\n kindOverrides.set(kind, price);\n }\n }\n\n return {\n basePricePerByte,\n kindOverrides,\n };\n}\n\n/**\n * Parse kind overrides JSON string to Map.\n */\nfunction parseKindOverridesJson(jsonStr: string): Map<number, bigint> {\n let parsed: unknown;\n try {\n parsed = JSON.parse(jsonStr);\n } catch {\n throw new PricingError(\n `Invalid JSON in RELAY_KIND_OVERRIDES: ${jsonStr}`,\n 'INVALID_ENV_CONFIG'\n );\n }\n\n if (typeof parsed !== 'object' || parsed === null) {\n throw new PricingError(\n `RELAY_KIND_OVERRIDES must be a JSON object`,\n 'INVALID_ENV_CONFIG'\n );\n }\n\n const result = new Map<number, bigint>();\n const obj = parsed as Record<string, unknown>;\n\n for (const [kindStr, priceValue] of Object.entries(obj)) {\n const kind = parseInt(kindStr, 10);\n if (isNaN(kind)) {\n throw new PricingError(\n `Invalid kind in RELAY_KIND_OVERRIDES: \"${kindStr}\" is not a valid integer`,\n 'INVALID_ENV_CONFIG'\n );\n }\n\n if (typeof priceValue !== 'string' && typeof priceValue !== 'number') {\n throw new PricingError(\n `Invalid price for kind ${kind} in RELAY_KIND_OVERRIDES: must be string or number`,\n 'INVALID_ENV_CONFIG'\n );\n }\n\n let price: bigint;\n try {\n price = BigInt(priceValue);\n } catch {\n throw new PricingError(\n `Invalid price for kind ${kind} in RELAY_KIND_OVERRIDES: \"${priceValue}\" is not a valid integer`,\n 'INVALID_ENV_CONFIG'\n );\n }\n\n if (price < 0n) {\n throw new PricingError(\n `Price for kind ${kind} in RELAY_KIND_OVERRIDES must be non-negative`,\n 'INVALID_ENV_CONFIG'\n );\n }\n\n result.set(kind, price);\n }\n\n return result;\n}\n","/**\n * @toon-protocol/relay\n *\n * ILP-gated Nostr relay with Business Logic Server.\n */\n\nexport const VERSION = '0.1.0';\n\n// Types\n// NOTE: the low-level WebSocket relay-server config is exported as\n// `RelayServerConfig` (renamed from `RelayConfig`) so the launcher's\n// `RelayConfig` (formerly `TownConfig`) can take the `RelayConfig` name.\nexport type { RelayServerConfig } from './types.js';\nexport { DEFAULT_RELAY_CONFIG } from './types.js';\n\n// Storage\nexport type { EventStore } from './storage/index.js';\nexport {\n InMemoryEventStore,\n SqliteEventStore,\n RelayError,\n} from './storage/index.js';\n\n// Filters\nexport { matchFilter } from './filters/index.js';\n\n// WebSocket\nexport type { Subscription } from './websocket/index.js';\nexport { ConnectionHandler, NostrRelayServer } from './websocket/index.js';\n\n// TOON encoding/decoding\nexport {\n encodeEventToToon,\n decodeEventFromToon,\n ToonEncodeError,\n ToonDecodeError,\n} from './toon/index.js';\n\n// Business Logic Server\nexport type {\n BlsConfig,\n HandlePacketRequest,\n HandlePacketAcceptResponse,\n HandlePacketRejectResponse,\n HandlePacketResponse,\n} from './bls/index.js';\nexport {\n BlsError,\n ILP_ERROR_CODES,\n BusinessLogicServer,\n isValidPubkey,\n} from './bls/index.js';\n\n// Pricing\nexport type { PricingConfig } from './pricing/index.js';\nexport {\n PricingError,\n PricingService,\n loadPricingConfigFromEnv,\n loadPricingConfigFromFile,\n} from './pricing/index.js';\n\n// Subscriber\nexport type { RelaySubscriberConfig } from './subscriber/index.js';\nexport { RelaySubscriber } from './subscriber/index.js';\n\n// ---------------------------------------------------------------------------\n// Launcher (formerly @toon-protocol/town)\n//\n// SDK-based relay launcher with a one-call programmatic API (startRelay()) and\n// handler implementations for ILP-gated Nostr services. Merged in from the\n// @toon-protocol/town package. Old `startTown`/`Town*` names are re-exported\n// as deprecated aliases.\n// ---------------------------------------------------------------------------\n\n// Relay launcher lifecycle API\nexport { startRelay } from './launcher/town.js';\nexport type {\n RelayConfig,\n RelayInstance,\n RelaySubscription,\n ResolvedRelayConfig,\n} from './launcher/town.js';\n\n// Deprecated launcher aliases (town → relay merge)\nexport { startTown } from './launcher/town.js';\nexport type {\n TownConfig,\n TownInstance,\n TownSubscription,\n ResolvedTownConfig,\n} from './launcher/town.js';\n\n// Health response\nexport { createHealthResponse } from './launcher/health.js';\nexport type {\n HealthConfig,\n HealthResponse,\n TeeHealthInfo,\n} from './launcher/health.js';\n\n// Event storage handler\nexport { createEventStorageHandler } from './launcher/handlers/event-storage-handler.js';\nexport type { EventStorageHandlerConfig } from './launcher/handlers/event-storage-handler.js';\n\n// x402 publish handler\nexport { createX402Handler } from './launcher/handlers/x402-publish-handler.js';\nexport type {\n X402HandlerConfig,\n X402Handler,\n} from './launcher/handlers/x402-publish-handler.js';\n\n// x402 pricing\nexport { calculateX402Price } from './launcher/handlers/x402-pricing.js';\nexport type { X402PricingConfig } from './launcher/handlers/x402-pricing.js';\n\n// x402 pre-flight\nexport { runPreflight } from './launcher/handlers/x402-preflight.js';\nexport type {\n PreflightResult,\n PreflightConfig,\n} from './launcher/handlers/x402-preflight.js';\n\n// x402 settlement\nexport { settleEip3009 } from './launcher/handlers/x402-settlement.js';\nexport type {\n X402SettlementResult,\n X402SettlementConfig,\n} from './launcher/handlers/x402-settlement.js';\n// Deprecated aliases -- use X402SettlementResult / X402SettlementConfig instead\nexport type {\n SettlementResult,\n SettlementConfig,\n} from './launcher/handlers/x402-settlement.js';\n\n// x402 types\nexport type {\n Eip3009Authorization,\n EventStoreLike,\n X402PublishRequest,\n X402PublishResponse,\n X402PricingResponse,\n} from './launcher/handlers/x402-types.js';\nexport {\n EIP_3009_TYPES,\n USDC_EIP712_DOMAIN,\n USDC_ABI,\n} from './launcher/handlers/x402-types.js';\n\n// Re-exports from @toon-protocol/bls removed to avoid circular dependency\n// Downstream consumers should import directly from @toon-protocol/bls instead\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/bls/types.ts","../src/bls/BusinessLogicServer.ts","../src/pricing/types.ts","../src/pricing/PricingService.ts","../src/pricing/config.ts","../src/index.ts"],"sourcesContent":["import { RelayError } from '../storage/index.js';\nimport type { PricingService } from '../pricing/index.js';\n\n/**\n * Regex for validating Nostr pubkeys (64 lowercase hex characters).\n */\nexport const PUBKEY_REGEX = /^[0-9a-f]{64}$/;\n\n/**\n * Validate that a string is a valid Nostr pubkey format.\n * @param pubkey - The pubkey to validate\n * @returns true if valid 64-character lowercase hex string\n */\nexport function isValidPubkey(pubkey: string): boolean {\n return PUBKEY_REGEX.test(pubkey);\n}\n\n/**\n * Configuration for the Business Logic Server.\n */\nexport interface BlsConfig {\n /** Base price per byte for event storage (used for simple pricing) */\n basePricePerByte: bigint;\n /** Optional PricingService for kind-based pricing overrides */\n pricingService?: PricingService;\n /** Optional owner pubkey - events from this pubkey bypass payment */\n ownerPubkey?: string;\n}\n\n/**\n * Incoming packet request from ILP connector.\n */\nexport interface HandlePacketRequest {\n /** Payment amount as string (parsed to bigint) */\n amount: string;\n /** ILP destination address */\n destination: string;\n /** Base64-encoded TOON Nostr event */\n data: string;\n /** Source ILP address */\n sourceAccount?: string;\n}\n\n/**\n * Response for accepted packet.\n */\nexport interface HandlePacketAcceptResponse {\n accept: true;\n metadata?: {\n eventId: string;\n storedAt: number;\n };\n}\n\n/**\n * Response for rejected packet.\n */\nexport interface HandlePacketRejectResponse {\n accept: false;\n /** ILP error code (F00, F06, etc.) */\n code: string;\n /** Human-readable error message */\n message: string;\n metadata?: {\n required?: string;\n received?: string;\n };\n}\n\n/**\n * Union type for packet response.\n */\nexport type HandlePacketResponse =\n | HandlePacketAcceptResponse\n | HandlePacketRejectResponse;\n\n/**\n * ILP error code constants.\n */\nexport const ILP_ERROR_CODES = {\n BAD_REQUEST: 'F00',\n INSUFFICIENT_AMOUNT: 'F06',\n INTERNAL_ERROR: 'T00',\n} as const;\n\n/**\n * BLS-specific error class.\n */\nexport class BlsError extends RelayError {\n constructor(message: string, code = 'BLS_ERROR') {\n super(message, code);\n this.name = 'BlsError';\n }\n}\n","import { Hono } from 'hono';\nimport { verifyEvent } from 'nostr-tools/pure';\nimport type { EventStore } from '../storage/index.js';\nimport { decodeEventFromToon } from '../toon/index.js';\nimport type {\n BlsConfig,\n HandlePacketRequest,\n HandlePacketAcceptResponse,\n HandlePacketRejectResponse,\n} from './types.js';\nimport { ILP_ERROR_CODES, BlsError, isValidPubkey } from './types.js';\n\n/**\n * Business Logic Server for ILP payment verification.\n *\n * Handles payment requests from an ILP connector, verifying that the\n * payment amount meets the required price for storing the included\n * Nostr event.\n */\nexport class BusinessLogicServer {\n private app: Hono;\n\n constructor(\n private config: BlsConfig,\n private eventStore: EventStore\n ) {\n // Validate ownerPubkey format if provided\n if (\n config.ownerPubkey !== undefined &&\n !isValidPubkey(config.ownerPubkey)\n ) {\n throw new BlsError(\n 'Invalid ownerPubkey format: must be 64 lowercase hex characters',\n 'INVALID_CONFIG'\n );\n }\n this.app = new Hono();\n this.setupRoutes();\n }\n\n /**\n * Set up HTTP routes.\n */\n private setupRoutes(): void {\n this.app.post('/handle-packet', async (c) => {\n try {\n const body = await c.req.json<HandlePacketRequest>();\n const response = this.handlePacket(body);\n return c.json(response, response.accept ? 200 : 400);\n } catch (error) {\n const response: HandlePacketRejectResponse = {\n accept: false,\n code: ILP_ERROR_CODES.INTERNAL_ERROR,\n message:\n error instanceof Error ? error.message : 'Internal server error',\n };\n return c.json(response, 500);\n }\n });\n\n this.app.get('/health', (c) => {\n return c.json({\n status: 'healthy',\n timestamp: Date.now(),\n });\n });\n }\n\n /**\n * Process a packet request.\n *\n * This method is public to support direct connector integration in embedded mode,\n * where the connector calls this method directly via setPacketHandler() instead\n * of making HTTP requests.\n */\n public handlePacket(\n request: HandlePacketRequest\n ): HandlePacketAcceptResponse | HandlePacketRejectResponse {\n // Validate required fields\n if (!request.amount || !request.destination || !request.data) {\n return {\n accept: false,\n code: ILP_ERROR_CODES.BAD_REQUEST,\n message: 'Missing required fields: amount, destination, data',\n };\n }\n\n // Decode base64 data\n let toonBytes: Uint8Array;\n try {\n toonBytes = Uint8Array.from(Buffer.from(request.data, 'base64'));\n } catch {\n return {\n accept: false,\n code: ILP_ERROR_CODES.BAD_REQUEST,\n message: 'Invalid base64 encoding in data field',\n };\n }\n\n // Decode TOON to Nostr event\n let event;\n try {\n event = decodeEventFromToon(toonBytes);\n } catch (error) {\n return {\n accept: false,\n code: ILP_ERROR_CODES.BAD_REQUEST,\n message: `Invalid TOON data: ${error instanceof Error ? error.message : 'Unknown error'}`,\n };\n }\n\n // Verify event signature\n if (!verifyEvent(event)) {\n return {\n accept: false,\n code: ILP_ERROR_CODES.BAD_REQUEST,\n message: 'Invalid event signature',\n };\n }\n\n // Self-write bypass: owner events skip payment verification\n if (this.config.ownerPubkey && event.pubkey === this.config.ownerPubkey) {\n try {\n this.eventStore.store(event);\n } catch (error) {\n throw new BlsError(\n `Failed to store event: ${error instanceof Error ? error.message : 'Unknown error'}`,\n 'STORAGE_ERROR'\n );\n }\n\n return {\n accept: true,\n metadata: {\n eventId: event.id,\n storedAt: Date.now(),\n },\n };\n }\n\n // Calculate price: use PricingService if provided, otherwise simple calculation\n const price = this.config.pricingService\n ? this.config.pricingService.calculatePriceFromBytes(\n toonBytes,\n event.kind\n )\n : BigInt(toonBytes.length) * this.config.basePricePerByte;\n\n // Parse and compare amounts\n let amount: bigint;\n try {\n amount = BigInt(request.amount);\n } catch {\n return {\n accept: false,\n code: ILP_ERROR_CODES.BAD_REQUEST,\n message: 'Invalid amount format',\n };\n }\n\n if (amount < price) {\n return {\n accept: false,\n code: ILP_ERROR_CODES.INSUFFICIENT_AMOUNT,\n message: 'Insufficient payment amount',\n metadata: {\n required: price.toString(),\n received: amount.toString(),\n },\n };\n }\n\n // Store event\n try {\n this.eventStore.store(event);\n } catch (error) {\n throw new BlsError(\n `Failed to store event: ${error instanceof Error ? error.message : 'Unknown error'}`,\n 'STORAGE_ERROR'\n );\n }\n\n const storedAt = Date.now();\n return {\n accept: true,\n metadata: {\n eventId: event.id,\n storedAt,\n },\n };\n }\n\n /**\n * Get the Hono app instance for testing or composition.\n */\n getApp(): Hono {\n return this.app;\n }\n\n /**\n * Start the HTTP server on the specified port.\n */\n start(port: number): void {\n // Note: In Node.js, you would typically use @hono/node-server\n // For now, this method is a placeholder for actual server startup\n // The actual server binding would be done by the consumer\n console.log(`BLS would start on port ${port}`);\n }\n}\n","import { RelayError } from '../storage/index.js';\n\n/**\n * Configuration for the Pricing Service.\n */\nexport interface PricingConfig {\n /** Base price per byte for event storage */\n basePricePerByte: bigint;\n /** Optional price overrides by event kind */\n kindOverrides?: Map<number, bigint>;\n}\n\n/**\n * Error class for pricing-specific errors.\n */\nexport class PricingError extends RelayError {\n constructor(message: string, code = 'PRICING_ERROR') {\n super(message, code);\n this.name = 'PricingError';\n }\n}\n","import type { NostrEvent } from 'nostr-tools/pure';\nimport { encodeEventToToon } from '../toon/index.js';\nimport type { PricingConfig } from './types.js';\nimport { PricingError } from './types.js';\n\n/**\n * Service for calculating event storage prices with kind-based overrides.\n */\nexport class PricingService {\n private readonly basePricePerByte: bigint;\n private readonly kindOverrides: Map<number, bigint>;\n\n constructor(config: PricingConfig) {\n // Validate basePricePerByte is non-negative\n if (config.basePricePerByte < 0n) {\n throw new PricingError(\n 'basePricePerByte must be non-negative',\n 'INVALID_CONFIG'\n );\n }\n\n // Validate all kindOverrides are non-negative\n if (config.kindOverrides) {\n for (const [kind, price] of config.kindOverrides.entries()) {\n if (price < 0n) {\n throw new PricingError(\n `kindOverride for kind ${kind} must be non-negative`,\n 'INVALID_CONFIG'\n );\n }\n }\n }\n\n this.basePricePerByte = config.basePricePerByte;\n this.kindOverrides = config.kindOverrides ?? new Map();\n }\n\n /**\n * Calculate price for a Nostr event.\n *\n * @param event - The Nostr event to price\n * @returns The calculated price as bigint\n */\n calculatePrice(event: NostrEvent): bigint {\n const toonBytes = encodeEventToToon(event);\n return this.calculatePriceFromBytes(toonBytes, event.kind);\n }\n\n /**\n * Calculate price from raw TOON bytes and event kind.\n *\n * @param bytes - The TOON-encoded event bytes\n * @param kind - The event kind number\n * @returns The calculated price as bigint\n */\n calculatePriceFromBytes(bytes: Uint8Array, kind: number): bigint {\n const pricePerByte = this.getPricePerByte(kind);\n return BigInt(bytes.length) * pricePerByte;\n }\n\n /**\n * Get the effective price per byte for a given kind.\n *\n * @param kind - The event kind number\n * @returns The price per byte (kind override if exists, otherwise base price)\n */\n getPricePerByte(kind: number): bigint {\n return this.kindOverrides.get(kind) ?? this.basePricePerByte;\n }\n}\n","import { readFileSync } from 'fs';\nimport type { PricingConfig } from './types.js';\nimport { PricingError } from './types.js';\n\n/**\n * Load pricing configuration from environment variables.\n *\n * Environment variables:\n * - RELAY_BASE_PRICE_PER_BYTE: Base price per byte (default: \"1\")\n * - RELAY_KIND_OVERRIDES: JSON object mapping kind to price (optional)\n * Format: {\"1\":\"5\",\"30023\":\"100\"}\n *\n * @returns PricingConfig loaded from environment\n * @throws PricingError if env vars contain invalid values\n */\nexport function loadPricingConfigFromEnv(): PricingConfig {\n // Parse base price\n const basePriceStr = process.env['RELAY_BASE_PRICE_PER_BYTE'] ?? '1';\n let basePricePerByte: bigint;\n try {\n basePricePerByte = BigInt(basePriceStr);\n } catch {\n throw new PricingError(\n `Invalid RELAY_BASE_PRICE_PER_BYTE: \"${basePriceStr}\" is not a valid integer`,\n 'INVALID_ENV_CONFIG'\n );\n }\n\n if (basePricePerByte < 0n) {\n throw new PricingError(\n `Invalid RELAY_BASE_PRICE_PER_BYTE: value must be non-negative`,\n 'INVALID_ENV_CONFIG'\n );\n }\n\n // Parse kind overrides if present\n const kindOverridesStr = process.env['RELAY_KIND_OVERRIDES'];\n let kindOverrides: Map<number, bigint> | undefined;\n\n if (kindOverridesStr) {\n kindOverrides = parseKindOverridesJson(kindOverridesStr);\n }\n\n return {\n basePricePerByte,\n kindOverrides,\n };\n}\n\n/**\n * Load pricing configuration from a JSON file.\n *\n * File format:\n * {\n * \"basePricePerByte\": \"10\",\n * \"kindOverrides\": {\n * \"0\": \"0\",\n * \"1\": \"5\",\n * \"30023\": \"100\"\n * }\n * }\n *\n * @param path - Path to the JSON config file\n * @returns PricingConfig loaded from file\n * @throws PricingError if file cannot be read or contains invalid values\n */\nexport function loadPricingConfigFromFile(path: string): PricingConfig {\n let fileContent: string;\n try {\n fileContent = readFileSync(path, 'utf-8');\n } catch (error) {\n throw new PricingError(\n `Failed to read config file: ${error instanceof Error ? error.message : 'Unknown error'}`,\n 'CONFIG_FILE_ERROR'\n );\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(fileContent);\n } catch {\n throw new PricingError(\n `Invalid JSON in config file: ${path}`,\n 'INVALID_FILE_CONFIG'\n );\n }\n\n if (typeof parsed !== 'object' || parsed === null) {\n throw new PricingError(\n `Config file must contain a JSON object`,\n 'INVALID_FILE_CONFIG'\n );\n }\n\n const config = parsed as Record<string, unknown>;\n\n // Parse base price\n const basePriceStr = config['basePricePerByte'];\n if (typeof basePriceStr !== 'string' && typeof basePriceStr !== 'number') {\n throw new PricingError(\n `Config file must contain \"basePricePerByte\" as string or number`,\n 'INVALID_FILE_CONFIG'\n );\n }\n\n let basePricePerByte: bigint;\n try {\n basePricePerByte = BigInt(basePriceStr);\n } catch {\n throw new PricingError(\n `Invalid basePricePerByte: \"${basePriceStr}\" is not a valid integer`,\n 'INVALID_FILE_CONFIG'\n );\n }\n\n if (basePricePerByte < 0n) {\n throw new PricingError(\n `basePricePerByte must be non-negative`,\n 'INVALID_FILE_CONFIG'\n );\n }\n\n // Parse kind overrides if present\n let kindOverrides: Map<number, bigint> | undefined;\n const kindOverridesValue = config['kindOverrides'];\n if (kindOverridesValue !== undefined) {\n if (typeof kindOverridesValue !== 'object' || kindOverridesValue === null) {\n throw new PricingError(\n `kindOverrides must be an object`,\n 'INVALID_FILE_CONFIG'\n );\n }\n\n kindOverrides = new Map();\n const overridesObj = kindOverridesValue as Record<string, unknown>;\n\n for (const [kindStr, priceValue] of Object.entries(overridesObj)) {\n const kind = parseInt(kindStr, 10);\n if (isNaN(kind)) {\n throw new PricingError(\n `Invalid kind in kindOverrides: \"${kindStr}\" is not a valid integer`,\n 'INVALID_FILE_CONFIG'\n );\n }\n\n if (typeof priceValue !== 'string' && typeof priceValue !== 'number') {\n throw new PricingError(\n `Invalid price for kind ${kind}: must be string or number`,\n 'INVALID_FILE_CONFIG'\n );\n }\n\n let price: bigint;\n try {\n price = BigInt(priceValue);\n } catch {\n throw new PricingError(\n `Invalid price for kind ${kind}: \"${priceValue}\" is not a valid integer`,\n 'INVALID_FILE_CONFIG'\n );\n }\n\n if (price < 0n) {\n throw new PricingError(\n `Price for kind ${kind} must be non-negative`,\n 'INVALID_FILE_CONFIG'\n );\n }\n\n kindOverrides.set(kind, price);\n }\n }\n\n return {\n basePricePerByte,\n kindOverrides,\n };\n}\n\n/**\n * Parse kind overrides JSON string to Map.\n */\nfunction parseKindOverridesJson(jsonStr: string): Map<number, bigint> {\n let parsed: unknown;\n try {\n parsed = JSON.parse(jsonStr);\n } catch {\n throw new PricingError(\n `Invalid JSON in RELAY_KIND_OVERRIDES: ${jsonStr}`,\n 'INVALID_ENV_CONFIG'\n );\n }\n\n if (typeof parsed !== 'object' || parsed === null) {\n throw new PricingError(\n `RELAY_KIND_OVERRIDES must be a JSON object`,\n 'INVALID_ENV_CONFIG'\n );\n }\n\n const result = new Map<number, bigint>();\n const obj = parsed as Record<string, unknown>;\n\n for (const [kindStr, priceValue] of Object.entries(obj)) {\n const kind = parseInt(kindStr, 10);\n if (isNaN(kind)) {\n throw new PricingError(\n `Invalid kind in RELAY_KIND_OVERRIDES: \"${kindStr}\" is not a valid integer`,\n 'INVALID_ENV_CONFIG'\n );\n }\n\n if (typeof priceValue !== 'string' && typeof priceValue !== 'number') {\n throw new PricingError(\n `Invalid price for kind ${kind} in RELAY_KIND_OVERRIDES: must be string or number`,\n 'INVALID_ENV_CONFIG'\n );\n }\n\n let price: bigint;\n try {\n price = BigInt(priceValue);\n } catch {\n throw new PricingError(\n `Invalid price for kind ${kind} in RELAY_KIND_OVERRIDES: \"${priceValue}\" is not a valid integer`,\n 'INVALID_ENV_CONFIG'\n );\n }\n\n if (price < 0n) {\n throw new PricingError(\n `Price for kind ${kind} in RELAY_KIND_OVERRIDES must be non-negative`,\n 'INVALID_ENV_CONFIG'\n );\n }\n\n result.set(kind, price);\n }\n\n return result;\n}\n","/**\n * @toon-protocol/relay\n *\n * ILP-gated Nostr relay with Business Logic Server.\n */\n\nexport const VERSION = '0.1.0';\n\n// Types\n// NOTE: the low-level WebSocket relay-server config is exported as\n// `RelayServerConfig` (renamed from `RelayConfig`) so the launcher's\n// `RelayConfig` (formerly `TownConfig`) can take the `RelayConfig` name.\nexport type { RelayServerConfig } from './types.js';\nexport { DEFAULT_RELAY_CONFIG } from './types.js';\n\n// Storage\nexport type { EventStore } from './storage/index.js';\nexport {\n InMemoryEventStore,\n SqliteEventStore,\n RelayError,\n} from './storage/index.js';\n\n// Filters\nexport { matchFilter } from './filters/index.js';\n\n// WebSocket\nexport type { Subscription } from './websocket/index.js';\nexport { ConnectionHandler, NostrRelayServer } from './websocket/index.js';\n\n// TOON encoding/decoding\nexport {\n encodeEventToToon,\n decodeEventFromToon,\n ToonEncodeError,\n ToonDecodeError,\n} from './toon/index.js';\n\n// Business Logic Server\nexport type {\n BlsConfig,\n HandlePacketRequest,\n HandlePacketAcceptResponse,\n HandlePacketRejectResponse,\n HandlePacketResponse,\n} from './bls/index.js';\nexport {\n BlsError,\n ILP_ERROR_CODES,\n BusinessLogicServer,\n isValidPubkey,\n} from './bls/index.js';\n\n// Pricing\nexport type { PricingConfig } from './pricing/index.js';\nexport {\n PricingError,\n PricingService,\n loadPricingConfigFromEnv,\n loadPricingConfigFromFile,\n} from './pricing/index.js';\n\n// Subscriber\nexport type { RelaySubscriberConfig } from './subscriber/index.js';\nexport { RelaySubscriber } from './subscriber/index.js';\n\n// ---------------------------------------------------------------------------\n// Launcher (formerly @toon-protocol/town)\n//\n// SDK-based relay launcher with a one-call programmatic API (startRelay()) and\n// handler implementations for ILP-gated Nostr services. Merged in from the\n// @toon-protocol/town package. Old `startTown`/`Town*` names are re-exported\n// as deprecated aliases.\n// ---------------------------------------------------------------------------\n\n// Relay launcher lifecycle API\nexport { startRelay } from './launcher/town.js';\nexport type {\n RelayConfig,\n RelayInstance,\n RelaySubscription,\n ResolvedRelayConfig,\n} from './launcher/town.js';\n\n// Deprecated launcher aliases (town → relay merge)\nexport { startTown } from './launcher/town.js';\nexport type {\n TownConfig,\n TownInstance,\n TownSubscription,\n ResolvedTownConfig,\n} from './launcher/town.js';\n\n// Health response\nexport { createHealthResponse } from './launcher/health.js';\nexport type {\n HealthConfig,\n HealthResponse,\n TeeHealthInfo,\n} from './launcher/health.js';\n\n// Event storage handler\nexport { createEventStorageHandler } from './launcher/handlers/event-storage-handler.js';\nexport type { EventStorageHandlerConfig } from './launcher/handlers/event-storage-handler.js';\n\n// x402 publish handler\nexport { createX402Handler } from './launcher/handlers/x402-publish-handler.js';\nexport type {\n X402HandlerConfig,\n X402Handler,\n} from './launcher/handlers/x402-publish-handler.js';\n\n// x402 pricing\nexport { calculateX402Price } from './launcher/handlers/x402-pricing.js';\nexport type { X402PricingConfig } from './launcher/handlers/x402-pricing.js';\n\n// x402 pre-flight\nexport { runPreflight } from './launcher/handlers/x402-preflight.js';\nexport type {\n PreflightResult,\n PreflightConfig,\n} from './launcher/handlers/x402-preflight.js';\n\n// x402 settlement\nexport { settleEip3009 } from './launcher/handlers/x402-settlement.js';\nexport type {\n X402SettlementResult,\n X402SettlementConfig,\n} from './launcher/handlers/x402-settlement.js';\n// Deprecated aliases -- use X402SettlementResult / X402SettlementConfig instead\nexport type {\n SettlementResult,\n SettlementConfig,\n} from './launcher/handlers/x402-settlement.js';\n\n// x402 types\nexport type {\n Eip3009Authorization,\n EventStoreLike,\n X402PublishRequest,\n X402PublishResponse,\n X402PricingResponse,\n} from './launcher/handlers/x402-types.js';\nexport {\n EIP_3009_TYPES,\n USDC_EIP712_DOMAIN,\n USDC_ABI,\n} from './launcher/handlers/x402-types.js';\n\n// Re-exports from @toon-protocol/bls removed to avoid circular dependency\n// Downstream consumers should import directly from @toon-protocol/bls instead\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;AAMO,IAAM,eAAe;AAOrB,SAAS,cAAc,QAAyB;AACrD,SAAO,aAAa,KAAK,MAAM;AACjC;AAgEO,IAAM,kBAAkB;AAAA,EAC7B,aAAa;AAAA,EACb,qBAAqB;AAAA,EACrB,gBAAgB;AAClB;AAKO,IAAM,WAAN,cAAuB,WAAW;AAAA,EACvC,YAAY,SAAiB,OAAO,aAAa;AAC/C,UAAM,SAAS,IAAI;AACnB,SAAK,OAAO;AAAA,EACd;AACF;;;AC7FA,SAAS,YAAY;AACrB,SAAS,mBAAmB;AAkBrB,IAAM,sBAAN,MAA0B;AAAA,EAG/B,YACU,QACA,YACR;AAFQ;AACA;AAGR,QACE,OAAO,gBAAgB,UACvB,CAAC,cAAc,OAAO,WAAW,GACjC;AACA,YAAM,IAAI;AAAA,QACR;AAAA,QACA;AAAA,MACF;AAAA,IACF;AACA,SAAK,MAAM,IAAI,KAAK;AACpB,SAAK,YAAY;AAAA,EACnB;AAAA,EAlBQ;AAAA;AAAA;AAAA;AAAA,EAuBA,cAAoB;AAC1B,SAAK,IAAI,KAAK,kBAAkB,OAAO,MAAM;AAC3C,UAAI;AACF,cAAM,OAAO,MAAM,EAAE,IAAI,KAA0B;AACnD,cAAM,WAAW,KAAK,aAAa,IAAI;AACvC,eAAO,EAAE,KAAK,UAAU,SAAS,SAAS,MAAM,GAAG;AAAA,MACrD,SAAS,OAAO;AACd,cAAM,WAAuC;AAAA,UAC3C,QAAQ;AAAA,UACR,MAAM,gBAAgB;AAAA,UACtB,SACE,iBAAiB,QAAQ,MAAM,UAAU;AAAA,QAC7C;AACA,eAAO,EAAE,KAAK,UAAU,GAAG;AAAA,MAC7B;AAAA,IACF,CAAC;AAED,SAAK,IAAI,IAAI,WAAW,CAAC,MAAM;AAC7B,aAAO,EAAE,KAAK;AAAA,QACZ,QAAQ;AAAA,QACR,WAAW,KAAK,IAAI;AAAA,MACtB,CAAC;AAAA,IACH,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASO,aACL,SACyD;AAEzD,QAAI,CAAC,QAAQ,UAAU,CAAC,QAAQ,eAAe,CAAC,QAAQ,MAAM;AAC5D,aAAO;AAAA,QACL,QAAQ;AAAA,QACR,MAAM,gBAAgB;AAAA,QACtB,SAAS;AAAA,MACX;AAAA,IACF;AAGA,QAAI;AACJ,QAAI;AACF,kBAAY,WAAW,KAAK,OAAO,KAAK,QAAQ,MAAM,QAAQ,CAAC;AAAA,IACjE,QAAQ;AACN,aAAO;AAAA,QACL,QAAQ;AAAA,QACR,MAAM,gBAAgB;AAAA,QACtB,SAAS;AAAA,MACX;AAAA,IACF;AAGA,QAAI;AACJ,QAAI;AACF,cAAQ,oBAAoB,SAAS;AAAA,IACvC,SAAS,OAAO;AACd,aAAO;AAAA,QACL,QAAQ;AAAA,QACR,MAAM,gBAAgB;AAAA,QACtB,SAAS,sBAAsB,iBAAiB,QAAQ,MAAM,UAAU,eAAe;AAAA,MACzF;AAAA,IACF;AAGA,QAAI,CAAC,YAAY,KAAK,GAAG;AACvB,aAAO;AAAA,QACL,QAAQ;AAAA,QACR,MAAM,gBAAgB;AAAA,QACtB,SAAS;AAAA,MACX;AAAA,IACF;AAGA,QAAI,KAAK,OAAO,eAAe,MAAM,WAAW,KAAK,OAAO,aAAa;AACvE,UAAI;AACF,aAAK,WAAW,MAAM,KAAK;AAAA,MAC7B,SAAS,OAAO;AACd,cAAM,IAAI;AAAA,UACR,0BAA0B,iBAAiB,QAAQ,MAAM,UAAU,eAAe;AAAA,UAClF;AAAA,QACF;AAAA,MACF;AAEA,aAAO;AAAA,QACL,QAAQ;AAAA,QACR,UAAU;AAAA,UACR,SAAS,MAAM;AAAA,UACf,UAAU,KAAK,IAAI;AAAA,QACrB;AAAA,MACF;AAAA,IACF;AAGA,UAAM,QAAQ,KAAK,OAAO,iBACtB,KAAK,OAAO,eAAe;AAAA,MACzB;AAAA,MACA,MAAM;AAAA,IACR,IACA,OAAO,UAAU,MAAM,IAAI,KAAK,OAAO;AAG3C,QAAI;AACJ,QAAI;AACF,eAAS,OAAO,QAAQ,MAAM;AAAA,IAChC,QAAQ;AACN,aAAO;AAAA,QACL,QAAQ;AAAA,QACR,MAAM,gBAAgB;AAAA,QACtB,SAAS;AAAA,MACX;AAAA,IACF;AAEA,QAAI,SAAS,OAAO;AAClB,aAAO;AAAA,QACL,QAAQ;AAAA,QACR,MAAM,gBAAgB;AAAA,QACtB,SAAS;AAAA,QACT,UAAU;AAAA,UACR,UAAU,MAAM,SAAS;AAAA,UACzB,UAAU,OAAO,SAAS;AAAA,QAC5B;AAAA,MACF;AAAA,IACF;AAGA,QAAI;AACF,WAAK,WAAW,MAAM,KAAK;AAAA,IAC7B,SAAS,OAAO;AACd,YAAM,IAAI;AAAA,QACR,0BAA0B,iBAAiB,QAAQ,MAAM,UAAU,eAAe;AAAA,QAClF;AAAA,MACF;AAAA,IACF;AAEA,UAAM,WAAW,KAAK,IAAI;AAC1B,WAAO;AAAA,MACL,QAAQ;AAAA,MACR,UAAU;AAAA,QACR,SAAS,MAAM;AAAA,QACf;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,SAAe;AACb,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,MAAoB;AAIxB,YAAQ,IAAI,2BAA2B,IAAI,EAAE;AAAA,EAC/C;AACF;;;ACjMO,IAAM,eAAN,cAA2B,WAAW;AAAA,EAC3C,YAAY,SAAiB,OAAO,iBAAiB;AACnD,UAAM,SAAS,IAAI;AACnB,SAAK,OAAO;AAAA,EACd;AACF;;;ACZO,IAAM,iBAAN,MAAqB;AAAA,EACT;AAAA,EACA;AAAA,EAEjB,YAAY,QAAuB;AAEjC,QAAI,OAAO,mBAAmB,IAAI;AAChC,YAAM,IAAI;AAAA,QACR;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAGA,QAAI,OAAO,eAAe;AACxB,iBAAW,CAAC,MAAM,KAAK,KAAK,OAAO,cAAc,QAAQ,GAAG;AAC1D,YAAI,QAAQ,IAAI;AACd,gBAAM,IAAI;AAAA,YACR,yBAAyB,IAAI;AAAA,YAC7B;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAEA,SAAK,mBAAmB,OAAO;AAC/B,SAAK,gBAAgB,OAAO,iBAAiB,oBAAI,IAAI;AAAA,EACvD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,eAAe,OAA2B;AACxC,UAAM,YAAY,kBAAkB,KAAK;AACzC,WAAO,KAAK,wBAAwB,WAAW,MAAM,IAAI;AAAA,EAC3D;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,wBAAwB,OAAmB,MAAsB;AAC/D,UAAM,eAAe,KAAK,gBAAgB,IAAI;AAC9C,WAAO,OAAO,MAAM,MAAM,IAAI;AAAA,EAChC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,gBAAgB,MAAsB;AACpC,WAAO,KAAK,cAAc,IAAI,IAAI,KAAK,KAAK;AAAA,EAC9C;AACF;;;ACrEA,SAAS,oBAAoB;AAetB,SAAS,2BAA0C;AAExD,QAAM,eAAe,QAAQ,IAAI,2BAA2B,KAAK;AACjE,MAAI;AACJ,MAAI;AACF,uBAAmB,OAAO,YAAY;AAAA,EACxC,QAAQ;AACN,UAAM,IAAI;AAAA,MACR,uCAAuC,YAAY;AAAA,MACnD;AAAA,IACF;AAAA,EACF;AAEA,MAAI,mBAAmB,IAAI;AACzB,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAGA,QAAM,mBAAmB,QAAQ,IAAI,sBAAsB;AAC3D,MAAI;AAEJ,MAAI,kBAAkB;AACpB,oBAAgB,uBAAuB,gBAAgB;AAAA,EACzD;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,EACF;AACF;AAmBO,SAAS,0BAA0B,MAA6B;AACrE,MAAI;AACJ,MAAI;AACF,kBAAc,aAAa,MAAM,OAAO;AAAA,EAC1C,SAAS,OAAO;AACd,UAAM,IAAI;AAAA,MACR,+BAA+B,iBAAiB,QAAQ,MAAM,UAAU,eAAe;AAAA,MACvF;AAAA,IACF;AAAA,EACF;AAEA,MAAI;AACJ,MAAI;AACF,aAAS,KAAK,MAAM,WAAW;AAAA,EACjC,QAAQ;AACN,UAAM,IAAI;AAAA,MACR,gCAAgC,IAAI;AAAA,MACpC;AAAA,IACF;AAAA,EACF;AAEA,MAAI,OAAO,WAAW,YAAY,WAAW,MAAM;AACjD,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAEA,QAAM,SAAS;AAGf,QAAM,eAAe,OAAO,kBAAkB;AAC9C,MAAI,OAAO,iBAAiB,YAAY,OAAO,iBAAiB,UAAU;AACxE,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAEA,MAAI;AACJ,MAAI;AACF,uBAAmB,OAAO,YAAY;AAAA,EACxC,QAAQ;AACN,UAAM,IAAI;AAAA,MACR,8BAA8B,YAAY;AAAA,MAC1C;AAAA,IACF;AAAA,EACF;AAEA,MAAI,mBAAmB,IAAI;AACzB,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAGA,MAAI;AACJ,QAAM,qBAAqB,OAAO,eAAe;AACjD,MAAI,uBAAuB,QAAW;AACpC,QAAI,OAAO,uBAAuB,YAAY,uBAAuB,MAAM;AACzE,YAAM,IAAI;AAAA,QACR;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAEA,oBAAgB,oBAAI,IAAI;AACxB,UAAM,eAAe;AAErB,eAAW,CAAC,SAAS,UAAU,KAAK,OAAO,QAAQ,YAAY,GAAG;AAChE,YAAM,OAAO,SAAS,SAAS,EAAE;AACjC,UAAI,MAAM,IAAI,GAAG;AACf,cAAM,IAAI;AAAA,UACR,mCAAmC,OAAO;AAAA,UAC1C;AAAA,QACF;AAAA,MACF;AAEA,UAAI,OAAO,eAAe,YAAY,OAAO,eAAe,UAAU;AACpE,cAAM,IAAI;AAAA,UACR,0BAA0B,IAAI;AAAA,UAC9B;AAAA,QACF;AAAA,MACF;AAEA,UAAI;AACJ,UAAI;AACF,gBAAQ,OAAO,UAAU;AAAA,MAC3B,QAAQ;AACN,cAAM,IAAI;AAAA,UACR,0BAA0B,IAAI,MAAM,UAAU;AAAA,UAC9C;AAAA,QACF;AAAA,MACF;AAEA,UAAI,QAAQ,IAAI;AACd,cAAM,IAAI;AAAA,UACR,kBAAkB,IAAI;AAAA,UACtB;AAAA,QACF;AAAA,MACF;AAEA,oBAAc,IAAI,MAAM,KAAK;AAAA,IAC/B;AAAA,EACF;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,EACF;AACF;AAKA,SAAS,uBAAuB,SAAsC;AACpE,MAAI;AACJ,MAAI;AACF,aAAS,KAAK,MAAM,OAAO;AAAA,EAC7B,QAAQ;AACN,UAAM,IAAI;AAAA,MACR,yCAAyC,OAAO;AAAA,MAChD;AAAA,IACF;AAAA,EACF;AAEA,MAAI,OAAO,WAAW,YAAY,WAAW,MAAM;AACjD,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAEA,QAAM,SAAS,oBAAI,IAAoB;AACvC,QAAM,MAAM;AAEZ,aAAW,CAAC,SAAS,UAAU,KAAK,OAAO,QAAQ,GAAG,GAAG;AACvD,UAAM,OAAO,SAAS,SAAS,EAAE;AACjC,QAAI,MAAM,IAAI,GAAG;AACf,YAAM,IAAI;AAAA,QACR,0CAA0C,OAAO;AAAA,QACjD;AAAA,MACF;AAAA,IACF;AAEA,QAAI,OAAO,eAAe,YAAY,OAAO,eAAe,UAAU;AACpE,YAAM,IAAI;AAAA,QACR,0BAA0B,IAAI;AAAA,QAC9B;AAAA,MACF;AAAA,IACF;AAEA,QAAI;AACJ,QAAI;AACF,cAAQ,OAAO,UAAU;AAAA,IAC3B,QAAQ;AACN,YAAM,IAAI;AAAA,QACR,0BAA0B,IAAI,8BAA8B,UAAU;AAAA,QACtE;AAAA,MACF;AAAA,IACF;AAEA,QAAI,QAAQ,IAAI;AACd,YAAM,IAAI;AAAA,QACR,kBAAkB,IAAI;AAAA,QACtB;AAAA,MACF;AAAA,IACF;AAEA,WAAO,IAAI,MAAM,KAAK;AAAA,EACxB;AAEA,SAAO;AACT;;;AC1OO,IAAM,UAAU;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@toon-protocol/relay",
3
- "version": "1.3.2",
3
+ "version": "1.3.4",
4
4
  "description": "ILP-gated Nostr relay with Business Logic Server",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -30,7 +30,7 @@
30
30
  "license": "MIT",
31
31
  "repository": {
32
32
  "type": "git",
33
- "url": "https://github.com/toon-protocol/town.git",
33
+ "url": "https://github.com/toon-protocol/relay.git",
34
34
  "directory": "packages/relay"
35
35
  },
36
36
  "author": "ALLiDoizCode",
@@ -38,21 +38,21 @@
38
38
  "access": "public"
39
39
  },
40
40
  "dependencies": {
41
- "@hono/node-server": "^1.19.14",
42
- "@toon-format/toon": "^1.4.0",
43
- "@toon-protocol/connector": "^3.13.0",
41
+ "@hono/node-server": "^1.0.0",
42
+ "@toon-protocol/connector": "^3.10.0",
44
43
  "@toon-protocol/core": "^1.4.2",
45
44
  "@toon-protocol/sdk": "^0.5.1",
46
- "better-sqlite3": "^11.10.0",
47
- "hono": "^4.12.26",
48
- "nostr-tools": "^2.23.5",
49
- "viem": "^2.52.2",
50
- "ws": "^8.21.0",
51
- "@toon-protocol/bls": "^1.2.2"
45
+ "@toon-format/toon": "^1.0.0",
46
+ "better-sqlite3": "^11.0.0",
47
+ "hono": "^4.11.10",
48
+ "nostr-tools": "^2.20.0",
49
+ "viem": "^2.47.2",
50
+ "ws": "^8.0.0",
51
+ "@toon-protocol/bls": "^1.2.4"
52
52
  },
53
53
  "devDependencies": {
54
- "@types/better-sqlite3": "^7.6.13",
55
- "@types/ws": "^8.18.1"
54
+ "@types/better-sqlite3": "^7.6.0",
55
+ "@types/ws": "^8.0.0"
56
56
  },
57
57
  "scripts": {
58
58
  "build": "tsup",