From eb49e4d81a20dbf43cfe446ddb0e0b2e87a0560f Mon Sep 17 00:00:00 2001 From: Vladimir Rogojin Date: Thu, 4 Jun 2026 15:23:35 +0200 Subject: [PATCH] feat(cli)(sphere-sdk#394): wire publishToIpfs + cidFetchGateways in buildSphereProviders MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes the CLI half of sphere-sdk issue #394. `buildSphereProviders` now imports `createUxfCarPublisher` + `DEFAULT_IPFS_GATEWAYS` from `@unicitylabs/sphere-sdk/impl/nodejs` (re-exported on the SDK side as part of #394) and exposes: - `publishToIpfs` — outgoing UXF CID-delivery callback wired from `createUxfCarPublisher(ipfsGateways)`. - `cidFetchGateways` — recipient-side fetch list so `uxf-cid` bundles resolve correctly on arrival. Both flow through `SphereProvidersBundle` and into `Sphere.init`: - `src/host/sphere-init.ts` adds explicit pass-through (the call site unpacks named fields, not a spread). - `src/legacy/legacy-cli.ts` is untouched — its `Sphere.init` already spreads `...initProviders`, which inherits the new fields automatically. Same for the migration call site at line ~2240. Crucially, this does NOT re-enable the deprecated `IpfsStorageProvider` (deprecated for wallet token storage; replaced by Profile). The UXF bundle publisher is a separate concern that survives the deprecation. We avoid the coupling by importing `createUxfCarPublisher` directly rather than passing `tokenSync.ipfs.enabled: true` to `createNodeProviders`. New config field `SphereProvidersConfig.ipfsGateways` lets callers override the gateway list (defaults to `DEFAULT_IPFS_GATEWAYS` which honors the `SPHERE_IPFS_GATEWAY` env override). Pass an empty array to disable the publisher entirely (sends > RELAY_SAFE_CAP_BYTES will then fail at the SDK's `INLINE_CAR_TOO_LARGE` pre-flight). Verified end-to-end via the round-trip soak at sphere-sdk:manual-test-roundtrip-391.sh with STRICT_CID_DELIVERY=1: 4-hop A→B→A→B→A succeeded, balance reconciliation passed (alice -0.5 UCT, bob +0.5 UCT), no DUPLICATE_BUNDLE_MEMBERSHIP, no INLINE_CAR_TOO_LARGE. With sphere-sdk #394b's 512 KiB cap the realistic 121 KB bundle stays inline so CID delivery isn't actually exercised here; for >512 KiB bundles the publisher path is the same mechanism, end-to-end testing pending soak coverage for that range. Pairs with sphere-sdk PR (branch feat/issue-394-cid-delivery-wiring). --- src/host/sphere-init.ts | 4 +++ src/shared/sphere-providers.ts | 64 +++++++++++++++++++++++++++++++--- 2 files changed, 63 insertions(+), 5 deletions(-) diff --git a/src/host/sphere-init.ts b/src/host/sphere-init.ts index 951b792..a58a96a 100644 --- a/src/host/sphere-init.ts +++ b/src/host/sphere-init.ts @@ -87,6 +87,10 @@ export async function initSphere(): Promise { oracle: providers.oracle, network: config.network, autoGenerate: false, + // sphere-sdk #394 — pass through the UXF CID-delivery wiring so + // sends of > RELAY_SAFE_CAP_BYTES bundles can promote to CID. + ...(providers.publishToIpfs ? { publishToIpfs: providers.publishToIpfs } : {}), + ...(providers.cidFetchGateways ? { cidFetchGateways: providers.cidFetchGateways } : {}), }); return sphere; diff --git a/src/shared/sphere-providers.ts b/src/shared/sphere-providers.ts index c0436ae..aa7cac8 100644 --- a/src/shared/sphere-providers.ts +++ b/src/shared/sphere-providers.ts @@ -24,7 +24,12 @@ import * as fs from 'node:fs'; import * as path from 'node:path'; -import { createNodeProviders } from '@unicitylabs/sphere-sdk/impl/nodejs'; +import { + createNodeProviders, + createUxfCarPublisher, + DEFAULT_IPFS_GATEWAYS, + type PublishToIpfsCallback, +} from '@unicitylabs/sphere-sdk/impl/nodejs'; import { createNodeProfileProviders } from '@unicitylabs/sphere-sdk/profile/node'; import type { NetworkType } from '@unicitylabs/sphere-sdk'; @@ -99,6 +104,21 @@ export interface SphereProvidersConfig { readonly market?: boolean; /** Enable the group-chat module. Default false. */ readonly groupChat?: boolean; + /** + * IPFS gateway URLs for outgoing UXF CID-delivery + incoming CID + * fetch. Defaults to `DEFAULT_IPFS_GATEWAYS` (which honors the + * `SPHERE_IPFS_GATEWAY` env override at module load time). Pass an + * empty array to disable the publisher entirely — large bundles + * will then throw `INLINE_CAR_TOO_LARGE` again. + * + * Sphere-sdk issue #394 — the CLI wires `publishToIpfs` + + * `cidFetchGateways` so the auto-CID promotion path + * (`AUTOMATED_CID_DELIVERY_ENABLED = true` post-#394, triggered at + * `RELAY_SAFE_CAP_BYTES = 96 KiB`) has a working publisher behind + * it. Without this wiring the SDK throws on multi-hop bundles + * that exceed the relay event ceiling. + */ + readonly ipfsGateways?: readonly string[]; } /** @@ -119,6 +139,19 @@ export interface SphereProvidersBundle { readonly price?: ReturnType['price']; readonly market?: ReturnType['market']; readonly groupChat?: ReturnType['groupChat']; + /** + * Outgoing UXF CID-delivery callback (sphere-sdk issue #394). Wired + * from `createUxfCarPublisher(ipfsGateways)`; passed through to + * `Sphere.init` so the SDK's `delivery: { kind: 'auto' }` path can + * promote bundles > `RELAY_SAFE_CAP_BYTES` (96 KiB) to CID-over-Nostr. + */ + readonly publishToIpfs?: PublishToIpfsCallback; + /** + * IPFS gateway URLs for the recipient pipeline's stream-fetch of + * incoming `uxf-cid` bundles. Without this, every `uxf-cid` event + * is silently dropped on receive. + */ + readonly cidFetchGateways?: readonly string[]; } /** @@ -128,9 +161,16 @@ export interface SphereProvidersBundle { * aggregator-pointer layer's `RootTrustBase` is the same instance the * rest of Sphere uses (SPEC §8.4.2 H6). * - * `tokenSync.ipfs` is NOT passed to `createNodeProviders` — that's the - * deprecated IPNS-based mutable-pointer path that this migration - * removes. Profile + aggregator pointer + IPFS CAR is the replacement. + * `tokenSync.ipfs` (the deprecated IPNS-based wallet-storage flag) is + * NOT passed to `createNodeProviders` — Profile + aggregator pointer + + * IPFS CAR replaced that wallet-storage path. The outgoing UXF + * bundle publisher (`publishToIpfs`) is a SEPARATE concern that + * survives the migration: bundles still need to be pinnable for the + * CID-by-reference (`uxf-cid`) Nostr delivery path. Sphere-sdk + * issue #394 closes the wiring here by importing the canonical + * `createUxfCarPublisher` from `@unicitylabs/sphere-sdk/impl/nodejs` + * directly, avoiding the deprecated `IpfsStorageProvider` tail that + * `tokenSync.ipfs.enabled: true` would otherwise also activate. */ export function buildSphereProviders( config: SphereProvidersConfig, @@ -141,7 +181,9 @@ export function buildSphereProviders( tokensDir: config.tokensDir, market: config.market ?? false, groupChat: config.groupChat ?? false, - // tokenSync.ipfs deliberately omitted — Profile replaces it. + // tokenSync.ipfs deliberately omitted — Profile replaces the + // deprecated IpfsStorageProvider wallet-storage path. The UXF + // bundle publisher below is wired independently. }); const profile = createNodeProfileProviders({ @@ -150,6 +192,16 @@ export function buildSphereProviders( oracle: legacy.oracle, }); + // Sphere-sdk issue #394 — wire the UXF CID-delivery publisher and + // the recipient's fetch-gateway list. The default gateway list + // honors the `SPHERE_IPFS_GATEWAY` env override at module load. + // Empty array disables — sends > 96 KiB will throw + // `INLINE_CAR_TOO_LARGE` if the kill-switch is also off. + const ipfsGateways: readonly string[] = + config.ipfsGateways ?? [...DEFAULT_IPFS_GATEWAYS]; + const publishToIpfs: PublishToIpfsCallback | undefined = + ipfsGateways.length > 0 ? createUxfCarPublisher(ipfsGateways) : undefined; + return { storage: profile.storage, tokenStorage: profile.tokenStorage, @@ -159,5 +211,7 @@ export function buildSphereProviders( price: legacy.price, market: legacy.market, groupChat: legacy.groupChat, + publishToIpfs, + cidFetchGateways: ipfsGateways.length > 0 ? ipfsGateways : undefined, }; }