You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Found by an audit of README.md, all of docs/, and CLAUDE.md against the code on fix/766-sphere-lifecycle. Severity-graded: WRONG = a consumer following it fails; STALE = describes an older shape; INCOMPLETE = correct but missing something new.
Headline: the primary onboarding path does not work as documented
Sphere.init calls resolvePaymentsV2Composition(options.walletApi, options.network) before anything else (core/Sphere.ts:651). network is required, and it is compared to walletApi.network as an exact string. Proven by execution:
resolvePaymentsV2Composition({network:'testnet2',baseUrl:'…'}, undefined)
→ INVALID_CONFIG: walletApi.network "testnet2" does not match the Sphere network "undefined"
resolvePaymentsV2Composition({network:'testnet2',baseUrl:'…'}, 'testnet')
→ INVALID_CONFIG: … does not match the Sphere network "testnet"
The docs' pervasive pattern — createBrowserProviders({ network: 'testnet' }) paired with walletApi: { network: 'testnet2' } — fails even after adding network, because all three must be the same literal. And createBrowserProviders/createNodeProviders do not return network in the bundle (impl/browser/index.ts:198-220), so {...providers} cannot supply it.
Affected: README.md (13 samples: :73, 238, 388, 395, 565, 579, 909, 1116, 1147, 1164, 1174, 1198, 1243), docs/INTEGRATION.md (8: :210, 252, 322, 440, 931, 996, 1030, 1171), docs/QUICKSTART-BROWSER.md (all 8 framework samples), docs/API.md (:22, 808, 840 — the init option block does not list network at all).
docs/QUICKSTART-NODEJS.md is the only consumer doc that gets this right, and CLAUDE.md:122-129 states the rule correctly — so the repo contradicts itself.
WRONG — selected; a consumer following any of these fails
File:line
Says
Code
README.md:334-337, 342, 1206
sphere.on('identity:changed', e => e.data.addressIndex)
Handlers receive the payload directly; no .data wrapper (Sphere.ts:4155). Every event.data.* read is undefined
ensureUnicityIdTokenStored/createUnicityIdMinter have zero hits — removed at the 2.0.0 bump. Contradicts README:1110 and API.md:755
INTEGRATION.md:580; API.md:314
Receive dedup "by genesis-stable tokenId via a durable seen-set"
Dedup is by (tokenId, stateHash) (Receive.ts:75-77); there is no seen-set. Pinned by history.test.ts:217
INTEGRATION.md:1285
generateMnemonic(12)
Signature is (strength: 128 | 256) — type error, and bip39 throws
API.md:82
identity is FullIdentity | null
Returns Identity | null — no privateKey
CONNECT.md:648 and CLAUDE.md:235
sphere_subscriberefuses the 4 wallet events
The host answers SUCCESS deliberately (ConnectHost.ts:1112-1114), commented that throwing "silently broke every dApp built before 2.1". A host implementer following this breaks pre-2.1 dApps
docs/CONNECT.md:393, 403, 503, 837, 849, 885 — six autoConnect/ConnectClient samples omit network. The gate does treat a missing network as a mismatch (compatibility.ts:71-76 → 4008), but these are option-focused fragments and the doc establishes network as required at :62, :65, :368, :451. Incomplete rather than wrong.
connect/types.ts:169 — intentDeadlineMs documented as "Default: 90000"; actual DEFAULT_INTENT_DEADLINE_MS = 180000 (ConnectHost.ts:94). The 4200 in that same comment is correct (INTENT_CANCELLED, protocol.ts:95)
tests/unit/connect/protocol-surface.test.ts:31 — repeats the dead METHOD_NOT_FOUND claim (connect/protocol.ts:75 is its only non-test reference; the real answer is PERMISSION_DENIED 4002)
Clean
No doc calls a removed export (scripts/check-removed-refs.mjs confirms). No doc passes network:'dev', accounting, swap or paymentsV2. The #765 network story is accurate everywhere. Connect counts (14/6/13, protocol 2.1, 4 wallet events, 4-of-14 while locked) all match source.
Suggested order
The missing network in every init sample — it breaks the primary onboarding path.
INTEGRATION.md's StorageProvider contract — silent data loss for custom providers.
The wrong signatures and the .data wrapper — each fails at runtime for a copying consumer.
The UnicityIdToken claim in two docs — describes a feature deleted two majors ago.
Everything else.
Audited 2026-09-03 against fix/766-sphere-lifecycle. Line numbers were accurate then; verify before editing.
Correction (2026-09-03, after re-verification)
Two entries in the original filing were wrong and have been fixed above. Recording the change rather than editing silently:
connect/types.ts was misreported. The original said the comment's error code was wrong (4200 → 4201) and the default was 120000. Both were mistaken: 4200 is correct (INTENT_CANCELLED), 4201 is a different code (INTENT_OUTCOME_UNKNOWN, protocol.ts:109), and 120000 is DEFAULT_HANDSHAKE_DEADLINE_MS — a different field entirely. The genuine defect is only that the documented default 90000 should be 180000.
The six Connect client samples were over-graded as WRONG; they are INCOMPLETE, and moved accordingly.
docs/CONNECT.md:875await hasExtension() was dropped entirely — hasExtension(): boolean exists and awaiting a boolean is valid. Redundant, not broken.
Everything else in this issue was verified directly against source and stands, including the network-missing headline, which was proven by executing resolvePaymentsV2Composition.
Found by an audit of
README.md, all ofdocs/, andCLAUDE.mdagainst the code onfix/766-sphere-lifecycle. Severity-graded: WRONG = a consumer following it fails; STALE = describes an older shape; INCOMPLETE = correct but missing something new.Headline: the primary onboarding path does not work as documented
Sphere.initcallsresolvePaymentsV2Composition(options.walletApi, options.network)before anything else (core/Sphere.ts:651).networkis required, and it is compared towalletApi.networkas an exact string. Proven by execution:The docs' pervasive pattern —
createBrowserProviders({ network: 'testnet' })paired withwalletApi: { network: 'testnet2' }— fails even after addingnetwork, because all three must be the same literal. AndcreateBrowserProviders/createNodeProvidersdo not returnnetworkin the bundle (impl/browser/index.ts:198-220), so{...providers}cannot supply it.Affected: README.md (13 samples:
:73, 238, 388, 395, 565, 579, 909, 1116, 1147, 1164, 1174, 1198, 1243), docs/INTEGRATION.md (8::210, 252, 322, 440, 931, 996, 1030, 1171), docs/QUICKSTART-BROWSER.md (all 8 framework samples), docs/API.md (:22, 808, 840— the init option block does not listnetworkat all).docs/QUICKSTART-NODEJS.mdis the only consumer doc that gets this right, andCLAUDE.md:122-129states the rule correctly — so the repo contradicts itself.WRONG — selected; a consumer following any of these fails
sphere.on('identity:changed', e => e.data.addressIndex).datawrapper (Sphere.ts:4155). Everyevent.data.*read isundefinedsphere.on('transport:relay_added' | 'relay_removed' | 'error')SphereEventType— TS error, never fires. These are on the transport provider's own busgetNametagForAddress(1)Sphere.ts:2114). A number returnsundefinedconst { sphere } = await Sphere.load({…})loadreturnsPromise<Sphere>—sphereisundefinedimportFromJSONwithoutwalletApi{success:false, error:'Sphere requires a wallet-api composition…'}relays: ['wss://relay.unicity.network']NETWORKS.mainnet.nostrRelaysis the testnet relaysendMessage(id, txt, {replyToId}),fetchMessages(id, {limit})replyToId?: stringandsince?: number— the objects land in the wrong slotensureUnicityIdTokenStored/createUnicityIdMinterhave zero hits — removed at the 2.0.0 bump. Contradicts README:1110 and API.md:755Receive.ts:75-77); there is no seen-set. Pinned byhistory.test.ts:217generateMnemonic(12)(strength: 128 | 256)— type error, and bip39 throwsidentityisFullIdentity | nullIdentity | null— noprivateKeysphere_subscriberefuses the 4 wallet eventsConnectHost.ts:1112-1114), commented that throwing "silently broke every dApp built before 2.1". A host implementer following this breaks pre-2.1 dAppsPostMessageTransport.forHost()no-args;WebSocketTransport.forClient/forHostforHost(target, options)needs two args; the WS methods do not exist (createServer/createClient)payments-v2/session/,transfer/DEFAULT_AGGREGATOR_URL/DEV_AGGREGATOR_URL, a'dev'trust-base case,RegistryNetworkwith'dev'INCOMPLETE — highest consequence first
docs/CONNECT.md:393, 403, 503, 837, 849, 885— sixautoConnect/ConnectClientsamples omitnetwork. The gate does treat a missing network as a mismatch (compatibility.ts:71-76→ 4008), but these are option-focused fragments and the doc establishesnetworkas required at:62,:65,:368,:451. Incomplete rather than wrong.docs/INTEGRATION.md:818publishes theStorageProviderinterface withsaveTrackedAddressesand no contract. The port now requires read-merge-write, union byindex, greaterupdatedAtwinshidden, serialized per backing store (storage/storage-provider.ts:73-105). A custom provider written from this doc clobbers addresses — the exact Remove the process-global state: one Sphere per network, disposable, and concurrent instances must not interfere #766 data-loss bug.docs/API.md:640—TrackedAddressEntry.indexneeds the uint32 rule; non-conforming rows are dropped on read.docs/API.md:20-44— init options omitnetwork(required),verification,groupChat,market,communications,discoverAddresses,debug,onProgress.Sphere.clear()/import()now destroy live Spheres on the same backing store — no reference doc says so.Genuinely absent
saveTrackedAddressesmerge contract and uint32 rule (source docstring + CHANGELOG only) — the one gap that causes silent data losssphere.isReadyandsphere.networkId— the CHANGELOG tells consumers to replaceSphere.isInitialized()withisReady, but no reference doc lists itTokenRegistry.create()/dispose()/isDisposed/ instancewaitForReady()verificationinit option outside VERIFICATION-WORKERS.md — refactor(sphere)!: remove the remaining process-global state — one Sphere per network, disposable, concurrent-safe #772 is what made it work atSphere.initprewarmSend()/discardPrewarm()— on the publicPaymentsV2interface, documented nowheredocs/MIGRATION-TOKEN-REGISTRY.mdis unreachable — linked only fromCHANGELOG.md:82, absent from README's Documentation section and CLAUDE.md, despite being the sole consumer-facing account of Remove the process-global state: one Sphere per network, disposable, and concurrent instances must not interfere #766/refactor(registry): a Sphere owns its token registry, and destroy() disposes it #767/refactor(sphere)!: remove the remaining process-global state — one Sphere per network, disposable, concurrent-safe #772Two stale SOURCE comments (same defect class)
connect/types.ts:169—intentDeadlineMsdocumented as "Default: 90000"; actualDEFAULT_INTENT_DEADLINE_MS = 180000(ConnectHost.ts:94). The4200in that same comment is correct (INTENT_CANCELLED,protocol.ts:95)tests/unit/connect/protocol-surface.test.ts:31— repeats the deadMETHOD_NOT_FOUNDclaim (connect/protocol.ts:75is its only non-test reference; the real answer isPERMISSION_DENIED4002)Clean
No doc calls a removed export (
scripts/check-removed-refs.mjsconfirms). No doc passesnetwork:'dev',accounting,swaporpaymentsV2. The #765 network story is accurate everywhere. Connect counts (14/6/13, protocol 2.1, 4 wallet events, 4-of-14 while locked) all match source.Suggested order
networkin every init sample — it breaks the primary onboarding path.INTEGRATION.md'sStorageProvidercontract — silent data loss for custom providers..datawrapper — each fails at runtime for a copying consumer.UnicityIdTokenclaim in two docs — describes a feature deleted two majors ago.Audited 2026-09-03 against
fix/766-sphere-lifecycle. Line numbers were accurate then; verify before editing.Correction (2026-09-03, after re-verification)
Two entries in the original filing were wrong and have been fixed above. Recording the change rather than editing silently:
connect/types.tswas misreported. The original said the comment's error code was wrong (4200→4201) and the default was120000. Both were mistaken:4200is correct (INTENT_CANCELLED),4201is a different code (INTENT_OUTCOME_UNKNOWN,protocol.ts:109), and120000isDEFAULT_HANDSHAKE_DEADLINE_MS— a different field entirely. The genuine defect is only that the documented default90000should be180000.docs/CONNECT.md:875await hasExtension()was dropped entirely —hasExtension(): booleanexists and awaiting a boolean is valid. Redundant, not broken.Everything else in this issue was verified directly against source and stands, including the
network-missing headline, which was proven by executingresolvePaymentsV2Composition.