Privacy, control, and safety
A private participant's transfer remains readable when any counterparty is public
Spark grants public transfer access when any sender or receiver is readable, and its test suite preserves full participant detail in that case. Because outgoing Lightning leaves go to one fixed, publicly indexed SSP identity, this is a concrete privacy limitation rather than a hypothetical two-wallet edge case. 123456
What the evidence shows
Technical and product details
- Documented behavior
Spark's current multi-sender/multi-receiver access check returns success as soon as any sender or receiver is publicly readable. It is an OR rule across participants, not an all-participants privacy rule. 1
Supporting material shown on this page
multi-participant privacy rulePinned source code · github.com · checked 2026-07-14 Relevant lines from the pinned source · open raw file 1943 return false, fmt.Errorf("transfer %s has no TransferSenders and/or TransferReceivers edges — every transfer must have at least one of each", transfer.ID) 1944 } 1945 1946 walletHandler := NewWalletSettingHandler(h.config) 1947 check := func(pubkey keys.Public) (bool, error) { 1948 if access, ok := accessMap[pubkey]; ok { 1949 return access, nil - Documented behavior
A current repository test locks in the consequence: a public sender/private receiver transfer and a private sender/public receiver transfer are returned with participant detail, while a transfer whose participants are all private is filtered. 23
Supporting material shown on this page
counterparty-privacy testPinned source code · github.com · checked 2026-07-14 Relevant lines from the pinned source · open raw file 31 ctx = knobs.InjectKnobsService(ctx, knobs.NewFixedKnobs(map[string]float64{knobs.KnobPrivacyEnabled: 100})) 32 33 // Privacy-enabled wallets the viewer has no access to: the third transfer's 34 // participants, so its access check fails. 35 _, err = dbTx.WalletSetting.Create().SetOwnerIdentityPublicKey(otherSender).SetPrivateEnabled(true).Save(ctx) 36 require.NoError(t, err)public transfer fieldsPinned source code · github.com · checked 2026-07-14 Relevant lines from the pinned source · open raw file 872 bytes sender_identity_public_key = 2; 873 bytes receiver_identity_public_key = 3; 874 TransferStatus status = 4; 875 uint64 total_value = 5; 876 google.protobuf.Timestamp expiry_time = 6; 877 repeated TransferLeaf leaves = 7; 878 google.protobuf.Timestamp created_time = 8; - Documented behavior
Outgoing Lightning payments transfer the user's selected Spark leaves to the SDK's fixed SSP identity. A dated unauthenticated mainnet query returned that exact SDK-published SSP identity with more than 2.25 million indexed transactions. 456
Supporting material shown on this page
Lightning transfer to fixed SSPPinned source code · github.com · checked 2026-07-14 Relevant lines from the pinned source · open raw file 4191 })); 4192 4193 const transferID = uuidv7(); 4194 4195 const startTransferRequest = 4196 await this.transferService.prepareTransferForLightning( 4197 leavesToSend,fixed SSP identityPinned source code · github.com · checked 2026-07-14 Relevant lines from the pinned source · open raw file 11const SSP_IDENTITY_PUBLIC_KEYS = { 12 LOCAL: "028c094a432d46a0ac95349d792c2e3730bd60c29188db716f56a99e39b95338b4", 13 REGTEST: { 14 PROD: "022bf283544b16c0622daecb79422007d167eca6ce9f0c98c0c49833b1f7170bfe", 15 }, 16 MAINNET: { 17 PROD: "023e33e2920326f64ea31058d44777442d97d7d5cbfcf54e3060bc1695e5261c93",sanitized mainnet observationReproducible audit manifest · Review of Spark's trust model · checked 2026-07-14
Interpretation
Why this matters for Spark's trust model
Spark privacy is not compositional. A user can enable privacy and still have a specific payment's identity, amount, time, and relationship exposed through a public counterparty. The fixed SSP makes this a concrete concern for ordinary Lightning and swap flows rather than a contrived edge case between two hand-picked wallets. 12456
This section states the trust-model consequence of the cited evidence. Conditional or inferred claims remain bounded by the next section.
Scope control
What this does not establish
- The live observation proves that the SSP identity was publicly queryable, not that any sampled counterparty had privacy enabled. When every participant is private and enforcement is fully enabled, the tested implementation filters the transfer. This finding concerns transfer-record visibility, not access to wallet private keys. 26
Sources and excerpts
Primary sources
The relevant details and available source-code excerpts appear alongside the claims above. This list preserves the complete original-source trail for independent verification.
multi-participant privacy rule
Pinned source code · github.com · Pinned or archival · checked 2026-07-14
counterparty-privacy test
Pinned source code · github.com · Pinned or archival · checked 2026-07-14
public transfer fields
Pinned source code · github.com · Pinned or archival · checked 2026-07-14
Lightning transfer to fixed SSP
Pinned source code · github.com · Pinned or archival · checked 2026-07-14
fixed SSP identity
Pinned source code · github.com · Pinned or archival · checked 2026-07-14
sanitized mainnet observation
Reproducible audit manifest · Review of Spark's trust model · Pinned or archival · checked 2026-07-14
How sources, absence findings, and limitations were evaluated