@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.
@@ -115,7 +115,6 @@ var RelayError = class extends Error {
115
115
  this.code = code;
116
116
  this.name = "RelayError";
117
117
  }
118
- code;
119
118
  };
120
119
  function isReplaceableKind(kind) {
121
120
  return kind >= 1e4 && kind <= 19999 && !(kind >= 10032 && kind <= 10099);
@@ -432,8 +431,6 @@ var ConnectionHandler = class {
432
431
  this.eventStore = eventStore;
433
432
  this.config = { ...DEFAULT_RELAY_CONFIG, ...config };
434
433
  }
435
- ws;
436
- eventStore;
437
434
  subscriptions = /* @__PURE__ */ new Map();
438
435
  config;
439
436
  /**
@@ -574,7 +571,6 @@ var NostrRelayServer = class {
574
571
  this.eventStore = eventStore;
575
572
  this.config = { ...DEFAULT_RELAY_CONFIG, ...config };
576
573
  }
577
- eventStore;
578
574
  wss = null;
579
575
  handlers = /* @__PURE__ */ new Map();
580
576
  config;
@@ -1250,6 +1246,48 @@ import {
1250
1246
  fromMnemonic,
1251
1247
  fromSecretKey
1252
1248
  } from "@toon-protocol/sdk";
1249
+
1250
+ // src/launcher/handlers/oblivious-write-handler.ts
1251
+ import { verifyEvent as verifyEvent2 } from "nostr-tools/pure";
1252
+ function createObliviousWriteHandler(config) {
1253
+ return {
1254
+ async handleWrite(c) {
1255
+ let body;
1256
+ try {
1257
+ body = await c.req.json();
1258
+ } catch {
1259
+ return c.json({ error: "Invalid request body" }, 400);
1260
+ }
1261
+ if (!body.event) {
1262
+ return c.json({ error: "Missing required field: event" }, 400);
1263
+ }
1264
+ const event = body.event;
1265
+ const payer = c.req.header("X-TOON-Payer");
1266
+ const amount = c.req.header("X-TOON-Amount");
1267
+ const chain = c.req.header("X-TOON-Chain");
1268
+ console.log(
1269
+ `[oblivious-write] event=${event.id} payer=${payer ?? "-"} amount=${amount ?? "-"} chain=${chain ?? "-"}`
1270
+ );
1271
+ if (!config.devMode && !verifyEvent2(event)) {
1272
+ return c.json({ error: "Invalid event signature" }, 422);
1273
+ }
1274
+ config.eventStore.store(event);
1275
+ config.onStored?.(event);
1276
+ return c.json(
1277
+ {
1278
+ eventId: event.id,
1279
+ storedAt: Math.floor(Date.now() / 1e3),
1280
+ payer,
1281
+ amount,
1282
+ chain
1283
+ },
1284
+ 200
1285
+ );
1286
+ }
1287
+ };
1288
+ }
1289
+
1290
+ // src/launcher/town.ts
1253
1291
  import {
1254
1292
  BootstrapService,
1255
1293
  createDiscoveryTracker,
@@ -1329,6 +1367,12 @@ async function startRelay(config) {
1329
1367
  "RelayConfig: provide either connector or connectorUrl, not both"
1330
1368
  );
1331
1369
  }
1370
+ const obliviousMode = config.obliviousMode ?? process.env["TOON_OBLIVIOUS_MODE"] === "true";
1371
+ if (obliviousMode && (hasConnector || hasConnectorUrl)) {
1372
+ throw new Error(
1373
+ "RelayConfig: obliviousMode is mutually exclusive with connector/connectorUrl (an oblivious relay runs no embedded connector)"
1374
+ );
1375
+ }
1332
1376
  if (hasConnectorUrl && config.ilpAddress === void 0) {
1333
1377
  throw new Error(
1334
1378
  "RelayConfig: ilpAddress is required when connectorUrl is set (must fall under the parent connector prefix, e.g. g.townhouse.<self>)"
@@ -1346,7 +1390,7 @@ async function startRelay(config) {
1346
1390
  const connectorUrl = config.connectorUrl;
1347
1391
  const basePricePerByte = config.feePerEvent !== void 0 ? BigInt(config.feePerEvent) : config.basePricePerByte ?? 10n;
1348
1392
  const routingBufferPercent = config.routingBufferPercent ?? 10;
1349
- const x402Enabled = config.x402Enabled ?? false;
1393
+ const x402Enabled = obliviousMode ? false : config.x402Enabled ?? false;
1350
1394
  const knownPeers = [...config.knownPeers ?? []];
1351
1395
  const dataDir = config.dataDir ?? "./data";
1352
1396
  const devMode = config.devMode ?? false;
@@ -1366,7 +1410,7 @@ async function startRelay(config) {
1366
1410
  const publishSeedEntryFlag = config.publishSeedEntry ?? false;
1367
1411
  const externalRelayUrl = config.externalRelayUrl ?? (config.ator?.enabled && config.ator.anonAddress ? config.ator.anonAddress : void 0);
1368
1412
  const requestedChain = process.env["TOON_CHAIN"] || config.chain;
1369
- const relayOnly = requestedChain === "none";
1413
+ const relayOnly = requestedChain === "none" || obliviousMode;
1370
1414
  if (relayOnly) {
1371
1415
  console.log("[Town] connector.relay_only", {
1372
1416
  reason: "no settlement chain configured (chain=none)"
@@ -1402,10 +1446,11 @@ async function startRelay(config) {
1402
1446
  seedRelays,
1403
1447
  publishSeedEntry: publishSeedEntryFlag,
1404
1448
  ...externalRelayUrl && { externalRelayUrl },
1405
- chain: chainConfig.name
1449
+ chain: chainConfig.name,
1450
+ obliviousMode
1406
1451
  };
1407
1452
  let autoCreatedConnector = null;
1408
- if (!hasConnector) {
1453
+ if (!hasConnector && !obliviousMode) {
1409
1454
  const btpServerPort = config.btpServerPort ?? 3e3;
1410
1455
  const connectorLogger = createConnectorLogger(
1411
1456
  nodeId,
@@ -1492,7 +1537,7 @@ async function startRelay(config) {
1492
1537
  const effectiveConnector = config.connector ?? autoCreatedConnector;
1493
1538
  mkdirSync(dataDir, { recursive: true });
1494
1539
  const dbPath = join(dataDir, "events.db");
1495
- const eventStore = new SqliteEventStore(dbPath);
1540
+ const eventStore = config.eventStore ?? new SqliteEventStore(dbPath);
1496
1541
  const effectiveChainRpcUrls = config.chainRpcUrls ?? (relayOnly ? void 0 : { [chainKey]: chainConfig.rpcUrl });
1497
1542
  const effectivePreferredTokens = config.preferredTokens ?? (relayOnly ? void 0 : { [chainKey]: chainConfig.usdcAddress });
1498
1543
  const effectiveTokenNetworks = config.tokenNetworks ?? (chainConfig.tokenNetworkAddress ? { [chainKey]: chainConfig.tokenNetworkAddress } : void 0);
@@ -1518,13 +1563,13 @@ async function startRelay(config) {
1518
1563
  preferredTokens: effectivePreferredTokens,
1519
1564
  tokenNetworks: effectiveTokenNetworks
1520
1565
  };
1521
- if (effectiveConnector.openChannel && effectiveConnector.getChannelState) {
1566
+ if (effectiveConnector?.openChannel && effectiveConnector.getChannelState) {
1522
1567
  channelClient = createDirectChannelClient(
1523
1568
  effectiveConnector
1524
1569
  );
1525
1570
  }
1526
1571
  }
1527
- const adminClient = createDirectConnectorAdmin(effectiveConnector);
1572
+ const adminClient = effectiveConnector ? createDirectConnectorAdmin(effectiveConnector) : void 0;
1528
1573
  const verifier = createVerificationPipeline({ devMode });
1529
1574
  const pricer = createPricingValidator({
1530
1575
  basePricePerByte,
@@ -1639,38 +1684,40 @@ async function startRelay(config) {
1639
1684
  })
1640
1685
  );
1641
1686
  });
1642
- app.post("/handle-packet", async (c) => {
1643
- try {
1644
- const body = await c.req.json();
1645
- if (body.amount === void 0 || body.amount === null || body.destination === void 0 || body.destination === null || body.data === void 0 || body.data === null) {
1646
- return c.json(
1647
- { accept: false, code: "F00", message: "Missing required fields" },
1648
- 400
1649
- );
1650
- }
1651
- const result = await handlePacket(body);
1652
- if (result.accept) {
1653
- try {
1654
- const toonBytes = Buffer.from(body.data, "base64");
1655
- const decoded = decodeEventFromToon2(toonBytes);
1656
- if (decoded && decoded.kind === ILP_PEER_INFO_KIND) {
1657
- discoveryTrackerRef.current?.processEvent(decoded);
1687
+ if (!obliviousMode) {
1688
+ app.post("/handle-packet", async (c) => {
1689
+ try {
1690
+ const body = await c.req.json();
1691
+ if (body.amount === void 0 || body.amount === null || body.destination === void 0 || body.destination === null || body.data === void 0 || body.data === null) {
1692
+ return c.json(
1693
+ { accept: false, code: "F00", message: "Missing required fields" },
1694
+ 400
1695
+ );
1696
+ }
1697
+ const result = await handlePacket(body);
1698
+ if (result.accept) {
1699
+ try {
1700
+ const toonBytes = Buffer.from(body.data, "base64");
1701
+ const decoded = decodeEventFromToon2(toonBytes);
1702
+ if (decoded && decoded.kind === ILP_PEER_INFO_KIND) {
1703
+ discoveryTrackerRef.current?.processEvent(decoded);
1704
+ }
1705
+ } catch {
1658
1706
  }
1659
- } catch {
1660
1707
  }
1708
+ return c.json(result, result.accept ? 200 : 400);
1709
+ } catch (error) {
1710
+ console.error("[Town] handle-packet error:", error);
1711
+ return c.json(
1712
+ { accept: false, code: "T00", message: "Internal server error" },
1713
+ 500
1714
+ );
1661
1715
  }
1662
- return c.json(result, result.accept ? 200 : 400);
1663
- } catch (error) {
1664
- console.error("[Town] handle-packet error:", error);
1665
- return c.json(
1666
- { accept: false, code: "T00", message: "Internal server error" },
1667
- 500
1668
- );
1669
- }
1670
- });
1671
- const ilpClient = createDirectIlpClient(effectiveConnector, {
1716
+ });
1717
+ }
1718
+ const ilpClient = effectiveConnector ? createDirectIlpClient(effectiveConnector, {
1672
1719
  toonDecoder: (bytes) => decodeEventFromToon2(bytes)
1673
- });
1720
+ }) : void 0;
1674
1721
  let x402WalletClient;
1675
1722
  let x402PublicClient;
1676
1723
  if (x402Enabled) {
@@ -1704,21 +1751,38 @@ async function startRelay(config) {
1704
1751
  }
1705
1752
  }
1706
1753
  }
1707
- const x402Handler = createX402Handler({
1708
- x402Enabled,
1709
- chainConfig,
1710
- basePricePerByte,
1711
- routingBufferPercent,
1712
- facilitatorAddress: config.facilitatorAddress ?? identity.evmAddress,
1713
- ownPubkey: identity.pubkey,
1714
- devMode,
1715
- eventStore,
1716
- ilpClient,
1717
- walletClient: x402WalletClient,
1718
- publicClient: x402PublicClient
1719
- });
1720
- app.get("/publish", (c) => x402Handler.handlePublish(c));
1721
- app.post("/publish", (c) => x402Handler.handlePublish(c));
1754
+ if (obliviousMode) {
1755
+ const obliviousHandler = createObliviousWriteHandler({
1756
+ eventStore,
1757
+ devMode,
1758
+ onStored: (event) => {
1759
+ try {
1760
+ wsRelayRef.current?.broadcastEvent(event);
1761
+ } catch {
1762
+ }
1763
+ if (event.kind === ILP_PEER_INFO_KIND) {
1764
+ discoveryTrackerRef.current?.processEvent(event);
1765
+ }
1766
+ }
1767
+ });
1768
+ app.post("/write", (c) => obliviousHandler.handleWrite(c));
1769
+ } else {
1770
+ const x402Handler = createX402Handler({
1771
+ x402Enabled,
1772
+ chainConfig,
1773
+ basePricePerByte,
1774
+ routingBufferPercent,
1775
+ facilitatorAddress: config.facilitatorAddress ?? identity.evmAddress,
1776
+ ownPubkey: identity.pubkey,
1777
+ devMode,
1778
+ eventStore,
1779
+ ilpClient,
1780
+ walletClient: x402WalletClient,
1781
+ publicClient: x402PublicClient
1782
+ });
1783
+ app.get("/publish", (c) => x402Handler.handlePublish(c));
1784
+ app.post("/publish", (c) => x402Handler.handlePublish(c));
1785
+ }
1722
1786
  const blsServer = serve({
1723
1787
  fetch: app.fetch,
1724
1788
  port: blsPort
@@ -1732,11 +1796,15 @@ async function startRelay(config) {
1732
1796
  await wsRelay.start();
1733
1797
  await new Promise((resolve) => setTimeout(resolve, 500));
1734
1798
  let running = true;
1735
- bootstrapService.setConnectorAdmin(adminClient);
1799
+ if (adminClient) {
1800
+ bootstrapService.setConnectorAdmin(adminClient);
1801
+ }
1736
1802
  if (channelClient) {
1737
1803
  bootstrapService.setChannelClient(channelClient);
1738
1804
  }
1739
- bootstrapService.setIlpClient(ilpClient);
1805
+ if (ilpClient) {
1806
+ bootstrapService.setIlpClient(ilpClient);
1807
+ }
1740
1808
  bootstrapService.on((event) => {
1741
1809
  switch (event.type) {
1742
1810
  case "bootstrap:peer-registered":
@@ -1749,7 +1817,7 @@ async function startRelay(config) {
1749
1817
  break;
1750
1818
  }
1751
1819
  });
1752
- if (effectiveConnector.setPacketHandler) {
1820
+ if (effectiveConnector?.setPacketHandler) {
1753
1821
  effectiveConnector.setPacketHandler(async (request) => {
1754
1822
  const result = await handlePacket(request);
1755
1823
  if (result.accept && discoveryTrackerRef.current) {
@@ -1775,7 +1843,9 @@ async function startRelay(config) {
1775
1843
  secretKey: identity.secretKey,
1776
1844
  settlementInfo
1777
1845
  });
1778
- discoveryTracker.setConnectorAdmin(adminClient);
1846
+ if (adminClient) {
1847
+ discoveryTracker.setConnectorAdmin(adminClient);
1848
+ }
1779
1849
  if (channelClient) {
1780
1850
  discoveryTracker.setChannelClient(channelClient);
1781
1851
  }
@@ -1845,7 +1915,7 @@ async function startRelay(config) {
1845
1915
  eventStore.store(ilpInfoEvent);
1846
1916
  const firstPeer = knownPeers[0];
1847
1917
  const genesisResult = results[0];
1848
- if (firstPeer && genesisResult) {
1918
+ if (ilpClient && firstPeer && genesisResult) {
1849
1919
  const genesisIlpAddress = genesisResult.peerInfo.ilpAddress;
1850
1920
  const toonBytes = encodeEventToToon3(ilpInfoEvent);
1851
1921
  const base64Toon = Buffer.from(toonBytes).toString("base64");
@@ -1901,7 +1971,7 @@ async function startRelay(config) {
1901
1971
  eventStore.store(serviceDiscoveryEvent);
1902
1972
  const firstPeer = knownPeers[0];
1903
1973
  const genesisResult = results[0];
1904
- if (firstPeer && genesisResult) {
1974
+ if (ilpClient && firstPeer && genesisResult) {
1905
1975
  const genesisIlpAddress = genesisResult.peerInfo.ilpAddress;
1906
1976
  const sdToonBytes = encodeEventToToon3(serviceDiscoveryEvent);
1907
1977
  const sdBase64Toon = Buffer.from(sdToonBytes).toString("base64");
@@ -2030,4 +2100,4 @@ export {
2030
2100
  startRelay,
2031
2101
  startTown
2032
2102
  };
2033
- //# sourceMappingURL=chunk-4RYYKZXO.js.map
2103
+ //# sourceMappingURL=chunk-NYKVCNJL.js.map