Review of Spark's trust model

Unilateral-exit readiness

Breez's default mobile persistence does not retain the needed exit tree

The reviewed Breez mobile configuration stores ordinary wallet data in SQLite but supplies no persistent tree store, falling back to an in-memory tree store. The lower-level exit service requires tree nodes that this default mobile path does not durably retain. 123

3 sourcesEvidence reviewed through July 14, 2026

What the evidence shows

Technical and product details

  1. Documented behavior

    The default Breez SQLite backend returns tree_store: None. 1

    Supporting material shown on this page

    SQLite backendPinned source code · github.com · checked 2026-07-14
    Relevant lines from the pinned source · open raw file
    33            PublicKey::from_slice(&identity).map_err(|e| SdkError::Generic(e.to_string()))?;
    34        let db_path = crate::default_storage_path(&self.storage_dir, &network, &identity)?;
    35        let storage = Arc::new(SqliteStorage::new(&db_path)?);
    36        Ok(Arc::new(ResolvedStores {
    37            storage,
    38            tree_store: None,
    39            token_output_store: None,
  2. Technical details

    WalletBuilder replaces a missing store with InMemoryTreeStore; normal leaf refresh uses include_parents=false; process death therefore does not preserve the ancestor graph through that default tree-store path. 23

    Supporting material shown on this page

    WalletBuilder fallbackPinned source code · github.com · checked 2026-07-14
    Relevant lines from the pinned source · open raw file
    120            self.config,
    121            self.spark_signer,
    122            self.session_store
    123                .unwrap_or(Arc::new(InMemorySessionStore::default())),
    124            self.tree_store
    125                .unwrap_or(Arc::new(InMemoryTreeStore::default())),
    126            self.token_output_store
    normal leaf queryPinned source code · github.com · checked 2026-07-14
    Relevant lines from the pinned source · open raw file
    199            .get_non_coordinator_operators()
    200            .map(|op| (op.id, op.client.clone()))
    201            .collect();
    202
    203        let coord_fut =
    204            self.query_nodes(&coordinator_client, false, None, available_leaf_statuses());
    205        let op_futs = operators.iter().map(|(id, client)| async move {
  3. Product consequence

    Durable operator-outage state is an additional integration feature, not a property supplied by the ordinary seed/SQLite setup. 123

    Supporting material shown on this page

    SQLite backendPinned source code · github.com · checked 2026-07-14
    Relevant lines from the pinned source · open raw file
    33            PublicKey::from_slice(&identity).map_err(|e| SdkError::Generic(e.to_string()))?;
    34        let db_path = crate::default_storage_path(&self.storage_dir, &network, &identity)?;
    35        let storage = Arc::new(SqliteStorage::new(&db_path)?);
    36        Ok(Arc::new(ResolvedStores {
    37            storage,
    38            tree_store: None,
    39            token_output_store: None,
    WalletBuilder fallbackPinned source code · github.com · checked 2026-07-14
    Relevant lines from the pinned source · open raw file
    120            self.config,
    121            self.spark_signer,
    122            self.session_store
    123                .unwrap_or(Arc::new(InMemorySessionStore::default())),
    124            self.tree_store
    125                .unwrap_or(Arc::new(InMemoryTreeStore::default())),
    126            self.token_output_store
    normal leaf queryPinned source code · github.com · checked 2026-07-14
    Relevant lines from the pinned source · open raw file
    199            .get_non_coordinator_operators()
    200            .map(|op| (op.id, op.client.clone()))
    201            .collect();
    202
    203        let coord_fut =
    204            self.query_nodes(&coordinator_client, false, None, available_leaf_statuses());
    205        let op_futs = operators.iter().map(|(id, client)| async move {

Scope control

What this does not establish

  • A wallet developer can supply another persistent tree store or a separate encrypted recovery-state exporter; this finding is limited to the reviewed default persistence path. 12

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

SQLite backend

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

Open original source
2

WalletBuilder fallback

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

Open original source
3

normal leaf query

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

Open original source

How sources, absence findings, and limitations were evaluated