Skip to content

Conversation

@coreyphillips
Copy link
Collaborator

Summary

This PR adds two major features for mobile optimization:

  1. Light Mode - Build-time configuration to minimize memory footprint for constrained environments
  2. Runtime Sync Intervals - Ability to adjust background task intervals at runtime for battery saving

Mobile Developer Usage

Android: Battery Saving for Foreground Service

When your app goes to background and only the foreground service keeps the node alive:

// Switch to battery-saving intervals
val intervals = LdkNode.batterySavingSyncIntervals()
node.updateSyncIntervals(intervals)

// When app returns to foreground, restore defaults
node.updateSyncIntervals(RuntimeSyncIntervals())

The batterySavingSyncIntervals() preset uses longer intervals:

Task Default Battery Saving
Peer reconnection 1 min 5 min
RGS sync 1 hour 2 hours
Pathfinding scores 1 hour 4 hours
Node announcements 1 hour 2 hours
On-chain wallet sync 80 sec 5 min
Lightning wallet sync 30 sec 2 min
Fee rate cache 10 min 30 min

iOS: Notification Service Extension (25MB Limit)

For iOS NSE to receive payments while staying under Apple's ~24MB memory limit, use light mode minimal:

let builder = Builder()
builder.setLightModeMinimal()  // Critical for iOS NSE

// Recommended: Disable background sync entirely for NSE
builder.setEsploraSyncConfig(EsploraSyncConfig(backgroundSyncConfig: nil))

// ... other configuration (entropy, network, etc.)
let node = try builder.build()

What setLightModeMinimal() enables:

Setting Effect
single_threaded_runtime: true Saves ~10-15MB by using single-threaded Tokio runtime
disable_listening: true No incoming connection listeners
disable_peer_reconnection: true Must connect to peers manually
disable_node_announcements: true No gossip broadcasts
disable_rgs_sync: true Uses cached routing data only
disable_pathfinding_scores_sync: true Uses local scores only
disable_liquidity_handler: true No LSPS1/LSPS2 background processing

The node can still:

  • Start and stop
  • Connect to peers manually via node.connect()
  • Receive payments (the primary NSE use case)
  • Send payments
  • Sync wallets manually via node.syncWallets()

Important: If backgroundSyncConfig is set to nil at build time, wallet sync intervals (onchainWalletSyncIntervalSecs, lightningWalletSyncIntervalSecs, feeRateCacheUpdateIntervalSecs) cannot be updated at runtime via updateSyncIntervals(). The other intervals (peer reconnection, RGS, pathfinding scores, node announcements) will still update. For iOS NSE, this is the recommended approach since you'll call syncWallets() manually and the extension is short-lived.

Recommended iOS NSE flow:

// In NotificationService.swift
func didReceive(_ request: UNNotificationRequest, 
                withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {

    let builder = Builder()
    builder.setLightModeMinimal()
    builder.setNetwork(Network.bitcoin)
    builder.setEsploraSyncConfig(EsploraSyncConfig(backgroundSyncConfig: nil))
    // ... entropy/seed configuration

    let node = try builder.build()
    try node.start()

    // Connect to your LSP manually (peer reconnection is disabled)
    try node.connect(nodeId: lspNodeId, address: lspAddress, persist: false)

    // Manually sync to pick up the incoming payment
    try node.syncWallets()

    // Process payment, update notification content
    // ...

    node.stop()
    contentHandler(notificationContent)
}

Changes

  • Add LightModeConfig for resource-constrained environments (iOS NSE, Android background)
  • Implement single_threaded_runtime flag to reduce memory (~10-15MB savings)
  • Implement disable_liquidity_handler flag
  • Add RuntimeSyncIntervals for runtime-adjustable background task intervals
  • Add battery_saving_sync_intervals() convenience function to UDL bindings
  • Fix indentation in skip_listening else block
  • Add documentation clarifying behavior when background_sync_config is None

Test Plan

  • Verify Android can call updateSyncIntervals() at runtime and intervals change
  • Verify batterySavingSyncIntervals() is accessible from Kotlin/Swift bindings
  • Verify iOS NSE can build node with setLightModeMinimal() and stay under 25MB
  • Verify node can still receive payments in light mode via manual syncWallets()
  • Verify updateSyncIntervals() correctly updates non-wallet intervals even when backgroundSyncConfig is nil
  • Run existing unit test suite

… optimization

- Add LightModeConfig for resource-constrained environments (iOS NSE, Android background)
  - single_threaded_runtime: reduces memory (~10-15MB savings)
  - disable_listening: skip inbound connection listeners
  - disable_peer_reconnection: skip automatic peer reconnection
  - disable_node_announcements: skip gossip broadcasts
  - disable_rgs_sync: skip Rapid Gossip Sync updates
  - disable_pathfinding_scores_sync: skip external scores fetching
  - disable_liquidity_handler: skip LSPS1/LSPS2 handling
- Add set_light_mode_minimal() for maximum resource savings
- Add RuntimeSyncIntervals for runtime-adjustable background task intervals
- Add Node::update_sync_intervals() to change intervals while running
- Add battery_saving() preset for reduced battery usage
- Expose all new APIs via UDL bindings for mobile platforms
@coreyphillips coreyphillips self-assigned this Jan 13, 2026
@coreyphillips coreyphillips added the enhancement New feature or request label Jan 13, 2026
coreyphillips and others added 2 commits January 13, 2026 23:54
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@ovitrif
Copy link

ovitrif commented Jan 14, 2026

Release

@ovitrif ovitrif changed the title Feat/light mode runtime sync intervals feat: light mode runtime sync intervals Jan 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants