Review of Spark's trust model

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

6 sourcesEvidence reviewed through July 14, 2026

What the evidence shows

Technical and product details

  1. 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
  2. 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;
  3. 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",

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.

1

multi-participant privacy rule

Pinned source code · github.com · Pinned or archival · checked 2026-07-14

Open original source
2

counterparty-privacy test

Pinned source code · github.com · Pinned or archival · checked 2026-07-14

Open original source
3

public transfer fields

Pinned source code · github.com · Pinned or archival · checked 2026-07-14

Open original source
4

Lightning transfer to fixed SSP

Pinned source code · github.com · Pinned or archival · checked 2026-07-14

Open original source
5

fixed SSP identity

Pinned source code · github.com · Pinned or archival · checked 2026-07-14

Open original source
6

sanitized mainnet observation

Reproducible audit manifest · Review of Spark's trust model · Pinned or archival · checked 2026-07-14

Open original source

How sources, absence findings, and limitations were evaluated